From ad547a6b5986815fda458221149728d9d9ab1d87 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 25 Feb 2021 19:22:25 +0000 Subject: restore Build,TODO from r57911 git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/dvipdfm-x/pdfcolor.c | 1560 ++++++++++++++++++++++++++++++++ 1 file changed, 1560 insertions(+) create mode 100644 Build/source/texk/dvipdfm-x/pdfcolor.c (limited to 'Build/source/texk/dvipdfm-x/pdfcolor.c') diff --git a/Build/source/texk/dvipdfm-x/pdfcolor.c b/Build/source/texk/dvipdfm-x/pdfcolor.c new file mode 100644 index 00000000000..a0f49834bdf --- /dev/null +++ b/Build/source/texk/dvipdfm-x/pdfcolor.c @@ -0,0 +1,1560 @@ +/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. + + Copyright (C) 2002-2020 by Jin-Hwan Cho and Shunsaku Hirata, + the dvipdfmx project team. + + Copyright (C) 1998, 1999 by Mark A. Wicks + + 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 USA. +*/ + +/* No page independence here... + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "system.h" +#include "mem.h" +#include "error.h" + +#include "dpxconf.h" +#include "dpxfile.h" + +#include "pdfdoc.h" +#include "pdfdev.h" +#include "pdfresource.h" + +#include "pdfcolor.h" + +int +pdf_color_type (const pdf_color *color) +{ + ASSERT(color); + + return color->type; +} + +int +pdf_color_rgbcolor (pdf_color *color, double r, double g, double b) +{ + ASSERT(color); + + if (r < 0.0 || r > 1.0) { + WARN("Invalid color value specified: red=%g", r); + return -1; + } + if (g < 0.0 || g > 1.0) { + WARN("Invalid color value specified: green=%g", g); + return -1; + } + if (b < 0.0 || b > 1.0) { + WARN("Invalid color value specified: blue=%g", b); + return -1; + } + color->values[0] = r; + color->values[1] = g; + color->values[2] = b; + + color->res_id = -1; + color->type = PDF_COLORSPACE_TYPE_RGB; + color->num_components = 3; + + color->spot_color_name = NULL; + + return 0; +} + +int +pdf_color_cmykcolor (pdf_color *color, + double c, double m, double y, double k) +{ + ASSERT(color); + + if (c < 0.0 || c > 1.0) { + WARN("Invalid color value specified: cyan=%g", c); + return -1; + } + if (m < 0.0 || m > 1.0) { + WARN("Invalid color value specified: magenta=%g", m); + return -1; + } + if (y < 0.0 || y > 1.0) { + WARN("Invalid color value specified: yellow=%g", y); + return -1; + } + if (k < 0.0 || k > 1.0) { + WARN("Invalid color value specified: black=%g", k); + return -1; + } + + color->values[0] = c; + color->values[1] = m; + color->values[2] = y; + color->values[3] = k; + + color->res_id = -1; + color->type = PDF_COLORSPACE_TYPE_CMYK; + color->num_components = 4; + + color->spot_color_name = NULL; + + return 0; +} + +int +pdf_color_graycolor (pdf_color *color, double g) +{ + ASSERT(color); + + if (g < 0.0 || g > 1.0) { + WARN("Invalid color value specified: gray=%g", g); + return -1; + } + + color->values[0] = g; + + color->res_id = -1; + color->type = PDF_COLORSPACE_TYPE_GRAY; + color->num_components = 1; + + color->spot_color_name = NULL; + + return 0; +} + +int +pdf_color_spotcolor (pdf_color *color, char* name, double c) +{ + ASSERT(color); + + if (c < 0.0 || c > 1.0) { + WARN("Invalid color value specified: grade=%g", c); + return -1; + } + + color->values[0] = c; + color->values[1] = 0.0; /* Dummy */ + + color->res_id = -1; /* ??? */ + color->type = PDF_COLORSPACE_TYPE_SPOT; + color->num_components = 2; + + color->spot_color_name = name; + + return 0; +} + + +void +pdf_color_copycolor (pdf_color *color1, const pdf_color *color2) +{ + ASSERT(color1 && color2); + + memcpy(color1, color2, sizeof(pdf_color)); +} + +/* Brighten up a color. f == 0 means no change, f == 1 means white. */ +void +pdf_color_brighten_color (pdf_color *dst, const pdf_color *src, double f) +{ + ASSERT(dst && src); + + if (src->type != PDF_COLORSPACE_TYPE_RGB && + src->type != PDF_COLORSPACE_TYPE_CMYK && + src->type != PDF_COLORSPACE_TYPE_GRAY) { + pdf_color_copycolor(dst, src); + return; + } + + if (f == 1.0) { + pdf_color_white(dst); + } else { + double f0, f1; + int n; + + pdf_color_copycolor(dst, src); + n = src->num_components; + f1 = n == 4 ? 0.0 : f; /* n == 4 is CMYK, others are RGB and Gray */ + f0 = 1.0-f; + + while (n--) + dst->values[n] = f0 * src->values[n] + f1; + } +} + +/* TODO: remove "is_white"... + * pdfdoc.c only use this but not necessary if we have a flag have_bgcolor + * to indicate if bg color was set. + */ +int +pdf_color_is_white (const pdf_color *color) +{ + int n; + double f; + + ASSERT(color); + + switch (color->type) { + case PDF_COLORSPACE_TYPE_GRAY: + case PDF_COLORSPACE_TYPE_RGB: + f = 1.0; + break; + case PDF_COLORSPACE_TYPE_CMYK: + f = 0.0; + break; + default: + return 0; + } + + n = color->num_components; + while (n--) + if (color->values[n] != f) + return 0; + + return 1; +} + +/* TODO: make_resource_name() in pdfresource.c with configurable prefix. */ +int +pdf_color_set_color (const pdf_color *color, char *buffer, size_t buffer_len, char mask) +{ + int len = 0; + int i; + + { + size_t estimate = 0; + if (color->num_components > 0) { + estimate += 5 * (color->num_components + 1) + 4; /* Assuming color values [0, 1]... */ + } + estimate += strlen(" /DeiceGray CS"); + if (estimate + 1 > buffer_len) { + WARN("Not enough buffer space allocated for writing set_color op..."); + return 0; + } + } + + switch (pdf_color_type(color)) { + case PDF_COLORSPACE_TYPE_DEVICEGRAY: + { + len += sprintf(buffer+len, " /DeviceGray %c%c", 'C' | mask, 'S' | mask); + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c%c", 'S' | mask, 'C' | mask); + } + break; + case PDF_COLORSPACE_TYPE_DEVICERGB: + { + len += sprintf(buffer+len, " /DeviceRGB %c%c", 'C' | mask, 'S' | mask); + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c%c", 'S' | mask, 'C' | mask); + } + break; + case PDF_COLORSPACE_TYPE_DEVICECMYK: + { + len += sprintf(buffer+len, " /DeviceCMYK %c%c", 'C' | mask, 'S' | mask); + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c%c", 'S' | mask, 'C' | mask); + } + break; + case PDF_COLORSPACE_TYPE_GRAY: + { + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c", 'G' | mask); + } + break; + case PDF_COLORSPACE_TYPE_RGB: + { + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c%c", 'R' | mask, 'G' | mask); + } + break; + case PDF_COLORSPACE_TYPE_CMYK: + { + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c", 'K' | mask); + } + break; + case PDF_COLORSPACE_TYPE_SPOT: + { + len = sprintf(buffer+len, + " /%s %c%c %g %c%c", + color->spot_color_name, + 'C' | mask, 'S' | mask, + ROUND(color->values[0], 0.001), + 'S' | mask, 'C' | mask); + } + break; + case PDF_COLORSPACE_TYPE_CALGRAY: + case PDF_COLORSPACE_TYPE_CALRGB: + case PDF_COLORSPACE_TYPE_LAB: + case PDF_COLORSPACE_TYPE_INDEXED: + { + char res_name[16]; + + snprintf(res_name, 16, "XC%d", color->res_id & 0xffff); /* TODO: Upper 16bits for category ID. See, pdfresource.c */ + res_name[15] = 0; + len += sprintf(buffer+len, " /%s %c%c", res_name, 'C' | mask, 'S' | mask); + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c%c", 'S' | mask, 'C' | mask); + pdf_doc_add_page_resource("ColorSpace", res_name, pdf_get_resource_reference(color->res_id)); + } + break; + case PDF_COLORSPACE_TYPE_PATTERN: + { + char res_name[16]; + + if (color->res_id < 0) { + len += sprintf(buffer+len, " /Pattern %c%c", 'C' | mask, 'S' | mask); + /* no color value but just a name */ + } else { + snprintf(res_name, 16, "XC%d", color->res_id & 0xffff); /* TODO: Upper 16bits for category ID. See, pdfresource.c */ + res_name[15] = 0; + len += sprintf(buffer+len, " /%s %c%c", res_name, 'C' | mask, 'S' | mask); + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + pdf_doc_add_page_resource("ColorSpace", res_name, pdf_get_resource_reference(color->res_id)); + } + snprintf(res_name, 16, "XP%d", color->pattern_id & 0xffff); /* TODO: see below */ + res_name[15] = 0; + len += sprintf(buffer+len, " /%s %c%c%c", res_name, 'S' | mask, 'C' | mask, 'N' | mask); + + pdf_doc_add_page_resource("Pattern", res_name, pdf_get_resource_reference(color->pattern_id)); + } + break; + default: + { + char res_name[16]; + + snprintf(res_name, 8, "XC%d", color->res_id & 0xffff); /* TODO: Upper 16bits for category ID. See, pdfresource.c */ + res_name[8] = 0; + len += sprintf(buffer+len, " /%s %c%c", res_name, 'C' | mask, 'S' | mask); + for (i = 0; i < color->num_components; i++) { + len += sprintf(buffer+len, " %g", ROUND(color->values[i], 0.001)); + } + len += sprintf(buffer+len, " %c%c%c", 'S' | mask, 'C' | mask, 'N' | mask); + pdf_doc_add_page_resource("ColorSpace", res_name, pdf_get_resource_reference(color->res_id)); + } + } + + return len; +} + +pdf_color current_fill = { + -1, + PDF_COLORSPACE_TYPE_GRAY, + 1, + NULL, + {0.0}, + -1 +}; + +pdf_color current_stroke = { + -1, + PDF_COLORSPACE_TYPE_GRAY, + 1, + NULL, + {0.0}, + -1 +}; + +/* + * This routine is not a real color matching. + * TODO: This can be removed too? + * Maybe we are currently always forcing installing color. + */ +int +pdf_color_compare (const pdf_color *color1, const pdf_color *color2) +{ + int n; + + if (color1->type != PDF_COLORSPACE_TYPE_GRAY || + color1->type != PDF_COLORSPACE_TYPE_RGB || + color1->type != PDF_COLORSPACE_TYPE_CMYK || + color1->type != PDF_COLORSPACE_TYPE_SPOT || + color2->type != PDF_COLORSPACE_TYPE_GRAY || + color2->type != PDF_COLORSPACE_TYPE_RGB || + color2->type != PDF_COLORSPACE_TYPE_CMYK || + color2->type != PDF_COLORSPACE_TYPE_SPOT || + color1->type != color2->type) { + return -1; + } + + n = color1->num_components; + while (--n >= 0) { + if (color1->values[n] != color2->values[n]) + return -1; + } + if (color1->spot_color_name && color2->spot_color_name) + return strcmp(color1->spot_color_name, color2->spot_color_name); + + return 0; +} + +/* Dvipdfm special */ +pdf_color default_color = { + -1, + PDF_COLORSPACE_TYPE_GRAY, + 1, + NULL, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + -1 +}; + +#define DEV_COLOR_STACK_MAX 128 + +static struct { + int current; + pdf_color stroke[DEV_COLOR_STACK_MAX]; + pdf_color fill[DEV_COLOR_STACK_MAX]; +} color_stack = { + 0, +}; + +void +pdf_color_clear_stack (void) +{ + if (color_stack.current > 0) { + WARN("You've mistakenly made a global color change within nested colors."); + } + while (color_stack.current--) { + if (color_stack.stroke[color_stack.current].spot_color_name) + RELEASE(color_stack.stroke[color_stack.current].spot_color_name); + if (color_stack.fill[color_stack.current].spot_color_name) + RELEASE(color_stack.fill[color_stack.current].spot_color_name); + } + color_stack.current = 0; + pdf_color_black(color_stack.stroke); + pdf_color_black(color_stack.fill); + return; +} + +void +pdf_color_set (pdf_color *sc, pdf_color *fc) +{ + pdf_color_copycolor(&color_stack.stroke[color_stack.current], sc); + pdf_color_copycolor(&color_stack.fill[color_stack.current], fc); + pdf_dev_reset_color(1); +} + +void +pdf_color_push (pdf_color *sc, pdf_color *fc) +{ + if (color_stack.current >= DEV_COLOR_STACK_MAX-1) { + WARN("Color stack overflow. Just ignore."); + } else { + color_stack.current++; + pdf_color_set(sc, fc); + } + return; +} + +void +pdf_color_pop (void) +{ + if (color_stack.current <= 0) { + WARN("Color stack underflow. Just ignore."); + } else { + color_stack.current--; + pdf_dev_reset_color(1); + } + return; +} + +void +pdf_color_get_current (pdf_color **sc, pdf_color **fc) +{ + *sc = &color_stack.stroke[color_stack.current]; + *fc = &color_stack.fill[color_stack.current]; + return; +} + +#if 0 +/* BUG (20060330): color change does not effect on the next page. + * The problem is due to the part of grestore because it restores + * the color values in the state of gsave which are not correct + * if the color values are changed inside of a page. + */ +void +pdf_dev_preserve_color (void) +{ + if (color_stack.current > 0) { + current_stroke = color_stack.stroke[color_stack.current]; + current_fill = color_stack.fill[color_stack.current]; + } +} +#endif + +/***************************** COLOR SPACE *****************************/ +/* Currently only for ICCBased color loaded while reading images */ +static int pdf_colorspace_defineresource (const char *ident, int subtype, void *cdata, pdf_obj *resource); +static int pdf_colorspace_findresource (const char *ident, int subtype, const void *cdata); + +static unsigned char nullbytes16[16] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static struct +{ + int major; + int minor; +} icc_versions[] = { + {0, 0}, /* PDF-1.0, we don't support them */ + {0, 0}, /* PDF-1.1, we don't support them */ + {0, 0}, /* PDF-1.2, we don't support them */ + {0x02, 0x10}, /* PDF-1.3 */ + {0x02, 0x20}, /* PDF-1.4 */ + {0x04, 0x00}, /* PDF-1.5 */ + {0x04, 0x00}, /* PDF-1.6 */ + {0x04, 0x20}, /* PDF-1.7 */ + {0x04, 0x20}, /* Dummy(1.8)*/ + {0x04, 0x20}, /* Dummy(1.9) */ + {0x04, 0x20} /* PDF-2.0 */ +}; + +static int +iccp_version_supported (int major, int minor) +{ + int idx; + + idx = pdf_get_version() - 10; + if (idx < 11) { + if (icc_versions[idx].major < major) + return 0; + else if (icc_versions[idx].major == major && + icc_versions[idx].minor < minor) + return 0; + else { + return 1; + } + } + + return 0; +} + +typedef uint32_t iccSig; +static iccSig +str2iccSig (const void *s) +{ + const char *p; + + p = (const char *) s; + + return (iccSig) ((p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]); +} + +typedef struct +{ + int32_t X, Y, Z; /* s15Fixed16Number */ +} iccXYZNumber; + +typedef struct +{ + int size; + iccSig CMMType; + int32_t version; + iccSig devClass; + iccSig colorSpace; + iccSig PCS; /* Profile Connection Space */ + char creationDate[12]; + iccSig acsp; + iccSig platform; + char flags[4]; + iccSig devMnfct; + iccSig devModel; + char devAttr[8]; + int32_t intent; + iccXYZNumber illuminant; + iccSig creator; + unsigned char ID[16]; /* MD5 checksum with Rendering intent, + * Header attrs, Profile ID fields are + * set to zeros. + */ + /* 28 bytes reserved - must be set to zeros */ +} iccHeader; + +#define iccNullSig 0 +static void +iccp_init_iccHeader (iccHeader *icch) +{ + ASSERT(icch); + + icch->size = 0; + icch->CMMType = iccNullSig; + icch->version = 0xFFFFFF; + icch->devClass = iccNullSig; + icch->colorSpace = iccNullSig; + icch->PCS = iccNullSig; + memset(icch->creationDate, 0, 12); + icch->acsp = str2iccSig("ascp"); + icch->platform = iccNullSig; + memset(icch->flags, 0, 4); + icch->devMnfct = iccNullSig; + icch->devModel = iccNullSig; + memset(icch->devAttr, 0, 8); + icch->intent = 0; + icch->illuminant.X = 0; + icch->illuminant.Y = 0; + icch->illuminant.Z = 0; + icch->creator = iccNullSig; + memset(icch->ID, 0, 16); +} + +#define ICC_INTENT_TYPE(n) ((int) (((n) >> 16) & 0xff)) +#define ICC_INTENT_PERCEPTUAL 0 +#define ICC_INTENT_RELATIVE 1 +#define ICC_INTENT_SATURATION 2 +#define ICC_INTENT_ABSOLUTE 3 + +/* + * In ICC profile stream dicrionary, there is /Range whose values must + * "match the information in the profile". But where is those values in? + * + * How should I treat rendering intent? + */ +struct iccbased_cdata +{ + int32_t sig; /* 'i' 'c' 'c' 'b' */ + + unsigned char checksum[16]; /* 16 bytes MD5 Checksum */ + int colorspace; /* input colorspace: + * RGB, Gray, CMYK, (Lab?) + */ + int alternate; /* alternate colorspace (id), unused */ +}; + +#define check_sig(d,p,q,r,s) ((d) && (d)->sig == ((p)<<24|(q)<<16|(r)<<8|(s))) + +static void +init_iccbased_cdata (struct iccbased_cdata *cdata) +{ + ASSERT(cdata); + + cdata->sig = ('i' << 24|'c' << 16|'c' << 8|'b'); + memset(cdata->checksum, 0, 16); + cdata->colorspace = PDF_COLORSPACE_TYPE_INVALID; + cdata->alternate = -1; + + return; +} + +static void +release_iccbased_cdata (struct iccbased_cdata *cdata) +{ + ASSERT(check_sig(cdata, 'i', 'c', 'c', 'b')); + + RELEASE(cdata); +} + +static int +get_num_components_iccbased (const struct iccbased_cdata *cdata) +{ + int num_components = 0; + + ASSERT(check_sig(cdata, 'i', 'c', 'c', 'b')); + + switch (cdata->colorspace) { + case PDF_COLORSPACE_TYPE_RGB: + num_components = 3; + break; + case PDF_COLORSPACE_TYPE_CMYK: + num_components = 4; + break; + case PDF_COLORSPACE_TYPE_GRAY: + num_components = 1; + break; + case PDF_COLORSPACE_TYPE_LAB: + num_components = 3; + break; + } + + return num_components; +} + +static int +compare_iccbased (const char *ident1, const struct iccbased_cdata *cdata1, + const char *ident2, const struct iccbased_cdata *cdata2) +{ + if (cdata1 && cdata2) { + + ASSERT(check_sig(cdata1, 'i', 'c', 'c', 'b')); + ASSERT(check_sig(cdata2, 'i', 'c', 'c', 'b')); + + if (memcmp(cdata1->checksum, nullbytes16, 16) && + memcmp(cdata2->checksum, nullbytes16, 16)) { + return memcmp(cdata1->checksum, cdata2->checksum, 16); + } + if (cdata1->colorspace != cdata2->colorspace) { + return (cdata1->colorspace - cdata2->colorspace); + } + + /* Continue if checksum unknown and colorspace is same. */ + } + + if (ident1 && ident2) + return strcmp(ident1, ident2); + + /* No way to compare */ + return -1; +} + +int +iccp_check_colorspace (int colortype, const void *profile, int proflen) +{ + iccSig colorspace; + const unsigned char *p; + + if (!profile || proflen < 128) + return -1; + + p = (const unsigned char *) profile; + + colorspace = str2iccSig(p + 16); + + switch (colortype) { + case PDF_COLORSPACE_TYPE_CALRGB: + case PDF_COLORSPACE_TYPE_RGB: + if (colorspace != str2iccSig("RGB ")) { + return -1; + } + break; + case PDF_COLORSPACE_TYPE_CALGRAY: + case PDF_COLORSPACE_TYPE_GRAY: + if (colorspace != str2iccSig("GRAY")) { + return -1; + } + break; + case PDF_COLORSPACE_TYPE_CMYK: + if (colorspace != str2iccSig("CMYK")) { + return -1; + } + break; + default: + return -1; + } + + return 0; +} + +pdf_obj * +iccp_get_rendering_intent (const void *profile, int proflen) +{ + pdf_obj *ri = NULL; + const unsigned char *p; + int32_t intent; + + if (!profile || proflen < 128) + return NULL; + + p = (const unsigned char *) profile; + + intent = (p[64] << 24)|(p[65] << 16)|(p[66] << 8)|p[67]; + switch (ICC_INTENT_TYPE(intent)) { + case ICC_INTENT_SATURATION: + ri = pdf_new_name("Saturation"); + break; + case ICC_INTENT_PERCEPTUAL: + ri = pdf_new_name("Perceptual"); + break; + case ICC_INTENT_ABSOLUTE: + ri = pdf_new_name("AbsoluteColorimetric"); + break; + case ICC_INTENT_RELATIVE: + ri = pdf_new_name("RelativeColorimetric"); + break; + default: + WARN("Invalid rendering intent type: %d", ICC_INTENT_TYPE(intent)); + ri = NULL; + } + + return ri; +} + +#define sget_signed_long(p) ((int32_t) ((p)[0] << 24|(p)[1] << 16|(p)[2] << 8|(p)[3])) +#define sget_signed_short(p) ((short) ((p)[0] << 8|(p)[1])) +#define get_iccSig(p) ((iccSig) ((p)[0] << 24|(p)[1] << 16|(p)[2] << 8|(p)[3])) + +static int +iccp_unpack_header (iccHeader *icch, + const void *profile, int proflen, int check_size) +{ + const unsigned char *p, *endptr; + + if (check_size) { + if (!profile || proflen < 128 || + proflen % 4 != 0) { + WARN("Profile size: %ld", proflen); + return -1; + } + } + + p = (const unsigned char *) profile; + endptr = p + 128; + + icch->size = sget_signed_long(p); + if (check_size) { + if (icch->size != proflen) { + WARN("ICC Profile size: %ld(header) != %ld", icch->size, proflen); + return -1; + } + } + p += 4; + + icch->CMMType = str2iccSig(p); + p += 4; + icch->version = sget_signed_long(p); + p += 4; + icch->devClass = str2iccSig(p); + p += 4; + icch->colorSpace = str2iccSig(p); + p += 4; + icch->PCS = str2iccSig(p); + p += 4; + memcpy(icch->creationDate, p, 12); + p += 12; + icch->acsp = str2iccSig(p); /* acsp */ + if (icch->acsp != str2iccSig("acsp")) { + WARN("Invalid ICC profile: not \"acsp\" - %c%c%c%c ", + p[0], p[1], p[2], p[3]); + return -1; + } + p += 4; + icch->platform = str2iccSig(p); + p += 4; + memcpy(icch->flags, p, 4); + p += 4; + icch->devMnfct = str2iccSig(p); + p += 4; + icch->devModel = str2iccSig(p); + p += 4; + memcpy(icch->devAttr, p, 8); + p += 8; + icch->intent = (p[0] << 24)|(p[1] << 16)|(p[2] << 8)|p[3]; + p += 4; + icch->illuminant.X = sget_signed_long(p); + p += 4; + icch->illuminant.Y = sget_signed_long(p); + p += 4; + icch->illuminant.Z = sget_signed_long(p); + p += 4; + icch->creator = str2iccSig(p); + p += 4; + memcpy(icch->ID, p, 16); + p += 16; + + /* 28 bytes reserved - must be set to zeros */ + for (; p < endptr; p++) { + if (*p != '\0') { + WARN("Reserved pad not zero: %02x (at offset %d in ICC profile header.)", + *p, 128 - ((int) (endptr - p))); + return -1; + } + } + + return 0; +} + +/* MD5 checksum with Rendering intent, + * Header attrs, Profile ID fields are + * set to zeros. + */ +#define ICC_HEAD_SECT1_START 0 +#define ICC_HEAD_SECT1_LENGTH 56 +/* 8 bytes devAttr, 4 bytes intent */ +#define ICC_HEAD_SECT2_START 68 +#define ICC_HEAD_SECT2_LENGTH 16 +/* 16 bytes ID (checksum) */ +#define ICC_HEAD_SECT3_START 100 +#define ICC_HEAD_SECT3_LENGTH 28 + +#include "dpxcrypt.h" +static void +iccp_get_checksum (unsigned char *checksum, const void *profile, int proflen) +{ + const unsigned char *p; + MD5_CONTEXT md5; + + p = (const unsigned char *) profile; + + MD5_init (&md5); + MD5_write(&md5, p + ICC_HEAD_SECT1_START, ICC_HEAD_SECT1_LENGTH); + MD5_write(&md5, nullbytes16, 12); + MD5_write(&md5, p + ICC_HEAD_SECT2_START, ICC_HEAD_SECT2_LENGTH); + MD5_write(&md5, nullbytes16, 16); + MD5_write(&md5, p + ICC_HEAD_SECT3_START, ICC_HEAD_SECT3_LENGTH); + + /* body */ + MD5_write(&md5, p + 128, proflen - 128); + + MD5_final(checksum, &md5); +} + +static void +print_iccp_header (iccHeader *icch, unsigned char *checksum) +{ + int i; + + ASSERT(icch); + +#define print_iccSig(s,t) if ((s) == 0) {\ + MESG("pdf_color>> %s:\t(null)\n", (t)); \ + } else if (!isprint(((s) >> 24) & 0xff) || \ + !isprint(((s) >> 16) & 0xff) || \ + !isprint(((s) >> 8) & 0xff) || \ + !isprint((s) & 0xff)) { \ + MESG("pdf_color>> %s:\t(invalid)\n", (t)); \ + } else { \ + MESG("pdf_color>> %s:\t%c%c%c%c\n", (t), \ + ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, \ + ((s) >> 8) & 0xff, (s) & 0xff); \ +} + + MESG("\n"); + MESG("pdf_color>> ICC Profile Info\n"); + MESG("pdf_color>> Profile Size:\t%ld bytes\n", icch->size); + print_iccSig(icch->CMMType, "CMM Type"); + MESG("pdf_color>> Profile Version:\t%d.%01d.%01d\n", + (icch->version >> 24) & 0xff, + (icch->version >> 20) & 0x0f, + (icch->version >> 16) & 0x0f); + print_iccSig(icch->devClass, "Device Class"); + print_iccSig(icch->colorSpace, "Color Space"); + print_iccSig(icch->PCS, "Connection Space"); + MESG("pdf_color>> Creation Date:\t"); + for (i = 0; i < 12; i += 2) { + if (i == 0) + MESG("%04u", + sget_unsigned_pair((unsigned char *) icch->creationDate)); + else { + MESG(":%02u", + sget_unsigned_pair((unsigned char *) (&icch->creationDate[i]))); + } + } + MESG("\n"); + print_iccSig(icch->platform, "Primary Platform"); + MESG("pdf_color>> Profile Flags:\t%02x:%02x:%02x:%02x\n", + icch->flags[0], icch->flags[1], icch->flags[2], icch->flags[3]); + print_iccSig(icch->devMnfct, "Device Mnfct"); + print_iccSig(icch->devModel, "Device Model"); + MESG("pdf_color>> Device Attr:\t"); + for (i = 0; i < 8; i++) { + if (i == 0) + MESG("%02x", icch->devAttr[i]); + else + MESG(":%02x", icch->devAttr[i]); + } + MESG("\n"); + MESG("pdf_color>> Rendering Intent:\t"); + switch (ICC_INTENT_TYPE(icch->intent)) { + case ICC_INTENT_SATURATION: + MESG("Saturation"); + break; + case ICC_INTENT_PERCEPTUAL: + MESG("Perceptual"); + break; + case ICC_INTENT_ABSOLUTE: + MESG("Absolute Colorimetric"); + break; + case ICC_INTENT_RELATIVE: + MESG("Relative Colorimetric"); + break; + default: + MESG("(invalid)"); + break; + } + MESG("\n"); + print_iccSig(icch->creator, "Creator"); + MESG("pdf_color>> Illuminant (XYZ):\t"); + MESG("%.3f %.3f %.3f\n", + (double) icch->illuminant.X / 0x10000, + (double) icch->illuminant.Y / 0x10000, + (double) icch->illuminant.Z / 0x10000); + MESG("pdf_color>> Checksum:\t"); + if (!memcmp(icch->ID, nullbytes16, 16)) { + MESG("(null)"); + } else { + for (i = 0; i < 16; i++) { + if (i == 0) + MESG("%02x", icch->ID[i]); + else + MESG(":%02x", icch->ID[i]); + } + } + MESG("\n"); + if (checksum) { + MESG("pdf_color>> Calculated:\t"); + for (i = 0; i < 16; i++) { + if (i == 0) + MESG("%02x", checksum[i]); + else + MESG(":%02x", checksum[i]); + } + MESG("\n"); + } + + return; +} + + +static int +iccp_devClass_allowed (int dev_class) +{ + int colormode; + + colormode = pdf_dev_get_param(PDF_DEV_PARAM_COLORMODE); + + switch (colormode) { +#if 0 + case PDF_DEV_COLORMODE_PDFX1: + break; + case PDF_DEV_COLORMODE_PDFX3: + if (dev_class != str2iccSig("prtr")) { + return 0; + } + break; +#endif + default: + if (dev_class != str2iccSig("scnr") && + dev_class != str2iccSig("mntr") && + dev_class != str2iccSig("prtr") && + dev_class != str2iccSig("spac")) { + return 0; + } + break; + } + + + return 1; +} + +int +iccp_load_profile (const char *ident, + const void *profile, int proflen) +{ + int cspc_id; + pdf_obj *resource; + pdf_obj *stream; + pdf_obj *stream_dict; + iccHeader icch; + int colorspace; + unsigned char checksum[16]; + struct iccbased_cdata *cdata; + + iccp_init_iccHeader(&icch); + if (iccp_unpack_header(&icch, profile, proflen, 1) < 0) { /* check size */ + WARN("Invalid ICC profile header in \"%s\"", ident); + print_iccp_header(&icch, NULL); + return -1; + } + + if (!iccp_version_supported((icch.version >> 24) & 0xff, + (icch.version >> 16) & 0xff)) { + WARN("ICC profile format spec. version %d.%01d.%01d" + " not supported in current PDF version setting.", + (icch.version >> 24) & 0xff, + (icch.version >> 20) & 0x0f, + (icch.version >> 16) & 0x0f); + WARN("ICC profile not embedded."); + print_iccp_header(&icch, NULL); + return -1; + } + + if (!iccp_devClass_allowed(icch.devClass)) { + WARN("Unsupported ICC Profile Device Class:"); + print_iccp_header(&icch, NULL); + return -1; + } + + if (icch.colorSpace == str2iccSig("RGB ")) { + colorspace = PDF_COLORSPACE_TYPE_RGB; + } else if (icch.colorSpace == str2iccSig("GRAY")) { + colorspace = PDF_COLORSPACE_TYPE_GRAY; + } else if (icch.colorSpace == str2iccSig("CMYK")) { + colorspace = PDF_COLORSPACE_TYPE_CMYK; + } else { + WARN("Unsupported input color space."); + print_iccp_header(&icch, NULL); + return -1; + } + + iccp_get_checksum(checksum, profile, proflen); + if (memcmp(icch.ID, nullbytes16, 16) && + memcmp(icch.ID, checksum, 16)) { + WARN("Invalid ICC profile: Inconsistent checksum."); + print_iccp_header(&icch, checksum); + return -1; + } + + cdata = NEW(1, struct iccbased_cdata); + init_iccbased_cdata(cdata); + cdata->colorspace = colorspace; + memcpy(cdata->checksum, checksum, 16); + + cspc_id = pdf_colorspace_findresource(ident, + PDF_COLORSPACE_TYPE_ICCBASED, cdata); + if (cspc_id >= 0) { + if (dpx_conf.verbose_level > 0) + MESG("(ICCP:[id=%d])", cspc_id); + release_iccbased_cdata(cdata); + return cspc_id; + } + if (dpx_conf.verbose_level > 1) { + print_iccp_header(&icch, checksum); + } + + resource = pdf_new_array(); + + stream = pdf_new_stream(STREAM_COMPRESS); + pdf_add_array(resource, pdf_new_name("ICCBased")); + pdf_add_array(resource, pdf_ref_obj (stream)); + + stream_dict = pdf_stream_dict(stream); + pdf_add_dict(stream_dict, pdf_new_name("N"), + pdf_new_number(get_num_components_iccbased(cdata))); + + pdf_add_stream (stream, profile, proflen); + pdf_release_obj(stream); + + cspc_id = pdf_colorspace_defineresource(ident, + PDF_COLORSPACE_TYPE_ICCBASED, + cdata, resource); + + return cspc_id; +} + +#if 0 +#define WBUF_SIZE 4096 +static unsigned char wbuf[WBUF_SIZE]; + +static pdf_obj * +iccp_load_file_stream (unsigned char *checksum, int length, FILE *fp) +{ + pdf_obj *stream; + MD5_CONTEXT md5; + int nb_read; + + rewind(fp); + + if (fread(wbuf, 1, 128, fp) != 128) { + return NULL; + } + length -= 128; + + stream = pdf_new_stream(STREAM_COMPRESS); + + MD5_init (&md5); + MD5_write(&md5, wbuf + ICC_HEAD_SECT1_START, ICC_HEAD_SECT1_LENGTH); + MD5_write(&md5, nullbytes16, 12); + MD5_write(&md5, wbuf + ICC_HEAD_SECT2_START, ICC_HEAD_SECT2_LENGTH); + MD5_write(&md5, nullbytes16, 16); + MD5_write(&md5, wbuf + ICC_HEAD_SECT3_START, ICC_HEAD_SECT3_LENGTH); + + pdf_add_stream(stream, wbuf, 128); + + /* body */ + while (length > 0) { + nb_read = fread(wbuf, 1, MIN(length, WBUF_SIZE), fp); + MD5_write(&md5, wbuf, nb_read); + pdf_add_stream(stream, wbuf, nb_read); + length -= nb_read; + } + + MD5_final(checksum, &md5); + + + return stream; +} + +int +pdf_colorspace_load_ICCBased (const char *ident, const char *filename) +{ + int cspc_id; + FILE *fp; + pdf_obj *resource; + pdf_obj *stream; + pdf_obj *stream_dict; + iccHeader icch; + int colorspace; + int size; + unsigned char checksum[16]; + struct iccbased_cdata *cdata; + + + fp = DPXFOPEN(filename, DPX_RES_TYPE_ICCPROFILE); + if (!fp) + return -1; + + size = file_size(fp); + if (size < 128) { + MFCLOSE(fp); + return -1; + } + if (fread(wbuf, 1, 128, fp) != 128) { + DPXFCLOSE(fp); + return -1; + } + + iccp_init_iccHeader(&icch); + if (iccp_unpack_header(&icch, wbuf, 128, 0) < 0) { + WARN("Invalid ICC profile header in \"%s\"", ident); + print_iccp_header(&icch, NULL); + DPXFCLOSE(fp); + return -1; + } + if (icch.size > size) { + WARN("File size smaller than recorded in header: %ld %ld", + icch.size, size); + DPXFCLOSE(fp); + return -1; + } + + if (!iccp_version_supported((icch.version >> 24) & 0xff, + (icch.version >> 16) & 0xff)) { + WARN("ICC profile format spec. version %d.%01d.%01d" + " not supported in current PDF version setting.", + (icch.version >> 24) & 0xff, + (icch.version >> 20) & 0x0f, + (icch.version >> 16) & 0x0f); + WARN("ICC profile not embedded."); + print_iccp_header(&icch, NULL); + DPXFCLOSE(fp); + return -1; + } + + if (!iccp_devClass_allowed(icch.devClass)) { + WARN("Unsupported ICC Profile Device Class:"); + print_iccp_header(&icch, NULL); + DPXFCLOSE(fp); + return -1; + } + + if (icch.colorSpace == str2iccSig("RGB ")) { + colorspace = PDF_COLORSPACE_TYPE_RGB; + } else if (icch.colorSpace == str2iccSig("GRAY")) { + colorspace = PDF_COLORSPACE_TYPE_GRAY; + } else if (icch.colorSpace == str2iccSig("CMYK")) { + colorspace = PDF_COLORSPACE_TYPE_CMYK; + } else { + WARN("Unsupported input color space."); + print_iccp_header(&icch, NULL); + DPXFCLOSE(fp); + return -1; + } + + stream = iccp_load_file_stream(checksum, icch.size, fp); + DPXFCLOSE(fp); + + if (!stream) { + WARN("Loading ICC Profile failed...: %s", filename); + return -1; + } + + if (memcmp(icch.ID, nullbytes16, 16) && + memcmp(icch.ID, checksum, 16)) { + WARN("Invalid ICC profile: Inconsistent checksum."); + print_iccp_header(&icch, NULL); + pdf_release_obj(stream); + return -1; + } + + cdata = NEW(1, struct iccbased_cdata); + init_iccbased_cdata(cdata); + cdata->colorspace = colorspace; + memcpy(cdata->checksum, checksum, 16); + + cspc_id = pdf_colorspace_findresource(ident, + PDF_COLORSPACE_TYPE_ICCBASED, cdata); + if (cspc_id >= 0) { + if (dpx_conf.verbose_level > 0) + MESG("(ICCP:[id=%d])", cspc_id); + release_iccbased_cdata(cdata); + pdf_release_obj(stream); + return cspc_id; + } + if (dpx_conf.verbose_level > 1) { + print_iccp_header(&icch, checksum); + } + + resource = pdf_new_array(); + + pdf_add_array(resource, pdf_new_name("ICCBased")); + pdf_add_array(resource, pdf_ref_obj (stream)); + + stream_dict = pdf_stream_dict(stream); + pdf_add_dict(stream_dict, pdf_new_name("N"), + pdf_new_number(get_num_components_iccbased(cdata))); + pdf_release_obj(stream); + + cspc_id = pdf_colorspace_defineresource(ident, + PDF_COLORSPACE_TYPE_ICCBASED, + cdata, resource); + + return cspc_id; +} +#endif + +typedef struct { + char *ident; + int subtype; + + pdf_obj *resource; + pdf_obj *reference; + + void *cdata; +} pdf_colorspace; + +static struct { + int count; + int capacity; + pdf_colorspace *colorspaces; +} cspc_cache = { + 0, 0, NULL +}; + +int +pdf_colorspace_findresource (const char *ident, + int type, const void *cdata) +{ + pdf_colorspace *colorspace; + int cspc_id, cmp = -1; + + for (cspc_id = 0; + cmp && cspc_id < cspc_cache.count; cspc_id++) { + colorspace = &cspc_cache.colorspaces[cspc_id]; + if (colorspace->subtype != type) + continue; + + switch (colorspace->subtype) { + case PDF_COLORSPACE_TYPE_ICCBASED: + cmp = compare_iccbased(ident, cdata, + colorspace->ident, colorspace->cdata); + break; + } + if (!cmp) + return cspc_id; + } + + return -1; /* not found */ +} + +static void +pdf_init_colorspace_struct (pdf_colorspace *colorspace) +{ + ASSERT(colorspace); + + colorspace->ident = NULL; + colorspace->subtype = PDF_COLORSPACE_TYPE_INVALID; + + colorspace->resource = NULL; + colorspace->reference = NULL; + colorspace->cdata = NULL; + + return; +} + +static void +pdf_clean_colorspace_struct (pdf_colorspace *colorspace) +{ + ASSERT(colorspace); + + if (colorspace->ident) + RELEASE(colorspace->ident); + if (colorspace->resource) + pdf_release_obj(colorspace->resource); + if (colorspace->reference) + pdf_release_obj(colorspace->reference); + colorspace->resource = NULL; + colorspace->reference = NULL; + + if (colorspace->cdata) { + switch (colorspace->subtype) { + case PDF_COLORSPACE_TYPE_ICCBASED: + release_iccbased_cdata(colorspace->cdata); + break; + } + } + colorspace->cdata = NULL; + colorspace->subtype = PDF_COLORSPACE_TYPE_INVALID; + + return; +} + +static void +pdf_flush_colorspace (pdf_colorspace *colorspace) +{ + ASSERT(colorspace); + + if (colorspace->resource) + pdf_release_obj(colorspace->resource); + if (colorspace->reference) + pdf_release_obj(colorspace->reference); + + colorspace->resource = NULL; + colorspace->reference = NULL; +} + +int +pdf_colorspace_defineresource (const char *ident, + int subtype, void *cdata, pdf_obj *resource) +{ + int cspc_id; + pdf_colorspace *colorspace; + + if (cspc_cache.count >= cspc_cache.capacity) { + cspc_cache.capacity += 16; + cspc_cache.colorspaces = RENEW(cspc_cache.colorspaces, + cspc_cache.capacity, pdf_colorspace); + } + cspc_id = cspc_cache.count; + colorspace = &cspc_cache.colorspaces[cspc_id]; + + pdf_init_colorspace_struct(colorspace); + if (ident) { + colorspace->ident = NEW(strlen(ident) + 1, char); + strcpy(colorspace->ident, ident); + } + colorspace->subtype = subtype; + colorspace->cdata = cdata; + colorspace->resource = resource; + + if (dpx_conf.verbose_level > 0) { + MESG("(ColorSpace:%s", ident); + if (dpx_conf.verbose_level > 1) { + switch (subtype) { + case PDF_COLORSPACE_TYPE_ICCBASED: + MESG("[ICCBased]"); + break; + case PDF_COLORSPACE_TYPE_CALRGB: + MESG("[CalRGB]"); + break; + case PDF_COLORSPACE_TYPE_CALGRAY: + MESG("[CalGray]"); + break; + } + } + MESG(")"); + } + + cspc_cache.count++; + + return cspc_id; +} + +pdf_obj * +pdf_get_colorspace_reference (int cspc_id) +{ + pdf_colorspace *colorspace; + + colorspace = &cspc_cache.colorspaces[cspc_id]; + if (!colorspace->reference) { + colorspace->reference = pdf_ref_obj(colorspace->resource); + pdf_release_obj(colorspace->resource); /* .... */ + colorspace->resource = NULL; + } + + return pdf_link_obj(colorspace->reference); +} + +#if 0 +int +pdf_get_colorspace_num_components (int cspc_id) +{ + pdf_colorspace *colorspace; + int num_components; + + colorspace = &cspc_cache.colorspaces[cspc_id]; + + switch (colorspace->subtype) { + case PDF_COLORSPACE_TYPE_ICCBASED: + num_components = get_num_components_iccbased(colorspace->cdata); + break; + case PDF_COLORSPACE_TYPE_DEVICEGRAY: + num_components = 1; + break; + case PDF_COLORSPACE_TYPE_DEVICERGB: + num_components = 3; + break; + case PDF_COLORSPACE_TYPE_DEVICECMYK: + num_components = 4; + break; + case PDF_COLORSPACE_TYPE_CALRGB: + num_components = 3; + break; + case PDF_COLORSPACE_TYPE_CALGRAY: + num_components = 1; + break; + default: + num_components = 0; + break; + } + + return num_components; +} + +int +pdf_get_colorspace_subtype (int cspc_id) +{ + pdf_colorspace *colorspace; + + colorspace = &cspc_cache.colorspaces[cspc_id]; + + return colorspace->subtype; +} +#endif + +void +pdf_init_colors (void) +{ + cspc_cache.count = 0; + cspc_cache.capacity = 0; + cspc_cache.colorspaces = NULL; +} + +void +pdf_close_colors (void) +{ + int i; + + for (i = 0; i < cspc_cache.count; i++) { + pdf_colorspace *colorspace; + + colorspace = &cspc_cache.colorspaces[i]; + pdf_flush_colorspace(colorspace); + pdf_clean_colorspace_struct(colorspace); + } + RELEASE(cspc_cache.colorspaces); + cspc_cache.colorspaces = NULL; + cspc_cache.count = cspc_cache.capacity = 0; + +} + +#define PDF_COLORSPACE_FAMILY_DEVICE 0 +#define PDF_COLORSPACE_FAMILY_CIEBASED 1 +#define PDF_COLORSPACE_FAMILY_SPECIAL 2 -- cgit v1.2.3