/* Copyright (C) 1999, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevpdfg.c,v 1.14.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* Graphics state management for pdfwrite driver */ #include "math_.h" #include "string_.h" #include "gx.h" #include "gserrors.h" #include "gsfunc0.h" #include "gsstate.h" #include "gxbitmap.h" /* for gxhttile.h in gzht.h */ #include "gxdht.h" #include "gxfarith.h" /* for gs_sin/cos_degrees */ #include "gxfmap.h" #include "gxht.h" #include "gxistate.h" #include "gzht.h" #include "gdevpdfx.h" #include "gdevpdfg.h" #include "gdevpdfo.h" #include "szlibx.h" /* ---------------- Miscellaneous ---------------- */ /* Reset the graphics state parameters to initial values. */ void pdf_reset_graphics(gx_device_pdf * pdev) { gx_color_index color = 0; /* black on DeviceGray and DeviceRGB */ if(pdev->color_info.num_components == 4) { color = gx_map_cmyk_color((gx_device *)pdev, frac2cv(frac_0), frac2cv(frac_0), frac2cv(frac_0), frac2cv(frac_1)); } color_set_pure(&pdev->fill_color, color); color_set_pure(&pdev->stroke_color, color); pdev->state.flatness = -1; { static const gx_line_params lp_initial = { gx_line_params_initial }; pdev->state.line_params = lp_initial; } pdev->fill_overprint = false; pdev->stroke_overprint = false; pdf_reset_text(pdev); } /* Set the fill or stroke color. */ private int pdf_reset_color(gx_device_pdf * pdev, const gx_drawing_color *pdc, gx_drawing_color * pdcolor, const psdf_set_color_commands_t *ppscc) { int code; /* * In principle, we can set colors in either stream or text * context. However, since we currently enclose all text * strings inside a gsave/grestore, this causes us to lose * track of the color when we leave text context. Therefore, * we require stream context for setting colors. */ #if 0 switch (pdev->context) { case PDF_IN_STREAM: case PDF_IN_TEXT: break; case PDF_IN_NONE: code = pdf_open_page(pdev, PDF_IN_STREAM); goto open; case PDF_IN_STRING: code = pdf_open_page(pdev, PDF_IN_TEXT); open:if (code < 0) return code; } #else code = pdf_open_page(pdev, PDF_IN_STREAM); if (code < 0) return code; #endif code = pdf_put_drawing_color(pdev, pdc, ppscc); if (code >= 0) *pdcolor = *pdc; return code; } int pdf_set_drawing_color(gx_device_pdf * pdev, const gx_drawing_color *pdc, gx_drawing_color * pdcolor, const psdf_set_color_commands_t *ppscc) { if (gx_device_color_equal(pdcolor, pdc)) return 0; return pdf_reset_color(pdev, pdc, pdcolor, ppscc); } int pdf_set_pure_color(gx_device_pdf * pdev, gx_color_index color, gx_drawing_color * pdcolor, const psdf_set_color_commands_t *ppscc) { gx_drawing_color dcolor; if (gx_dc_is_pure(pdcolor) && gx_dc_pure_color(pdcolor) == color) return 0; color_set_pure(&dcolor, color); return pdf_reset_color(pdev, &dcolor, pdcolor, ppscc); } /* Get the (string) name of a separation, */ /* returning a newly allocated string with a / prefixed. */ /****** BOGUS for all but standard separations ******/ int pdf_separation_name(gx_device_pdf *pdev, cos_value_t *pvalue, gs_separation_name sname) { static const char *const snames[] = { gs_ht_separation_name_strings }; static char buf[sizeof(ulong) * 8 / 3 + 2]; /****** BOGUS ******/ const char *str; uint len; byte *chars; if ((ulong)sname < countof(snames)) { str = snames[(int)sname]; } else { /****** TOTALLY BOGUS ******/ sprintf(buf, "S%ld", (ulong)sname); str = buf; } len = strlen(str); chars = gs_alloc_string(pdev->pdf_memory, len + 1, "pdf_separation_name"); if (chars == 0) return_error(gs_error_VMerror); chars[0] = '/'; memcpy(chars + 1, str, len); cos_string_value(pvalue, chars, len + 1); return 0; } /* ------ Support ------ */ /* Print a Boolean using a format. */ private void pprintb1(stream *s, const char *format, bool b) { pprints1(s, format, (b ? "true" : "false")); } /* ---------------- Graphics state updating ---------------- */ /* ------ Functions ------ */ /* Define the maximum size of a Function reference. */ #define MAX_FN_NAME_CHARS 9 /* /Default, /Identity */ #define MAX_FN_CHARS max(MAX_REF_CHARS + 4, MAX_FN_NAME_CHARS) /* * Create and write a Function for a gx_transfer_map. We use this for * transfer, BG, and UCR functions. If check_identity is true, check for * an identity map. Return 1 if the map is the identity map, otherwise * return 0. */ private data_source_proc_access(transfer_map_access); /* check prototype */ private int transfer_map_access(const gs_data_source_t *psrc, ulong start, uint length, byte *buf, const byte **ptr) { const gx_transfer_map *map = (const gx_transfer_map *)psrc->data.str.data; uint i; if (ptr) *ptr = buf; for (i = 0; i < length; ++i) buf[i] = frac2byte(map->values[(uint)start + i]); return 0; } private int transfer_map_access_signed(const gs_data_source_t *psrc, ulong start, uint length, byte *buf, const byte **ptr) { const gx_transfer_map *map = (const gx_transfer_map *)psrc->data.str.data; uint i; *ptr = buf; for (i = 0; i < length; ++i) buf[i] = (byte) ((frac2float(map->values[(uint)start + i]) + 1) * 127.5 + 0.5); return 0; } private int pdf_write_transfer_map(gx_device_pdf *pdev, const gx_transfer_map *map, int range0, bool check_identity, const char *key, char *ids) { gs_memory_t *mem = pdev->pdf_memory; gs_function_Sd_params_t params; static const float domain01[2] = { 0, 1 }; static const int size = transfer_map_size; float range01[2]; gs_function_t *pfn; long id; int code; if (map == 0) { *ids = 0; /* no map */ return 1; } if (check_identity) { /* Check for an identity map. */ int i; if (map->proc == gs_identity_transfer) i = transfer_map_size; else for (i = 0; i < transfer_map_size; ++i) if (map->values[i] != bits2frac(i, log2_transfer_map_size)) break; if (i == transfer_map_size) { strcpy(ids, key); strcat(ids, "/Identity"); return 1; } } params.m = 1; params.Domain = domain01; params.n = 1; range01[0] = range0, range01[1] = 1; params.Range = range01; params.Order = 1; params.DataSource.access = (range0 < 0 ? transfer_map_access_signed : transfer_map_access); params.DataSource.data.str.data = (const byte *)map; /* bogus */ /* DataSource */ params.BitsPerSample = 8; /* could be 16 */ params.Encode = 0; params.Decode = 0; params.Size = &size; code = gs_function_Sd_init(&pfn, ¶ms, mem); if (code < 0) return code; code = pdf_write_function(pdev, pfn, &id); gs_function_free(pfn, false, mem); if (code < 0) return code; sprintf(ids, "%s %ld 0 R", key, id); return 0; } private int pdf_write_transfer(gx_device_pdf *pdev, const gx_transfer_map *map, const char *key, char *ids) { return pdf_write_transfer_map(pdev, map, 0, true, key, ids); } /* ------ Halftones ------ */ /* * Recognize the predefined PDF halftone functions. Note that because the * corresponding PostScript functions use single-precision floats, the * functions used for testing must do the same in order to get identical * results. Currently we only do this for a few of the functions. */ #ifdef __PROTOTYPES__ #define HT_FUNC(name, expr)\ private floatp name(floatp xd, floatp yd) {\ float x = (float)xd, y = (float)yd;\ return d2f(expr);\ } #else #define HT_FUNC(name, expr)\ private floatp name(x, y) floatp x, y; {\ float x = (float)xd, y = (float)yd;\ return d2f(expr);\ } #endif /* * In most versions of gcc (e.g., 2.7.2.3, 2.95.4), return (float)xxx * doesn't actually do the coercion. Force this here. Note that if we * use 'inline', it doesn't work. */ private float d2f(floatp d) { float f = (float)d; return f; } private floatp ht_Round(floatp xf, floatp yf) { float x = (float)xf, y = (float)yf; float xabs = fabs(x), yabs = fabs(y); if (d2f(xabs + yabs) <= 1) return d2f(1 - d2f(d2f(x * x) + d2f(y * y))); xabs -= 1, yabs -= 1; return d2f(d2f(d2f(xabs * xabs) + d2f(yabs * yabs)) - 1); } private floatp ht_Diamond(floatp xf, floatp yf) { float x = (float)xf, y = (float)yf; float xabs = fabs(x), yabs = fabs(y); if (d2f(xabs + yabs) <= 0.75) return d2f(1 - d2f(d2f(x * x) + d2f(y * y))); if (d2f(xabs + yabs) <= d2f(1.23)) return d2f(1 - d2f(d2f(d2f(0.85) * xabs) + yabs)); xabs -= 1, yabs -= 1; return d2f(d2f(d2f(xabs * xabs) + d2f(yabs * yabs)) - 1); } private floatp ht_Ellipse(floatp xf, floatp yf) { float x = (float)xf, y = (float)yf; float xabs = fabs(x), yabs = fabs(y); /* * The PDF Reference, 2nd edition, incorrectly specifies the * computation w = 4 * |x| + 3 * |y| - 3. The PostScript code in the * same book correctly implements w = 3 * |x| + 4 * |y| - 3. */ float w = (float)(d2f(d2f(3 * xabs) + d2f(4 * yabs)) - 3); if (w < 0) { yabs /= 0.75; return d2f(1 - d2f((d2f(x * x) + d2f(yabs * yabs)) / 4)); } if (w > 1) { xabs = 1 - xabs, yabs = d2f(1 - yabs) / 0.75; return d2f(d2f((d2f(xabs * xabs) + d2f(yabs * yabs)) / 4) - 1); } return d2f(0.5 - w); } /* * Most of these are recognized properly even without d2f. We've only * added d2f where it apparently makes a difference. */ private float d2fsin_d(double x) { return d2f(gs_sin_degrees(d2f(x))); } private float d2fcos_d(double x) { return d2f(gs_cos_degrees(d2f(x))); } HT_FUNC(ht_EllipseA, 1 - (x * x + 0.9 * y * y)) HT_FUNC(ht_InvertedEllipseA, x * x + 0.9 * y * y - 1) HT_FUNC(ht_EllipseB, 1 - sqrt(x * x + 0.625 * y * y)) HT_FUNC(ht_EllipseC, 1 - (0.9 * x * x + y * y)) HT_FUNC(ht_InvertedEllipseC, 0.9 * x * x + y * y - 1) HT_FUNC(ht_Line, -fabs(y)) HT_FUNC(ht_LineX, x) HT_FUNC(ht_LineY, y) HT_FUNC(ht_Square, -max(fabs(x), fabs(y))) HT_FUNC(ht_Cross, -min(fabs(x), fabs(y))) HT_FUNC(ht_Rhomboid, (0.9 * fabs(x) + fabs(y)) / 2) HT_FUNC(ht_DoubleDot, (d2fsin_d(x * 360) + d2fsin_d(y * 360)) / 2) HT_FUNC(ht_InvertedDoubleDot, -(d2fsin_d(x * 360) + d2fsin_d(y * 360)) / 2) HT_FUNC(ht_SimpleDot, 1 - d2f(d2f(x * x) + d2f(y * y))) HT_FUNC(ht_InvertedSimpleDot, d2f(d2f(x * x) + d2f(y * y)) - 1) HT_FUNC(ht_CosineDot, (d2fcos_d(x * 180) + d2fcos_d(y * 180)) / 2) HT_FUNC(ht_Double, (d2fsin_d(x * 180) + d2fsin_d(y * 360)) / 2) HT_FUNC(ht_InvertedDouble, -(d2fsin_d(x * 180) + d2fsin_d(y * 360)) / 2) typedef struct ht_function_s { const char *fname; floatp (*proc)(P2(floatp, floatp)); } ht_function_t; private const ht_function_t ht_functions[] = { {"Round", ht_Round}, {"Diamond", ht_Diamond}, {"Ellipse", ht_Ellipse}, {"EllipseA", ht_EllipseA}, {"InvertedEllipseA", ht_InvertedEllipseA}, {"EllipseB", ht_EllipseB}, {"EllipseC", ht_EllipseC}, {"InvertedEllipseC", ht_InvertedEllipseC}, {"Line", ht_Line}, {"LineX", ht_LineX}, {"LineY", ht_LineY}, {"Square", ht_Square}, {"Cross", ht_Cross}, {"Rhomboid", ht_Rhomboid}, {"DoubleDot", ht_DoubleDot}, {"InvertedDoubleDot", ht_InvertedDoubleDot}, {"SimpleDot", ht_SimpleDot}, {"InvertedSimpleDot", ht_InvertedSimpleDot}, {"CosineDot", ht_CosineDot}, {"Double", ht_Double}, {"InvertedDouble", ht_InvertedDouble} }; /* Write each kind of halftone. */ private int pdf_write_spot_function(gx_device_pdf *pdev, const gx_ht_order *porder, long *pid) { /****** DOESN'T HANDLE STRIP HALFTONES ******/ int w = porder->width, h = porder->height; uint num_bits = porder->num_bits; gs_function_Sd_params_t params; static const float domain_spot[4] = { -1, 1, -1, 1 }; static const float range_spot[4] = { -1, 1 }; int size[2]; gs_memory_t *mem = pdev->pdf_memory; /* * Even though the values are logically ushort, we must always store * them in big-endian order, so we access them as bytes. */ byte *values; gs_function_t *pfn; uint i; int code = 0; params.m = 2; params.Domain = domain_spot; params.n = 1; params.Range = range_spot; params.Order = 0; /* default */ /* * We could use 8, 16, or 32 bits per sample to save space, but for * simplicity, we always use 16. */ if (num_bits > 0x10000) return_error(gs_error_rangecheck); params.BitsPerSample = 16; params.Encode = 0; /* * The default Decode array maps the actual data values [1 .. w*h] to a * sub-interval of the Range, but that's OK, since all that matters is * the relative values, not the absolute values. */ params.Decode = 0; size[0] = w; size[1] = h; params.Size = size; /* Create the (temporary) threshold array. */ values = gs_alloc_byte_array(mem, num_bits, 2, "pdf_write_spot_function"); if (values == 0) return_error(gs_error_VMerror); for (i = 0; i < num_bits; ++i) { gs_int_point pt; int value; if ((code = porder->procs->bit_index(porder, i, &pt)) < 0) break; value = pt.y * w + pt.x; /* Always store the values in big-endian order. */ values[i * 2] = (byte)(value >> 8); values[i * 2 + 1] = (byte)value; } data_source_init_bytes(¶ms.DataSource, (const byte *)values, sizeof(*values) * num_bits); if (code >= 0 && (code = gs_function_Sd_init(&pfn, ¶ms, mem)) >= 0 ) { code = pdf_write_function(pdev, pfn, pid); gs_function_free(pfn, false, mem); } gs_free_object(mem, values, "pdf_write_spot_function"); return code; } private int pdf_write_spot_halftone(gx_device_pdf *pdev, const gs_spot_halftone *psht, const gx_ht_order *porder, long *pid) { char trs[17 + MAX_FN_CHARS + 1]; int code = pdf_write_transfer(pdev, porder->transfer, "/TransferFunction", trs); long id, spot_id; stream *s; int i = countof(ht_functions); gs_memory_t *mem = pdev->pdf_memory; if (code < 0) return code; /* * See if we can recognize the spot function, by comparing its sampled * values against those in the order. */ { gs_screen_enum senum; gx_ht_order order; int code; order = *porder; code = gs_screen_order_alloc(&order, mem); if (code < 0) goto notrec; for (i = 0; i < countof(ht_functions); ++i) { floatp (*spot_proc)(P2(floatp, floatp)) = ht_functions[i].proc; gs_point pt; gs_screen_enum_init_memory(&senum, &order, NULL, &psht->screen, mem); while ((code = gs_screen_currentpoint(&senum, &pt)) == 0 && gs_screen_next(&senum, spot_proc(pt.x, pt.y)) >= 0) DO_NOTHING; if (code < 0) continue; /* Compare the bits and levels arrays. */ if (memcmp(order.levels, porder->levels, order.num_levels * sizeof(*order.levels))) continue; if (memcmp(order.bit_data, porder->bit_data, order.num_bits * porder->procs->bit_data_elt_size)) continue; /* We have a match. */ break; } gx_ht_order_release(&order, mem, false); } notrec: if (i == countof(ht_functions)) { /* Create and write a Function for the spot function. */ pdf_write_spot_function(pdev, porder, &spot_id); } *pid = id = pdf_begin_separate(pdev); s = pdev->strm; /* Use the original, requested frequency and angle. */ pprintg2(s, "<screen.frequency, psht->screen.angle); if (i < countof(ht_functions)) pprints1(s, "/SpotFunction/%s", ht_functions[i].fname); else pprintld1(s, "/SpotFunction %ld 0 R", spot_id); stream_puts(s, trs); if (psht->accurate_screens) stream_puts(s, "/AccurateScreens true"); stream_puts(s, ">>\n"); return pdf_end_separate(pdev); } private int pdf_write_screen_halftone(gx_device_pdf *pdev, const gs_screen_halftone *psht, const gx_ht_order *porder, long *pid) { gs_spot_halftone spot; spot.screen = *psht; spot.accurate_screens = false; spot.transfer = 0; spot.transfer_closure.proc = 0; return pdf_write_spot_halftone(pdev, &spot, porder, pid); } private int pdf_write_colorscreen_halftone(gx_device_pdf *pdev, const gs_colorscreen_halftone *pcsht, const gx_device_halftone *pdht, long *pid) { int i; stream *s; long ht_ids[4]; for (i = 0; i < 4; ++i) { int code = pdf_write_screen_halftone(pdev, &pcsht->screens.indexed[i], &pdht->components[i].corder, &ht_ids[i]); if (code < 0) return code; } *pid = pdf_begin_separate(pdev); s = pdev->strm; pprintld1(s, "<transfer, "/TransferFunction", trs); long id = pdf_begin_separate(pdev); stream *s = pdev->strm; pdf_data_writer_t writer; if (code < 0) return code; *pid = id; pprintd2(s, "<width, ptht->height); stream_puts(s, trs); code = pdf_begin_data(pdev, &writer); if (code < 0) return code; stream_write(writer.binary.strm, ptht->thresholds.data, ptht->thresholds.size); return pdf_end_data(&writer); } private int pdf_write_threshold2_halftone(gx_device_pdf *pdev, const gs_threshold2_halftone *ptht, const gx_ht_order *porder, long *pid) { char trs[17 + MAX_FN_CHARS + 1]; int code = pdf_write_transfer(pdev, porder->transfer, "/TransferFunction", trs); long id = pdf_begin_separate(pdev); stream *s = pdev->strm; pdf_data_writer_t writer; if (code < 0) return code; *pid = id; pprintd2(s, "<width, ptht->height); if (ptht->width2 && ptht->height2) pprintd2(s, "/Width2 %d/Height2 %d", ptht->width2, ptht->height2); stream_puts(s, trs); code = pdf_begin_data(pdev, &writer); if (code < 0) return code; s = writer.binary.strm; if (ptht->bytes_per_sample == 2) stream_write(s, ptht->thresholds.data, ptht->thresholds.size); else { /* Expand 1-byte to 2-byte samples. */ int i; for (i = 0; i < ptht->thresholds.size; ++i) { byte b = ptht->thresholds.data[i]; stream_putc(s, b); stream_putc(s, b); } } return pdf_end_data(&writer); } private int pdf_write_multiple_halftone(gx_device_pdf *pdev, const gs_multiple_halftone *pmht, const gx_device_halftone *pdht, long *pid) { stream *s; int i, code; gs_memory_t *mem = pdev->pdf_memory; long *ids; ids = (long *)gs_alloc_byte_array(mem, pmht->num_comp, sizeof(long), "pdf_write_multiple_halftone"); if (ids == 0) return_error(gs_error_VMerror); for (i = 0; i < pmht->num_comp; ++i) { const gs_halftone_component *const phtc = &pmht->components[i]; const gx_ht_order *porder = (pdht->components == 0 ? &pdht->order : &pdht->components[i].corder); switch (phtc->type) { case ht_type_spot: code = pdf_write_spot_halftone(pdev, &phtc->params.spot, porder, &ids[i]); break; case ht_type_threshold: code = pdf_write_threshold_halftone(pdev, &phtc->params.threshold, porder, &ids[i]); break; case ht_type_threshold2: code = pdf_write_threshold2_halftone(pdev, &phtc->params.threshold2, porder, &ids[i]); break; default: code = gs_note_error(gs_error_rangecheck); } if (code < 0) { gs_free_object(mem, ids, "pdf_write_multiple_halftone"); return code; } } *pid = pdf_begin_separate(pdev); s = pdev->strm; stream_puts(s, "<num_comp; ++i) { const gs_halftone_component *const phtc = &pmht->components[i]; cos_value_t value; code = pdf_separation_name(pdev, &value, phtc->cname); if (code < 0) return code; cos_value_write(&value, pdev); gs_free_string(mem, value.contents.chars.data, value.contents.chars.size, "pdf_write_multiple_halftone"); pprintld1(s, " %ld 0 R\n", ids[i]); } stream_puts(s, ">>\n"); gs_free_object(mem, ids, "pdf_write_multiple_halftone"); return pdf_end_separate(pdev); } /* * Update the halftone. This is a separate procedure only for * readability. */ private int pdf_update_halftone(gx_device_pdf *pdev, const gs_imager_state *pis, char *hts) { const gs_halftone *pht = pis->halftone; const gx_device_halftone *pdht = pis->dev_ht; int code; long id; switch (pht->type) { case ht_type_screen: code = pdf_write_screen_halftone(pdev, &pht->params.screen, &pdht->order, &id); break; case ht_type_colorscreen: code = pdf_write_colorscreen_halftone(pdev, &pht->params.colorscreen, pdht, &id); break; case ht_type_spot: code = pdf_write_spot_halftone(pdev, &pht->params.spot, &pdht->order, &id); break; case ht_type_threshold: code = pdf_write_threshold_halftone(pdev, &pht->params.threshold, &pdht->order, &id); break; case ht_type_threshold2: code = pdf_write_threshold2_halftone(pdev, &pht->params.threshold2, &pdht->order, &id); break; case ht_type_multiple: case ht_type_multiple_colorscreen: code = pdf_write_multiple_halftone(pdev, &pht->params.multiple, pdht, &id); break; default: return_error(gs_error_rangecheck); } if (code < 0) return code; sprintf(hts, "/HT %ld 0 R", id); pdev->halftone_id = pis->dev_ht->id; return code; } /* ------ Graphics state updating ------ */ /* Open an ExtGState. */ private int pdf_open_gstate(gx_device_pdf *pdev, pdf_resource_t **ppres) { if (*ppres) return 0; return pdf_begin_resource(pdev, resourceExtGState, gs_no_id, ppres); } /* Finish writing an ExtGState. */ private int pdf_end_gstate(gx_device_pdf *pdev, pdf_resource_t *pres) { if (pres) { int code; stream_puts(pdev->strm, ">>\n"); code = pdf_end_resource(pdev); pres->object->written = true; /* don't write at end of page */ if (code < 0) return code; code = pdf_open_page(pdev, PDF_IN_STREAM); if (code < 0) return code; pprintld1(pdev->strm, "/R%ld gs\n", pdf_resource_id(pres)); } return 0; } /* * Update the transfer functions(s). This is a separate procedure only * for readability. */ private int pdf_update_transfer(gx_device_pdf *pdev, const gs_imager_state *pis, char *trs) { int i; bool multiple = false, update = false; gs_id transfer_ids[4]; int code = 0; for (i = 0; i < 4; ++i) { transfer_ids[i] = pis->set_transfer.indexed[i]->id; if (pdev->transfer_ids[i] != transfer_ids[i]) update = true; if (transfer_ids[i] != transfer_ids[0]) multiple = true; } if (update) { int mask; if (!multiple) { code = pdf_write_transfer(pdev, pis->set_transfer.indexed[0], "/TR", trs); if (code < 0) return code; mask = code == 0; } else { strcpy(trs, "/TR["); mask = 0; for (i = 0; i < 4; ++i) { code = pdf_write_transfer_map(pdev, pis->set_transfer.indexed[i], 0, false, "", trs + strlen(trs)); if (code < 0) return code; mask |= (code == 0) << i; } strcat(trs, "]"); } memcpy(pdev->transfer_ids, transfer_ids, sizeof(pdev->transfer_ids)); pdev->transfer_not_identity = mask; } return code; } /* * Update the current alpha if necessary. Note that because Ghostscript * stores separate opacity and shape alpha, a rangecheck will occur if * both are different from the current setting. */ private int pdf_update_alpha(gx_device_pdf *pdev, const gs_imager_state *pis, const char *ca_format, pdf_resource_t **ppres) { bool ais; floatp alpha; int code; if (pdev->state.opacity.alpha != pis->opacity.alpha) { if (pdev->state.shape.alpha != pis->shape.alpha) return_error(gs_error_rangecheck); ais = false; alpha = pdev->state.opacity.alpha = pis->opacity.alpha; } else if (pdev->state.shape.alpha != pis->shape.alpha) { ais = true; alpha = pdev->state.shape.alpha = pis->shape.alpha; } else return 0; code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; pprintb1(pdev->strm, "/AIS %s", ais); pprintg1(pdev->strm, ca_format, alpha); return 0; } /* * Update the graphics subset common to all high-level drawing operations. */ private int pdf_prepare_drawing(gx_device_pdf *pdev, const gs_imager_state *pis, const char *ca_format, pdf_resource_t **ppres) { int code = 0; if (pdev->CompatibilityLevel >= 1.4) { if (pdev->state.blend_mode != pis->blend_mode) { static const char *const bm_names[] = { GS_BLEND_MODE_NAMES }; code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; pprints1(pdev->strm, "/BM/%s", bm_names[pis->blend_mode]); pdev->state.blend_mode = pis->blend_mode; } code = pdf_update_alpha(pdev, pis, ca_format, ppres); if (code < 0) return code; } else { /* * If the graphics state calls for any transparency functions, * we can't represent them, so return a rangecheck. */ if (pis->opacity.alpha != 1 || pis->opacity.mask != 0 || pis->shape.alpha != 1 || pis->shape.mask != 0 || pis->transparency_stack != 0 ) return_error(gs_error_rangecheck); } /* * We originally thought the remaining items were only needed for * fill and stroke, but in fact they are needed for images as well. */ /* * Update halftone, transfer function, black generation, undercolor * removal, halftone phase, overprint mode, smoothness, blend mode, text * knockout. */ if (pdev->CompatibilityLevel >= 1.2) { gs_int_point phase, dev_phase; char hts[5 + MAX_FN_CHARS + 1], trs[5 + MAX_FN_CHARS * 4 + 6 + 1], bgs[5 + MAX_FN_CHARS + 1], ucrs[6 + MAX_FN_CHARS + 1]; hts[0] = trs[0] = bgs[0] = ucrs[0] = 0; if (pdev->params.PreserveHalftoneInfo && pdev->halftone_id != pis->dev_ht->id ) { code = pdf_update_halftone(pdev, pis, hts); if (code < 0) return code; } if (pdev->params.TransferFunctionInfo == tfi_Preserve) { code = pdf_update_transfer(pdev, pis, trs); if (code < 0) return code; } if (pdev->params.UCRandBGInfo == ucrbg_Preserve) { if (pdev->black_generation_id != pis->black_generation->id) { code = pdf_write_transfer_map(pdev, pis->black_generation, 0, false, "/BG", bgs); if (code < 0) return code; pdev->black_generation_id = pis->black_generation->id; } if (pdev->undercolor_removal_id != pis->undercolor_removal->id) { code = pdf_write_transfer_map(pdev, pis->undercolor_removal, -1, false, "/UCR", ucrs); if (code < 0) return code; pdev->undercolor_removal_id = pis->undercolor_removal->id; } } if (hts[0] || trs[0] || bgs[0] || ucrs[0]) { code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; stream_puts(pdev->strm, hts); stream_puts(pdev->strm, trs); stream_puts(pdev->strm, bgs); stream_puts(pdev->strm, ucrs); } gs_currenthalftonephase((const gs_state *)pis, &phase); gs_currenthalftonephase((const gs_state *)&pdev->state, &dev_phase); if (dev_phase.x != phase.x || dev_phase.y != phase.y) { code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; pprintd2(pdev->strm, "/HTP[%d %d]", phase.x, phase.y); gx_imager_setscreenphase(&pdev->state, phase.x, phase.y, gs_color_select_all); } } if (pdev->CompatibilityLevel >= 1.3) { if (pdev->overprint_mode != pdev->params.OPM) { code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; pprintd1(pdev->strm, "/OPM %d", pdev->params.OPM); pdev->overprint_mode = pdev->params.OPM; } if (pdev->state.smoothness != pis->smoothness) { code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; pprintg1(pdev->strm, "/SM %g", pis->smoothness); pdev->state.smoothness = pis->smoothness; } if (pdev->CompatibilityLevel >= 1.4) { if (pdev->state.text_knockout != pis->text_knockout) { code = pdf_open_gstate(pdev, ppres); if (code < 0) return code; pprintb1(pdev->strm, "/TK %s", pis->text_knockout); pdev->state.text_knockout = pis->text_knockout; } } } return code; } /* Update the graphics state for filling. */ int pdf_prepare_fill(gx_device_pdf *pdev, const gs_imager_state *pis) { pdf_resource_t *pres = 0; int code = pdf_prepare_drawing(pdev, pis, "/ca %g", &pres); if (code < 0) return code; /* Update overprint. */ if (pdev->CompatibilityLevel >= 1.2) { if (pdev->params.PreserveOverprintSettings && pdev->fill_overprint != pis->overprint ) { code = pdf_open_gstate(pdev, &pres); if (code < 0) return code; /* PDF 1.2 only has a single overprint setting. */ if (pdev->CompatibilityLevel < 1.3) { pprintb1(pdev->strm, "/OP %s", pis->overprint); pdev->stroke_overprint = pis->overprint; } else { pprintb1(pdev->strm, "/op %s", pis->overprint); } pdev->fill_overprint = pis->overprint; } } return pdf_end_gstate(pdev, pres); } /* Update the graphics state for stroking. */ int pdf_prepare_stroke(gx_device_pdf *pdev, const gs_imager_state *pis) { pdf_resource_t *pres = 0; int code = pdf_prepare_drawing(pdev, pis, "/CA %g", &pres); if (code < 0) return code; /* Update overprint, stroke adjustment. */ if (pdev->CompatibilityLevel >= 1.2) { if (pdev->params.PreserveOverprintSettings && pdev->stroke_overprint != pis->overprint ) { code = pdf_open_gstate(pdev, &pres); if (code < 0) return code; pprintb1(pdev->strm, "/OP %s", pis->overprint); pdev->stroke_overprint = pis->overprint; /* PDF 1.2 only has a single overprint setting. */ if (pdev->CompatibilityLevel < 1.3) pdev->fill_overprint = pis->overprint; } if (pdev->state.stroke_adjust != pis->stroke_adjust) { code = pdf_open_gstate(pdev, &pres); if (code < 0) return code; pprintb1(pdev->strm, "/SA %s", pis->stroke_adjust); pdev->state.stroke_adjust = pis->stroke_adjust; } } return pdf_end_gstate(pdev, pres); } /* Update the graphics state for an image other than an ImageType 1 mask. */ int pdf_prepare_image(gx_device_pdf *pdev, const gs_imager_state *pis) { pdf_resource_t *pres = 0; int code = pdf_prepare_drawing(pdev, pis, "/ca %g", &pres); if (code < 0) return code; return pdf_end_gstate(pdev, pres); } /* Update the graphics state for an ImageType 1 mask. */ int pdf_prepare_imagemask(gx_device_pdf *pdev, const gs_imager_state *pis, const gx_drawing_color *pdcolor) { int code = pdf_prepare_image(pdev, pis); if (code < 0) return code; return pdf_set_drawing_color(pdev, pdcolor, &pdev->fill_color, &psdf_set_fill_color_commands); }