/* * << Haru Free PDF Library 2.0.3 >> -- hpdf_image.c * * Copyright (c) 1999-2006 Takeshi Kanno * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. * It is provided "as is" without express or implied warranty. * * 2006.08.12 modified. */ #include "hpdf_conf.h" #include "hpdf_utils.h" #include "hpdf.h" static const char *COL_CMYK = "DeviceCMYK"; static const char *COL_RGB = "DeviceRGB"; static const char *COL_GRAY = "DeviceGray"; static HPDF_STATUS LoadJpegHeader (HPDF_Image image, HPDF_Stream stream); /*---------------------------------------------------------------------------*/ static HPDF_STATUS LoadJpegHeader (HPDF_Image image, HPDF_Stream stream) { HPDF_UINT16 tag; HPDF_UINT16 height; HPDF_UINT16 width; HPDF_BYTE precision; HPDF_BYTE num_components; const char *color_space_name; HPDF_UINT len; HPDF_STATUS ret; HPDF_Array array; HPDF_PTRACE ((" HPDF_Image_LoadJpegHeader\n")); len = 2; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&tag, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); HPDF_UInt16Swap (&tag); if (tag != 0xFFD8) return HPDF_INVALID_JPEG_DATA; /* find SOF record */ for (;;) { HPDF_UINT16 size; len = 2; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&tag, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); HPDF_UInt16Swap (&tag); len = 2; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&size, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); HPDF_UInt16Swap (&size); HPDF_PTRACE (("tag=%04X size=%u\n", tag, size)); if (tag == 0xFFC0 || tag == 0xFFC1 || tag == 0xFFC2 || tag == 0xFFC9) { len = 1; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&precision, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); len = 2; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&height, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); HPDF_UInt16Swap (&height); len = 2; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&width, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); HPDF_UInt16Swap (&width); len = 1; if (HPDF_Stream_Read (stream, (HPDF_BYTE *)&num_components, &len) != HPDF_OK) return HPDF_Error_GetCode (stream->error); break; } else if ((tag | 0x00FF) != 0xFFFF) /* lost marker */ return HPDF_SetError (image->error, HPDF_UNSUPPORTED_JPEG_FORMAT, 0); if (HPDF_Stream_Seek (stream, size - 2, HPDF_SEEK_CUR) != HPDF_OK) return HPDF_Error_GetCode (stream->error); } if (HPDF_Dict_AddNumber (image, "Height", height) != HPDF_OK) return HPDF_Error_GetCode (stream->error); if (HPDF_Dict_AddNumber (image, "Width", width) != HPDF_OK) return HPDF_Error_GetCode (stream->error); /* classification of RGB and CMYK is less than perfect * YCbCr and YCCK are classified into RGB or CMYK. * * It is necessary to read APP14 data to distinguish colorspace perfectly. */ switch (num_components) { case 1: color_space_name = COL_GRAY; break; case 3: color_space_name = COL_RGB; break; case 4: array = HPDF_Array_New (image->mmgr); if (!array) return HPDF_Error_GetCode (stream->error); ret = HPDF_Dict_Add (image, "Decode", array); if (ret != HPDF_OK) return HPDF_Error_GetCode (stream->error); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 1)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 0)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 1)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 0)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 1)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 0)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 1)); ret += HPDF_Array_Add (array, HPDF_Number_New (image->mmgr, 0)); if (ret != HPDF_OK) return HPDF_Error_GetCode (stream->error); color_space_name = COL_CMYK; break; default: return HPDF_SetError (image->error, HPDF_UNSUPPORTED_JPEG_FORMAT, 0); } if (HPDF_Dict_Add (image, "ColorSpace", HPDF_Name_New (image->mmgr, color_space_name)) != HPDF_OK) return HPDF_Error_GetCode (stream->error); if (HPDF_Dict_Add (image, "BitsPerComponent", HPDF_Number_New (image->mmgr, precision)) != HPDF_OK) return HPDF_Error_GetCode (stream->error); return HPDF_OK; } HPDF_Image HPDF_Image_LoadJpegImage (HPDF_MMgr mmgr, HPDF_Stream jpeg_data, HPDF_Xref xref) { HPDF_Dict image; HPDF_STATUS ret = HPDF_OK; HPDF_PTRACE ((" HPDF_Image_LoadJpegImage\n")); image = HPDF_DictStream_New (mmgr, xref); if (!image) return NULL; image->header.obj_class |= HPDF_OSUBCLASS_XOBJECT; /* add requiered elements */ image->filter = HPDF_STREAM_FILTER_DCT_DECODE; ret += HPDF_Dict_AddName (image, "Type", "XObject"); ret += HPDF_Dict_AddName (image, "Subtype", "Image"); if (ret != HPDF_OK) return NULL; if (LoadJpegHeader (image, jpeg_data) != HPDF_OK) return NULL; if (HPDF_Stream_Seek (jpeg_data, 0, HPDF_SEEK_SET) != HPDF_OK) return NULL; for (;;) { HPDF_BYTE buf[HPDF_STREAM_BUF_SIZ]; HPDF_UINT len = HPDF_STREAM_BUF_SIZ; HPDF_STATUS ret = HPDF_Stream_Read (jpeg_data, buf, &len); if (ret != HPDF_OK) { if (ret == HPDF_STREAM_EOF) { if (len > 0) { ret = HPDF_Stream_Write (image->stream, buf, len); if (ret != HPDF_OK) return NULL; } break; } else return NULL; } if (HPDF_Stream_Write (image->stream, buf, len) != HPDF_OK) return NULL; } return image; } HPDF_Image HPDF_Image_LoadRawImage (HPDF_MMgr mmgr, HPDF_Stream raw_data, HPDF_Xref xref, HPDF_UINT width, HPDF_UINT height, HPDF_ColorSpace color_space) { HPDF_Dict image; HPDF_STATUS ret = HPDF_OK; HPDF_UINT size; HPDF_PTRACE ((" HPDF_Image_LoadRawImage\n")); if (color_space != HPDF_CS_DEVICE_GRAY && color_space != HPDF_CS_DEVICE_RGB && color_space != HPDF_CS_DEVICE_CMYK) { HPDF_SetError (mmgr->error, HPDF_INVALID_COLOR_SPACE, 0); return NULL; } image = HPDF_DictStream_New (mmgr, xref); if (!image) return NULL; image->header.obj_class |= HPDF_OSUBCLASS_XOBJECT; ret += HPDF_Dict_AddName (image, "Type", "XObject"); ret += HPDF_Dict_AddName (image, "Subtype", "Image"); if (ret != HPDF_OK) return NULL; if (color_space == HPDF_CS_DEVICE_GRAY) { size = width * height; ret = HPDF_Dict_AddName (image, "ColorSpace", COL_GRAY); } else if (color_space == HPDF_CS_DEVICE_CMYK) { size = width * height * 4; ret = HPDF_Dict_AddName (image, "ColorSpace", COL_CMYK); } else { size = width * height * 3; ret = HPDF_Dict_AddName (image, "ColorSpace", COL_RGB); } if (ret != HPDF_OK) return NULL; if (HPDF_Dict_AddNumber (image, "Width", width) != HPDF_OK) return NULL; if (HPDF_Dict_AddNumber (image, "Height", height) != HPDF_OK) return NULL; if (HPDF_Dict_AddNumber (image, "BitsPerComponent", 8) != HPDF_OK) return NULL; if (HPDF_Stream_WriteToStream (raw_data, image->stream, 0, NULL) != HPDF_OK) return NULL; if (image->stream->size != size) { HPDF_SetError (image->error, HPDF_INVALID_IMAGE, 0); return NULL; } return image; } HPDF_Image HPDF_Image_LoadRawImageFromMem (HPDF_MMgr mmgr, const HPDF_BYTE *buf, HPDF_Xref xref, HPDF_UINT width, HPDF_UINT height, HPDF_ColorSpace color_space, HPDF_UINT bits_per_component) { HPDF_Dict image; HPDF_STATUS ret = HPDF_OK; HPDF_UINT size; HPDF_PTRACE ((" HPDF_Image_LoadRawImageFromMem\n")); if (color_space != HPDF_CS_DEVICE_GRAY && color_space != HPDF_CS_DEVICE_RGB) { HPDF_SetError (mmgr->error, HPDF_INVALID_COLOR_SPACE, 0); return NULL; } if (bits_per_component != 1 && bits_per_component != 2 && bits_per_component != 4 && bits_per_component != 8) { HPDF_SetError (mmgr->error, HPDF_INVALID_IMAGE, 0); return NULL; } image = HPDF_DictStream_New (mmgr, xref); if (!image) return NULL; image->header.obj_class |= HPDF_OSUBCLASS_XOBJECT; ret += HPDF_Dict_AddName (image, "Type", "XObject"); ret += HPDF_Dict_AddName (image, "Subtype", "Image"); if (ret != HPDF_OK) return NULL; if (color_space == HPDF_CS_DEVICE_GRAY) { size = (HPDF_DOUBLE)width * height / (8 / bits_per_component) + 0.876; ret = HPDF_Dict_AddName (image, "ColorSpace", COL_GRAY); } else { size = (HPDF_DOUBLE)width * height / (8 / bits_per_component) + 0.876; size *= 3; ret = HPDF_Dict_AddName (image, "ColorSpace", COL_RGB); } if (ret != HPDF_OK) return NULL; if (HPDF_Dict_AddNumber (image, "Width", width) != HPDF_OK) return NULL; if (HPDF_Dict_AddNumber (image, "Height", height) != HPDF_OK) return NULL; if (HPDF_Dict_AddNumber (image, "BitsPerComponent", bits_per_component) != HPDF_OK) return NULL; if (HPDF_Stream_Write (image->stream, buf, size) != HPDF_OK) return NULL; return image; } HPDF_BOOL HPDF_Image_Validate (HPDF_Image image) { HPDF_Name subtype; HPDF_PTRACE ((" HPDF_Image_Validate\n")); if (!image) return HPDF_FALSE; if (image->header.obj_class != (HPDF_OSUBCLASS_XOBJECT | HPDF_OCLASS_DICT)) { HPDF_RaiseError (image->error, HPDF_INVALID_IMAGE, 0); return HPDF_FALSE; } subtype = HPDF_Dict_GetItem (image, "Subtype", HPDF_OCLASS_NAME); if (!subtype || HPDF_StrCmp (subtype->value, "Image") != 0) { HPDF_RaiseError (image->error, HPDF_INVALID_IMAGE, 0); return HPDF_FALSE; } return HPDF_TRUE; } HPDF_EXPORT(HPDF_Point) HPDF_Image_GetSize (HPDF_Image image) { HPDF_Number width; HPDF_Number height; HPDF_Point ret = {0, 0}; HPDF_PTRACE ((" HPDF_Image_GetSize\n")); if (!HPDF_Image_Validate (image)) return ret; width = HPDF_Dict_GetItem (image, "Width", HPDF_OCLASS_NUMBER); height = HPDF_Dict_GetItem (image, "Height", HPDF_OCLASS_NUMBER); if (width && height) { ret.x = width->value; ret.y = height->value; } return ret; } HPDF_EXPORT(HPDF_STATUS) HPDF_Image_GetSize2 (HPDF_Image image, HPDF_Point *size) { HPDF_Number width; HPDF_Number height; size->x = 0; size->y = 0; HPDF_PTRACE ((" HPDF_Image_GetSize\n")); if (!HPDF_Image_Validate (image)) return HPDF_INVALID_IMAGE; width = HPDF_Dict_GetItem (image, "Width", HPDF_OCLASS_NUMBER); height = HPDF_Dict_GetItem (image, "Height", HPDF_OCLASS_NUMBER); if (width && height) { size->x = width->value; size->y = height->value; } return HPDF_OK; } HPDF_EXPORT(HPDF_UINT) HPDF_Image_GetBitsPerComponent (HPDF_Image image) { HPDF_Number n; HPDF_PTRACE ((" HPDF_Image_GetBitsPerComponent\n")); if (!HPDF_Image_Validate (image)) return 0; n = HPDF_Dict_GetItem (image, "BitsPerComponent", HPDF_OCLASS_NUMBER); if (!n) return 0; return n->value; } HPDF_EXPORT(const char*) HPDF_Image_GetColorSpace (HPDF_Image image) { HPDF_Name n; HPDF_PTRACE ((" HPDF_Image_GetColorSpace\n")); n = HPDF_Dict_GetItem (image, "ColorSpace", HPDF_OCLASS_NAME); if (!n) { HPDF_CheckError (image->error); return NULL; } return n->value; } HPDF_EXPORT(HPDF_UINT) HPDF_Image_GetWidth (HPDF_Image image) { return HPDF_Image_GetSize (image).x; } HPDF_EXPORT(HPDF_UINT) HPDF_Image_GetHeight (HPDF_Image image) { return HPDF_Image_GetSize (image).y; } HPDF_STATUS HPDF_Image_SetMask (HPDF_Image image, HPDF_BOOL mask) { HPDF_Boolean image_mask; if (!HPDF_Image_Validate (image)) return HPDF_INVALID_IMAGE; if (mask && HPDF_Image_GetBitsPerComponent (image) != 1) return HPDF_SetError (image->error, HPDF_INVALID_BIT_PER_COMPONENT, 0); image_mask = HPDF_Dict_GetItem (image, "ImageMask", HPDF_OCLASS_BOOLEAN); if (!image_mask) { HPDF_STATUS ret; image_mask = HPDF_Boolean_New (image->mmgr, HPDF_FALSE); if ((ret = HPDF_Dict_Add (image, "ImageMask", image_mask)) != HPDF_OK) return ret; } image_mask->value = mask; return HPDF_OK; } HPDF_EXPORT(HPDF_STATUS) HPDF_Image_SetMaskImage (HPDF_Image image, HPDF_Image mask_image) { if (!HPDF_Image_Validate (image)) return HPDF_INVALID_IMAGE; if (!HPDF_Image_Validate (mask_image)) return HPDF_INVALID_IMAGE; if (HPDF_Image_SetMask (mask_image, HPDF_TRUE) != HPDF_OK) return HPDF_CheckError (image->error); return HPDF_Dict_Add (image, "Mask", mask_image); } HPDF_EXPORT(HPDF_STATUS) HPDF_Image_SetColorMask (HPDF_Image image, HPDF_UINT rmin, HPDF_UINT rmax, HPDF_UINT gmin, HPDF_UINT gmax, HPDF_UINT bmin, HPDF_UINT bmax) { HPDF_Array array; const char *name; HPDF_STATUS ret = HPDF_OK; if (!HPDF_Image_Validate (image)) return HPDF_INVALID_IMAGE; if (HPDF_Dict_GetItem (image, "ImageMask", HPDF_OCLASS_BOOLEAN)) return HPDF_RaiseError (image->error, HPDF_INVALID_OPERATION, 0); if (HPDF_Image_GetBitsPerComponent (image) != 8) return HPDF_RaiseError (image->error, HPDF_INVALID_BIT_PER_COMPONENT, 0); name = HPDF_Image_GetColorSpace (image); if (!name || HPDF_StrCmp (COL_RGB, name) != 0) return HPDF_RaiseError (image->error, HPDF_INVALID_COLOR_SPACE, 0); /* Each integer must be in the range 0 to 2^BitsPerComponent - 1 */ if (rmax > 255 || gmax > 255 || bmax > 255) return HPDF_RaiseError (image->error, HPDF_INVALID_PARAMETER, 0); array = HPDF_Array_New (image->mmgr); if (!array) return HPDF_CheckError (image->error); ret += HPDF_Dict_Add (image, "Mask", array); ret += HPDF_Array_AddNumber (array, rmin); ret += HPDF_Array_AddNumber (array, rmax); ret += HPDF_Array_AddNumber (array, gmin); ret += HPDF_Array_AddNumber (array, gmax); ret += HPDF_Array_AddNumber (array, bmin); ret += HPDF_Array_AddNumber (array, bmax); if (ret != HPDF_OK) return HPDF_CheckError (image->error); return HPDF_OK; }