diff --git a/pdfio-content.c b/pdfio-content.c index fe53d28..0052036 100644 --- a/pdfio-content.c +++ b/pdfio-content.c @@ -16,6 +16,21 @@ #include +// +// Global constants... +// + +const double pdfioAdobeRGBGamma = 2.2; +const double pdfioAdobeRGBMatrix[3][3] = { { 0.57667, 0.18556, 0.18823 }, { 0.29734, 0.62736, 0.07529 }, { 0.02703, 0.07069, 0.99134 } }; +const double pdfioAdobeRGBWhitePoint[3] = { 0.9505, 1.0, 1.0890 }; +const double pdfioDisplayP3Gamma = 2.2; +const double pdfioDisplayP3Matrix[3][3] = { { 0.48657, 0.26567, 0.19822 }, { +0.22897, 0.69174, 0.07929 }, { 0.00000, 0.04511, 1.04394 } }; +const double pdfioDisplayP3WhitePoint[3] = { 0.9505, 1.0, 1.0890 }; +const double pdfioSRGBGamma = 2.2; +const double pdfioSRGBMatrix[3][3] = { { 0.4124, 0.3576, 0.1805 }, { 0.2126, 0.7152, 0.0722 }, { 0.0193, 0.1192, 0.9505 } }; +const double pdfioSRGBWhitePoint[3] = { 0.9505, 1.0, 1.0890 }; + // // Local types... // @@ -29,6 +44,7 @@ typedef pdfio_obj_t *(*_pdfio_image_func_t)(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd); +static pdfio_array_t *create_calcolor(pdfio_file_t *pdf, size_t num_colors, double gamma, const double matrix[3][3], const double white_point[3]); static bool write_string(pdfio_stream_t *st, const char *s); @@ -68,12 +84,12 @@ bool // O - `true` on success, `false` on failure pdfioContentDrawImage( pdfio_stream_t *st, // I - Stream const char *name, // I - Image name - double x, // I - X offset of image - double y, // I - Y offset of image - double w, // I - Width of image - double h) // I - Height of image + double x, // I - X offset of image + double y, // I - Y offset of image + double width, // I - Width of image + double height) // I - Height of image { - return (pdfioStreamPrintf(st, "q %g 0 0 %g %g %g cm/%s Do Q\n", w, h, x, y, name)); + return (pdfioStreamPrintf(st, "q %g 0 0 %g %g %g cm/%s Do Q\n", width, height, x, y, name)); } @@ -135,7 +151,7 @@ pdfioContentMatrixConcat( bool // O - `true` on success, `false` on failure pdfioContentMatrixRotate( pdfio_stream_t *st, // I - Stream - double degrees) // I - Rotation angle in degrees counter-clockwise + double degrees) // I - Rotation angle in degrees counter-clockwise { double dcos = cos(degrees / M_PI); // Cosine double dsin = sin(degrees / M_PI); // Sine @@ -151,8 +167,8 @@ pdfioContentMatrixRotate( bool // O - `true` on success, `false` on failure pdfioContentMatrixScale( pdfio_stream_t *st, // I - Stream - double sx, // I - X scale - double sy) // I - Y scale + double sx, // I - X scale + double sy) // I - Y scale { return (pdfioStreamPrintf(st, "%g 0 0 %g 0 0 cm\n", sx, sy)); } @@ -165,8 +181,8 @@ pdfioContentMatrixScale( bool // O - `true` on success, `false` on failure pdfioContentMatrixTranslate( pdfio_stream_t *st, // I - Stream - double tx, // I - X offset - double ty) // I - Y offset + double tx, // I - X offset + double ty) // I - Y offset { return (pdfioStreamPrintf(st, "1 0 0 1 %g %g cm\n", tx, ty)); } @@ -191,12 +207,12 @@ pdfioContentPathClose( bool // O - `true` on success, `false` on failure pdfioContentPathCurve( pdfio_stream_t *st, // I - Stream - double x1, // I - X position 1 - double y1, // I - Y position 1 - double x2, // I - X position 2 - double y2, // I - Y position 2 - double x3, // I - X position 3 - double y3) // I - Y position 3 + double x1, // I - X position 1 + double y1, // I - Y position 1 + double x2, // I - X position 2 + double y2, // I - Y position 2 + double x3, // I - X position 3 + double y3) // I - Y position 3 { return (pdfioStreamPrintf(st, "%g %g %g %g %g %g c\n", x1, y1, x2, y2, x3, y3)); } @@ -209,10 +225,10 @@ pdfioContentPathCurve( bool // O - `true` on success, `false` on failure pdfioContentPathCurve13( pdfio_stream_t *st, // I - Stream - double x1, // I - X position 1 - double y1, // I - Y position 1 - double x3, // I - X position 3 - double y3) // I - Y position 3 + double x1, // I - X position 1 + double y1, // I - Y position 1 + double x3, // I - X position 3 + double y3) // I - Y position 3 { return (pdfioStreamPrintf(st, "%g %g %g %g v\n", x1, y1, x3, y3)); } @@ -225,10 +241,10 @@ pdfioContentPathCurve13( bool // O - `true` on success, `false` on failure pdfioContentPathCurve23( pdfio_stream_t *st, // I - Stream - double x2, // I - X position 2 - double y2, // I - Y position 2 - double x3, // I - X position 3 - double y3) // I - Y position 3 + double x2, // I - X position 2 + double y2, // I - Y position 2 + double x3, // I - X position 3 + double y3) // I - Y position 3 { return (pdfioStreamPrintf(st, "%g %g %g %g y\n", x2, y2, x3, y3)); } @@ -241,8 +257,8 @@ pdfioContentPathCurve23( bool // O - `true` on success, `false` on failure pdfioContentPathLineTo( pdfio_stream_t *st, // I - Stream - double x, // I - X position - double y) // I - Y position + double x, // I - X position + double y) // I - Y position { return (pdfioStreamPrintf(st, "%g %g l\n", x, y)); } @@ -255,8 +271,8 @@ pdfioContentPathLineTo( bool // O - `true` on success, `false` on failure pdfioContentPathMoveTo( pdfio_stream_t *st, // I - Stream - double x, // I - X position - double y) // I - Y position + double x, // I - X position + double y) // I - Y position { return (pdfioStreamPrintf(st, "%g %g m\n", x, y)); } @@ -269,9 +285,12 @@ pdfioContentPathMoveTo( bool // O - `true` on success, `false` on failure pdfioContentPathRect( pdfio_stream_t *st, // I - Stream - pdfio_rect_t *rect) // I - Rectangle + double x, // I - X offset + double y, // I - Y offset + double width, // I - Width + double height) // I - Height { - return (pdfioStreamPrintf(st, "%g %g %g %g re\n", rect->x1, rect->y1, rect->x2 - rect->x1, rect->y2 - rect->y1)); + return (pdfioStreamPrintf(st, "%g %g %g %g re\n", x, y, width, height)); } @@ -320,10 +339,10 @@ pdfioContentSetDashPattern( bool // O - `true` on success, `false` on failure pdfioContentSetFillColorDeviceCMYK( pdfio_stream_t *st, // I - Stream - double c, // I - Cyan value (0.0 to 1.0) - double m, // I - Magenta value (0.0 to 1.0) - double y, // I - Yellow value (0.0 to 1.0) - double k) // I - Black value (0.0 to 1.0) + double c, // I - Cyan value (0.0 to 1.0) + double m, // I - Magenta value (0.0 to 1.0) + double y, // I - Yellow value (0.0 to 1.0) + double k) // I - Black value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g %g %g %g k\n", c, m, y, k)); } @@ -336,7 +355,7 @@ pdfioContentSetFillColorDeviceCMYK( bool // O - `true` on success, `false` on failure pdfioContentSetFillColorDeviceGray( pdfio_stream_t *st, // I - Stream - double g) // I - Gray value (0.0 to 1.0) + double g) // I - Gray value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g g\n", g)); } @@ -349,9 +368,9 @@ pdfioContentSetFillColorDeviceGray( bool // O - `true` on success, `false` on failure pdfioContentSetFillColorDeviceRGB( pdfio_stream_t *st, // I - Stream - double r, // I - Red value (0.0 to 1.0) - double g, // I - Green value (0.0 to 1.0) - double b) // I - Blue value (0.0 to 1.0) + double r, // I - Red value (0.0 to 1.0) + double g, // I - Green value (0.0 to 1.0) + double b) // I - Blue value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g %g %g rg\n", r, g, b)); } @@ -364,7 +383,7 @@ pdfioContentSetFillColorDeviceRGB( bool // O - `true` on success, `false` on failure pdfioContentSetFillColorGray( pdfio_stream_t *st, // I - Stream - double g) // I - Gray value (0.0 to 1.0) + double g) // I - Gray value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g sc\n", g)); } @@ -377,9 +396,9 @@ pdfioContentSetFillColorGray( bool // O - `true` on success, `false` on failure pdfioContentSetFillColorRGB( pdfio_stream_t *st, // I - Stream - double r, // I - Red value (0.0 to 1.0) - double g, // I - Green value (0.0 to 1.0) - double b) // I - Blue value (0.0 to 1.0) + double r, // I - Red value (0.0 to 1.0) + double g, // I - Green value (0.0 to 1.0) + double b) // I - Blue value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g %g %g sc\n", r, g, b)); } @@ -405,7 +424,7 @@ pdfioContentSetFillColorSpace( bool // O - `true` on success, `false` on failure pdfioContentSetFlatness( pdfio_stream_t *st, // I - Stream - double flatness) // I - Flatness value (0.0 to 100.0) + double flatness) // I - Flatness value (0.0 to 100.0) { return (pdfioStreamPrintf(st, "%g i\n", flatness)); } @@ -444,7 +463,7 @@ pdfioContentSetLineJoin( bool // O - `true` on success, `false` on failure pdfioContentSetLineWidth( pdfio_stream_t *st, // I - Stream - double width) // I - Line width value + double width) // I - Line width value { return (pdfioStreamPrintf(st, "%g w\n", width)); } @@ -457,7 +476,7 @@ pdfioContentSetLineWidth( bool // O - `true` on success, `false` on failure pdfioContentSetMiterLimit( pdfio_stream_t *st, // I - Stream - double limit) // I - Miter limit value + double limit) // I - Miter limit value { return (pdfioStreamPrintf(st, "%g M\n", limit)); } @@ -470,10 +489,10 @@ pdfioContentSetMiterLimit( bool // O - `true` on success, `false` on failure pdfioContentSetStrokeColorDeviceCMYK( pdfio_stream_t *st, // I - Stream - double c, // I - Cyan value (0.0 to 1.0) - double m, // I - Magenta value (0.0 to 1.0) - double y, // I - Yellow value (0.0 to 1.0) - double k) // I - Black value (0.0 to 1.0) + double c, // I - Cyan value (0.0 to 1.0) + double m, // I - Magenta value (0.0 to 1.0) + double y, // I - Yellow value (0.0 to 1.0) + double k) // I - Black value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g %g %g %g K\n", c, m, y, k)); } @@ -486,7 +505,7 @@ pdfioContentSetStrokeColorDeviceCMYK( bool // O - `true` on success, `false` on failure pdfioContentSetStrokeColorDeviceGray( pdfio_stream_t *st, // I - Stream - double g) // I - Gray value (0.0 to 1.0) + double g) // I - Gray value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g G\n", g)); } @@ -499,9 +518,9 @@ pdfioContentSetStrokeColorDeviceGray( bool // O - `true` on success, `false` on failure pdfioContentSetStrokeColorDeviceRGB( pdfio_stream_t *st, // I - Stream - double r, // I - Red value (0.0 to 1.0) - double g, // I - Green value (0.0 to 1.0) - double b) // I - Blue value (0.0 to 1.0) + double r, // I - Red value (0.0 to 1.0) + double g, // I - Green value (0.0 to 1.0) + double b) // I - Blue value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g %g %g RG\n", r, g, b)); } @@ -514,7 +533,7 @@ pdfioContentSetStrokeColorDeviceRGB( bool // O - `true` on success, `false` on failure pdfioContentSetStrokeColorGray( pdfio_stream_t *st, // I - Stream - double g) // I - Gray value (0.0 to 1.0) + double g) // I - Gray value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g SC\n", g)); } @@ -527,9 +546,9 @@ pdfioContentSetStrokeColorGray( bool // O - `true` on success, `false` on failure pdfioContentSetStrokeColorRGB( pdfio_stream_t *st, // I - Stream - double r, // I - Red value (0.0 to 1.0) - double g, // I - Green value (0.0 to 1.0) - double b) // I - Blue value (0.0 to 1.0) + double r, // I - Red value (0.0 to 1.0) + double g, // I - Green value (0.0 to 1.0) + double b) // I - Blue value (0.0 to 1.0) { return (pdfioStreamPrintf(st, "%g %g %g SC\n", r, g, b)); } @@ -555,7 +574,7 @@ pdfioContentSetStrokeColorSpace( bool // O - `true` on success, `false` on failure pdfioContentSetTextCharacterSpacing( pdfio_stream_t *st, // I - Stream - double spacing) // I - Character spacing + double spacing) // I - Character spacing { return (pdfioStreamPrintf(st, "%g Tc\n", spacing)); } @@ -569,7 +588,7 @@ bool // O - `true` on success, `false` on failure pdfioContentSetTextFont( pdfio_stream_t *st, // I - Stream const char *name, // I - Font name - double size) // I - Font size + double size) // I - Font size { return (pdfioStreamPrintf(st, "/%s %g Tf\n", name, size)); } @@ -582,7 +601,7 @@ pdfioContentSetTextFont( bool // O - `true` on success, `false` on failure pdfioContentSetTextLeading( pdfio_stream_t *st, // I - Stream - double leading) // I - Leading (line height) value + double leading) // I - Leading (line height) value { return (pdfioStreamPrintf(st, "%g TL\n", leading)); } @@ -621,7 +640,7 @@ pdfioContentSetTextRenderingMode( bool // O - `true` on success, `false` on failure pdfioContentSetTextRise( pdfio_stream_t *st, // I - Stream - double rise) // I - Y offset + double rise) // I - Y offset { return (pdfioStreamPrintf(st, "%g Ts\n", rise)); } @@ -634,7 +653,7 @@ pdfioContentSetTextRise( bool // O - `true` on success, `false` on failure pdfioContentSetTextWordSpacing( pdfio_stream_t *st, // I - Stream - double spacing) // I - Spacing between words + double spacing) // I - Spacing between words { return (pdfioStreamPrintf(st, "%g Tw\n", spacing)); } @@ -647,7 +666,7 @@ pdfioContentSetTextWordSpacing( bool // O - `true` on success, `false` on failure pdfioContentSetTextXScaling( pdfio_stream_t *st, // I - Stream - double percent) // I - Horizontal scaling in percent + double percent) // I - Horizontal scaling in percent { return (pdfioStreamPrintf(st, "%g Tz\n", percent)); } @@ -671,8 +690,8 @@ pdfioContentStroke(pdfio_stream_t *st) // I - Stream bool // O - `true` on success, `false` on failure pdfioContentTextMoveLine( pdfio_stream_t *st, // I - Stream - double tx, // I - X offset - double ty) // I - Y offset + double tx, // I - X offset + double ty) // I - Y offset { return (pdfioStreamPrintf(st, "%g %g TD\n", tx, ty)); } @@ -685,8 +704,8 @@ pdfioContentTextMoveLine( bool // O - `true` on success, `false` on failure pdfioContentTextMoveTo( pdfio_stream_t *st, // I - Stream - double tx, // I - X offset - double ty) // I - Y offset + double tx, // I - X offset + double ty) // I - Y offset { return (pdfioStreamPrintf(st, "%g %g Td\n", tx, ty)); } @@ -731,7 +750,7 @@ bool // O - `true` on success, `false` on failure pdfioContentTextShowJustified( pdfio_stream_t *st, // I - Stream size_t num_fragments, // I - Number of text fragments - const double *offsets, // I - Text offsets before fragments + const double *offsets, // I - Text offsets before fragments const char * const *fragments) // I - Text fragments { size_t i; // Looping var @@ -789,16 +808,39 @@ pdfioPageDictAddCalibratedColorSpace( pdfio_dict_t *dict, // I - Page dictionary const char *name, // I - Color space name size_t num_colors, // I - Number of color components - const double *white_point, // I - CIE XYZ white point - double gamma) // I - Gamma value + double gamma, // I - Gamma value + const double matrix[3][3], // I - Color transform matrix + const double white_point[3]) // I - CIE XYZ white point { - (void)dict; - (void)name; - (void)num_colors; - (void)white_point; - (void)gamma; + pdfio_dict_t *resources; // Resource dictionary + pdfio_dict_t *colorspace; // ColorSpace dictionary - return (false); + + // Range check input... + if (!dict || !name || !num_colors || gamma <= 0.0) + return (false); + + // Get the ColorSpace dictionary... + if ((resources = pdfioDictGetDict(dict, "Resources")) == NULL) + { + if ((resources = pdfioDictCreate(dict->pdf)) == NULL) + return (false); + + if (!pdfioDictSetDict(dict, "Resources", resources)) + return (false); + } + + if ((colorspace = pdfioDictGetDict(resources, "ColorSpace")) == NULL) + { + if ((colorspace = pdfioDictCreate(dict->pdf)) == NULL) + return (false); + + if (!pdfioDictSetDict(resources, "ColorSpace", colorspace)) + return (false); + } + + // Now set the color space reference and return... + return (pdfioDictSetArray(colorspace, name, create_calcolor(dict->pdf, num_colors, gamma, matrix, white_point))); } @@ -838,7 +880,7 @@ pdfioPageDictAddImage( if (!dict || !name || !obj) return (false); - // Get the images dictionary... + // Get the Resources dictionary... if ((resources = pdfioDictGetDict(dict, "Resources")) == NULL) { if ((resources = pdfioDictCreate(dict->pdf)) == NULL) @@ -848,6 +890,7 @@ pdfioPageDictAddImage( return (false); } + // Get the XObject dictionary... if ((xobject = pdfioDictGetDict(resources, "XObject")) == NULL) { if ((xobject = pdfioDictCreate(dict->pdf)) == NULL) @@ -857,7 +900,7 @@ pdfioPageDictAddImage( return (false); } - // Now set the image reference in the images resource dictionary and return... + // Now set the image reference in the XObject resource dictionary and return... return (pdfioDictSetObject(xobject, name, obj)); } @@ -1064,6 +1107,12 @@ copy_jpeg(pdfio_dict_t *dict, // I - Dictionary if ((*bufptr >= 0xc0 && *bufptr <= 0xc3) || (*bufptr >= 0xc5 && *bufptr <= 0xc7) || (*bufptr >= 0xc9 && *bufptr <= 0xcb) || (*bufptr >= 0xcd && *bufptr <= 0xcf)) { // SOFn marker, look for dimensions... + if (bufptr[3] != 8) + { + _pdfioFileError(dict->pdf, "Unable to load %d-bit JPEG image.", bufptr[3]); + return (NULL); + } + width = (unsigned)((bufptr[6] << 8) | bufptr[7]); height = (unsigned)((bufptr[4] << 8) | bufptr[5]); num_colors = bufptr[8]; @@ -1100,7 +1149,7 @@ copy_jpeg(pdfio_dict_t *dict, // I - Dictionary pdfioDictSetNumber(dict, "Height", height); pdfioDictSetNumber(dict, "BitsPerComponent", 8); // TODO: Add proper JPEG CalRGB/Gray color spaces - pdfioDictSetName(dict, "ColorSpace", num_colors == 3 ? "DeviceRGB" : "DeviceGray"); + pdfioDictSetArray(dict, "ColorSpace", create_calcolor(dict->pdf, num_colors, pdfioSRGBGamma, pdfioSRGBMatrix, pdfioSRGBWhitePoint)); pdfioDictSetName(dict, "Filter", "DCTDecode"); obj = pdfioFileCreateObject(dict->pdf, dict); @@ -1137,6 +1186,88 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary } +// +// 'create_calcolor()' - Create a CalGray or CalRGB color array. +// + +static pdfio_array_t * // O - Array +create_calcolor( + pdfio_file_t *pdf, // I - PDF file + size_t num_colors, // I - Number of colors (1 or 3) + double gamma, // I - Gamma value + const double matrix[3][3], // I - XYZ transform + const double white_point[3]) // I - White point +{ + size_t i; // Looping var + pdfio_array_t *calcolor; // Array to hold calibrated color space + pdfio_dict_t *dict; // Dictionary to hold color values + pdfio_array_t *value; // Value for white point, matrix, and gamma + + + // Create the array with two values - a name and a dictionary... + if ((calcolor = pdfioArrayCreate(pdf)) == NULL) + return (NULL); + + if (num_colors == 1) + pdfioArrayAppendName(calcolor, "CalGray"); + else + pdfioArrayAppendName(calcolor, "CalRGB"); + + if ((dict = pdfioDictCreate(pdf)) == NULL) + return (NULL); + + pdfioArrayAppendDict(calcolor, dict); + + // Then add the values... + if (num_colors == 1) + { + pdfioDictSetNumber(dict, "Gamma", gamma); + } + else + { + if ((value = pdfioArrayCreate(pdf)) == NULL) + return (NULL); + + for (i = 0; i < num_colors; i ++) + pdfioArrayAppendNumber(value, gamma); + + pdfioDictSetArray(dict, "Gamma", value); + } + + if (white_point) + { + if ((value = pdfioArrayCreate(pdf)) == NULL) + return (NULL); + + pdfioArrayAppendNumber(value, white_point[0]); + pdfioArrayAppendNumber(value, white_point[1]); + pdfioArrayAppendNumber(value, white_point[2]); + + pdfioDictSetArray(dict, "WhitePoint", value); + } + + if (num_colors > 1 && matrix) + { + if ((value = pdfioArrayCreate(pdf)) == NULL) + return (NULL); + + pdfioArrayAppendNumber(value, matrix[0][0]); + pdfioArrayAppendNumber(value, matrix[1][0]); + pdfioArrayAppendNumber(value, matrix[2][0]); + pdfioArrayAppendNumber(value, matrix[0][1]); + pdfioArrayAppendNumber(value, matrix[1][1]); + pdfioArrayAppendNumber(value, matrix[2][1]); + pdfioArrayAppendNumber(value, matrix[0][2]); + pdfioArrayAppendNumber(value, matrix[1][2]); + pdfioArrayAppendNumber(value, matrix[2][2]); + + pdfioDictSetArray(dict, "Matrix", value); + } + + return (calcolor); +} + + // // 'write_string()' - Write a PDF string. // diff --git a/pdfio-content.h b/pdfio-content.h index cbc32d6..4612d1f 100644 --- a/pdfio-content.h +++ b/pdfio-content.h @@ -59,16 +59,23 @@ typedef enum pdfio_textrendering_e // Text rendering modes PDFIO_TEXTRENDERING_TEXT_PATH // Add text to path (invisible) } pdfio_textrendering_t; -extern const double pdfioAdobeRGBGamma; +extern const double pdfioAdobeRGBGamma PDFIO_PUBLIC; // AdobeRGB gamma -extern const double pdfioAdobeRGBWhitePoint[]; +extern const double pdfioAdobeRGBMatrix[3][3] PDFIO_PUBLIC; + // AdobeRGB matrix +extern const double pdfioAdobeRGBWhitePoint[3] PDFIO_PUBLIC; // AdobeRGB white point -extern const double pdfioDisplayP3Gamma; +extern const double pdfioDisplayP3Gamma PDFIO_PUBLIC; // Display P3 gamma -extern const double pdfioDisplayP3WhitePoint[]; +extern const double pdfioDisplayP3Matrix[3][3] PDFIO_PUBLIC; + // Display P3 matrix +extern const double pdfioDisplayP3WhitePoint[3] PDFIO_PUBLIC; // Display P3 white point -extern const double pdfioSRGBGamma; // sRGB gamma -extern const double pdfioSRGBWhitePoint[]; +extern const double pdfioSRGBGamma PDFIO_PUBLIC; + // sRGB gamma +extern const double pdfioSRGBMatrix[3][3] PDFIO_PUBLIC; + // sRGB matrix +extern const double pdfioSRGBWhitePoint[3] PDFIO_PUBLIC; // sRGB white point @@ -93,7 +100,7 @@ extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, d extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) PDFIO_PUBLIC; extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) PDFIO_PUBLIC; extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) PDFIO_PUBLIC; -extern bool pdfioContentPathRect(pdfio_stream_t *st, pdfio_rect_t *rect) PDFIO_PUBLIC; +extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) PDFIO_PUBLIC; extern bool pdfioContentRestore(pdfio_stream_t *st) PDFIO_PUBLIC; extern bool pdfioContentSave(pdfio_stream_t *st) PDFIO_PUBLIC; extern bool pdfioContentSetDashPattern(pdfio_stream_t *st, int phase, int on, int off) PDFIO_PUBLIC; @@ -140,7 +147,7 @@ extern double pdfioImageGetWidth(pdfio_obj_t *obj) PDFIO_PUBLIC; // Page dictionary helpers... extern bool pdfioPageDictAddICCColorSpace(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) PDFIO_PUBLIC; -extern bool pdfioPageDictAddCalibratedColorSpace(pdfio_dict_t *dict, const char *name, size_t num_colors, const double *white_point, double gamma) PDFIO_PUBLIC; +extern bool pdfioPageDictAddCalibratedColorSpace(pdfio_dict_t *dict, const char *name, size_t num_colors, double gamma, const double matrix[3][3], const double white_point[3]) PDFIO_PUBLIC; extern bool pdfioPageDictAddFont(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) PDFIO_PUBLIC; extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) PDFIO_PUBLIC; diff --git a/testfiles/gray.jpg b/testfiles/gray.jpg index 9f5459a..b4e4e9a 100644 Binary files a/testfiles/gray.jpg and b/testfiles/gray.jpg differ diff --git a/testpdfio.c b/testpdfio.c index 7f16cd2..beb89de 100644 --- a/testpdfio.c +++ b/testpdfio.c @@ -13,6 +13,7 @@ #include "pdfio-private.h" #include "pdfio-content.h" +#include // @@ -24,6 +25,8 @@ static int do_unit_tests(void); static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error); static ssize_t token_consume_cb(const char **s, size_t bytes); static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes); +static int write_color_patch(pdfio_stream_t *st, bool device); +static int write_color_test(pdfio_file_t *pdf, int number); static int write_page(pdfio_file_t *pdf, int number, pdfio_obj_t *image); @@ -227,12 +230,9 @@ do_unit_tests(void) else return (1); - // Write a few pages... - for (i = 1; i < 16; i ++) - { - if (write_page(outpdf, i, (i & 1) ? color_jpg : gray_jpg)) - return (1); - } + // Write a page with a color image... + if (write_page(outpdf, 2, color_jpg)) + return (1); // Copy the third page from the test PDF file... fputs("pdfioFileGetPage(2): ", stdout); @@ -247,6 +247,14 @@ do_unit_tests(void) else return (1); + // Write a page with a grayscale image... + if (write_page(outpdf, 4, gray_jpg)) + return (1); + + // Write a page that tests multiple color spaces... + if (write_color_test(outpdf, 5)) + return (1); + // Close the test PDF file... fputs("pdfioFileClose(\"testfiles/testpdfio.pdf\": ", stdout); if (pdfioFileClose(pdf)) @@ -337,6 +345,299 @@ token_peek_cb(const char **s, // IO - Test string } +// +// 'write_color_patch()' - Write a color patch... +// + +static int // O - 1 on failure, 0 on success +write_color_patch(pdfio_stream_t *st, // I - Content stream + bool device)// I - Use device color? +{ + int col, // Current column + row; // Current row + double x, y, // Relative position + r, // Radius + hue, // Hue angle + sat, // Saturation + cc, // Computed color + red, // Red value + green, // Green value + blue; // Blue value + + + // Draw an 11x11 patch... + for (col = 0; col < 21; col ++) + { + for (row = 0; row < 21; row ++) + { + // Compute color in patch... + x = 0.1 * (col - 10); + y = 0.1 * (row - 10); + r = sqrt(x * x + y * y); + + if (r == 0.0) + { + red = green = blue = 1.0; + } + else + { + if ((sat = fabs(x)) < fabs(y)) + sat = fabs(y); + sat = pow(sat, 1.5); + + x /= r; + y /= r; + hue = 3.0 * atan2(y, x) / M_PI; + if (hue < 0.0) + hue += 6.0; + + cc = sat * (1.0 - fabs(fmod(hue, 2.0) - 1.0)) + 1.0 - sat; + + if (hue < 1.0) + { + red = 1.0; + green = cc; + blue = 1.0 - sat; + } + else if (hue < 2.0) + { + red = cc; + green = 1.0; + blue = 1.0 - sat; + } + else if (hue < 3.0) + { + red = 1.0 - sat; + green = 1.0; + blue = cc; + } + else if (hue < 4.0) + { + red = 1.0 - sat; + green = cc; + blue = 1.0; + } + else if (hue < 5.0) + { + red = cc; + green = 1.0 - sat; + blue = 1.0; + } + else + { + red = 1.0; + green = 1.0 - sat; + blue = cc; + } + } + + // Draw it... + if (device) + { + // Use device CMYK color instead of a calibrated RGB space... + double cyan = 1.0 - red; // Cyan color + double magenta = 1.0 - green; // Magenta color + double yellow = 1.0 - blue; // Yellow color + double black = cyan; // Black color + + if (black > magenta) + black = magenta; + if (black > yellow) + black = yellow; + + cyan -= black; + magenta -= black; + yellow -= black; + + printf("pdfioContentSetFillColorDeviceCMYK(c=%g, m=%g, y=%g, k=%g): ", cyan, magenta, yellow, black); + if (pdfioContentSetFillColorDeviceCMYK(st, cyan, magenta, yellow, black)) + puts("PASS"); + else + return (1); + } + else + { + // Use calibrate RGB space... + printf("pdfioContentSetFillColorRGB(r=%g, g=%g, b=%g): ", red, green, blue); + if (pdfioContentSetFillColorRGB(st, red, green, blue)) + puts("PASS"); + else + return (1); + } + + printf("pdfioContentPathRect(x=%g, y=%g, w=%g, h=%g): ", col * 9.0, row * 9.0, 9.0, 9.0); + if (pdfioContentPathRect(st, col * 9.0, row * 9.0, 9.0, 9.0)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentFill(even_odd=false): ", stdout); + if (pdfioContentFill(st, false)) + puts("PASS"); + else + return (1); + } + } + + return (0); +} + + +// +// 'write_color_test()' - Write a color test page... +// + +static int // O - 1 on failure, 0 on success +write_color_test(pdfio_file_t *pdf, // I - PDF file + int number) // I - Page number +{ + pdfio_dict_t *dict; // Page dictionary + pdfio_stream_t *st; // Page contents stream + + + fputs("pdfioDictCreate: ", stdout); + if ((dict = pdfioDictCreate(pdf)) != NULL) + puts("PASS"); + else + return (1); + + fputs("pdfioPageDictAddCalibratedColorSpace(AdobeRGB): ", stdout); + if (pdfioPageDictAddCalibratedColorSpace(dict, "AdobeRGB", 3, pdfioAdobeRGBGamma, pdfioAdobeRGBMatrix, pdfioAdobeRGBWhitePoint)) + puts("PASS"); + else + return (1); + + fputs("pdfioPageDictAddCalibratedColorSpace(DisplayP3): ", stdout); + if (pdfioPageDictAddCalibratedColorSpace(dict, "DisplayP3", 3, pdfioDisplayP3Gamma, pdfioDisplayP3Matrix, pdfioDisplayP3WhitePoint)) + puts("PASS"); + else + return (1); + + fputs("pdfioPageDictAddCalibratedColorSpace(sRGB): ", stdout); + if (pdfioPageDictAddCalibratedColorSpace(dict, "sRGB", 3, pdfioSRGBGamma, pdfioSRGBMatrix, pdfioSRGBWhitePoint)) + puts("PASS"); + else + return (1); + + printf("pdfioFileCreatePage(%d): ", number); + + if ((st = pdfioFileCreatePage(pdf, dict)) != NULL) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSave(): ", stdout); + if (pdfioContentSave(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSetFillColorSpace(AdobeRGB): ", stdout); + if (pdfioContentSetFillColorSpace(st, "AdobeRGB")) + puts("PASS"); + else + return (1); + + fputs("pdfioContentMatrixTranslate(82, 180): ", stdout); + if (pdfioContentMatrixTranslate(st, 82, 180)) + puts("PASS"); + else + return (1); + + if (write_color_patch(st, false)) + return (1); + + fputs("pdfioContentRestore(): ", stdout); + if (pdfioContentRestore(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSave(): ", stdout); + if (pdfioContentSave(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSetFillColorSpace(DisplayP3): ", stdout); + if (pdfioContentSetFillColorSpace(st, "DisplayP3")) + puts("PASS"); + else + return (1); + + fputs("pdfioContentMatrixTranslate(316, 180): ", stdout); + if (pdfioContentMatrixTranslate(st, 316, 180)) + puts("PASS"); + else + return (1); + + if (write_color_patch(st, false)) + return (1); + + fputs("pdfioContentRestore(): ", stdout); + if (pdfioContentRestore(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSave(): ", stdout); + if (pdfioContentSave(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSetFillColorSpace(sRGB): ", stdout); + if (pdfioContentSetFillColorSpace(st, "sRGB")) + puts("PASS"); + else + return (1); + + fputs("pdfioContentMatrixTranslate(82, 414): ", stdout); + if (pdfioContentMatrixTranslate(st, 82, 414)) + puts("PASS"); + else + return (1); + + if (write_color_patch(st, false)) + return (1); + + fputs("pdfioContentRestore(): ", stdout); + if (pdfioContentRestore(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentSave(): ", stdout); + if (pdfioContentSave(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioContentMatrixTranslate(316, 414): ", stdout); + if (pdfioContentMatrixTranslate(st, 316, 414)) + puts("PASS"); + else + return (1); + + if (write_color_patch(st, true)) + return (1); + + fputs("pdfioContentRestore(): ", stdout); + if (pdfioContentRestore(st)) + puts("PASS"); + else + return (1); + + fputs("pdfioStreamClose: ", stdout); + if (pdfioStreamClose(st)) + puts("PASS"); + else + return (1); + + return (0); +} + + // // 'write_page()' - Write a page to a PDF file. //