diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2019-10-15 12:24:12 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2020-08-13 11:26:55 +0200 |
commit | e088156d5b620e5e639580dacf85c6dc13823c74 (patch) | |
tree | 57f5c025e203279944da512166c20bc0521d8ccd /devices/gdevtsep.c | |
download | ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2 ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip |
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'devices/gdevtsep.c')
-rw-r--r-- | devices/gdevtsep.c | 3017 |
1 files changed, 3017 insertions, 0 deletions
diff --git a/devices/gdevtsep.c b/devices/gdevtsep.c new file mode 100644 index 00000000..5fb1025e --- /dev/null +++ b/devices/gdevtsep.c @@ -0,0 +1,3017 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* tiffgray device: 8-bit Gray uncompressed TIFF device + * tiff32nc device: 32-bit CMYK uncompressed TIFF device + * tiffsep device: Generate individual TIFF gray files for each separation + * as well as a 'composite' 32-bit CMYK for the page. + * tiffsep1 device: Generate individual TIFF 1-bit files for each separation + * tiffscaled device:Mono TIFF device (error-diffused downscaled output from + * 8-bit Gray internal rendering) + * tiffscaled8 device:Greyscale TIFF device (downscaled output from + * 8-bit Gray internal rendering) + * tiffscaled24 device:24-bit RGB TIFF device (dithered downscaled output + * from 24-bit RGB internal rendering) + * tiffscaled32 device:32-bit CMYK TIFF device (downscaled output + * from 32-bit CMYK internal rendering) + * tiffscaled4 device:4-bit CMYK TIFF device (dithered downscaled output + * from 32-bit CMYK internal rendering) + */ + +#include "stdint_.h" /* for tiff.h */ +#include "gdevtifs.h" +#include "gdevprn.h" +#include "gdevdevn.h" +#include "gsequivc.h" +#include "gxdht.h" +#include "gxiodev.h" +#include "stdio_.h" +#include "ctype_.h" +#include "gxgetbit.h" +#include "gdevppla.h" +#include "gxdownscale.h" +#include "gp.h" +#include "gstiffio.h" +#include "gscms.h" +#include "gsicc_cache.h" +#include "gxdevsop.h" +#include "gsicc.h" + +/* + * Some of the code in this module is based upon the gdevtfnx.c module. + * gdevtfnx.c has the following message: + * Thanks to Alan Barclay <alan@escribe.co.uk> for donating the original + * version of this code to Ghostscript. + */ + +/* ------ The device descriptors ------ */ + +/* Default X and Y resolution */ +#define X_DPI 72 +#define Y_DPI 72 + +/* ------ The tiffgray device ------ */ + +static dev_proc_print_page(tiffgray_print_page); + +/* FIXME: From initial analysis this is NOT safe for bg_printing, but might be fixable */ + +static const gx_device_procs tiffgray_procs = +prn_color_params_procs(tiff_open, gdev_prn_output_page_seekable, tiff_close, + gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb, + tiff_get_params, tiff_put_params); + +const gx_device_tiff gs_tiffgray_device = { + prn_device_body(gx_device_tiff, tiffgray_procs, "tiffgray", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, + 0, 0, 0, 0, /* Margins */ + 1, 8, 255, 0, 256, 0, tiffgray_print_page), + ARCH_IS_BIG_ENDIAN /* default to native endian (i.e. use big endian iff the platform is so*/, + false, /* default to *not* bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +static int +tiffscaled_spec_op(gx_device *dev_, int op, void *data, int datasize) +{ + if (op == gxdso_supports_iccpostrender) { + return true; + } + return gdev_prn_dev_spec_op(dev_, op, data, datasize); +} + +/* ------ The tiffscaled device ------ */ + +dev_proc_open_device(tiff_open_s); +static dev_proc_print_page(tiffscaled_print_page); +static int tiff_set_icc_color_fields(gx_device_printer *pdev); + +static const gx_device_procs tiffscaled_procs = +prn_color_params_procs(tiff_open, + gdev_prn_output_page_seekable, + tiff_close, + gx_default_gray_map_rgb_color, + gx_default_gray_map_color_rgb, + tiff_get_params_downscale, + tiff_put_params_downscale); + +const gx_device_tiff gs_tiffscaled_device = { + prn_device_body(gx_device_tiff, + tiffscaled_procs, + "tiffscaled", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + 600, 600, /* 600 dpi by default */ + 0, 0, 0, 0, /* Margins */ + 1, /* num components */ + 8, /* bits per sample */ + 255, 0, 256, 0, + tiffscaled_print_page), + ARCH_IS_BIG_ENDIAN,/* default to native endian (i.e. use big endian iff the platform is so */ + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* ------ The tiffscaled8 device ------ */ + +static dev_proc_print_page(tiffscaled8_print_page); + +static const gx_device_procs tiffscaled8_procs = { + tiff_open_s, NULL, NULL, gdev_prn_output_page_seekable, tiff_close, + gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb, NULL, NULL, + NULL, NULL, NULL, NULL, tiff_get_params_downscale, tiff_put_params_downscale, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, tiffscaled_spec_op +}; + +const gx_device_tiff gs_tiffscaled8_device = { + prn_device_body(gx_device_tiff, + tiffscaled8_procs, + "tiffscaled8", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + 600, 600, /* 600 dpi by default */ + 0, 0, 0, 0, /* Margins */ + 1, /* num components */ + 8, /* bits per sample */ + 255, 0, 256, 0, + tiffscaled8_print_page), + ARCH_IS_BIG_ENDIAN,/* default to native endian (i.e. use big endian iff the platform is so */ + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* ------ The tiffscaled24 device ------ */ + +static dev_proc_print_page(tiffscaled24_print_page); + +static const gx_device_procs tiffscaled24_procs = { + tiff_open_s, NULL, NULL, gdev_prn_output_page_seekable, tiff_close, + gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb, NULL, NULL, + NULL, NULL, NULL, NULL, tiff_get_params_downscale, tiff_put_params_downscale, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, tiffscaled_spec_op +}; + +const gx_device_tiff gs_tiffscaled24_device = { + prn_device_body(gx_device_tiff, + tiffscaled24_procs, + "tiffscaled24", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + 600, 600, /* 600 dpi by default */ + 0, 0, 0, 0, /* Margins */ + 3, /* num components */ + 24, /* bits per sample */ + 255, 255, 256, 256, + tiffscaled24_print_page), + ARCH_IS_BIG_ENDIAN,/* default to native endian (i.e. use big endian iff the platform is so */ + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* ------ The tiffscaled32 device ------ */ + +static dev_proc_print_page(tiffscaled32_print_page); + +static const gx_device_procs tiffscaled32_procs = { + tiff_open_s, NULL, NULL, gdev_prn_output_page_seekable, tiff_close, + NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL, + tiff_get_params_downscale_cmyk, tiff_put_params_downscale_cmyk, + cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tiffscaled_spec_op +}; + +const gx_device_tiff gs_tiffscaled32_device = { + prn_device_body(gx_device_tiff, + tiffscaled32_procs, + "tiffscaled32", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + 600, 600, /* 600 dpi by default */ + 0, 0, 0, 0, /* Margins */ + 4, /* num components */ + 32, /* bits per sample */ + 255, 255, 256, 256, + tiffscaled32_print_page), + ARCH_IS_BIG_ENDIAN,/* default to native endian (i.e. use big endian iff the platform is so */ + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* ------ The tiffscaled4 device ------ */ + +static dev_proc_print_page(tiffscaled4_print_page); + +static const gx_device_procs tiffscaled4_procs = { + tiff_open, NULL, NULL, gdev_prn_output_page_seekable, tiff_close, + NULL, cmyk_8bit_map_color_cmyk, NULL, NULL, NULL, NULL, NULL, NULL, + tiff_get_params_downscale_cmyk_ets, tiff_put_params_downscale_cmyk_ets, + cmyk_8bit_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device +}; + +const gx_device_tiff gs_tiffscaled4_device = { + prn_device_body(gx_device_tiff, + tiffscaled4_procs, + "tiffscaled4", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + 600, 600, /* 600 dpi by default */ + 0, 0, 0, 0, /* Margins */ + 4, /* num components */ + 32, /* bits per sample */ + 255, 255, 256, 256, + tiffscaled4_print_page), + ARCH_IS_BIG_ENDIAN,/* default to native endian (i.e. use big endian iff the platform is so */ + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* ------ Private functions ------ */ + +static void +tiff_set_gray_fields(gx_device_printer *pdev, TIFF *tif, + unsigned short bits_per_sample, + int compression, + long max_strip_size) +{ + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + + tiff_set_compression(pdev, tif, compression, max_strip_size); +} + +static int +tiffgray_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + if (!tfdev->UseBigTIFF && tfdev->Compression==COMPRESSION_NONE && + pdev->height > ((unsigned long) 0xFFFFFFFF - gp_ftell(file))/(pdev->width)) /* note width is never 0 in print_page */ + return_error(gs_error_rangecheck); /* this will overflow 32 bits */ + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + tiff_set_gray_fields(pdev, tfdev->tif, 8, tfdev->Compression, tfdev->MaxStripSize); + + return tiff_print_page(pdev, tfdev->tif, 0); +} + +static int +tiffscaled_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + tiff_set_gray_fields(pdev, tfdev->tif, 1, tfdev->Compression, + tfdev->MaxStripSize); + + + return tiff_downscale_and_print_page(pdev, tfdev->tif, + tfdev->downscale.downscale_factor, + tfdev->downscale.min_feature_size, + tfdev->AdjustWidth, + 1, 1, + 0, 0, NULL, + tfdev->downscale.ets); +} + +static int +tiffscaled8_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + if (tfdev->icclink != NULL && tfdev->icclink->num_output != 1) + { + code = tiff_set_icc_color_fields(pdev); + if (code < 0) + return code; + } else { + tiff_set_gray_fields(pdev, tfdev->tif, 8, tfdev->Compression, + tfdev->MaxStripSize); + } + return tiff_downscale_and_print_page(pdev, tfdev->tif, + tfdev->downscale.downscale_factor, + tfdev->downscale.min_feature_size, + tfdev->AdjustWidth, + 8, 1, + 0, 0, NULL, + 0); +} + +static void +tiff_set_rgb_fields(gx_device_tiff *tfdev) +{ + cmm_profile_t *icc_profile; + + if (tfdev->icc_struct->postren_profile != NULL) + icc_profile = tfdev->icc_struct->postren_profile; + else if (tfdev->icc_struct->oi_profile != NULL) + icc_profile = tfdev->icc_struct->oi_profile; + else + icc_profile = tfdev->icc_struct->device_profile[0]; + + switch (icc_profile->data_cs) { + case gsRGB: + TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + break; + case gsCIELAB: + TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB); + break; + default: + TIFFSetField(tfdev->tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + break; + } + TIFFSetField(tfdev->tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); + TIFFSetField(tfdev->tif, TIFFTAG_SAMPLESPERPIXEL, 3); + + tiff_set_compression((gx_device_printer *)tfdev, tfdev->tif, + tfdev->Compression, tfdev->MaxStripSize); +} + + +static int +tiffscaled24_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + if (tfdev->icclink != NULL && tfdev->icclink->num_output != 3) { + code = tiff_set_icc_color_fields(pdev); + if (code < 0) + return code; + } else { + TIFFSetField(tfdev->tif, TIFFTAG_BITSPERSAMPLE, 8); + tiff_set_rgb_fields(tfdev); + } + + return tiff_downscale_and_print_page(pdev, tfdev->tif, + tfdev->downscale.downscale_factor, + tfdev->downscale.min_feature_size, + tfdev->AdjustWidth, + 8, 3, + 0, 0, NULL, + 0); +} + +static void +tiff_set_cmyk_fields(gx_device_printer *pdev, TIFF *tif, + short bits_per_sample, + uint16 compression, + long max_strip_size) +{ + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED); + TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); + + tiff_set_compression(pdev, tif, compression, max_strip_size); +} + +static int +tiffscaled32_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + if (tfdev->icclink != NULL && tfdev->icclink->num_output != 4) + { + code = tiff_set_icc_color_fields(pdev); + if (code < 0) + return code; + } else { + tiff_set_cmyk_fields(pdev, tfdev->tif, 8, tfdev->Compression, + tfdev->MaxStripSize); + } + + return tiff_downscale_and_print_page(pdev, tfdev->tif, + tfdev->downscale.downscale_factor, + tfdev->downscale.min_feature_size, + tfdev->AdjustWidth, + 8, 4, + tfdev->downscale.trap_w, tfdev->downscale.trap_h, + tfdev->downscale.trap_order, + 0); +} + +static int +tiffscaled4_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + tiff_set_cmyk_fields(pdev, + tfdev->tif, + 1, + tfdev->Compression, + tfdev->MaxStripSize); + + return tiff_downscale_and_print_page(pdev, tfdev->tif, + tfdev->downscale.downscale_factor, + tfdev->downscale.min_feature_size, + tfdev->AdjustWidth, + 1, 4, + tfdev->downscale.trap_w, tfdev->downscale.trap_h, + tfdev->downscale.trap_order, + tfdev->downscale.ets); +} + +/* Called when the post render ICC profile is in a different color space +* type compared to the output ICC profile (e.g. cmyk vs rgb) */ +static int +tiff_set_icc_color_fields(gx_device_printer *pdev) +{ + gx_device_tiff *tfdev = (gx_device_tiff *)pdev; + + TIFFSetField(tfdev->tif, TIFFTAG_BITSPERSAMPLE, 8); + switch (tfdev->icclink->num_output) + { + case 1: + tiff_set_gray_fields(pdev, tfdev->tif, 8, tfdev->Compression, + tfdev->MaxStripSize); + break; + case 3: + tiff_set_rgb_fields(tfdev); + break; + case 4: + tiff_set_cmyk_fields(pdev, tfdev->tif, + pdev->color_info.depth / pdev->color_info.num_components, + tfdev->Compression, tfdev->MaxStripSize); + break; + default: + return gs_error_undefined; + } + return 0; +} + +static int +tiffsep_spec_op(gx_device *dev_, int op, void *data, int datasize) +{ + if (op == gxdso_supports_iccpostrender || op == gxdso_supports_devn) { + return true; + } + return gdev_prn_dev_spec_op(dev_, op, data, datasize); +} + +/* ------ The cmyk devices ------ */ + +static dev_proc_print_page(tiffcmyk_print_page); + +#define cmyk_procs(p_map_color_rgb, p_map_cmyk_color)\ + tiff_open, NULL, NULL, gdev_prn_output_page_seekable, tiff_close,\ + NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\ + tiff_get_params, tiff_put_params,\ + p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device + +/* 8-bit-per-plane separated CMYK color. */ + +static const gx_device_procs tiffcmyk_procs = { + cmyk_procs(cmyk_8bit_map_color_cmyk, cmyk_8bit_map_cmyk_color) +}; + +const gx_device_tiff gs_tiff32nc_device = { + prn_device_body(gx_device_tiff, tiffcmyk_procs, "tiff32nc", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, + 0, 0, 0, 0, /* Margins */ + 4, 32, 255, 255, 256, 256, tiffcmyk_print_page), + ARCH_IS_BIG_ENDIAN /* default to native endian (i.e. use big endian iff the platform is so*/, + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + true, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* 16-bit-per-plane separated CMYK color. */ + +static const gx_device_procs tiff64nc_procs = { + cmyk_procs(cmyk_16bit_map_color_cmyk, cmyk_16bit_map_cmyk_color) +}; + +const gx_device_tiff gs_tiff64nc_device = { + prn_device_body(gx_device_tiff, tiff64nc_procs, "tiff64nc", + DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, + X_DPI, Y_DPI, + 0, 0, 0, 0, /* Margins */ + 4, 64, 255, 255, 256, 256, tiffcmyk_print_page), + ARCH_IS_BIG_ENDIAN /* default to native endian (i.e. use big endian iff the platform is so*/, + false, /* default to not bigtiff */ + COMPRESSION_NONE, + TIFF_DEFAULT_STRIP_SIZE, + 0, /* Adjust size */ + false, /* write_datetime */ + GX_DOWNSCALER_PARAMS_DEFAULTS, + 0 +}; + +/* ------ Private functions ------ */ + +static int +tiffcmyk_print_page(gx_device_printer * pdev, gp_file * file) +{ + gx_device_tiff *const tfdev = (gx_device_tiff *)pdev; + int code; + + if (!tfdev->UseBigTIFF && tfdev->Compression==COMPRESSION_NONE && + pdev->height > ((unsigned long) 0xFFFFFFFF - gp_ftell(file))/(pdev->width)) /* note width is never 0 in print_page */ + return_error(gs_error_rangecheck); /* this will overflow 32 bits */ + + code = gdev_tiff_begin_page(tfdev, file); + if (code < 0) + return code; + + tiff_set_cmyk_fields(pdev, + tfdev->tif, + pdev->color_info.depth / pdev->color_info.num_components, + tfdev->Compression, + tfdev->MaxStripSize); + + return tiff_print_page(pdev, tfdev->tif, 0); +} + +/* ---------- The tiffsep device ------------ */ + +#define NUM_CMYK_COMPONENTS 4 +#define MAX_COLOR_VALUE 255 /* We are using 8 bits per colorant */ + +/* The device descriptor */ +static dev_proc_open_device(tiffsep_prn_open); +static dev_proc_close_device(tiffsep_prn_close); +static dev_proc_get_params(tiffsep_get_params); +static dev_proc_put_params(tiffsep_put_params); +static dev_proc_print_page(tiffsep_print_page); +static dev_proc_get_color_mapping_procs(tiffsep_get_color_mapping_procs); +static dev_proc_get_color_comp_index(tiffsep_get_color_comp_index); +static dev_proc_encode_color(tiffsep_encode_color); +static dev_proc_decode_color(tiffsep_decode_color); +static dev_proc_update_spot_equivalent_colors(tiffsep_update_spot_equivalent_colors); +static dev_proc_ret_devn_params(tiffsep_ret_devn_params); +static dev_proc_open_device(tiffsep1_prn_open); +static dev_proc_close_device(tiffsep1_prn_close); +static dev_proc_put_params(tiffsep1_put_params); +static dev_proc_print_page(tiffsep1_print_page); +static dev_proc_fill_path(sep1_fill_path); + +/* common to tiffsep and tiffsepo1 */ +#define tiffsep_devices_common\ + gx_device_common;\ + gx_prn_device_common;\ + /* tiff state for separation files */\ + gp_file *sep_file[GX_DEVICE_COLOR_MAX_COMPONENTS];\ + TIFF *tiff[GX_DEVICE_COLOR_MAX_COMPONENTS]; \ + bool NoSeparationFiles; /* true = no separation files created only a composite file */\ + bool BigEndian; /* true = big endian; false = little endian */\ + bool UseBigTIFF; /* true = output bigtiff, false don't */ \ + bool write_datetime; /* true = write DATETIME tag, false = don't */ \ + bool PrintSpotCMYK; /* true = print CMYK equivalents for spot inks; false = do nothing */\ + uint16 Compression; /* for the separation files, same values as TIFFTAG_COMPRESSION */\ + long MaxStripSize;\ + long BitsPerComponent;\ + int max_spots;\ + bool lock_colorants;\ + gx_downscaler_params downscale;\ + gs_devn_params devn_params; /* DeviceN generated parameters */\ + equivalent_cmyk_color_params equiv_cmyk_colors;\ + bool warning_given /* avoid issuing lots of warnings */ + +/* + * A structure definition for a DeviceN type device + */ +typedef struct tiffsep_device_s { + tiffsep_devices_common; + gp_file *comp_file; /* Underlying file for tiff_comp */ + TIFF *tiff_comp; /* tiff file for comp file */ + gsicc_link_t *icclink; /* link profile if we are doing post rendering */ +} tiffsep_device; + +/* threshold array structure */ +typedef struct threshold_array_s { + int dheight, dwidth; + byte *dstart; +} threshold_array_t; + +typedef struct tiffsep1_device_s { + tiffsep_devices_common; + threshold_array_t thresholds[GX_DEVICE_COLOR_MAX_COMPONENTS + 1]; /* one extra for Default */ + dev_t_proc_fill_path((*fill_path), gx_device); /* we forward to here */ +} tiffsep1_device; + +/* GC procedures */ +static +ENUM_PTRS_WITH(tiffsep_device_enum_ptrs, tiffsep_device *pdev) +{ + if (index < pdev->devn_params.separations.num_separations) + ENUM_RETURN(pdev->devn_params.separations.names[index].data); + ENUM_PREFIX(st_device_printer, + pdev->devn_params.separations.num_separations); + return 0; +} +ENUM_PTRS_END + +static RELOC_PTRS_WITH(tiffsep_device_reloc_ptrs, tiffsep_device *pdev) +{ + RELOC_PREFIX(st_device_printer); + { + int i; + + for (i = 0; i < pdev->devn_params.separations.num_separations; ++i) { + RELOC_PTR(tiffsep_device, devn_params.separations.names[i].data); + } + } +} +RELOC_PTRS_END + +/* Even though tiffsep_device_finalize is the same as gx_device_finalize, */ +/* we need to implement it separately because st_composite_final */ +/* declares all 3 procedures as private. */ +static void +tiffsep_device_finalize(const gs_memory_t *cmem, void *vpdev) +{ + /* We need to deallocate the names. */ + devn_free_params((gx_device*) vpdev); + gx_device_finalize(cmem, vpdev); +} + +gs_private_st_composite_final(st_tiffsep_device, tiffsep_device, + "tiffsep_device", tiffsep_device_enum_ptrs, tiffsep_device_reloc_ptrs, + tiffsep_device_finalize); + +/* + * Macro definition for tiffsep device procedures + */ +#define sep_device_procs(open, close, encode_color, decode_color, update_spot_colors,put_params, fill_path) \ +{ open,\ + gx_default_get_initial_matrix,\ + NULL, /* sync_output */\ + gdev_prn_output_page_seekable, /* output_page */\ + close, /* close */\ + NULL, /* map_rgb_color - not used */\ + tiffsep_decode_color, /* map_color_rgb */\ + NULL, /* fill_rectangle */\ + NULL, /* tile_rectangle */\ + NULL, /* copy_mono */\ + NULL, /* copy_color */\ + NULL, /* draw_line */\ + NULL, /* get_bits */\ + tiffsep_get_params, /* get_params */\ + put_params, /* put_params */\ + NULL, /* map_cmyk_color - not used */\ + NULL, /* get_xfont_procs */\ + NULL, /* get_xfont_device */\ + NULL, /* map_rgb_alpha_color */\ + gx_page_device_get_page_device, /* get_page_device */\ + NULL, /* get_alpha_bits */\ + NULL, /* copy_alpha */\ + NULL, /* get_band */\ + NULL, /* copy_rop */\ + fill_path, /* fill_path */\ + NULL, /* stroke_path */\ + NULL, /* fill_mask */\ + NULL, /* fill_trapezoid */\ + NULL, /* fill_parallelogram */\ + NULL, /* fill_triangle */\ + NULL, /* draw_thin_line */\ + NULL, /* begin_image */\ + NULL, /* image_data */\ + NULL, /* end_image */\ + NULL, /* strip_tile_rectangle */\ + NULL, /* strip_copy_rop */\ + NULL, /* get_clipping_box */\ + NULL, /* begin_typed_image */\ + NULL, /* get_bits_rectangle */\ + NULL, /* map_color_rgb_alpha */\ + NULL, /* create_compositor */\ + NULL, /* get_hardware_params */\ + NULL, /* text_begin */\ + NULL, /* finish_copydevice */\ + NULL, /* begin_transparency_group */\ + NULL, /* end_transparency_group */\ + NULL, /* begin_transparency_mask */\ + NULL, /* end_transparency_mask */\ + NULL, /* discard_transparency_layer */\ + tiffsep_get_color_mapping_procs,/* get_color_mapping_procs */\ + tiffsep_get_color_comp_index, /* get_color_comp_index */\ + encode_color, /* encode_color */\ + decode_color, /* decode_color */\ + NULL, /* pattern_manage */\ + NULL, /* fill_rectangle_hl_color */\ + NULL, /* include_color_space */\ + NULL, /* fill_linear_color_scanline */\ + NULL, /* fill_linear_color_trapezoid */\ + NULL, /* fill_linear_color_triangle */\ + update_spot_colors, /* update_spot_equivalent_colors */\ + tiffsep_ret_devn_params, /* ret_devn_params */\ + NULL, /* fillpage */\ + NULL, /* push_transparency_state */\ + NULL, /* pop_transparency_state */\ + NULL, /* put_image */\ + tiffsep_spec_op /* dev_spec_op */\ +} + + +#define tiffsep_devices_body(dtype, procs, dname, ncomp, pol, depth, mg, mc, sl, cn, print_page, compr)\ + std_device_full_body_type_extended(dtype, &procs, dname,\ + &st_tiffsep_device,\ + (int)((long)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10),\ + (int)((long)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10),\ + X_DPI, Y_DPI,\ + ncomp, /* MaxComponents */\ + ncomp, /* NumComp */\ + pol, /* Polarity */\ + depth, 0, /* Depth, GrayIndex */\ + mg, mc, /* MaxGray, MaxColor */\ + mg + 1, mc + 1, /* DitherGray, DitherColor */\ + sl, /* Linear & Separable? */\ + cn, /* Process color model name */\ + 0, 0, /* offsets */\ + 0, 0, 0, 0 /* margins */\ + ),\ + prn_device_body_rest_(print_page),\ + { 0 }, /* tiff state for separation files */\ + { 0 }, /* separation files */\ + false, /* NoSeparationFiles */\ + ARCH_IS_BIG_ENDIAN /* true = big endian; false = little endian */,\ + false, /* UseBigTIFF */\ + true, /* write_datetime */ \ + false, /* PrintSpotCMYK */\ + compr /* COMPRESSION_* */,\ + TIFF_DEFAULT_STRIP_SIZE,/* MaxStripSize */\ + 8, /* BitsPerComponent */\ + GS_SOFT_MAX_SPOTS, /* max_spots */\ + false, /* Colorants not locked */\ + GX_DOWNSCALER_PARAMS_DEFAULTS + +#define GCIB (ARCH_SIZEOF_GX_COLOR_INDEX * 8) + +/* + * TIFF devices with CMYK process color model and spot color support. + */ +static const gx_device_procs spot_cmyk_procs = + sep_device_procs(tiffsep_prn_open, tiffsep_prn_close, tiffsep_encode_color, tiffsep_decode_color, + tiffsep_update_spot_equivalent_colors, tiffsep_put_params, NULL); + +static const gx_device_procs spot1_cmyk_procs = + sep_device_procs(tiffsep1_prn_open, tiffsep1_prn_close, tiffsep_encode_color, tiffsep_decode_color, + tiffsep_update_spot_equivalent_colors, tiffsep1_put_params, sep1_fill_path); + +const tiffsep_device gs_tiffsep_device = +{ + tiffsep_devices_body(tiffsep_device, spot_cmyk_procs, "tiffsep", ARCH_SIZEOF_GX_COLOR_INDEX, GX_CINFO_POLARITY_SUBTRACTIVE, GCIB, MAX_COLOR_VALUE, MAX_COLOR_VALUE, GX_CINFO_SEP_LIN, "DeviceCMYK", tiffsep_print_page, COMPRESSION_LZW), + /* devn_params specific parameters */ + { 8, /* Ignored - Bits per color */ + DeviceCMYKComponents, /* Names of color model colorants */ + 4, /* Number colorants for CMYK */ + 0, /* MaxSeparations has not been specified */ + -1, /* PageSpotColors has not been specified */ + {0}, /* SeparationNames */ + 0, /* SeparationOrder names */ + {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */ + }, + { true }, /* equivalent CMYK colors for spot colors */ + false, /* warning_given */ +}; + +const tiffsep1_device gs_tiffsep1_device = +{ + tiffsep_devices_body(tiffsep1_device, spot1_cmyk_procs, "tiffsep1", ARCH_SIZEOF_GX_COLOR_INDEX, GX_CINFO_POLARITY_SUBTRACTIVE, GCIB, MAX_COLOR_VALUE, MAX_COLOR_VALUE, GX_CINFO_SEP_LIN, "DeviceCMYK", tiffsep1_print_page, COMPRESSION_CCITTFAX4), + /* devn_params specific parameters */ + { 8, /* Ignored - Bits per color */ + DeviceCMYKComponents, /* Names of color model colorants */ + 4, /* Number colorants for CMYK */ + 0, /* MaxSeparations has not been specified */ + -1, /* PageSpotColors has not been specified */ + {0}, /* SeparationNames */ + 0, /* SeparationOrder names */ + {0, 1, 2, 3, 4, 5, 6, 7 } /* Initial component SeparationOrder */ + }, + { true }, /* equivalent CMYK colors for spot colors */ + false, /* warning_given */ + { {0} }, /* threshold arrays */ + 0, /* fill_path */ +}; + +#undef NC +#undef SL +#undef ENCODE_COLOR +#undef DECODE_COLOR + +static const uint32_t bit_order[32]={ +#if ARCH_IS_BIG_ENDIAN + 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001 +#else + 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000 +#endif + }; + +/* + * The following procedures are used to map the standard color spaces into + * the color components for the tiffsep device. + */ +static void +tiffsep_gray_cs_to_cm(gx_device * dev, frac gray, frac out[]) +{ + int * map = ((tiffsep_device *) dev)->devn_params.separation_order_map; + + gray_cs_to_devn_cm(dev, map, gray, out); +} + +static void +tiffsep_rgb_cs_to_cm(gx_device * dev, const gs_gstate *pgs, + frac r, frac g, frac b, frac out[]) +{ + int * map = ((tiffsep_device *) dev)->devn_params.separation_order_map; + + rgb_cs_to_devn_cm(dev, map, pgs, r, g, b, out); +} + +static void +tiffsep_cmyk_cs_to_cm(gx_device * dev, + frac c, frac m, frac y, frac k, frac out[]) +{ + const gs_devn_params *devn = tiffsep_ret_devn_params(dev); + const int *map = devn->separation_order_map; + int j; + + if (devn->num_separation_order_names > 0) { + + /* We need to make sure to clear everything */ + for (j = 0; j < dev->color_info.num_components; j++) + out[j] = frac_0; + + for (j = 0; j < devn->num_separation_order_names; j++) { + switch (map[j]) { + case 0 : + out[0] = c; + break; + case 1: + out[1] = m; + break; + case 2: + out[2] = y; + break; + case 3: + out[3] = k; + break; + default: + break; + } + } + } else { + cmyk_cs_to_devn_cm(dev, map, c, m, y, k, out); + } +} + +static const gx_cm_color_map_procs tiffsep_cm_procs = { + tiffsep_gray_cs_to_cm, + tiffsep_rgb_cs_to_cm, + tiffsep_cmyk_cs_to_cm +}; + +/* + * These are the handlers for returning the list of color space + * to color model conversion routines. + */ +static const gx_cm_color_map_procs * +tiffsep_get_color_mapping_procs(const gx_device * dev) +{ + return &tiffsep_cm_procs; +} + +/* + * Encode a list of colorant values into a gx_color_index_value. + * With 32 bit gx_color_index values, we simply pack values. + */ +static gx_color_index +tiffsep_encode_color(gx_device *dev, const gx_color_value colors[]) +{ + int bpc = ((tiffsep_device *)dev)->devn_params.bitspercomponent; + gx_color_index color = 0; + int i = 0; + int ncomp = dev->color_info.num_components; + COLROUND_VARS; + + COLROUND_SETUP(bpc); + for (; i < ncomp; i++) { + color <<= bpc; + color |= COLROUND_ROUND(colors[i]); + } + return (color == gx_no_color_index ? color ^ 1 : color); +} + +/* + * Decode a gx_color_index value back to a list of colorant values. + * With 32 bit gx_color_index values, we simply pack values. + */ +static int +tiffsep_decode_color(gx_device * dev, gx_color_index color, gx_color_value * out) +{ + int bpc = ((tiffsep_device *)dev)->devn_params.bitspercomponent; + int drop = sizeof(gx_color_value) * 8 - bpc; + int mask = (1 << bpc) - 1; + int i = 0; + int ncomp = dev->color_info.num_components; + + for (; i < ncomp; i++) { + out[ncomp - i - 1] = (gx_color_value) ((color & mask) << drop); + color >>= bpc; + } + return 0; +} + +/* + * Device proc for updating the equivalent CMYK color for spot colors. + */ +static int +tiffsep_update_spot_equivalent_colors(gx_device * dev, const gs_gstate * pgs) +{ + tiffsep_device * pdev = (tiffsep_device *)dev; + + update_spot_equivalent_cmyk_colors(dev, pgs, + &pdev->devn_params, &pdev->equiv_cmyk_colors); + return 0; +} + +/* + * Device proc for returning a pointer to DeviceN parameter structure + */ +static gs_devn_params * +tiffsep_ret_devn_params(gx_device * dev) +{ + tiffsep_device * pdev = (tiffsep_device *)dev; + + return &pdev->devn_params; +} + +/* Get parameters. We provide a default CRD. */ +static int +tiffsep_get_params(gx_device * pdev, gs_param_list * plist) +{ + tiffsep_device * const pdevn = (tiffsep_device *) pdev; + int code = gdev_prn_get_params(pdev, plist); + int ecode = code; + gs_param_string comprstr; + + if (code < 0) + return code; + + code = devn_get_params(pdev, plist, + &(((tiffsep_device *)pdev)->devn_params), + &(((tiffsep_device *)pdev)->equiv_cmyk_colors)); + if (code < 0) + return code; + + if ((code = param_write_bool(plist, "NoSeparationFiles", &pdevn->NoSeparationFiles)) < 0) + ecode = code; + if ((code = param_write_bool(plist, "BigEndian", &pdevn->BigEndian)) < 0) + ecode = code; + if ((code = param_write_bool(plist, "TIFFDateTime", &pdevn->write_datetime)) < 0) + ecode = code; + if ((code = tiff_compression_param_string(&comprstr, pdevn->Compression)) < 0 || + (code = param_write_string(plist, "Compression", &comprstr)) < 0) + ecode = code; + if ((code = param_write_long(plist, "MaxStripSize", &pdevn->MaxStripSize)) < 0) + ecode = code; + if ((code = param_write_long(plist, "BitsPerComponent", &pdevn->BitsPerComponent)) < 0) + ecode = code; + if ((code = param_write_int(plist, "MaxSpots", &pdevn->max_spots)) < 0) + ecode = code; + if ((code = param_write_bool(plist, "LockColorants", &pdevn->lock_colorants)) < 0) + ecode = code; + if ((code = param_write_bool(plist, "PrintSpotCMYK", &pdevn->PrintSpotCMYK)) < 0) + ecode = code; + if ((code = gx_downscaler_write_params(plist, &pdevn->downscale, + GX_DOWNSCALER_PARAMS_MFS | + GX_DOWNSCALER_PARAMS_TRAP)) < 0) + ecode = code; + + return ecode; +} + +/* Set parameters. We allow setting the number of bits per component. */ +static int +tiffsep_put_params(gx_device * pdev, gs_param_list * plist) +{ + tiffsep_device * const pdevn = (tiffsep_device *) pdev; + int code; + const char *param_name; + gs_param_string comprstr; + long bpc = pdevn->BitsPerComponent; + int max_spots = pdevn->max_spots; + + switch (code = param_read_bool(plist, (param_name = "NoSeparationFiles"), + &pdevn->NoSeparationFiles)) { + default: + param_signal_error(plist, param_name, code); + return code; + case 0: + case 1: + break; + } + /* Read BigEndian option as bool */ + switch (code = param_read_bool(plist, (param_name = "BigEndian"), &pdevn->BigEndian)) { + default: + param_signal_error(plist, param_name, code); + return code; + case 0: + case 1: + break; + } + switch (code = param_read_bool(plist, (param_name = "TIFFDateTime"), &pdevn->write_datetime)) { + default: + param_signal_error(plist, param_name, code); + case 0: + case 1: + break; + } + switch (code = param_read_bool(plist, (param_name = "PrintSpotCMYK"), &pdevn->PrintSpotCMYK)) { + default: + param_signal_error(plist, param_name, code); + return code; + case 0: + case 1: + break; + } + + switch (code = param_read_long(plist, (param_name = "BitsPerComponent"), &bpc)) { + case 0: + if ((bpc == 1) || (bpc == 8)) { + pdevn->BitsPerComponent = bpc; + break; + } + code = gs_error_rangecheck; + default: + param_signal_error(plist, param_name, code); + return code; + case 1: + break; + } + + /* Read Compression */ + switch (code = param_read_string(plist, (param_name = "Compression"), &comprstr)) { + case 0: + if ((code = tiff_compression_id(&pdevn->Compression, &comprstr)) < 0) { + + errprintf(pdevn->memory, "Unknown compression setting\n"); + + param_signal_error(plist, param_name, code); + return code; + } + /* Because pdevn->BitsPerComponent is ignored for tiffsep(1) we have to get + * the value based on whether we're called from tiffsep or tiffsep1 + */ + bpc = (dev_proc(pdev, put_params) == tiffsep1_put_params) ? 1 : 8; + if (!tiff_compression_allowed(pdevn->Compression, bpc)) { + errprintf(pdevn->memory, "Invalid compression setting for this bitdepth\n"); + + param_signal_error(plist, param_name, gs_error_rangecheck); + return_error(gs_error_rangecheck); + } + break; + case 1: + break; + default: + param_signal_error(plist, param_name, code); + return code; + } + switch (code = param_read_long(plist, (param_name = "MaxStripSize"), &pdevn->MaxStripSize)) { + case 0: + /* + * Strip must be large enough to accommodate a raster line. + * If the max strip size is too small, we still write a single + * line per strip rather than giving an error. + */ + if (pdevn->MaxStripSize >= 0) + break; + code = gs_error_rangecheck; + default: + param_signal_error(plist, param_name, code); + return code; + case 1: + break; + } + switch (code = param_read_bool(plist, (param_name = "LockColorants"), + &(pdevn->lock_colorants))) { + case 0: + break; + case 1: + break; + default: + param_signal_error(plist, param_name, code); + return code; + } + switch (code = param_read_int(plist, (param_name = "MaxSpots"), &max_spots)) { + case 0: + if ((max_spots >= 0) || (max_spots <= GS_CLIENT_COLOR_MAX_COMPONENTS-4)) { + pdevn->max_spots = max_spots; + break; + } + emprintf1(pdev->memory, "MaxSpots must be between 0 and %d\n", + GS_CLIENT_COLOR_MAX_COMPONENTS-4); + return_error(gs_error_rangecheck); + case 1: + break; + default: + param_signal_error(plist, param_name, code); + return code; + } + + code = gx_downscaler_read_params(plist, &pdevn->downscale, + GX_DOWNSCALER_PARAMS_MFS | GX_DOWNSCALER_PARAMS_TRAP); + if (code < 0) + return code; + + code = devn_printer_put_params(pdev, plist, + &(pdevn->devn_params), &(pdevn->equiv_cmyk_colors)); + + return(code); +} + +static int +tiffsep1_put_params(gx_device * pdev, gs_param_list * plist) +{ + tiffsep1_device * const tfdev = (tiffsep1_device *) pdev; + int code; + + if ((code = tiffsep_put_params(pdev, plist)) < 0) + return code; + + /* put_params may have changed the fill_path proc -- we need it set to ours */ + if (dev_proc(pdev, fill_path) != sep1_fill_path) { + tfdev->fill_path = dev_proc(pdev, fill_path); + set_dev_proc(pdev, fill_path, sep1_fill_path); + } + return code; + +} + +static void build_comp_to_sep_map(tiffsep_device *, short *); +static int number_output_separations(int, int, int, int); +static int create_separation_file_name(tiffsep_device *, char *, uint, int, bool); +static byte * threshold_from_order( gx_ht_order *, int *, int *, gs_memory_t *); +static int sep1_ht_order_to_thresholds(gx_device *pdev, const gs_gstate *pgs); +static void sep1_free_thresholds(tiffsep1_device *); +dev_proc_fill_path(clist_fill_path); + +/* Open the tiffsep1 device. This will now be using planar buffers so that + we are not limited to 64 bit chunky */ +int +tiffsep1_prn_open(gx_device * pdev) +{ + gx_device_printer *ppdev = (gx_device_printer *)pdev; + tiffsep1_device *pdev_sep = (tiffsep1_device *) pdev; + int code, k; + + /* Use our own warning and error message handlers in libtiff */ + tiff_set_handlers(); + + /* With planar the depth can be more than 64. Update the color + info to reflect the proper depth and number of planes */ + pdev_sep->warning_given = false; + if (pdev_sep->devn_params.page_spot_colors >= 0) { + pdev->color_info.num_components = + (pdev_sep->devn_params.page_spot_colors + + pdev_sep->devn_params.num_std_colorant_names); + if (pdev->color_info.num_components > pdev->color_info.max_components) + pdev->color_info.num_components = pdev->color_info.max_components; + } else { + /* We do not know how many spots may occur on the page. + For this reason we go ahead and allocate the maximum that we + have available. Note, lack of knowledge only occurs in the case + of PS files. With PDF we know a priori the number of spot + colorants. */ + int num_comp = pdev_sep->max_spots + 4; /* Spots + CMYK */ + if (num_comp > GS_CLIENT_COLOR_MAX_COMPONENTS) + num_comp = GS_CLIENT_COLOR_MAX_COMPONENTS; + pdev->color_info.num_components = num_comp; + pdev->color_info.max_components = num_comp; + } + /* Push this to the max amount as a default if someone has not set it */ + if (pdev_sep->devn_params.num_separation_order_names == 0) + for (k = 0; k < GS_CLIENT_COLOR_MAX_COMPONENTS; k++) { + pdev_sep->devn_params.separation_order_map[k] = k; + } + pdev->color_info.depth = pdev->color_info.num_components * + pdev_sep->devn_params.bitspercomponent; + pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN; + code = gdev_prn_open_planar(pdev, true); + while (pdev->child) + pdev = pdev->child; + ppdev = (gx_device_printer *)pdev; + pdev_sep = (tiffsep1_device *)pdev; + + ppdev->file = NULL; + pdev->icc_struct->supports_devn = true; + + /* gdev_prn_open_planae may have changed the fill_path proc -- we need it set to ours */ + if (dev_proc(pdev, fill_path) != sep1_fill_path) { + pdev_sep->fill_path = pdev->procs.fill_path; + set_dev_proc(pdev, fill_path, sep1_fill_path); + } + return code; +} + +/* Close the tiffsep device */ +int +tiffsep1_prn_close(gx_device * pdev) +{ + tiffsep1_device * const tfdev = (tiffsep1_device *) pdev; + int num_dev_comp = tfdev->color_info.num_components; + int num_std_colorants = tfdev->devn_params.num_std_colorant_names; + int num_order = tfdev->devn_params.num_separation_order_names; + int num_spot = tfdev->devn_params.separations.num_separations; + char *name= NULL; + int code = gdev_prn_close(pdev); + short map_comp_to_sep[GX_DEVICE_COLOR_MAX_COMPONENTS]; + int comp_num; + int num_comp = number_output_separations(num_dev_comp, num_std_colorants, + num_order, num_spot); + const char *fmt; + gs_parsed_file_name_t parsed; + + if (code < 0) + return code; + + name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_prn_close(name)"); + if (!name) + return_error(gs_error_VMerror); + + code = gx_parse_output_file_name(&parsed, &fmt, tfdev->fname, + strlen(tfdev->fname), pdev->memory); + if (code < 0) { + goto done; + } + + /* If we are doing separate pages, delete the old default file */ + if (parsed.iodev == iodev_default(pdev->memory)) { /* filename includes "%nnd" */ + char *compname = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_prn_close(compname)"); + if (!compname) { + code = gs_note_error(gs_error_VMerror); + goto done; + } + + if (fmt) { + long count1 = pdev->PageCount; + + while (*fmt != 'l' && *fmt != '%') + --fmt; + if (*fmt == 'l') + gs_sprintf(compname, parsed.fname, count1); + else + gs_sprintf(compname, parsed.fname, (int)count1); + parsed.iodev->procs.delete_file(parsed.iodev, compname); + } else { + parsed.iodev->procs.delete_file(parsed.iodev, tfdev->fname); + } + gs_free_object(pdev->memory, compname, "tiffsep1_prn_close(compname)"); + } + + build_comp_to_sep_map((tiffsep_device *)tfdev, map_comp_to_sep); + /* Close the separation files */ + for (comp_num = 0; comp_num < num_comp; comp_num++ ) { + if (tfdev->sep_file[comp_num] != NULL) { + int sep_num = map_comp_to_sep[comp_num]; + + code = create_separation_file_name((tiffsep_device *)tfdev, name, + gp_file_name_sizeof, sep_num, true); + if (code < 0) { + goto done; + } + code = gx_device_close_output_file(pdev, name, tfdev->sep_file[comp_num]); + if (code >= 0) + code = gs_remove_outputfile_control_path(pdev->memory, name); + if (code < 0) { + goto done; + } + tfdev->sep_file[comp_num] = NULL; + } + if (tfdev->tiff[comp_num]) { + TIFFCleanup(tfdev->tiff[comp_num]); + tfdev->tiff[comp_num] = NULL; + } + } + /* If we have thresholds, free them and clear the pointers */ + if( tfdev->thresholds[0].dstart != NULL) { + sep1_free_thresholds(tfdev); + } + +done: + + if (name) + gs_free_object(pdev->memory, name, "tiffsep1_prn_close(name)"); + return code; +} + + +static int +sep1_fill_path(gx_device * pdev, const gs_gstate * pgs, + gx_path * ppath, const gx_fill_params * params, + const gx_device_color * pdevc, const gx_clip_path * pcpath) +{ + tiffsep1_device * const tfdev = (tiffsep1_device *)pdev; + + /* If we haven't already converted the ht into thresholds, do it now */ + if( tfdev->thresholds[0].dstart == NULL) { + int code = sep1_ht_order_to_thresholds(pdev, pgs); + + if (code < 0) + return code; + } + return (tfdev->fill_path)( pdev, pgs, ppath, params, pdevc, pcpath); +} + +/* + * This routine will check to see if the color component name match those + * that are available amoung the current device's color components. + * + * Parameters: + * dev - pointer to device data structure. + * pname - pointer to name (zero termination not required) + * nlength - length of the name + * + * This routine returns a positive value (0 to n) which is the device colorant + * number if the name is found. It returns GX_DEVICE_COLOR_MAX_COMPONENTS if + * the colorant is not being used due to a SeparationOrder device parameter. + * It returns a negative value if not found. + */ +static int +tiffsep_get_color_comp_index(gx_device * dev, const char * pname, + int name_size, int component_type) +{ + tiffsep_device * pdev = (tiffsep_device *) dev; + int index; + + if (strncmp(pname, "None", name_size) == 0) return -1; + index = devn_get_color_comp_index(dev, + &(pdev->devn_params), &(pdev->equiv_cmyk_colors), + pname, name_size, component_type, ENABLE_AUTO_SPOT_COLORS); + /* This is a one shot deal. That is it will simply post a notice once that + some colorants will be converted due to a limit being reached. It will + not list names of colorants since then I would need to keep track of + which ones I have already mentioned. Also, if someone is fooling with + num_order, then this warning is not given since they should know what + is going on already */ + if (index < 0 && component_type == SEPARATION_NAME && + pdev->warning_given == false && + pdev->devn_params.num_separation_order_names == 0) { + dmlprintf(dev->memory, "**** Max spot colorants reached.\n"); + dmlprintf(dev->memory, "**** Some colorants will be converted to equivalent CMYK values.\n"); + dmlprintf(dev->memory, "**** If this is a Postscript file, try using the -dMaxSpots= option.\n"); + pdev->warning_given = true; + } + return index; +} + +/* + * There can be a conflict if a separation name is used as part of the file + * name for a separation output file. PostScript and PDF do not restrict + * the characters inside separation names. However most operating systems + * have some sort of restrictions. For instance: /, \, and : have special + * meaning under Windows. This implies that there should be some sort of + * escape sequence for special characters. This routine exists as a place + * to put the handling of that escaping. However it is not actually + * implemented. Instead we just map them to '_'. + */ +static void +copy_separation_name(tiffsep_device * pdev, + char * buffer, int max_size, int sep_num, int escape) +{ + int sep_size = pdev->devn_params.separations.names[sep_num].size; + const byte *p = pdev->devn_params.separations.names[sep_num].data; + int r, w; + + /* Previously the code here would simply replace any char that wasn't + * passed by gp_file_name_good_char (and %) with '_'. The grounds for + * gp_file_name_good_char are obvious enough. The reason for '%' is + * that the string gets fed to a printf style consumer later. It had + * problems in that any top bit set char was let through, which upset + * the file handling routines as they assume the filenames are in + * utf-8 format. */ + + /* New code: Copy the name, escaping non gp_file_name_good_char chars, + * % and top bit set chars using %02x format. In addition, if 'escape' + * is set, output % as %% to allow for printf later. + */ + r = 0; + w = 0; + while (r < sep_size && w < max_size-1) + { + int c = p[r++]; + if (c >= 127 || + !gp_file_name_good_char(c) || + c == '%') + { + /* Top bit set, backspace, or char we can't represent on the + * filesystem. */ + if (w + 2 + escape >= max_size-1) + break; + buffer[w++] = '%'; + if (escape) + buffer[w++] = '%'; + buffer[w++] = "0123456789ABCDEF"[c>>4]; + buffer[w++] = "0123456789ABCDEF"[c&15]; + } + else + { + buffer[w++] = c; + } + } + buffer[w] = 0; /* Terminate string */ +} + +/* + * Determine the length of the base file name. If the file name includes + * the extension '.tif', then we remove it from the length of the file + * name. + */ +static int +length_base_file_name(tiffsep_device * pdev, bool *double_f) +{ + int base_filename_length = strlen(pdev->fname); + +#define REMOVE_TIF_FROM_BASENAME 1 +#if REMOVE_TIF_FROM_BASENAME + if (base_filename_length > 4 && + pdev->fname[base_filename_length - 4] == '.' && + toupper(pdev->fname[base_filename_length - 3]) == 'T' && + toupper(pdev->fname[base_filename_length - 2]) == 'I' && + toupper(pdev->fname[base_filename_length - 1]) == 'F') { + base_filename_length -= 4; + *double_f = false; + } + else if (base_filename_length > 5 && + pdev->fname[base_filename_length - 5] == '.' && + toupper(pdev->fname[base_filename_length - 4]) == 'T' && + toupper(pdev->fname[base_filename_length - 3]) == 'I' && + toupper(pdev->fname[base_filename_length - 2]) == 'F' && + toupper(pdev->fname[base_filename_length - 1]) == 'F') { + base_filename_length -= 5; + *double_f = true; + } +#endif +#undef REMOVE_TIF_FROM_BASENAME + + return base_filename_length; +} + +/* + * Create a name for a separation file. + */ +static int +create_separation_file_name(tiffsep_device * pdev, char * buffer, + uint max_size, int sep_num, bool use_sep_name) +{ + bool double_f = false; + uint base_filename_length = length_base_file_name(pdev, &double_f); + + /* + * In most cases it is more convenient if we append '.tif' to the end + * of the file name. + */ +#define APPEND_TIF_TO_NAME 1 +#define SUFFIX_SIZE (4 * APPEND_TIF_TO_NAME) + + memcpy(buffer, pdev->fname, base_filename_length); + buffer[base_filename_length++] = use_sep_name ? '(' : '.'; + buffer[base_filename_length] = 0; /* terminate the string */ + + if (sep_num < pdev->devn_params.num_std_colorant_names) { + if (max_size < strlen(pdev->devn_params.std_colorant_names[sep_num])) + return_error(gs_error_rangecheck); + strcat(buffer, pdev->devn_params.std_colorant_names[sep_num]); + } + else { + sep_num -= pdev->devn_params.num_std_colorant_names; + if (use_sep_name) { + copy_separation_name(pdev, buffer + base_filename_length, + max_size - SUFFIX_SIZE - 2, sep_num, 1); + } else { + /* Max of 10 chars in %d format */ + if (max_size < base_filename_length + 11) + return_error(gs_error_rangecheck); + gs_sprintf(buffer + base_filename_length, "s%d", sep_num); + } + } + if (use_sep_name) + strcat(buffer, ")"); + +#if APPEND_TIF_TO_NAME + if (double_f) { + if (max_size < strlen(buffer) + SUFFIX_SIZE + 1) + return_error(gs_error_rangecheck); + strcat(buffer, ".tiff"); + } + else { + if (max_size < strlen(buffer) + SUFFIX_SIZE) + return_error(gs_error_rangecheck); + strcat(buffer, ".tif"); + } +#endif + return 0; +} + +/* + * Determine the number of output separations for the tiffsep device. + * + * There are several factors which affect the number of output separations + * for the tiffsep device. + * + * Due to limitations on the size of a gx_color_index, we are limited to a + * maximum of 8 colors per pass. Thus the tiffsep device is set to 8 + * components. However this is not usually the number of actual separation + * files to be created. + * + * If the SeparationOrder parameter has been specified, then we use it to + * select the number and which separation files are created. + * + * If the SeparationOrder parameter has not been specified, then we use the + * nuber of process colors (CMYK) and the number of spot colors unless we + * exceed the 8 component maximum for the device. + * + * Note: Unlike most other devices, the tiffsep device will accept more than + * four spot colors. However the extra spot colors will not be imaged + * unless they are selected by the SeparationOrder parameter. (This does + * allow the user to create more than 8 separations by a making multiple + * passes and using the SeparationOrder parameter.) +*/ +static int +number_output_separations(int num_dev_comp, int num_std_colorants, + int num_order, int num_spot) +{ + int num_comp = num_std_colorants + num_spot; + + if (num_comp > num_dev_comp) + num_comp = num_dev_comp; + if (num_order) + num_comp = num_order; + return num_comp; +} + +/* + * This routine creates a list to map the component number to a separation number. + * Values less than 4 refer to the CMYK colorants. Higher values refer to a + * separation number. + * + * This is the inverse of the separation_order_map. + */ +static void +build_comp_to_sep_map(tiffsep_device * pdev, short * map_comp_to_sep) +{ + int num_sep = pdev->devn_params.separations.num_separations; + int num_std_colorants = pdev->devn_params.num_std_colorant_names; + int sep_num; + int num_channels; + + /* since both proc colors and spot colors are packed in same encoded value we + need to have this limit */ + + num_channels = + ( (num_std_colorants + num_sep) < (GX_DEVICE_COLOR_MAX_COMPONENTS) ? (num_std_colorants + num_sep) : (GX_DEVICE_COLOR_MAX_COMPONENTS) ); + + for (sep_num = 0; sep_num < num_channels; sep_num++) { + int comp_num = pdev->devn_params.separation_order_map[sep_num]; + + if (comp_num >= 0 && comp_num < GX_DEVICE_COLOR_MAX_COMPONENTS) + map_comp_to_sep[comp_num] = sep_num; + } +} + +/* Open the tiffsep device. This will now be using planar buffers so that + we are not limited to 64 bit chunky */ +int +tiffsep_prn_open(gx_device * pdev) +{ + gx_device_printer *ppdev = (gx_device_printer *)pdev; + tiffsep_device *pdev_sep = (tiffsep_device *) pdev; + int code, k; + bool force_pdf, force_ps; + cmm_dev_profile_t *profile_struct; + gsicc_rendering_param_t rendering_params; + + /* Use our own warning and error message handlers in libtiff */ + tiff_set_handlers(); + + /* There are 2 approaches to the use of a DeviceN ICC output profile. + One is to simply limit our device to only output the colorants + defined in the output ICC profile. The other is to use the + DeviceN ICC profile to color manage those N colorants and + to let any other separations pass through unmolested. The define + LIMIT_TO_ICC sets the option to limit our device to only the ICC + colorants defined by -sICCOutputColors (or to the ones that are used + as default names if ICCOutputColors is not used). The pass through option + (LIMIT_TO_ICC set to 0) makes life a bit more difficult since we don't + know if the page_spot_colors overlap with any spot colorants that exist + in the DeviceN ICC output profile. Hence we don't know how many planes + to use for our device. This is similar to the issue when processing + a PostScript file. So that I remember, the cases are + DeviceN Profile? limit_icc Result + 0 0 force_pdf 0 force_ps 0 (no effect) + 0 0 force_pdf 0 force_ps 0 (no effect) + 1 0 force_pdf 0 force_ps 1 (colorants not known) + 1 1 force_pdf 1 force_ps 0 (colorants known) + */ + code = dev_proc(pdev, get_profile)((gx_device *)pdev, &profile_struct); + if (profile_struct->spotnames == NULL) { + force_pdf = false; + force_ps = false; + } else { +#if LIMIT_TO_ICC + force_pdf = true; + force_ps = false; +#else + force_pdf = false; + force_ps = true; +#endif + } + + /* For the planar device we need to set up the bit depth of each plane. + For other devices this is handled in check_device_separable where + we compute the bit shift for the components etc. */ + for (k = 0; k < GS_CLIENT_COLOR_MAX_COMPONENTS; k++) { + pdev->color_info.comp_bits[k] = 8; + } + + /* With planar the depth can be more than 64. Update the color + info to reflect the proper depth and number of planes */ + pdev_sep->warning_given = false; + if ((pdev_sep->devn_params.page_spot_colors >= 0 || force_pdf) && !force_ps) { + if (force_pdf) { + /* Use the information that is in the ICC profle. We will be here + anytime that we have limited ourselves to a fixed number + of colorants specified by the DeviceN ICC profile */ + pdev->color_info.num_components = + (pdev_sep->devn_params.separations.num_separations + + pdev_sep->devn_params.num_std_colorant_names); + if (pdev->color_info.num_components > pdev->color_info.max_components) + pdev->color_info.num_components = pdev->color_info.max_components; + /* Limit us only to the ICC colorants */ + pdev->color_info.max_components = pdev->color_info.num_components; + } else { + /* Do not allow the spot count to update if we have specified the + colorants already */ + if (!(pdev_sep->lock_colorants)) { + pdev->color_info.num_components = + (pdev_sep->devn_params.page_spot_colors + + pdev_sep->devn_params.num_std_colorant_names); + if (pdev->color_info.num_components > pdev->color_info.max_components) + pdev->color_info.num_components = pdev->color_info.max_components; + } + } + } else { + /* We do not know how many spots may occur on the page. + For this reason we go ahead and allocate the maximum that we + have available. Note, lack of knowledge only occurs in the case + of PS files. With PDF we know a priori the number of spot + colorants. */ + if (!(pdev_sep->lock_colorants)) { + int num_comp = pdev_sep->max_spots + 4; /* Spots + CMYK */ + if (num_comp > GS_CLIENT_COLOR_MAX_COMPONENTS) + num_comp = GS_CLIENT_COLOR_MAX_COMPONENTS; + pdev->color_info.num_components = num_comp; + pdev->color_info.max_components = num_comp; + } + } + /* Push this to the max amount as a default if someone has not set it */ + if (pdev_sep->devn_params.num_separation_order_names == 0) + for (k = 0; k < GS_CLIENT_COLOR_MAX_COMPONENTS; k++) { + pdev_sep->devn_params.separation_order_map[k] = k; + } + pdev->color_info.depth = pdev->color_info.num_components * + pdev_sep->devn_params.bitspercomponent; + pdev->color_info.separable_and_linear = GX_CINFO_SEP_LIN; + code = gdev_prn_open_planar(pdev, true); + while (pdev->child) + pdev = pdev->child; + ppdev = (gx_device_printer *)pdev; + + ppdev->file = NULL; + pdev->icc_struct->supports_devn = true; + + /* Set up the icc link settings at this time. Only CMYK post render profiles + are allowed */ + code = dev_proc(pdev, get_profile)((gx_device *)pdev, &profile_struct); + if (code < 0) + return_error(gs_error_undefined); + + if (profile_struct->postren_profile != NULL && + profile_struct->postren_profile->data_cs == gsCMYK) { + rendering_params.black_point_comp = gsBLACKPTCOMP_ON; + rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; + rendering_params.override_icc = false; + rendering_params.preserve_black = gsBLACKPRESERVE_OFF; + rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC; + rendering_params.cmm = gsCMM_DEFAULT; + if (profile_struct->oi_profile != NULL) { + pdev_sep->icclink = gsicc_alloc_link_dev(pdev->memory, + profile_struct->oi_profile, profile_struct->postren_profile, + &rendering_params); + } else if (profile_struct->link_profile != NULL) { + pdev_sep->icclink = gsicc_alloc_link_dev(pdev->memory, + profile_struct->link_profile, profile_struct->postren_profile, + &rendering_params); + } else { + pdev_sep->icclink = gsicc_alloc_link_dev(pdev->memory, + profile_struct->device_profile[0], profile_struct->postren_profile, + &rendering_params); + } + /* If it is identity, release it now and set link to NULL */ + if (pdev_sep->icclink->is_identity) { + pdev_sep->icclink->procs.free_link(pdev_sep->icclink); + gsicc_free_link_dev(pdev->memory, pdev_sep->icclink); + pdev_sep->icclink = NULL; + } + } + return code; +} + +static int +tiffsep_close_sep_file(tiffsep_device *tfdev, const char *fn, int comp_num) +{ + int code; + + if (tfdev->tiff[comp_num]) { + TIFFCleanup(tfdev->tiff[comp_num]); + tfdev->tiff[comp_num] = NULL; + } + + code = gx_device_close_output_file((gx_device *)tfdev, + fn, + tfdev->sep_file[comp_num]); + tfdev->sep_file[comp_num] = NULL; + tfdev->tiff[comp_num] = NULL; + + return code; +} + +static int +tiffsep_close_comp_file(tiffsep_device *tfdev, const char *fn) +{ + int code; + + if (tfdev->tiff_comp) { + TIFFCleanup(tfdev->tiff_comp); + tfdev->tiff_comp = NULL; + } + + code = gx_device_close_output_file((gx_device *)tfdev, + fn, + tfdev->comp_file); + tfdev->comp_file = NULL; + + return code; +} + +/* Close the tiffsep device */ +int +tiffsep_prn_close(gx_device * pdev) +{ + tiffsep_device * const pdevn = (tiffsep_device *) pdev; + int num_dev_comp = pdevn->color_info.num_components; + int num_std_colorants = pdevn->devn_params.num_std_colorant_names; + int num_order = pdevn->devn_params.num_separation_order_names; + int num_spot = pdevn->devn_params.separations.num_separations; + short map_comp_to_sep[GX_DEVICE_COLOR_MAX_COMPONENTS]; + char *name = NULL; + int code; + int comp_num; + int num_comp = number_output_separations(num_dev_comp, num_std_colorants, + num_order, num_spot); + if (pdevn->icclink != NULL) { + pdevn->icclink->procs.free_link(pdevn->icclink); + gsicc_free_link_dev(pdevn->memory, pdevn->icclink); + pdevn->icclink = NULL; + } + + name = (char *)gs_alloc_bytes(pdevn->memory, gp_file_name_sizeof, "tiffsep_prn_close(name)"); + if (!name) + return_error(gs_error_VMerror); + + if (pdevn->tiff_comp) { + TIFFCleanup(pdevn->tiff_comp); + pdevn->tiff_comp = NULL; + } + code = gdev_prn_close(pdev); + if (code < 0) { + goto done; + } + + build_comp_to_sep_map(pdevn, map_comp_to_sep); + /* Close the separation files */ + for (comp_num = 0; comp_num < num_comp; comp_num++ ) { + if (pdevn->sep_file[comp_num] != NULL) { + int sep_num = pdevn->devn_params.separation_order_map[comp_num]; + + code = create_separation_file_name(pdevn, name, + gp_file_name_sizeof, sep_num, true); + if (code < 0) { + goto done; + } + code = tiffsep_close_sep_file(pdevn, name, comp_num); + if (code >= 0) + code = gs_remove_outputfile_control_path(pdevn->memory, name); + if (code < 0) { + goto done; + } + } + } + +done: + if (name) + gs_free_object(pdev->memory, name, "tiffsep_prn_close(name)"); + return code; +} + +/* + * Build a CMYK equivalent to a raster line from planar buffer + */ +static void +build_cmyk_raster_line_fromplanar(gs_get_bits_params_t *params, byte * dest, + int width, int num_comp, + cmyk_composite_map * cmyk_map, int num_order, + tiffsep_device * const tfdev) +{ + int pixel, comp_num; + uint temp, cyan, magenta, yellow, black; + cmyk_composite_map * cmyk_map_entry; + byte *start = dest; + + for (pixel = 0; pixel < width; pixel++) { + cmyk_map_entry = cmyk_map; + temp = *(params->data[tfdev->devn_params.separation_order_map[0]] + pixel); + cyan = cmyk_map_entry->c * temp; + magenta = cmyk_map_entry->m * temp; + yellow = cmyk_map_entry->y * temp; + black = cmyk_map_entry->k * temp; + cmyk_map_entry++; + for (comp_num = 1; comp_num < num_comp; comp_num++) { + temp = + *(params->data[tfdev->devn_params.separation_order_map[comp_num]] + pixel); + cyan += cmyk_map_entry->c * temp; + magenta += cmyk_map_entry->m * temp; + yellow += cmyk_map_entry->y * temp; + black += cmyk_map_entry->k * temp; + cmyk_map_entry++; + } + cyan /= frac_1; + magenta /= frac_1; + yellow /= frac_1; + black /= frac_1; + if (cyan > MAX_COLOR_VALUE) + cyan = MAX_COLOR_VALUE; + if (magenta > MAX_COLOR_VALUE) + magenta = MAX_COLOR_VALUE; + if (yellow > MAX_COLOR_VALUE) + yellow = MAX_COLOR_VALUE; + if (black > MAX_COLOR_VALUE) + black = MAX_COLOR_VALUE; + *dest++ = cyan; + *dest++ = magenta; + *dest++ = yellow; + *dest++ = black; + } + /* And now apply the post rendering profile to the scan line if it exists. + In place conversion */ + if (tfdev->icclink != NULL) { + gsicc_bufferdesc_t buffer_desc; + + gsicc_init_buffer(&buffer_desc, tfdev->icclink->num_input, 1, false, + false, false, 0, width * 4, 1, width); + tfdev->icclink->procs.map_buffer(NULL, tfdev->icclink, &buffer_desc, + &buffer_desc, start, start); + } +} + +static void +build_cmyk_raster_line_fromplanar_1bpc(gs_get_bits_params_t *params, byte * dest, + int width, int num_comp, + cmyk_composite_map * cmyk_map, int num_order, + tiffsep_device * const tfdev) +{ + int pixel, comp_num; + uint temp, cyan, magenta, yellow, black; + cmyk_composite_map * cmyk_map_entry; + + for (pixel = 0; pixel < width; pixel++) { + cmyk_map_entry = cmyk_map; + temp = *(params->data[tfdev->devn_params.separation_order_map[0]] + (pixel>>3)); + temp = ((temp<<(pixel & 7))>>7) & 1; + cyan = cmyk_map_entry->c * temp; + magenta = cmyk_map_entry->m * temp; + yellow = cmyk_map_entry->y * temp; + black = cmyk_map_entry->k * temp; + cmyk_map_entry++; + for (comp_num = 1; comp_num < num_comp; comp_num++) { + temp = + *(params->data[tfdev->devn_params.separation_order_map[comp_num]] + (pixel>>3)); + temp = ((temp<<(pixel & 7))>>7) & 1; + cyan += cmyk_map_entry->c * temp; + magenta += cmyk_map_entry->m * temp; + yellow += cmyk_map_entry->y * temp; + black += cmyk_map_entry->k * temp; + cmyk_map_entry++; + } + cyan /= frac_1; + magenta /= frac_1; + yellow /= frac_1; + black /= frac_1; + if (cyan > 1) + cyan = 1; + if (magenta > 1) + magenta = 1; + if (yellow > 1) + yellow = 1; + if (black > 1) + black = 1; + if ((pixel & 1) == 0) + *dest = (cyan<<7) | (magenta<<6) | (yellow<<5) | (black<<4); + else + *dest++ |= (cyan<<3) | (magenta<<2) | (yellow<<1) | black; + } +} +static void +build_cmyk_raster_line_fromplanar_2bpc(gs_get_bits_params_t *params, byte * dest, + int width, int num_comp, + cmyk_composite_map * cmyk_map, int num_order, + tiffsep_device * const tfdev) +{ + int pixel, comp_num; + uint temp, cyan, magenta, yellow, black; + cmyk_composite_map * cmyk_map_entry; + + for (pixel = 0; pixel < width; pixel++) { + cmyk_map_entry = cmyk_map; + temp = *(params->data[tfdev->devn_params.separation_order_map[0]] + (pixel>>2)); + temp = (((temp<<((pixel & 3)<<1))>>6) & 3) * 85; + cyan = cmyk_map_entry->c * temp; + magenta = cmyk_map_entry->m * temp; + yellow = cmyk_map_entry->y * temp; + black = cmyk_map_entry->k * temp; + cmyk_map_entry++; + for (comp_num = 1; comp_num < num_comp; comp_num++) { + temp = + *(params->data[tfdev->devn_params.separation_order_map[comp_num]] + (pixel>>2)); + temp = (((temp<<((pixel & 3)<<1))>>6) & 3) * 85; + cyan += cmyk_map_entry->c * temp; + magenta += cmyk_map_entry->m * temp; + yellow += cmyk_map_entry->y * temp; + black += cmyk_map_entry->k * temp; + cmyk_map_entry++; + } + cyan /= frac_1; + magenta /= frac_1; + yellow /= frac_1; + black /= frac_1; + if (cyan > 3) + cyan = 3; + if (magenta > 3) + magenta = 3; + if (yellow > 3) + yellow = 3; + if (black > 3) + black = 3; + *dest++ = (cyan<<6) | (magenta<<4) | (yellow<<2) | black; + } +} + +static void +build_cmyk_raster_line_fromplanar_4bpc(gs_get_bits_params_t *params, byte * dest, + int width, int num_comp, + cmyk_composite_map * cmyk_map, int num_order, + tiffsep_device * const tfdev) +{ + int pixel, comp_num; + uint temp, cyan, magenta, yellow, black; + cmyk_composite_map * cmyk_map_entry; + + for (pixel = 0; pixel < width; pixel++) { + cmyk_map_entry = cmyk_map; + temp = *(params->data[tfdev->devn_params.separation_order_map[0]] + (pixel>>1)); + if (pixel & 1) + temp >>= 4; + temp &= 15; + cyan = cmyk_map_entry->c * temp; + magenta = cmyk_map_entry->m * temp; + yellow = cmyk_map_entry->y * temp; + black = cmyk_map_entry->k * temp; + cmyk_map_entry++; + for (comp_num = 1; comp_num < num_comp; comp_num++) { + temp = + *(params->data[tfdev->devn_params.separation_order_map[comp_num]] + (pixel>>1)); + if (pixel & 1) + temp >>= 4; + temp &= 15; + cyan += cmyk_map_entry->c * temp; + magenta += cmyk_map_entry->m * temp; + yellow += cmyk_map_entry->y * temp; + black += cmyk_map_entry->k * temp; + cmyk_map_entry++; + } + cyan /= frac_1; + magenta /= frac_1; + yellow /= frac_1; + black /= frac_1; + if (cyan > 15) + cyan = 15; + if (magenta > 15) + magenta = 15; + if (yellow > 15) + yellow = 15; + if (black > 15) + black = 15; + *dest++ = (cyan<<4) | magenta; + *dest++ = (yellow<<4) | black; + } +} + +static int +sep1_ht_order_to_thresholds(gx_device *pdev, const gs_gstate *pgs) +{ + tiffsep1_device * const tfdev = (tiffsep1_device *)pdev; + gs_memory_t *mem = pdev->memory; + + /* If we have thresholds, free them and clear the pointers */ + if( tfdev->thresholds[0].dstart != NULL) { + sep1_free_thresholds(tfdev); + } else { + int nc, j; + gx_ht_order *d_order; + threshold_array_t *dptr; + + if (pgs->dev_ht == NULL) { + emprintf(mem, "sep1_order_to_thresholds: no dev_ht available\n"); + return_error(gs_error_rangecheck); /* error condition */ + } + nc = pgs->dev_ht->num_comp; + for( j=0; j<nc; j++ ) { + d_order = &(pgs->dev_ht->components[j].corder); + dptr = &(tfdev->thresholds[j]); + dptr->dstart = threshold_from_order( d_order, &(dptr->dwidth), &(dptr->dheight), mem); + if( dptr->dstart == NULL ) { + emprintf(mem, + "sep1_order_to_thresholds: conversion to thresholds failed.\n"); + return_error(gs_error_rangecheck); /* error condition */ + } + } + } + return 0; +} + +static void +sep1_free_thresholds(tiffsep1_device *tfdev) +{ + int i; + + for (i=0; i < GX_DEVICE_COLOR_MAX_COMPONENTS + 1; i++) { + threshold_array_t *dptr = &(tfdev->thresholds[i]); + + if (dptr->dstart != NULL) { + gs_free(tfdev->memory, dptr->dstart, dptr->dwidth * dptr->dheight, 1, + "tiffsep1_threshold_array"); + dptr->dstart = NULL; + } + } +} + +/************************************************************************/ +/* This routine generates a threshold matrix for use in */ +/* the color dithering routine from the "order" info in */ +/* the current graphics state. */ +/* */ +/************************************************************************/ + +static byte* +threshold_from_order( gx_ht_order *d_order, int *Width, int *Height, gs_memory_t *memory) +{ + int i, j, l, prev_l; + unsigned char *thresh; + int num_repeat, shift; + + /* We can have simple or complete orders. Simple ones tile the threshold + with shifts. To handle those we simply loop over the number of + repeats making sure to shift columns when we set our threshold values */ + num_repeat = d_order->full_height / d_order->height; + shift = d_order->shift; + +#ifdef DEBUG +if ( gs_debug_c('h') ) { + dmprintf2(memory, " width=%d, height=%d,", + d_order->width, d_order->height ); + dmprintf2(memory, " num_levels=%d, raster=%d\n", + d_order->num_levels, d_order->raster ); +} +#endif + + thresh = (byte *)gs_malloc(memory, d_order->width * d_order->full_height, 1, + "tiffsep1_threshold_array"); + if( thresh == NULL ) { +#ifdef DEBUG + emprintf(memory, "threshold_from_order, malloc failed\n"); + emprintf2(memory, " width=%d, height=%d,", + d_order->width, d_order->height ); + emprintf2(memory, " num_levels=%d, raster=%d\n", + d_order->num_levels, d_order->raster ); +#endif + return thresh ; /* error if allocation failed */ + } + for( i=0; i<d_order->num_bits; i++ ) + thresh[i] = 1; + + *Width = d_order->width; + *Height = d_order->full_height; + + prev_l = 0; + l = 1; + while( l < d_order->num_levels ) { + if( d_order->levels[l] > d_order->levels[prev_l] ) { + int t_level = (256*l)/d_order->num_levels; + +#ifdef DEBUG + if ( gs_debug_c('h') ) + dmprintf2(memory, " level[%3d]=%3d\n", l, d_order->levels[l]); +#endif + for( j=d_order->levels[prev_l]; j<d_order->levels[l]; j++) { + gs_int_point col_row = { 0, 0 }; + int col_kk, row_kk, kk; + + d_order->procs->bit_index(d_order, j, &col_row); +#ifdef DEBUG + if ( gs_debug_c('h') ) + dmprintf3(memory, "row=%2d, col=%2d, t_level=%3d\n", + col_row.y, col_row.x, t_level); +#endif + if( col_row.x < (int)d_order->width ) { + for (kk = 0; kk < num_repeat; kk++) { + row_kk = col_row.y + kk * d_order->height; + col_kk = col_row.x + kk * shift; + col_kk = col_kk % d_order->width; + *(thresh + col_kk + (row_kk * d_order->width)) = t_level; + } + } + } + prev_l = l; + } + l++; + } + +#ifdef DEBUG + if (gs_debug_c('h')) { + for( i=0; i<(int)d_order->height; i++ ) { + dmprintf1(memory, "threshold array row %3d= ", i); + for( j=(int)d_order->width-1; j>=0; j-- ) + dmprintf1(memory, "%3d ", *(thresh+j+(i*d_order->width)) ); + dmprintf(memory, "\n"); + } + } +#endif + + return thresh; +} + + /* + * This function prints out CMYK value with separation name for every + * separation. Where the original alternate colour space was DeviceCMYK, and the output + * ICC profile is CMYK, no transformation takes place. Where the original alternate space + * was not DeviceCMYK, the colour management system will be used to generate CMYK values + * from the original tint transform. + * NB if the output profile is DeviceN then we will use the DeviceCMYK profile to map the + * equivalents, *not* the DeviceN profile. This is a peculiar case..... + */ +static int +print_cmyk_equivalent_colors(tiffsep_device *tfdev, int num_comp, cmyk_composite_map *cmyk_map) +{ + int comp_num; + char *name = (char *)gs_alloc_bytes(tfdev->memory, gp_file_name_sizeof, + "tiffsep_print_cmyk_equivalent_colors(name)"); + + if (!name) { + return_error(gs_error_VMerror); + } + + for (comp_num = 0; comp_num < num_comp; comp_num++) { + int sep_num = tfdev->devn_params.separation_order_map[comp_num]; + + if (sep_num < tfdev->devn_params.num_std_colorant_names) { + if (gp_file_name_sizeof < strlen(tfdev->devn_params.std_colorant_names[sep_num])) { + if (name) + gs_free_object(tfdev->memory, name, "tiffsep_print_cmyk_equivalent_colors(name)"); + return_error(gs_error_rangecheck); + } + strcpy(name, tfdev->devn_params.std_colorant_names[sep_num]); + } else { + sep_num -= tfdev->devn_params.num_std_colorant_names; + if (gp_file_name_sizeof < tfdev->devn_params.separations.names[sep_num].size) { + if (name) + gs_free_object(tfdev->memory, name, "tiffsep_print_cmyk_equivalent_colors(name)"); + return_error(gs_error_rangecheck); + } + + memcpy(name, (char *)tfdev->devn_params.separations.names[sep_num].data, tfdev->devn_params.separations.names[sep_num].size); + name[tfdev->devn_params.separations.names[sep_num].size] = '\0'; + } + + dmlprintf5(tfdev->memory, "%%%%SeparationColor: \"%s\" 100%% ink = %hd %hd %hd %hd CMYK\n", + name, + cmyk_map[comp_num].c, + cmyk_map[comp_num].m, + cmyk_map[comp_num].y, + cmyk_map[comp_num].k); + } + + if (name) { + gs_free_object(tfdev->memory, name, "tiffsep_print_cmyk_equivalent_colors(name)"); + } + + return 0; +} + +/* + * Output the image data for the tiff separation (tiffsep) device. The data + * for the tiffsep device is written in separate planes to separate files. + * + * The DeviceN parameters (SeparationOrder, SeparationColorNames, and + * MaxSeparations) are applied to the tiffsep device. + */ +static int +tiffsep_print_page(gx_device_printer * pdev, gp_file * file) +{ + tiffsep_device * const tfdev = (tiffsep_device *)pdev; + int num_std_colorants = tfdev->devn_params.num_std_colorant_names; + int num_order = tfdev->devn_params.num_separation_order_names; + int num_spot = tfdev->devn_params.separations.num_separations; + int num_comp, comp_num, sep_num, code = 0, code1 = 0; + cmyk_composite_map cmyk_map[GX_DEVICE_COLOR_MAX_COMPONENTS]; + char *name = NULL; + bool double_f = false; + int base_filename_length = length_base_file_name(tfdev, &double_f); + int save_depth = pdev->color_info.depth; + int save_numcomps = pdev->color_info.num_components; + const char *fmt; + gs_parsed_file_name_t parsed; + int plane_count = 0; /* quiet compiler */ + int factor = tfdev->downscale.downscale_factor; + int mfs = tfdev->downscale.min_feature_size; + int dst_bpc = tfdev->BitsPerComponent; + gx_downscaler_t ds; + int width = gx_downscaler_scale(tfdev->width, factor); + int height = gx_downscaler_scale(tfdev->height, factor); + + name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep_print_page(name)"); + if (!name) + return_error(gs_error_VMerror); + + /* Print the names of the spot colors */ + if (num_order == 0) { + for (sep_num = 0; sep_num < num_spot; sep_num++) { + copy_separation_name(tfdev, name, + gp_file_name_sizeof - base_filename_length - SUFFIX_SIZE, sep_num, 0); + dmlprintf1(pdev->memory, "%%%%SeparationName: %s\n", name); + } + } + + /* + * Since different pages may have different spot colors, if this is for a + * page after Page 1, we require that each output file is unique with a "fmt" + * (i.e. %d) as part of the filename. We create individual separation files + * for each page of the input. + * Since the TIFF lib requires seeakable files, /dev/null or nul: are + * not allowed (as they are with the psdcmyk devices). + */ + code = gx_parse_output_file_name(&parsed, &fmt, tfdev->fname, + strlen(tfdev->fname), pdev->memory); + if (code < 0 || (fmt == NULL && tfdev->PageCount > 0)) { + emprintf(tfdev->memory, + "\nUse of the %%d format is required to output more than one page to tiffsep.\n" + "See doc/Devices.htm#TIFF for details.\n\n"); + code = gs_note_error(gs_error_ioerror); + } + /* Write the page directory for the CMYK equivalent file. */ + if (!tfdev->comp_file) { + pdev->color_info.depth = dst_bpc*4; /* Create directory for 32 bit cmyk */ + if (!tfdev->UseBigTIFF && tfdev->Compression==COMPRESSION_NONE && + height > ((unsigned long) 0xFFFFFFFF - (file ? gp_ftell(file) : 0))/(width*4)) { /* note width is never 0 in print_page */ + dmprintf(pdev->memory, "CMYK composite file would be too large! Reduce resolution or enable compression.\n"); + return_error(gs_error_rangecheck); /* this will overflow 32 bits */ + } + + code = gx_device_open_output_file((gx_device *)pdev, pdev->fname, true, true, &(tfdev->comp_file)); + if (code < 0) { + goto done; + } + + tfdev->tiff_comp = tiff_from_filep(pdev, pdev->dname, tfdev->comp_file, tfdev->BigEndian, tfdev->UseBigTIFF); + if (!tfdev->tiff_comp) { + code = gs_note_error(gs_error_invalidfileaccess); + goto done; + } + + } + code = tiff_set_fields_for_printer(pdev, tfdev->tiff_comp, factor, 0, tfdev->write_datetime); + + if (dst_bpc == 1 || dst_bpc == 8) { + tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, dst_bpc, tfdev->Compression, tfdev->MaxStripSize); + } + else { + /* Catch-all just for safety's sake */ + tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, dst_bpc, COMPRESSION_NONE, tfdev->MaxStripSize); + } + + pdev->color_info.depth = save_depth; + if (code < 0) { + goto done; + } + + /* Set up the separation output files */ + num_comp = number_output_separations( tfdev->color_info.num_components, + num_std_colorants, num_order, num_spot); + + if (!tfdev->NoSeparationFiles && !num_order && num_comp < num_std_colorants + num_spot) { + dmlprintf(pdev->memory, "Warning: skipping one or more colour separations, see: Devices.htm#TIFF\n"); + } + + if (!tfdev->NoSeparationFiles) { + for (comp_num = 0; comp_num < num_comp; comp_num++) { + int sep_num = tfdev->devn_params.separation_order_map[comp_num]; + + code = create_separation_file_name(tfdev, name, gp_file_name_sizeof, + sep_num, true); + if (code < 0) { + goto done; + } + + /* + * Close the old separation file if we are creating individual files + * for each page. + */ + if (tfdev->sep_file[comp_num] != NULL && fmt != NULL) { + code = tiffsep_close_sep_file(tfdev, name, comp_num); + if (code >= 0) + code = gs_remove_outputfile_control_path(tfdev->memory, name); + if (code < 0) + return code; + } + /* Open the separation file, if not already open */ + if (tfdev->sep_file[comp_num] == NULL) { + code = gs_add_outputfile_control_path(tfdev->memory, name); + if (code < 0) { + goto done; + } + code = gx_device_open_output_file((gx_device *)pdev, name, + true, true, &(tfdev->sep_file[comp_num])); + if (code < 0) { + goto done; + } + tfdev->tiff[comp_num] = tiff_from_filep(pdev, name, + tfdev->sep_file[comp_num], + tfdev->BigEndian, tfdev->UseBigTIFF); + if (!tfdev->tiff[comp_num]) { + code = gs_note_error(gs_error_ioerror); + goto done; + } + } + + pdev->color_info.depth = dst_bpc; /* Create files for 8 bit gray */ + pdev->color_info.num_components = 1; + if (!tfdev->UseBigTIFF && tfdev->Compression == COMPRESSION_NONE && + height * 8 / dst_bpc > ((unsigned long)0xFFFFFFFF - (file ? gp_ftell(file) : 0)) / width) /* note width is never 0 in print_page */ + { + code = gs_note_error(gs_error_rangecheck); /* this will overflow 32 bits */ + goto done; + } + + + code = tiff_set_fields_for_printer(pdev, tfdev->tiff[comp_num], factor, 0, tfdev->write_datetime); + tiff_set_gray_fields(pdev, tfdev->tiff[comp_num], dst_bpc, tfdev->Compression, tfdev->MaxStripSize); + pdev->color_info.depth = save_depth; + pdev->color_info.num_components = save_numcomps; + if (code < 0) { + goto done; + } + } + } + + build_cmyk_map((gx_device*) tfdev, num_comp, &tfdev->equiv_cmyk_colors, cmyk_map); + if (tfdev->PrintSpotCMYK) { + code = print_cmyk_equivalent_colors(tfdev, num_comp, cmyk_map); + if (code < 0) { + goto done; + } + } + + { + int raster_plane = bitmap_raster(width * 8); + byte *planes[GS_CLIENT_COLOR_MAX_COMPONENTS] = { 0 }; + int cmyk_raster = width * NUM_CMYK_COMPONENTS; + int pixel, y; + byte * sep_line; + int plane_index; + int offset_plane = 0; + + sep_line = + gs_alloc_bytes(pdev->memory, cmyk_raster, "tiffsep_print_page"); + if (!sep_line) { + code = gs_note_error(gs_error_VMerror); + goto done; + } + + if (!tfdev->NoSeparationFiles) + for (comp_num = 0; comp_num < num_comp; comp_num++ ) + TIFFCheckpointDirectory(tfdev->tiff[comp_num]); + TIFFCheckpointDirectory(tfdev->tiff_comp); + + /* Write the page data. */ + { + gs_get_bits_params_t params; + int byte_width; + + /* Return planar data */ + params.options = (GB_RETURN_POINTER | GB_RETURN_COPY | + GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD | + GB_PACKING_PLANAR | GB_COLORS_NATIVE | GB_ALPHA_NONE); + params.x_offset = 0; + params.raster = bitmap_raster(width * pdev->color_info.depth); + + if (num_order > 0) { + /* In this case, there was a specification for a separation + color order, which indicates what colorants we will + actually creat individual separation files for. We need + to allocate for the standard colorants. This is due to the + fact that even when we specify a single spot colorant, we + still create the composite CMYK output file. */ + for (comp_num = 0; comp_num < num_std_colorants; comp_num++) { + planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane, + "tiffsep_print_page"); + params.data[comp_num] = planes[comp_num]; + if (params.data[comp_num] == NULL) { + code = gs_note_error(gs_error_VMerror); + goto cleanup; + } + } + offset_plane = num_std_colorants; + /* Now we need to make sure that we do not allocate extra + planes if any of the colorants in the order list are + one of the standard colorant names */ + plane_index = plane_count = num_std_colorants; + for (comp_num = 0; comp_num < num_comp; comp_num++) { + int temp_pos; + + temp_pos = tfdev->devn_params.separation_order_map[comp_num]; + if (temp_pos >= num_std_colorants) { + /* We have one that is not a standard colorant name + so allocate a new plane */ + planes[plane_count] = gs_alloc_bytes(pdev->memory, raster_plane, + "tiffsep_print_page"); + /* Assign the new plane to the appropriate position */ + params.data[plane_index] = planes[plane_count]; + if (params.data[plane_index] == NULL) { + code = gs_note_error(gs_error_VMerror); + goto cleanup; + } + plane_count += 1; + } else { + /* Assign params.data with the appropriate std. + colorant plane position */ + params.data[plane_index] = planes[temp_pos]; + } + plane_index += 1; + } + } else { + /* Sep color order number was not specified so just render all + the planes that we can */ + for (comp_num = 0; comp_num < num_comp; comp_num++) { + planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane, + "tiffsep_print_page"); + params.data[comp_num] = planes[comp_num]; + if (params.data[comp_num] == NULL) { + code = gs_note_error(gs_error_VMerror); + goto cleanup; + } + } + } + code = gx_downscaler_init_planar_trapped(&ds, (gx_device *)pdev, ¶ms, + num_comp, factor, mfs, 8, dst_bpc, + tfdev->downscale.trap_w, tfdev->downscale.trap_h, + tfdev->downscale.trap_order); + if (code < 0) + goto cleanup; + byte_width = (width * dst_bpc + 7)>>3; + for (y = 0; y < height; ++y) { + code = gx_downscaler_get_bits_rectangle(&ds, ¶ms, y); + if (code < 0) + goto cleanup; + /* Write separation data (tiffgray format) */ + if (!tfdev->NoSeparationFiles) { + for (comp_num = 0; comp_num < num_comp; comp_num++) { + byte *src; + byte *dest = sep_line; + + if (num_order > 0) { + src = params.data[tfdev->devn_params.separation_order_map[comp_num]]; + } + else + src = params.data[comp_num]; + for (pixel = 0; pixel < byte_width; pixel++, dest++, src++) + *dest = MAX_COLOR_VALUE - *src; /* Gray is additive */ + TIFFWriteScanline(tfdev->tiff[comp_num], (tdata_t)sep_line, y, 0); + } + } + /* Write CMYK equivalent data */ + switch(dst_bpc) + { + default: + case 8: + build_cmyk_raster_line_fromplanar(¶ms, sep_line, width, + num_comp, cmyk_map, num_order, + tfdev); + break; + case 4: + build_cmyk_raster_line_fromplanar_4bpc(¶ms, sep_line, width, + num_comp, cmyk_map, num_order, + tfdev); + break; + case 2: + build_cmyk_raster_line_fromplanar_2bpc(¶ms, sep_line, width, + num_comp, cmyk_map, num_order, + tfdev); + break; + case 1: + build_cmyk_raster_line_fromplanar_1bpc(¶ms, sep_line, width, + num_comp, cmyk_map, num_order, + tfdev); + break; + } + TIFFWriteScanline(tfdev->tiff_comp, (tdata_t)sep_line, y, 0); + } +cleanup: + if (num_order > 0) { + /* Free up the standard colorants if num_order was set. + In this process, we need to make sure that none of them + were the standard colorants. plane_count should have + the sum of the std. colorants plus any non-standard + ones listed in separation color order */ + for (comp_num = 0; comp_num < plane_count; comp_num++) { + gs_free_object(pdev->memory, planes[comp_num], + "tiffsep_print_page"); + } + } else { + for (comp_num = 0; comp_num < num_comp; comp_num++) { + gs_free_object(pdev->memory, planes[comp_num + offset_plane], + "tiffsep_print_page"); + } + } + gx_downscaler_fin(&ds); + gs_free_object(pdev->memory, sep_line, "tiffsep_print_page"); + } + code1 = code; + if (!tfdev->NoSeparationFiles) { + for (comp_num = 0; comp_num < num_comp; comp_num++) { + TIFFWriteDirectory(tfdev->tiff[comp_num]); + if (fmt) { + int sep_num = tfdev->devn_params.separation_order_map[comp_num]; + + code = create_separation_file_name(tfdev, name, gp_file_name_sizeof, sep_num, false); + if (code < 0) { + code1 = code; + continue; + } + code = tiffsep_close_sep_file(tfdev, name, comp_num); + if (code >= 0) + code = gs_remove_outputfile_control_path(tfdev->memory, name); + if (code < 0) { + code1 = code; + } + } + } + } + TIFFWriteDirectory(tfdev->tiff_comp); + if (fmt) { + code = tiffsep_close_comp_file(tfdev, pdev->fname); + } + if (code1 < 0) { + code = code1; + } + } + +done: + if (name) + gs_free_object(pdev->memory, name, "tiffsep_print_page(name)"); + return code; +} + +/* + * Output the image data for the tiff separation (tiffsep1) device. The data + * for the tiffsep1 device is written in separate planes to separate files. + * + * The DeviceN parameters (SeparationOrder, SeparationColorNames, and + * MaxSeparations) are applied to the tiffsep device. + */ +static int +tiffsep1_print_page(gx_device_printer * pdev, gp_file * file) +{ + tiffsep1_device * const tfdev = (tiffsep1_device *)pdev; + int num_std_colorants = tfdev->devn_params.num_std_colorant_names; + int num_order = tfdev->devn_params.num_separation_order_names; + int num_spot = tfdev->devn_params.separations.num_separations; + int num_comp, comp_num, code = 0, code1 = 0; + short map_comp_to_sep[GX_DEVICE_COLOR_MAX_COMPONENTS]; + char *name = NULL; + int save_depth = pdev->color_info.depth; + int save_numcomps = pdev->color_info.num_components; + const char *fmt; + gs_parsed_file_name_t parsed; + int non_encodable_count = 0; + + if (tfdev->thresholds[0].dstart == NULL) + return_error(gs_error_rangecheck); + + name = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_print_page(name)"); + if (!name) + return_error(gs_error_VMerror); + + build_comp_to_sep_map((tiffsep_device *)tfdev, map_comp_to_sep); + + /* + * Since different pages may have different spot colors, if this is for a + * page after Page 1, we require that each output file is unique with a "fmt" + * (i.e. %d) as part of the filename. We create individual separation files + * for each page of the input. + * Since the TIFF lib requires seeakable files, /dev/null or nul: are + * not allowed (as they are with the psdcmyk devices). + */ + code = gx_parse_output_file_name(&parsed, &fmt, tfdev->fname, + strlen(tfdev->fname), pdev->memory); + if (code < 0 || (fmt == NULL && tfdev->PageCount > 0)) { + emprintf(tfdev->memory, + "\nUse of the %%d format is required to output more than one page to tiffsep1.\n" + "See doc/Devices.htm#TIFF for details.\n\n"); + code = gs_note_error(gs_error_ioerror); + } + /* If the output file is on disk and the name contains a page #, */ + /* then delete the previous file. */ + if (pdev->file != NULL && parsed.iodev == iodev_default(pdev->memory) && fmt) { + long count1 = pdev->PageCount; + char *compname = (char *)gs_alloc_bytes(pdev->memory, gp_file_name_sizeof, "tiffsep1_print_page(compname)"); + if (!compname) { + code = gs_note_error(gs_error_VMerror); + goto done; + } + + gx_device_close_output_file((gx_device *)pdev, pdev->fname, pdev->file); + pdev->file = NULL; + + while (*fmt != 'l' && *fmt != '%') + --fmt; + if (*fmt == 'l') + gs_sprintf(compname, parsed.fname, count1); + else + gs_sprintf(compname, parsed.fname, (int)count1); + parsed.iodev->procs.delete_file(parsed.iodev, compname); + /* we always need an open printer (it will get deleted in tiffsep1_prn_close */ + code = gdev_prn_open_printer((gx_device *)pdev, 1); + + gs_free_object(pdev->memory, compname, "tiffsep_print_page(compname)"); + if (code < 0) { + goto done; + } + } + + /* Set up the separation output files */ + num_comp = number_output_separations( tfdev->color_info.num_components, + num_std_colorants, num_order, num_spot); + for (comp_num = 0; comp_num < num_comp; comp_num++ ) { + int sep_num = map_comp_to_sep[comp_num]; + + code = create_separation_file_name((tiffsep_device *)tfdev, name, + gp_file_name_sizeof, sep_num, true); + if (code < 0) { + goto done; + } + + /* Open the separation file, if not already open */ + if (tfdev->sep_file[comp_num] == NULL) { + code = gs_add_outputfile_control_path(tfdev->memory, name); + if (code < 0) { + goto done; + } + code = gx_device_open_output_file((gx_device *)pdev, name, + true, true, &(tfdev->sep_file[comp_num])); + if (code < 0) { + goto done; + } + tfdev->tiff[comp_num] = tiff_from_filep(pdev, name, + tfdev->sep_file[comp_num], + tfdev->BigEndian, tfdev->UseBigTIFF); + if (!tfdev->tiff[comp_num]) { + code = gs_note_error(gs_error_ioerror); + goto done; + } + } + + pdev->color_info.depth = 8; /* Create files for 8 bit gray */ + pdev->color_info.num_components = 1; + code = tiff_set_fields_for_printer(pdev, tfdev->tiff[comp_num], 1, 0, tfdev->write_datetime); + tiff_set_gray_fields(pdev, tfdev->tiff[comp_num], 1, tfdev->Compression, tfdev->MaxStripSize); + pdev->color_info.depth = save_depth; + pdev->color_info.num_components = save_numcomps; + if (code < 0) { + goto done; + } + + } /* end initialization of separation files */ + + + { /* Get the expanded contone line, halftone and write out the dithered separations */ + byte *planes[GS_CLIENT_COLOR_MAX_COMPONENTS]; + int width = tfdev->width; + int raster_plane = bitmap_raster(width * 8); + int dithered_raster = ((7 + width) / 8) + ARCH_SIZEOF_LONG; + int pixel, y; + gs_get_bits_params_t params; + gs_int_rect rect; + /* the dithered_line is assumed to be 32-bit aligned by the alloc */ + uint32_t *dithered_line = (uint32_t *)gs_alloc_bytes(pdev->memory, dithered_raster, + "tiffsep1_print_page"); + + memset(planes, 0, sizeof(*planes) * GS_CLIENT_COLOR_MAX_COMPONENTS); + + /* Return planar data */ + params.options = (GB_RETURN_POINTER | GB_RETURN_COPY | + GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD | + GB_PACKING_PLANAR | GB_COLORS_NATIVE | GB_ALPHA_NONE); + params.x_offset = 0; + params.raster = bitmap_raster(width * pdev->color_info.depth); + + code = 0; + for (comp_num = 0; comp_num < num_comp; comp_num++) { + planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane, + "tiffsep1_print_page"); + if (planes[comp_num] == NULL) { + code = gs_error_VMerror; + break; + } + } + + if (code < 0 || dithered_line == NULL) { + code = gs_note_error(gs_error_VMerror); + goto cleanup; + } + + for (comp_num = 0; comp_num < num_comp; comp_num++ ) + TIFFCheckpointDirectory(tfdev->tiff[comp_num]); + + rect.p.x = 0; + rect.q.x = pdev->width; + /* Loop for the lines */ + for (y = 0; y < pdev->height; ++y) { + rect.p.y = y; + rect.q.y = y + 1; + /* We have to reset the pointers since get_bits_rect will have moved them */ + for (comp_num = 0; comp_num < num_comp; comp_num++) + params.data[comp_num] = planes[comp_num]; + code = (*dev_proc(pdev, get_bits_rectangle))((gx_device *)pdev, &rect, ¶ms, NULL); + if (code < 0) + break; + + /* Dither the separation and write it out */ + for (comp_num = 0; comp_num < num_comp; comp_num++ ) { + +/***** #define SKIP_HALFTONING_FOR_TIMING *****/ /* uncomment for timing test */ +#ifndef SKIP_HALFTONING_FOR_TIMING + + /* + * Define 32-bit writes by default. Testing shows that while this is more + * complex code, it runs measurably and consistently faster than the more + * obvious 8-bit code. The 8-bit code is kept to help future optimization + * efforts determine what affects tight loop optimization. Subtracting the + * time when halftoning is skipped shows that the 32-bit halftoning is + * 27% faster. + */ +#define USE_32_BIT_WRITES + byte *thresh_line_base = tfdev->thresholds[comp_num].dstart + + ((y % tfdev->thresholds[comp_num].dheight) * + tfdev->thresholds[comp_num].dwidth) ; + byte *thresh_ptr = thresh_line_base; + byte *thresh_limit = thresh_ptr + tfdev->thresholds[comp_num].dwidth; + byte *src = params.data[comp_num]; +#ifdef USE_32_BIT_WRITES + uint32_t *dest = dithered_line; + uint32_t val = 0; + const uint32_t *mask = &bit_order[0]; +#else /* example 8-bit code */ + byte *dest = dithered_line; + byte val = 0; + byte mask = 0x80; +#endif /* USE_32_BIT_WRITES */ + + for (pixel = 0; pixel < width; pixel++, src++) { +#ifdef USE_32_BIT_WRITES + if (*src < *thresh_ptr++) + val |= *mask; + if (++mask == &(bit_order[32])) { + *dest++ = val; + val = 0; + mask = &bit_order[0]; + } +#else /* example 8-bit code */ + if (*src < *thresh_ptr++) + val |= mask; + mask >>= 1; + if (mask == 0) { + *dest++ = val; + val = 0; + mask = 0x80; + } +#endif /* USE_32_BIT_WRITES */ + if (thresh_ptr >= thresh_limit) + thresh_ptr = thresh_line_base; + } /* end src pixel loop - collect last bits if any */ + /* the following relies on their being enough 'pad' in dithered_line */ +#ifdef USE_32_BIT_WRITES + if (mask != &bit_order[0]) { + *dest = val; + } +#else /* example 8-bit code */ + if (mask != 0x80) { + *dest = val; + } +#endif /* USE_32_BIT_WRITES */ +#endif /* SKIP_HALFTONING_FOR_TIMING */ + TIFFWriteScanline(tfdev->tiff[comp_num], (tdata_t)dithered_line, y, 0); + } /* end component loop */ + } + /* Update the strip data */ + for (comp_num = 0; comp_num < num_comp; comp_num++ ) { + TIFFWriteDirectory(tfdev->tiff[comp_num]); + if (fmt) { + int sep_num = map_comp_to_sep[comp_num]; + + code = create_separation_file_name((tiffsep_device *)tfdev, name, gp_file_name_sizeof, sep_num, false); + if (code < 0) { + code1 = code; + continue; + } + code = tiffsep_close_sep_file((tiffsep_device *)tfdev, name, comp_num); + if (code >= 0) + code = gs_remove_outputfile_control_path(tfdev->memory, name); + if (code < 0) { + code1 = code; + } + } + } + code = code1; + + /* free any allocations and exit with code */ +cleanup: + gs_free_object(pdev->memory, dithered_line, "tiffsep1_print_page"); + for (comp_num = 0; comp_num < num_comp; comp_num++) { + gs_free_object(pdev->memory, planes[comp_num], "tiffsep1_print_page"); + } + } + /* + * If we have any non encodable pixels then signal an error. + */ + if (non_encodable_count) { + dmlprintf1(pdev->memory, "WARNING: Non encodable pixels = %d\n", non_encodable_count); + code = gs_note_error(gs_error_rangecheck); + } + +done: + if (name) + gs_free_object(pdev->memory, name, "tiffsep1_print_page(name)"); + return code; +} + +/* The tiffscaled contone devices have to be able to change their color model +to allow a more flexible use of the post render ICC profile with the output +intent. For example, if we are wanting to render to a CMYK intermediate +output intent but we want the output to be in sRGB then we need to use +-sDEVICE=tiffscaled24 -dUsePDFX3Profile -sOutputICCProfile=default_cmyk.icc +-sPostRenderProfile=srgb.icc . This should then render to a temporary +buffer the is in the OutputIntent color space and then be converted to +sRGB. This should look like the result we get when we go out to the +tiffscaled32 device. This is in contrast to the command line +sDEVICE=tiffscaled24 -dUsePDFX3Profile -sPostRenderProfile=srgb.icc which would +end up using the output intent as a proofing profile. The results may be similar +but not exact as overprint and spot colors would not appear correctly due to the +additive color model during rendering. */ +int +tiff_open_s(gx_device *pdev) +{ + int code; + + /* Take care of any color model changes now */ + if (pdev->icc_struct->postren_profile != NULL && + pdev->icc_struct->device_profile[0]->num_comps != pdev->color_info.num_components && + pdev->color_info.depth == 8 * pdev->color_info.num_components) { + + code = gx_change_color_model((gx_device*)pdev, + pdev->icc_struct->device_profile[0]->num_comps, 8); + if (code < 0) + return code; + + /* Reset the device procs */ + memset(&(pdev->procs), 0, sizeof(pdev->procs)); + switch (pdev->icc_struct->device_profile[0]->num_comps) { + case 1: + pdev->procs = tiffscaled8_procs; + pdev->color_info.dither_colors = 0; + pdev->color_info.max_color = 0; + break; + case 3: + pdev->procs = tiffscaled24_procs; + pdev->color_info.dither_colors = 0; + pdev->color_info.max_color = 0; + break; + case 4: + pdev->procs = tiffscaled32_procs; + pdev->color_info.dither_colors = 256; + pdev->color_info.max_color = 255; + break; + } + check_device_separable(pdev); + gx_device_fill_in_procs(pdev); + } + return tiff_open(pdev); +} + |