/*---------------------------------------------------------------------------* | PDFlib - A library for generating PDF on the fly | +---------------------------------------------------------------------------+ | Copyright (c) 1997-2007 Thomas Merz and PDFlib GmbH. All rights reserved. | +---------------------------------------------------------------------------+ | | | This software is subject to the PDFlib license. It is NOT in the | | public domain. Extended versions and commercial licenses are | | available, please check http://www.pdflib.com. | | | *---------------------------------------------------------------------------*/ /* $Id: p_image.c,v 1.2 2009/10/20 18:14:16 scuri Exp $ * * PDFlib image routines * */ #define P_IMAGE_C #include "p_intern.h" #include "p_color.h" #include "p_defopt.h" #include "p_font.h" #include "p_image.h" #include "p_layer.h" #include "p_tagged.h" static void pdf_init_image_struct(PDF *p, pdf_image *image) { (void) p; /********** option variables *************/ image->verbose = p->debug[(int) 'i']; image->verbose = pdf_get_errorpolicy(p, NULL, image->verbose); image->bitreverse = pdc_false; image->bpc = pdc_undef; image->components = pdc_undef; image->height_pixel = pdc_undef; image->ignoremask = pdc_false; image->ignoreorient = pdc_false; image->doinline = pdc_false; image->interpolate = pdc_false; image->invert = pdc_false; image->jpegoptimize = pdc_true; image->passthrough = pdc_undef; image->K = 0; image->imagemask = pdc_false; image->mask = pdc_undef; image->ri = AutoIntent; image->page = 1; image->reference = pdf_ref_direct; image->width_pixel = pdc_undef; image->topdown_save = pdc_false; image->iconname = (char *) NULL; /*****************************************/ image->orientation = 1; image->transparent = pdc_false; image->compression = pdf_comp_none; image->predictor = pred_default; image->in_use = pdc_false; image->corrupt = pdc_false; image->fp = (pdc_file *) NULL; image->filename = (char *) NULL; image->params = (char *) NULL; image->dpi_x = 0; image->dpi_y = 0; image->strips = 1; image->rowsperstrip = 1; image->colorspace = pdc_undef; image->dochandle = pdc_undef; /* this means "not a PDI page" */ image->use_raw = pdc_false; image->pixelmode = pdc_undef; image->type = pdf_img_auto; image->transval[0] = 0; image->transval[1] = 0; image->transval[2] = 0; image->transval[3] = 0; /********* image-type specific stuff *****/ /* This is ugly, but we must do it here since both the TIFF and JPEG * modules are affected. */ image->info.jpeg.jpegifoffset = 0L; } void pdf_init_images(PDF *p) { int im; p->images_capacity = IMAGES_CHUNKSIZE; p->images = (pdf_image *) pdc_malloc(p->pdc, sizeof(pdf_image) * p->images_capacity, "pdf_init_images"); for (im = 0; im < p->images_capacity; im++) pdf_init_image_struct(p, &(p->images[im])); } void pdf_grow_images(PDF *p) { int im; p->images = (pdf_image *) pdc_realloc(p->pdc, p->images, sizeof(pdf_image) * 2 * p->images_capacity, "pdf_grow_images"); for (im = p->images_capacity; im < 2 * p->images_capacity; im++) pdf_init_image_struct(p, &(p->images[im])); for (im = 0; im < p->images_capacity; im++) p->images[im].src.private_data = &(p->images[im]); p->images_capacity *= 2; } void pdf_cleanup_image(PDF *p, int im) { pdf_image *image = &p->images[im]; /* clean up parameter string if necessary */ if (image->params) { pdc_free(p->pdc, image->params); image->params = NULL; } if (image->filename) { pdc_free(p->pdc, image->filename); image->filename = NULL; } if (image->fp) { pdc_fclose(image->fp); image->fp = NULL; } if (image->iconname) { pdc_free(p->pdc, image->iconname); image->iconname = NULL; } /* type-specific cleanups */ if (image->type == pdf_img_gif) pdf_cleanup_gif(p, image); if (image->type == pdf_img_jpeg) pdf_cleanup_jpeg(p, image); /* free the image slot and prepare for next use */ pdf_init_image_struct(p, image); } void pdf_cleanup_images(PDF *p) { int im; if (!p->images) return; /* Free images which the caller left open */ /* When we think of inter-document survival of images, ** we MUST NOT FORGET that the current TIFF algorithm ** depends on contiguous image slots for the image strips! */ for (im = 0; im < p->images_capacity; im++) { if (p->images[im].in_use) /* found used slot */ pdf_cleanup_image(p, im); /* free image descriptor */ } pdc_free(p->pdc, p->images); p->images = NULL; } void pdf_init_xobjects(PDF *p) { int idx; p->xobjects_number = 0; if (p->xobjects == (pdf_xobject *) 0) { p->xobjects_capacity = XOBJECTS_CHUNKSIZE; p->xobjects = (pdf_xobject *) pdc_malloc(p->pdc, sizeof(pdf_xobject) * p->xobjects_capacity, "pdf_init_xobjects"); } for (idx = 0; idx < p->xobjects_capacity; idx++) p->xobjects[idx].flags = 0; } int pdf_new_xobject(PDF *p, pdf_xobj_type type, pdc_id obj_id) { static const char fn[] = "pdf_new_xobject"; int i, slot = p->xobjects_number++; if (slot == p->xobjects_capacity) { p->xobjects = (pdf_xobject *) pdc_realloc(p->pdc, p->xobjects, sizeof(pdf_xobject) * 2 * p->xobjects_capacity, fn); for (i = p->xobjects_capacity; i < 2 * p->xobjects_capacity; i++) p->xobjects[i].flags = 0; p->xobjects_capacity *= 2; } if (obj_id == PDC_NEW_ID) obj_id = pdc_begin_obj(p->out, PDC_NEW_ID); p->xobjects[slot].obj_id = obj_id; p->xobjects[slot].type = type; p->xobjects[slot].flags = xobj_flag_used; return slot; } pdc_id pdf_get_xobject(PDF *p, int im) { if (im >= 0 && im < p->images_capacity) { pdf_image *img = &p->images[im]; if (img->in_use) return p->xobjects[img->no].obj_id; } return PDC_BAD_ID; } void pdf_write_xobjects(PDF *p) { if (p->xobjects_number > 0) { pdc_bool hit = pdc_false; int i; for (i = 0; i < p->xobjects_number; ++i) { if (p->xobjects[i].flags & xobj_flag_write) { if (!hit) { pdc_puts(p->out, "/XObject"); pdc_begin_dict(p->out); hit = pdc_true; } pdc_printf(p->out, "/I%d", i); pdc_objref(p->out, "", p->xobjects[i].obj_id); p->xobjects[i].flags &= ~xobj_flag_write; } } if (hit) pdc_end_dict(p->out); } } void pdf_get_page_xobjects(PDF *p, pdf_reslist *rl) { int i; for (i = 0; i < p->xobjects_number; i++) { if (p->xobjects[i].flags & xobj_flag_write) { p->xobjects[i].flags &= ~xobj_flag_write; pdf_add_reslist(p, rl, i); } } } void pdf_mark_page_xobject(PDF *p, int n) { p->xobjects[n].flags |= xobj_flag_write; } void pdf_cleanup_xobjects(PDF *p) { if (p->xobjects) { pdc_free(p->pdc, p->xobjects); p->xobjects = NULL; } } /* ---------------------------- put image ----------------------------------- */ void pdf_put_inline_image(PDF *p, int im) { static const char *fn = "pdf_put_inline_image"; pdf_image *image; pdc_matrix m; PDF_data_source *src; int i; image = &p->images[im]; /* Image object */ image->no = -1; pdf__save(p); pdc_scale_matrix(image->width, image->height, &m); pdf_concat_raw(p, &m); pdc_puts(p->out, "BI"); pdc_printf(p->out, "/W %d", (int) image->width); pdc_printf(p->out, "/H %d", (int) image->height); /* Acrobat 7 and 8 require /BPC even for image masks */ pdc_printf(p->out, "/BPC %d", image->bpc); if (image->imagemask == pdc_true) { pdc_puts(p->out, "/IM true"); } else if (image->colorspace != pdc_undef) { switch (p->colorspaces[image->colorspace].type) { case DeviceGray: pdc_printf(p->out, "/CS/G"); break; case DeviceRGB: pdc_printf(p->out, "/CS/RGB"); break; case DeviceCMYK: pdc_printf(p->out, "/CS/CMYK"); break; default: pdc_error(p->pdc, PDF_E_INT_BADCS, fn, pdc_errprintf(p->pdc, "%d", image->colorspace), pdc_errprintf(p->pdc, "%d", (int) p->colorspaces[image->colorspace].type), 0); break; } } if (image->compression != pdf_comp_none) { pdc_printf(p->out, "/F/%s", pdc_get_keyword(image->compression, pdf_shortfilter_pdfkeylist)); } /* prepare precompressed (raw) image data */ if (image->use_raw && (image->params || image->predictor != pred_default || image->compression == pdf_comp_ccitt)) { pdc_printf(p->out, "/DP[<<"); /* write EarlyChange */ if (image->params) pdc_puts(p->out, image->params); if (image->compression == pdf_comp_ccitt) { if (image->K != 0) pdc_printf(p->out, "/K %d", image->K); } if (image->compression == pdf_comp_flate || image->compression == pdf_comp_lzw) { if (image->predictor != pred_default) { pdc_printf(p->out, "/Predictor %d", (int) image->predictor); pdc_printf(p->out, "/Columns %d", (int) image->width); if (image->bpc != 8) pdc_printf(p->out, "/BitsPerComponent %d", image->bpc); if (image->components != 1) /* 1 is default */ pdc_printf(p->out, "/Colors %d", image->components); } } if (image->compression == pdf_comp_ccitt) { if ((int) image->width != 1728) /* CCITT default width */ pdc_printf(p->out, "/Columns %d", (int) image->width); /* /Rows is optional, but it helps in certain cases with damaged * CCITT compressed image data */ pdc_printf(p->out, "/Rows %d", (int) fabs(image->height)); } pdc_puts(p->out, ">>]"); /* DecodeParms dict and array */ } if (image->ri != AutoIntent) { pdc_printf(p->out, "/Intent/%s", pdc_get_keyword(image->ri, pdf_renderingintent_pdfkeylist)); } if (image->interpolate) { pdc_puts(p->out, "/I true"); } if (image->invert) { pdc_puts(p->out, "/D[1 0"); for (i = 1; i < image->components; i++) pdc_puts(p->out, " 1 0"); pdc_puts(p->out, "]ID\n"); } else { pdc_puts(p->out, " ID\n"); } /* Write the actual image data to the content stream */ src = &image->src; /* We can't use pdf_copy_stream() here because it automatically * generates a stream object, which is not correct for inline * image data. */ if (src->init) src->init(p, src); while (src->fill(p, src)) pdc_write(p->out, src->next_byte, src->bytes_available); if (src->terminate) src->terminate(p, src); /* Acrobat requires whitespace between image data and "EI" */ pdc_puts(p->out, "\nEI\n"); pdf__restore(p); /* Do the equivalent of PDF_close_image() since the image handle * cannot be re-used anyway. */ pdf_cleanup_image(p, im); } void pdf_put_image(PDF *p, int im, pdc_bool firststrip, pdc_bool checkcontentstream) { static const char *fn = "pdf_put_image"; pdc_bool logg3 = pdc_logg_is_enabled(p->pdc, 3, trc_image); pdc_id length_id; pdf_image *image; int i; pdf_compression compression; image = &p->images[im]; if (logg3) pdc_logg(p->pdc, "\t\t\tput image %d to PDF file ...\n", im); /* Images may also be written to the output before the first page */ if (checkcontentstream && PDF_GET_STATE(p) == pdf_state_page) pdf_end_contents_section(p); pdc_logg_cond(p->pdc, 2, trc_image, "\tpdf_put_image:\n" "\t\t\tim = %d\n" "\t\t\timage->colorspace = %d\n", im, image->colorspace); if (image->colorspace != pdc_undef) pdc_logg_cond(p->pdc, 2, trc_image, "\t\t\tcolor space type = %d\n", (int) p->colorspaces[image->colorspace].type); /* Image object */ image->no = pdf_new_xobject(p, image_xobject, PDC_NEW_ID); pdc_begin_dict(p->out); /* XObject */ pdc_puts(p->out, "/Subtype/Image\n"); pdc_printf(p->out, "/Width %d\n", (int) image->width); pdc_printf(p->out, "/Height %d\n", (int) fabs(image->height)); /* * Transparency handling */ /* Masking by color: single transparent color value */ if (image->transparent && image->colorspace != pdc_undef) { pdf_colorspace *cs = &p->colorspaces[image->colorspace]; switch (cs->type) { case Indexed: case DeviceGray: pdc_printf(p->out,"/Mask[%d %d]\n", (int) image->transval[0], (int) image->transval[0]); break; case DeviceRGB: pdc_printf(p->out,"/Mask[%d %d %d %d %d %d]\n", (int) image->transval[0], (int) image->transval[0], (int) image->transval[1], (int) image->transval[1], (int) image->transval[2], (int) image->transval[2]); break; case DeviceCMYK: pdc_printf(p->out,"/Mask[%d %d %d %d %d %d %d %d]\n", (int) image->transval[0], (int) image->transval[0], (int) image->transval[1], (int) image->transval[1], (int) image->transval[2], (int) image->transval[2], (int) image->transval[3], (int) image->transval[3]); break; default: pdc_error(p->pdc, PDF_E_INT_BADCS, fn, pdc_errprintf(p->pdc, "%d", image->colorspace), pdc_errprintf(p->pdc, "%d", (int) p->colorspaces[image->colorspace].type), 0); } /* Masking by position: separate bitmap mask */ } else if (image->mask != pdc_undef && p->images[image->mask].bpc > 1) { pdc_objref(p->out, "/SMask", p->xobjects[p->images[image->mask].no].obj_id); } else if (image->mask != pdc_undef) { pdc_objref(p->out, "/Mask", p->xobjects[p->images[image->mask].no].obj_id); } /* * /BitsPerComponent is optional for image masks according to the * PDF reference, but some viewers require it nevertheless. * We must therefore always write it. */ if (image->type != pdf_img_jpeg2000) pdc_printf(p->out, "/BitsPerComponent %d\n", image->bpc); if (image->imagemask) { pdc_puts(p->out, "/ImageMask true\n"); if (image->type == pdf_img_jpeg2000) pdc_puts(p->out, "/SMaskInData 1\n"); } else if (image->colorspace != pdc_undef) { switch (p->colorspaces[image->colorspace].type) { case DeviceGray: break; case DeviceRGB: break; case DeviceCMYK: break; case Indexed: break; default: pdc_error(p->pdc, PDF_E_INT_BADCS, fn, pdc_errprintf(p->pdc, "%d", image->colorspace), pdc_errprintf(p->pdc, "%d", (int) p->colorspaces[image->colorspace].type), 0); } pdc_puts(p->out, "/ColorSpace"); pdf_write_colorspace(p, image->colorspace, pdc_false); pdc_puts(p->out, "\n"); } if (image->invert) { pdc_puts(p->out, "/Decode[1 0"); for (i = 1; i < image->components; i++) pdc_puts(p->out, " 1 0"); pdc_end_array(p->out); } if (image->ri != AutoIntent) { pdc_printf(p->out, "/Intent/%s\n", pdc_get_keyword(image->ri, pdf_renderingintent_pdfkeylist)); } if (image->interpolate) { pdc_puts(p->out, "/Interpolate true\n"); } /* special case: referenced image data instead of direct data */ if (image->reference != pdf_ref_direct) { if (image->compression != pdf_comp_none) { pdc_printf(p->out, "/FFilter[/%s]\n", pdc_get_keyword(image->compression, pdf_filter_pdfkeylist)); } if (image->compression == pdf_comp_ccitt) { pdc_puts(p->out, "/FDecodeParms[<<"); if ((int) image->width != 1728) /* CCITT default width */ pdc_printf(p->out, "/Columns %d", (int) image->width); /* /Rows is optional, but it helps in certain cases with damaged * CCITT compressed image data */ pdc_printf(p->out, "/Rows %d", (int) fabs(image->height)); if (image->K != 0) pdc_printf(p->out, "/K %d", image->K); pdc_puts(p->out, ">>]\n"); } if (image->reference == pdf_ref_file) { /* LATER: make image file name platform-neutral: * Change : to / on the Mac * Change \ to / on Windows */ pdc_puts(p->out, "/F"); pdc_put_pdfstring(p->out, image->filename, 0); pdc_puts(p->out, "/Length 0"); } else if (image->reference == pdf_ref_url) { pdc_puts(p->out, "/F<out, image->filename, 0); pdc_puts(p->out, ">>/Length 0"); } pdc_end_dict(p->out); /* XObject */ /* We must avoid pdc_begin/end_pdfstream() here in order to * generate a really empty stream. * Note: this doesn't work with AES encryption, but we don't * bother to fix it since file references are no longer * supported anyway. */ pdc_puts(p->out, "stream\n"); /* dummy image stream */ pdc_puts(p->out, "endstream\n"); pdc_end_obj(p->out); /* XObject */ if (PDF_GET_STATE(p) == pdf_state_page) pdf_begin_contents_section(p); return; } compression = image->compression; /* * Now the (more common) handling of actual image * data to be included in the PDF output. */ /* force compression if not a recognized precompressed image format */ if ((!image->use_raw || compression == pdf_comp_none) && pdc_get_compresslevel(p->out)) compression = pdf_comp_flate; if (compression != pdf_comp_none) pdc_printf(p->out, "/Filter/%s\n", pdc_get_keyword(compression, pdf_filter_pdfkeylist)); /* prepare precompressed (raw) image data; avoid empty DecodeParms */ if (image->use_raw && (image->params || image->predictor != pred_default || compression == pdf_comp_ccitt)) { pdc_printf(p->out, "/DecodeParms<<"); /* write EarlyChange */ if (image->params) pdc_puts(p->out, image->params); if (compression == pdf_comp_ccitt) { if (image->K != 0) pdc_printf(p->out, "/K %d", image->K); } if (compression == pdf_comp_flate || compression == pdf_comp_lzw) { if (image->predictor != pred_default) { pdc_printf(p->out, "/Predictor %d", (int) image->predictor); pdc_printf(p->out, "/Columns %d", (int) image->width); if (image->bpc != 8) pdc_printf(p->out, "/BitsPerComponent %d", image->bpc); if (image->components != 1) /* 1 is default */ pdc_printf(p->out, "/Colors %d", image->components); } } if (compression == pdf_comp_ccitt) { if ((int) image->width != 1728) /* CCITT default width */ pdc_printf(p->out, "/Columns %d", (int) image->width); /* /Rows is optional, but it helps in certain cases with damaged * CCITT compressed image data */ pdc_printf(p->out, "/Rows %d", (int) fabs(image->height)); } pdc_puts(p->out, ">>\n"); /* DecodeParms dict */ } /* Write the actual image data */ length_id = pdc_alloc_id(p->out); pdc_objref(p->out, "/Length", length_id); pdc_end_dict(p->out); /* XObject */ /* image data */ /* * We must check "image->compression" here since this describes the * actual status of the input data, as opposed to "compression" * which describes the desired status of the output data. */ pdf_copy_stream(p, &image->src, !image->use_raw || image->compression == pdf_comp_none); pdc_end_obj(p->out); /* XObject */ pdc_put_pdfstreamlength(p->out, length_id); if (p->flush & pdc_flush_content) pdc_flush_stream(p->out); /* * Write colormap information for indexed color spaces */ if (firststrip && image->colorspace != pdc_undef && p->colorspaces[image->colorspace].type == Indexed) { pdf_write_colormap(p, image->colorspace); } if (checkcontentstream && PDF_GET_STATE(p) == pdf_state_page) pdf_begin_contents_section(p); if (p->flush & pdc_flush_content) pdc_flush_stream(p->out); } /* ---------------------------- fit image ----------------------------------- */ void pdf__fit_image(PDF *p, int im, pdc_scalar x, pdc_scalar y, const char *optlist) { pdf_image *image; int legal_states; pdf_check_handle(p, im, pdc_imagehandle); image = &p->images[im]; if (PDF_GET_STATE(p) == pdf_state_glyph && !pdf_get_t3colorized(p) && image->imagemask == pdc_false) legal_states = pdf_state_page | pdf_state_pattern | pdf_state_template; else if (PDF_GET_STATE(p) == pdf_state_pattern && pdf_get_shading_painttype(p) == 2 && image->imagemask == pdc_false) legal_states = pdf_state_page | pdf_state_glyph | pdf_state_template; else legal_states = pdf_state_content; PDF_CHECK_STATE(p, legal_states); if (PDF_GET_STATE(p) == pdf_state_template && im == p->templ) pdc_error(p->pdc, PDF_E_TEMPLATE_SELF, pdc_errprintf(p->pdc, "%d", im), 0, 0, 0); pdc_check_number(p->pdc, "x", x); pdc_check_number(p->pdc, "y", y); pdf_place_xobject(p, im, x, y, optlist); } void pdf_init_xobject_options(PDF *p, pdf_xobject_options *xo) { xo->adjustpage = pdc_false; xo->blind = pdc_false; xo->filename = NULL; xo->flags = 0; xo->imagewarning = p->debug[(int) 'i']; xo->ignoreorientation = pdc_false; xo->im = -1; xo->mask = 0; xo->dpi[0] = 0; xo->dpi[1] = 0; xo->page = 1; xo->scale[0] = 1; xo->scale[1] = 1; } void pdf_get_xobject_options(PDF *p, pdf_xobject_options *xo, pdc_resopt *resopts) { int inum; (void) p; if (!(xo->flags & is_block)) { pdc_get_optvalues("adjustpage", resopts, &xo->adjustpage, NULL); pdc_get_optvalues("blind", resopts, &xo->blind, NULL); } if (xo->flags & is_image) { if (pdc_get_optvalues("ignoreorientation", resopts, &xo->ignoreorientation, NULL)) xo->mask |= (1L << xo_ignoreorientation); inum = pdc_get_optvalues("dpi", resopts, xo->dpi, NULL); if (inum) { if (inum == 1) xo->dpi[1] = xo->dpi[0]; xo->mask |= (1L << xo_dpi); } } if (xo->flags & is_block) { if (pdc_get_optvalues("imagewarning", resopts, &xo->imagewarning, NULL)) xo->mask |= (1L << xo_imagewarning); } inum = pdc_get_optvalues("scale", resopts, xo->scale, NULL); if (inum) { if (inum == 1) xo->scale[1] = xo->scale[0]; xo->mask |= (1L << xo_scale); } } /* definitions of fit xobject options */ static const pdc_defopt pdf_fit_xobject_options[] = { PDF_XOBJECT_OPTIONS1 PDF_XOBJECT_OPTIONS2 PDF_XOBJECT_OPTIONS3 PDF_FIT_OPTIONS1 PDF_FIT_OPTIONS2 PDC_OPT_TERMINATE }; pdc_resopt * pdf_parse_fitxobject_optlist(PDF *p, int im, pdf_xobject_options *xo, pdf_fit_options *fit, const char *optlist) { pdc_resopt *resopts = NULL; pdf_image *image = &p->images[im]; /* initialize */ pdf_init_xobject_options(p, xo); xo->im = im; if (p->xobjects[image->no].type == image_xobject) { xo->flags |= is_image; xo->dpi[0] = dpi_internal; xo->dpi[1] = dpi_internal; xo->ignoreorientation = image->ignoreorient; } pdf_init_fit_options(p, pdc_false, fit); fit->flags |= is_image; /* parsing option list */ if (optlist && strlen(optlist)) { pdc_clientdata data; pdf_set_clientdata(p, &data); resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_fit_xobject_options, &data, pdc_true); pdf_get_xobject_options(p, xo, resopts); pdf_get_fit_options(p, pdc_false, fit, resopts); } return resopts; } void pdf_place_xobject(PDF *p, int im, pdc_scalar x, pdc_scalar y, const char *optlist) { pdf_xobject_options xo; pdf_fit_options fit; /* initialize */ pdf_parse_fitxobject_optlist(p, im, &xo, &fit, optlist); fit.refpoint[0] = x; fit.refpoint[1] = y; /* put out xobject */ if (!xo.blind) { pdf_end_text(p); pdf_begin_contents_section(p); pdf__save(p); } pdf_fit_xobject_internal(p, &xo, &fit, NULL); if (!xo.blind) pdf__restore(p); } void pdf_fit_xobject_internal(PDF *p, pdf_xobject_options *xo, pdf_fit_options *fit, pdc_matrix *immatrix) { pdc_bool logg3 = pdc_logg_is_enabled(p->pdc, 3, trc_image); pdf_image *image = &p->images[xo->im]; pdf_xobject_options xo_save; pdc_rectangle matchrect; pdf_fit_options fit_save; pdc_matrix m, mm, sm, ctm_clip, ctm_save; pdc_vector tmpscale, elemscale, fitscale, purescale; pdc_vector elemsize, mirror, shift, relpos; pdc_vector polyline[5]; pdc_box fitbox, clipbox, elembox, redbox; pdc_scalar x, y, ss; pdc_scalar rowsize = 1, lastratio = 1; pdc_scalar dpi_x, dpi_y, tx = 0, ty = 0, boxwidth, boxheight; pdc_bool kclip = pdc_false, kcliptiff = pdc_false; int indangle, indmirror; int is, ip, islast; int imageno; /* initialize */ tmpscale.x = 1; tmpscale.y = 1; if (image->mask != pdc_undef) { ctm_save = p->curr_ppt->gstate[p->curr_ppt->sl].ctm; xo_save = *xo; fit_save = *fit; } else { pdc_identity_matrix(&ctm_save); } /* box size */ boxwidth = fit->boxsize[0]; boxheight = fit->boxsize[1]; /* element size */ elemsize.x = fabs(image->width); elemsize.y = fabs(image->height); if (logg3) pdc_logg(p->pdc, "\t\t\tfitbox size: width=%g, height=%g\n" "\t\t\telement size: width=%g, height=%g\n", boxwidth, boxheight, elemsize.x, elemsize.y); /* clipping */ if (!kclip) { kclip = pdf_get_mbox_clipping(p, fit->matchbox, elemsize.x, elemsize.y, &clipbox); } if (logg3 && kclip) pdc_logg(p->pdc, "\t\t\tclip box: llx=%g, lly=%g, urx=%g, ury=%g\n", clipbox.ll.x, clipbox.ll.y, clipbox.ur.x, clipbox.ur.y); /* TIFF image orientation */ if (image->orientation != 1 && !xo->ignoreorientation) { /* Tag Orientation = 1, 2, 3, 4, 5, 6, 7, 8 */ const int addangle[8] = {0, 0, 180, 180, 90, 270, 270, 90}; const int rowmirror[8] = {1, -1, 1, -1, -1, 1, -1, 1}; if (logg3) pdc_logg(p->pdc, "\t\t\torientation tag: %d\n", image->orientation); is = image->orientation - 1; fit->orientate += addangle[is]; if (fit->orientate >= 360) fit->orientate -= 360; tmpscale.x = rowmirror[is]; if (kclip) { switch (addangle[is]) { default: elembox = clipbox; break; case 90: elembox.ll.x = clipbox.ll.y; elembox.ll.y = elemsize.x - clipbox.ur.x; elembox.ur.x = clipbox.ur.y; elembox.ur.y = elemsize.x - clipbox.ll.x; break; case 180: elembox.ll.x = elemsize.x - clipbox.ur.x; elembox.ll.y = elemsize.y - clipbox.ur.y; elembox.ur.x = elemsize.x - clipbox.ll.x; elembox.ur.y = elemsize.y - clipbox.ll.y; break; case 270: elembox.ll.x = elemsize.y - clipbox.ur.y; elembox.ll.y = clipbox.ll.x; elembox.ur.x = elemsize.y - clipbox.ll.y; elembox.ur.y = clipbox.ur.x; break; } clipbox = elembox; if (tmpscale.x == -1) { clipbox.ll.x = elemsize.x - elembox.ur.x; clipbox.ur.x = elemsize.x - elembox.ll.x; } } } /* Compensate inverted direction handling in TIFFReadRGBAImageOriented() */ if (!image->use_raw && image->pixelmode == pdc_true) { tmpscale.y = -1; elembox = clipbox; clipbox.ll.y = elemsize.y - elembox.ur.y; clipbox.ur.y = elemsize.y - elembox.ll.y; } if (logg3 && kclip) pdc_logg(p->pdc, "\t\t\tclip box: llx=%g, lly=%g, urx=%g, ury=%g " "(corrected)\n", clipbox.ll.x, clipbox.ll.y, clipbox.ur.x, clipbox.ur.y); /* image scale */ elemscale.x = tmpscale.x * xo->scale[0]; elemscale.y = tmpscale.y * xo->scale[1]; if (logg3) pdc_logg(p->pdc, "\t\t\telement scaling: sx=%g, sy=%g " "(user, correction mirroring)\n", elemscale.x, elemscale.y); /* element size */ elemsize.x = fabs(clipbox.ur.x - clipbox.ll.x); elemsize.y = fabs(clipbox.ur.y - clipbox.ll.y); /* calculation of image scale and size */ if (xo->flags & is_image) { tmpscale.x = 1.0; tmpscale.y = 1.0; if (xo->dpi[0] == dpi_internal) { dpi_x = image->dpi_x; dpi_y = image->dpi_y; if (dpi_x > 0 && dpi_y > 0) { tmpscale.x = 72.0 / dpi_x; tmpscale.y = 72.0 / dpi_y; } else if (dpi_x < 0 && dpi_y < 0) { tmpscale.y = dpi_y / dpi_x; } } else if (xo->dpi[0] > 0) { tmpscale.x = 72.0 / xo->dpi[0]; tmpscale.y = 72.0 / xo->dpi[1]; } elemscale.x *= tmpscale.x; elemscale.y *= tmpscale.y; rowsize = elemscale.y * image->rowsperstrip; if (logg3) pdc_logg(p->pdc, "\t\t\telement scaling: sx=%g, sy=%g " "(relating to 72dpi)\n", tmpscale.x, tmpscale.y); } /* pure scaling without mirroring */ purescale.x = fabs(elemscale.x); purescale.y = fabs(elemscale.y); /* element size */ elemsize.x *= purescale.x; elemsize.y *= purescale.y; if (logg3) pdc_logg(p->pdc, "\t\t\telement size: width=%g, height=%g (scaled)\n", elemsize.x, elemsize.y); if (xo->flags & is_image) { elemscale.x *= image->width; elemscale.y *= image->height; lastratio = (elemscale.y / rowsize) - (image->strips - 1); } /* mirroring */ indmirror = 0; if (elemscale.x < 0 && elemscale.y < 0) indmirror = 2; else if (elemscale.x < 0) indmirror = 1; else if (elemscale.y < 0) indmirror = 3; /* orientation */ indangle = fit->orientate / 90; if (indangle % 2) { ss = elemsize.x; elemsize.x = elemsize.y; elemsize.y = ss; } /* box for fitting */ fitbox.ll.x = 0; fitbox.ll.y = 0; fitbox.ur.x = boxwidth; fitbox.ur.y = boxheight; /* relative position */ relpos.x = fit->position[0] / 100.0; relpos.y = fit->position[1] / 100.0; /* calculate image box */ if (fit->fitmethod == pdc_tauto) fit->fitmethod = pdc_meet; pdc_place_element(fit->fitmethod, 1.0, &fitbox, &relpos, &elemsize, &relpos, &elembox, &fitscale); if (logg3) pdc_logg(p->pdc, "\t\t\telement scaling: sx=%g, sy=%g " "(relating to fitbox)\n", fitscale.x, fitscale.y); /* reference point */ x = fit->refpoint[0]; y = fit->refpoint[1]; /* adjust page size */ if (!xo->blind && xo->adjustpage && PDF_GET_STATE(p) == pdf_state_page) { pdc_scalar urx, ury, width, height, theight; urx = 2 * x + elembox.ur.x; ury = 2 * y + elembox.ur.y; pdc_transform_point(&p->curr_ppt->gstate[p->curr_ppt->sl].ctm, urx, ury, &width, &theight); height = (p->ydirection > 0) ? theight : pdf_get_pageheight(p) - p->ydirection * theight; if (height < p->ydirection * theight / 2.0) pdc_error(p->pdc, PDF_E_IMAGE_NOADJUST, 0, 0, 0, 0); width = fabs(width); height = fabs(height); if ((width < PDF_ACRO_MINPAGE || width > PDF_ACRO_MAXPAGE || height < PDF_ACRO_MINPAGE || height > PDF_ACRO_MAXPAGE)) pdc_warning(p->pdc, PDF_E_PAGE_SIZE_ACRO, 0, 0, 0, 0); pdf_set_pagebox(p, pdf_mediabox, 0, 0, width, height); pdf_set_pagebox(p, pdf_artbox, 0, 0, 0, 0); pdf_set_pagebox(p, pdf_bleedbox, 0, 0, 0, 0); pdf_set_pagebox(p, pdf_cropbox, 0, 0, 0, 0); pdf_set_pagebox(p, pdf_trimbox, 0, 0, 0, 0); } /* reference point */ pdc_translation_matrix(x, y, &m); /* optional rotation */ if (fabs(fit->rotate) > PDC_FLOAT_PREC) { pdc_rotation_matrix(p->ydirection * fit->rotate, &mm); pdc_multiply_matrix(&mm, &m); } /* output after translation and rotation */ if (!xo->blind) { /* new CTM */ if (fit->showborder || fit->fitmethod == pdc_clip || fit->fitmethod == pdc_slice) { pdf_concat_raw(p, &m); pdc_identity_matrix(&m); } /* show border */ if (fit->showborder) { pdf__rect(p, 0, 0, boxwidth, boxheight); pdf__stroke(p); } /* clipping */ if (fit->fitmethod == pdc_clip || fit->fitmethod == pdc_slice) { if (boxwidth < PDC_FLOAT_PREC) boxwidth = PDF_ACRO_MAXPAGE; if (boxheight < PDC_FLOAT_PREC) boxheight = PDF_ACRO_MAXPAGE; pdf__rect(p, 0, 0, boxwidth, boxheight); pdf__clip(p); ctm_clip = p->curr_ppt->gstate[p->curr_ppt->sl].ctm; redbox.ll.x = 0; redbox.ll.y = 0; redbox.ur.x = boxwidth; redbox.ur.y = p->ydirection * boxheight; } } /* translation of element box */ elembox.ll.y *= p->ydirection; elembox.ur.y *= p->ydirection; pdc_box2polyline(NULL, &elembox, polyline); ip = indangle + indmirror; if (ip >= 4) ip -= 4; tx = polyline[ip].x; ty = polyline[ip].y; pdc_translation_matrix(tx, ty, &mm); pdc_multiply_matrix(&mm, &m); boxwidth = elembox.ur.x - elembox.ll.x; boxheight = elembox.ur.y - elembox.ll.y; /* orientation of image */ if (fit->orientate != 0) { pdc_rotation_matrix(p->ydirection * fit->orientate, &mm); pdc_multiply_matrix(&mm, &m); if (indangle % 2) { ss = fitscale.x; fitscale.x = fitscale.y; fitscale.y = ss; ss = boxwidth; boxwidth = p->ydirection * boxheight; boxheight = p->ydirection * ss; } } /* mirroring of image */ mirror.x = elemscale.x * fitscale.x; elemscale.x = fabs(mirror.x); mirror.x /= elemscale.x; if (image->strips == 1) mirror.y = p->ydirection * elemscale.y * fitscale.y; else mirror.y = p->ydirection * rowsize * fitscale.y; elemscale.y = fabs(mirror.y); mirror.y /= elemscale.y; pdc_scale_matrix(mirror.x, mirror.y, &mm); pdc_multiply_matrix(&mm, &m); if (logg3) pdc_logg(p->pdc, "\t\t\tcumulative mirroring: mx=%g, my=%g\n", mirror.x, mirror.y); /* matchbox or clip path */ if (!xo->blind && (fit->matchbox || kclip || kcliptiff)) { pdf_concat_raw(p, &m); pdc_identity_matrix(&m); if (fit->matchbox) { matchrect.llx = 0; matchrect.lly = 0; matchrect.urx = boxwidth; matchrect.ury = p->ydirection * boxheight; if (fit->fitmethod == pdc_clip || fit->fitmethod == pdc_slice) { pdc_rectangle cliprect; pdc_matrix ctm_inv; pdc_invert_matrix(p->pdc, &ctm_inv, &p->curr_ppt->gstate[p->curr_ppt->sl].ctm); pdc_multiply_matrix(&ctm_clip, &ctm_inv); pdc_box2polyline(&ctm_inv, &redbox, polyline); pdc_polyline2rect(polyline, 4, &cliprect); pdc_rect_intersect(&matchrect, &matchrect, &cliprect); } pdf_set_mbox_rectangle(p, fit->matchbox, &matchrect, 0); pdf_draw_mbox_rectangle(p, fit->matchbox, mbox_area | mbox_border); pdf_add_page_mbox(p, fit->matchbox); } /* displacement of image because of clipping */ shift = clipbox.ll; purescale.x *= fitscale.x; purescale.y *= fitscale.y; if (kclip) { shift.x *= -purescale.x; shift.y *= -purescale.y; } if (kclip) { /* user clipping path */ pdf__rect(p, 0, 0, boxwidth, boxheight); pdf__clip(p); } if (kclip) { pdc_translation_matrix(shift.x, shift.y, &mm); pdc_multiply_matrix(&mm, &m); } } /* scaling of image */ pdc_scale_matrix(elemscale.x, elemscale.y, &mm); pdc_multiply_matrix(&mm, &m); /* ctm */ if (xo->blind) { if (immatrix == NULL) mm = p->curr_ppt->gstate[p->curr_ppt->sl].ctm; else mm = *immatrix; pdc_multiply_matrix(&m, &mm); } else { pdf_concat_raw(p, &m); mm = p->curr_ppt->gstate[p->curr_ppt->sl].ctm; } pdc_logg_cond(p->pdc, 5, trc_image, "\t\t\tCTM components of image %s\"%s\":\n" "\t\t\ta = %f\n" "\t\t\tb = %f\n" "\t\t\tc = %f\n" "\t\t\td = %f\n" "\t\t\te = %f\n" "\t\t\tf = %f\n", image->imagemask ? "mask " : "", image->filename, mm.a, mm.b, mm.c, mm.d, mm.e, mm.f); if (!xo->blind) pdf_cleanup_fit_options(p, fit); /* check image orientation */ if (immatrix != NULL) { *immatrix = mm; return; } if (image->mask != pdc_undef) { sm = ctm_save; xo_save.im = image->mask; xo_save.blind = pdc_true; pdf_fit_xobject_internal(p, &xo_save, &fit_save, &sm); if ((sm.a * mm.a < 0) || (sm.b * mm.b < 0) || (sm.c * mm.c < 0) || (sm.d * mm.d < 0)) { pdc_error(p->pdc, PDF_E_IMAGE_NOMATCH, p->images[image->mask].filename, image->filename, 0, 0); } } if (!(xo->flags & is_image) && !xo->blind) { pdf_reset_gstate(p); pdf_reset_tstate(p); } if (!xo->blind) { /* last strip first */ if (image->strips > 1 && lastratio != 1.0) { pdc_scale_matrix(1, lastratio, &m); pdf_concat_raw(p, &m); } /* put out image strips separately if available */ islast = image->strips - 1; imageno = image->no + islast; for (is = islast; is >= 0; is--) { pdc_printf(p->out, "/I%d Do\n", imageno); p->xobjects[imageno].flags |= xobj_flag_write; if (image->strips > 1 && is > 0) { pdc_translation_matrix(0, 1, &m); if (is == islast && lastratio != 1.0) { pdc_scale_matrix(1, (1.0 / lastratio), &sm); pdc_multiply_matrix(&sm, &m); } pdf_concat_raw(p, &m); imageno--; } } if (image->mask != pdc_undef) { p->xobjects[p->images[image->mask].no].flags |= xobj_flag_write; if (p->images[image->mask].bpc > 1) pdf_set_autotgroup(p, pdc_true); } } } /* ---------------------------- info image --------------------------------- */ void pdf_get_image_size(PDF *p, int im, pdc_scalar *width, pdc_scalar *height) { pdf_image *image; pdf_check_handle(p, im, pdc_imagehandle); image = &p->images[im]; if (image->orientation < 5 || image->ignoreorient) { if (width) *width = image->width; if (height) *height = fabs(image->height); } else { if (width) *width = fabs(image->height); if (height) *height = image->width; } } void pdf_get_image_resolution(PDF *p, int im, pdc_scalar *dpi_x, pdc_scalar *dpi_y) { pdf_image *image; pdf_check_handle(p, im, pdc_imagehandle); image = &p->images[im]; if (image->orientation < 5 || image->ignoreorient) { if (dpi_x) *dpi_x = image->dpi_x; if (dpi_y) *dpi_y = image->dpi_y; } else { if (dpi_x) *dpi_x = image->dpi_y; if (dpi_y) *dpi_y = image->dpi_x; } } int pdf_get_image_colorspace(PDF *p, int im) { pdf_image *image; pdf_check_handle(p, im, pdc_imagehandle); image = &p->images[im]; if (image->colorspace != pdc_undef) return (int) p->colorspaces[image->colorspace].type; return (int) NoColor; } const char * pdf_get_image_filename(PDF *p, pdf_image *image) { return pdc_errprintf(p->pdc, "%.*s", PDC_ERR_MAXSTRLEN, image->filename); } /* ---------------------------- load image --------------------------------- */ static const pdc_keyconn pdf_extension_names[] = { {".bmp", pdf_img_bmp}, {".ccitt", pdf_img_ccitt}, {".g3", pdf_img_ccitt}, {".g4", pdf_img_ccitt}, {".fax", pdf_img_ccitt}, {".gif", pdf_img_gif}, {".jpg", pdf_img_jpeg}, {".jpeg", pdf_img_jpeg}, {".jpx", pdf_img_jpeg2000}, {".jp2", pdf_img_jpeg2000}, {".jpf", pdf_img_jpeg2000}, {".jpm", pdf_img_jpeg2000}, {".j2k", pdf_img_jpeg2000}, {".png", pdf_img_png}, {".raw", pdf_img_raw}, {".tif", pdf_img_tiff}, {".tiff", pdf_img_tiff}, {NULL, 0} }; /* allowed values for options 'bpc' */ static const pdc_keyconn pdf_bpcvalues[] = { {"1", 1}, {"2", 2}, {"4", 4}, {"8", 8}, {"16", 16}, {NULL, 0} }; /* allowed values for options 'components' */ static const pdc_keyconn pdf_compvalues[] = { {"1", 1}, {"3", 3}, {"4", 4}, {NULL, 0} }; #define PDF_OPIOPT_FLAG PDC_OPT_UNSUPP #define PDF_ICCOPT_FLAG PDC_OPT_UNSUPP #define PDF_CLIPPATH_FLAG PDC_OPT_UNSUPP #define PDF_METADATA_FLAG PDC_OPT_UNSUPP #define PDF_LAYER_FLAG PDC_OPT_UNSUPP /* definitions of open image options */ static const pdc_defopt pdf_open_image_options[] = { {"hypertextencoding", pdc_stringlist, PDC_OPT_NONE, 1, 1, 0.0, PDF_MAX_NAMESTRING, NULL}, {"bitreverse", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"bpc", pdc_integerlist, PDC_OPT_INTLIST, 1, 1, 1.0, 16.0, pdf_bpcvalues}, {"components", pdc_integerlist, PDC_OPT_INTLIST, 1, 1, 1.0, 4.0, pdf_compvalues}, {"height", pdc_integerlist, 0, 1, 1, 1.0, PDC_INT_MAX, NULL}, {"width", pdc_integerlist, 0, 1, 1, 1.0, PDC_INT_MAX, NULL}, {"honoriccprofile", pdc_booleanlist, PDF_ICCOPT_FLAG, 1, 1, 0.0, 0.0, NULL}, /* ordering of the next three options is significant */ {"iccprofile", pdc_iccprofilehandle, PDF_ICCOPT_FLAG, 1, 1, 1.0, 0.0, NULL}, {"colorize", pdc_colorhandle, PDC_OPT_IGNOREIF1, 1, 1, 0.0, 0.0, NULL}, {"mask", pdc_booleanlist, PDC_OPT_IGNOREIF2, 1, 1, 0.0, 0.0, NULL}, {"honorclippingpath", pdc_booleanlist, PDF_CLIPPATH_FLAG, 1, 1, 0.0, 0.0, NULL}, {"clippingpathname", pdc_stringlist, PDF_CLIPPATH_FLAG, 1, 1, 1.0, 255.0, NULL}, {"ignoremask", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"ignoreorientation", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, /* deprecated */ {"imagewarning", pdc_booleanlist, PDC_OPT_PDFLIB_7, 1, 1, 0.0, 0.0, NULL}, {"inline", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"interpolate", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"invert", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"jpegoptimize", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"passthrough", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"K", pdc_integerlist, 0, 1, 1, -1.0, 1.0, NULL}, {"masked", pdc_imagehandle, 0, 1, 1, 0.0, 0.0, NULL}, {"page", pdc_integerlist, 0, 1, 1, 1.0, PDC_INT_MAX, NULL}, {"renderingintent", pdc_keywordlist, 0, 1, 1, 0.0, 0.0, pdf_renderingintent_pdfkeylist}, {"reftype", pdc_keywordlist, 0, 1, 1, 0.0, 0.0, pdf_reftype_keys}, {"template", pdc_booleanlist, 0, 1, 1, 0.0, 0.0, NULL}, {"iconname", pdc_stringlist, 0, 1, 1, 1.0, PDF_MAX_NAMESTRING, NULL}, {"OPI-1.3", pdc_stringlist, PDF_OPIOPT_FLAG, 1, 1, 0.0, PDC_INT_MAX, NULL}, {"OPI-2.0", pdc_stringlist, PDF_OPIOPT_FLAG | PDC_OPT_IGNOREIF1, 1, 1, 0.0, PDC_INT_MAX, NULL}, {"metadata", pdc_stringlist, PDF_METADATA_FLAG, 1, 1, 0.0, PDC_INT_MAX, NULL}, {"layer", pdc_layerhandle, PDF_LAYER_FLAG, 1, 1, 0.0, 0.0, NULL}, PDF_ERRORPOLICY_OPTION PDC_OPT_TERMINATE }; int pdf__load_image( PDF *p, const char *type, const char *filename, const char *optlist) { const char *keyword = NULL, *stemp1 = NULL, *stemp2 = NULL, *stemp3 = NULL; char qualname[32], uptype[16], testfilename[PDC_FILENAMELEN + 1]; pdc_clientdata data; pdc_resopt *resopts; pdc_encoding htenc; int htcp; pdf_image_type imgtype; pdc_file *fp = NULL; int colorize = pdc_undef; pdf_image *image; pdc_bool indjpeg = pdc_false; pdc_bool templ = pdc_false; pdc_bool verbose = p->debug[(int) 'i']; int legal_states = 0; int i, k, inum, imageslot, retval = -1, errcode = 0; if (type == NULL || *type == '\0') pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "type", 0, 0, 0); /* parsing image type */ k = pdc_get_keycode_ci(type, pdf_image_keylist); if (k == PDC_KEY_NOTFOUND) pdc_error(p->pdc, PDC_E_ILLARG_STRING, "type", type, 0, 0); imgtype = (pdf_image_type) k; type = pdc_get_keyword(imgtype, pdf_image_keylist); /* parsing option list */ pdf_set_clientdata(p, &data); resopts = pdc_parse_optionlist(p->pdc, optlist, pdf_open_image_options, &data, pdc_true); keyword = "imagewarning"; pdc_get_optvalues(keyword, resopts, &verbose, NULL); verbose = pdf_get_errorpolicy(p, resopts, verbose); /* filename must be already converted to UTF-8 */ pdc_logg_cond(p->pdc, 1, trc_image, "\tImage file: \"%s\"\n", filename); /* search for image file */ fp = pdc_fsearch_fopen(p->pdc, filename, NULL, "image ", PDC_FILE_BINARY); /* automatic check */ if (imgtype == pdf_img_auto) { pdf_tiff_info tiff_info; /* search for files with other extensions */ if (fp == NULL) { pdc_logg_cond(p->pdc, 1, trc_image, "\tImage file not found; searching for files " "with alternative extensions\n"); for (i = 0; i < 2; i++) { for (k = 0; k < 100; k++) { const char *extension = pdf_extension_names[k].word; if (extension) { strcpy(testfilename, filename); strcpy(uptype, extension); if (i) pdc_strtoupper(uptype); strcat(testfilename, uptype); fp = pdc_fsearch_fopen(p->pdc, testfilename, NULL, "image ", PDC_FILE_BINARY); if (fp != NULL) break; } else { break; } } if (fp != NULL) break; } if (fp != NULL) { filename = (const char *) testfilename; pdc_logg_cond(p->pdc, 1, trc_image, "\tImage file \"%s\" found\n", filename); } else { fp = pdc_fsearch_fopen(p->pdc, filename, NULL, "image ", PDC_FILE_BINARY); } } /* automatic type check */ if (fp != NULL) { if (pdf_is_BMP_file(p, fp)) imgtype = pdf_img_bmp; else if (pdf_is_GIF_file(p, fp)) imgtype = pdf_img_gif; else if (pdf_is_PNG_file(p, fp)) imgtype = pdf_img_png; else if (pdf_is_TIFF_file(p, fp, &tiff_info, pdc_true)) imgtype = pdf_img_tiff; else if (pdf_is_JPEG_file(p, fp)) imgtype = pdf_img_jpeg; else if (pdf_is_JPX_file(p, fp)) imgtype = pdf_img_jpeg2000; else { pdc_fclose(fp); pdc_set_errmsg(p->pdc, PDF_E_IMAGE_UNKNOWN, filename, 0, 0, 0); if (verbose) PDC_RETHROW(p->pdc); return -1; } type = pdc_get_keyword(imgtype, pdf_image_keylist); } } if (fp == NULL) { if (verbose) PDC_RETHROW(p->pdc); return -1; } pdc_fclose(fp); strcpy(uptype, type); pdc_strtoupper(uptype); pdc_logg_cond(p->pdc, 1, trc_image, "\tImage type \"%s\" detected\n", uptype); if (imgtype == pdf_img_jpeg2000) { pdc_set_errmsg(p->pdc, PDF_E_UNSUPP_JPEG2000, 0, 0, 0, 0); if (verbose) PDC_RETHROW(p->pdc); return -1; } /* find free slot */ for (imageslot = 0; imageslot < p->images_capacity; imageslot++) if (!p->images[imageslot].in_use) break; if (imageslot == p->images_capacity) pdf_grow_images(p); image = &p->images[imageslot]; /* copy filename */ image->filename = pdc_strdup(p->pdc, filename); /* inherit global flags */ image->verbose = verbose; image->ri = p->rendintent; /* parsing optlist */ if (optlist && strlen(optlist)) { keyword = "reftype"; if (pdc_get_optvalues(keyword, resopts, &inum, NULL)) { image->reference = (pdf_ref_type) inum; if (image->reference != pdf_ref_direct && imgtype != pdf_img_ccitt && imgtype != pdf_img_jpeg && imgtype != pdf_img_raw) { pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNSUPP, keyword, uptype, 0, 0); image->reference = pdf_ref_direct; } } indjpeg = (imgtype == pdf_img_jpeg && image->reference != pdf_ref_direct) ? pdc_true : pdc_false; keyword = "bpc"; if (pdc_get_optvalues(keyword, resopts, &image->bpc, NULL)) { if (imgtype != pdf_img_raw && !indjpeg) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); if (image->bpc == 16) { if (p->compatibility < PDC_1_5) { errcode = PDC_E_OPT_VERSION; stemp1 = "bpc=16"; stemp2 = pdc_get_pdfversion(p->pdc, p->compatibility); goto PDF_IMAGE_ERROR; } } } keyword = "components"; if (pdc_get_optvalues(keyword, resopts, &image->components, NULL)) { if (imgtype != pdf_img_raw && !indjpeg) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "height"; if (pdc_get_optvalues(keyword, resopts, &image->height_pixel, NULL)) { if (imgtype != pdf_img_ccitt && imgtype != pdf_img_raw && !indjpeg) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "width"; if (pdc_get_optvalues(keyword, resopts, &image->width_pixel, NULL)) { if (imgtype != pdf_img_raw && imgtype != pdf_img_ccitt && !indjpeg) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "bitreverse"; if (pdc_get_optvalues(keyword, resopts, &image->bitreverse, NULL)) { if (image->bitreverse && (imgtype != pdf_img_ccitt || image->reference != pdf_ref_direct)) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "colorize"; if (pdc_get_optvalues(keyword, resopts, &colorize, NULL)) { if (imgtype == pdf_img_jpeg2000) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "ignoremask"; if (pdc_get_optvalues(keyword, resopts, &image->ignoremask, NULL)) { if (imgtype == pdf_img_bmp || imgtype == pdf_img_ccitt || imgtype == pdf_img_raw) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNSUPP, keyword, uptype, 0, 0); } keyword = "ignoreorientation"; if (pdc_get_optvalues(keyword, resopts, &image->ignoreorient, NULL)) { if (imgtype == pdf_img_tiff) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNSUPP, keyword, uptype, 0, 0); } keyword = "inline"; if (pdc_get_optvalues(keyword, resopts, &image->doinline, NULL) && image->doinline) { if (imgtype != pdf_img_ccitt && imgtype != pdf_img_jpeg && imgtype != pdf_img_raw) { pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNSUPP, keyword, uptype, 0, 0); image->doinline = pdc_false; } else if (image->reference != pdf_ref_direct) { pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); image->doinline = pdc_false; } } keyword = "interpolate"; pdc_get_optvalues(keyword, resopts, &image->interpolate, NULL); keyword = "invert"; if (pdc_get_optvalues(keyword, resopts, &image->invert, NULL)) { if (imgtype == pdf_img_jpeg2000) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "jpegoptimize"; pdc_get_optvalues(keyword, resopts, &image->jpegoptimize, NULL); keyword = "passthrough"; pdc_get_optvalues(keyword, resopts, &image->passthrough, NULL); keyword = "K"; if (pdc_get_optvalues(keyword, resopts, &image->K, NULL)) { if (imgtype != pdf_img_ccitt) pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNREAS, keyword, uptype, 0, 0); } keyword = "mask"; pdc_get_optvalues(keyword, resopts, &image->imagemask, NULL); keyword = "masked"; if (pdc_get_optvalues(keyword, resopts, &image->mask, NULL)) { if (!p->images[image->mask].in_use || p->images[image->mask].strips != 1 || (p->compatibility <= PDC_1_3 && (p->images[image->mask].imagemask != pdc_true || p->images[image->mask].bpc != 1))) { errcode = PDF_E_IMAGE_OPTBADMASK; stemp1 = keyword; stemp2 = pdc_errprintf(p->pdc, "%d", image->mask); goto PDF_IMAGE_ERROR; } if (p->colorspaces[p->images[image->mask].colorspace].type != DeviceGray) { errcode = PDF_E_IMAGE_BADMASK; stemp1 = pdc_errprintf(p->pdc, "%s", p->images[image->mask].filename); goto PDF_IMAGE_ERROR; } } keyword = "renderingintent"; if (pdc_get_optvalues(keyword, resopts, &inum, NULL)) image->ri = (pdf_renderingintent) inum; keyword = "page"; if (pdc_get_optvalues(keyword, resopts, &image->page, NULL)) { if (imgtype != pdf_img_tiff) { if (image->page == 1) { pdc_warning(p->pdc, PDF_E_IMAGE_OPTUNSUPP, keyword, uptype, 0, 0); } else { errcode = PDF_E_IMAGE_NOPAGE; stemp1 = pdc_errprintf(p->pdc, "%d", image->page); stemp2 = uptype; stemp3 = pdc_errprintf(p->pdc, "%s", image->filename); goto PDF_IMAGE_ERROR; } } } keyword = "template"; if (pdc_get_optvalues(keyword, resopts, &templ, NULL)) { if (templ && image->doinline) { pdc_warning(p->pdc, PDC_E_OPT_IGNORE, keyword, "inline", 0, 0); templ = pdc_false; } } htenc = pdf_get_hypertextencoding_opt(p, resopts, &htcp, pdc_true); keyword = "iconname"; if (pdf_get_opt_textlist(p, keyword, resopts, htenc, htcp, pdc_true, NULL, &image->iconname, NULL)) { if (image->doinline) { image->iconname = NULL; pdc_warning(p->pdc, PDC_E_OPT_IGNORE, keyword, "inline", 0, 0); } else { pdc_save_lastopt(resopts, PDC_OPT_SAVE1ELEM); templ = pdc_true; } } } /* precise scope diagnosis */ if (image->doinline) legal_states = pdf_state_content; else if (templ) legal_states = pdf_state_document; else legal_states = pdf_state_document | pdf_state_page | pdf_state_font; PDF_CHECK_STATE(p, legal_states); /* required options */ if (imgtype == pdf_img_raw || imgtype == pdf_img_ccitt || indjpeg) { keyword = ""; if (image->height_pixel == pdc_undef) keyword = "height"; else if (image->width_pixel == pdc_undef) keyword = "width"; else { image->width = image->width_pixel; image->height = image->height_pixel; } if (imgtype == pdf_img_ccitt) { image->components = 1; image->bpc = 1; } if (image->bpc == pdc_undef) keyword = "bpc"; else if (image->components == pdc_undef) keyword = "components"; if (*keyword) { errcode = PDC_E_OPT_NOTFOUND; stemp1 = keyword; goto PDF_IMAGE_ERROR; } } /* set colorspace */ if (colorize != pdc_undef) { image->colorspace = colorize; } else if (image->imagemask == pdc_true) { image->colorspace = (int) DeviceGray; } else { switch(image->components) { case 1: image->colorspace = DeviceGray; break; case 3: image->colorspace = DeviceRGB; break; case 4: image->colorspace = DeviceCMYK; break; default: break; } } /* try to open image file */ if (image->reference == pdf_ref_direct) { strcpy(qualname, uptype); strcat(qualname, " "); image->fp = pdc_fsearch_fopen(p->pdc, image->filename, NULL, qualname, PDC_FILE_BINARY); if (image->fp == NULL) goto PDF_IMAGE_ERROR; } /* set image type */ image->type = imgtype; /* call working function */ switch (imgtype) { case pdf_img_bmp: retval = pdf_process_BMP_data(p, imageslot); break; case pdf_img_ccitt: retval = pdf_process_CCITT_data(p, imageslot); break; case pdf_img_gif: retval = pdf_process_GIF_data(p, imageslot); break; case pdf_img_jpeg: if (image->passthrough == pdc_undef) image->passthrough = pdc_false; retval = pdf_process_JPEG_data(p, imageslot); break; case pdf_img_jpeg2000: retval = pdf_process_JPX_data(p, imageslot); break; case pdf_img_png: retval = pdf_process_PNG_data(p, imageslot); break; default: case pdf_img_raw: retval = pdf_process_RAW_data(p, imageslot); break; case pdf_img_tiff: if (image->passthrough == pdc_undef) image->passthrough = pdc_true; retval = pdf_process_TIFF_data(p, imageslot); break; } /* cleanup */ if (retval == -1) { pdf_cleanup_image(p, imageslot); if (verbose) PDC_RETHROW(p->pdc); } else { if (image->fp) pdc_fclose(image->fp); image->fp = NULL; /* logging protocol */ if (image->in_use && pdc_logg_is_enabled(p->pdc, 1, trc_image)) { pdc_scalar width, height, dpi_x, dpi_y; pdf_get_image_size(p, imageslot, &width, &height); pdf_get_image_resolution(p, imageslot, &dpi_x, &dpi_y); pdc_logg(p->pdc, "\tImage width: %g pixel\n" "\tImage height: %g pixel\n" "\tImage x resolution: %g dpi\n" "\tImage y resolution: %g dpi\n", width, height, dpi_x, dpi_y); if (imgtype == pdf_img_tiff) pdc_logg(p->pdc, "\tOrientation tag: %d\n", image->orientation); } if (templ) { retval = pdf_embed_image(p, imageslot); pdf_cleanup_image(p, imageslot); } } return retval; PDF_IMAGE_ERROR: pdf_cleanup_image(p, imageslot); if (errcode) pdc_set_errmsg(p->pdc, errcode, stemp1, stemp2, stemp3, 0); if (verbose) PDC_RETHROW(p->pdc); return retval; } void pdf__close_image(PDF *p, int image) { pdf_check_handle(p, image, pdc_imagehandle); pdf_cleanup_image(p, image); } #define MAX_THUMBNAIL_SIZE 106 void pdf__add_thumbnail(PDF *p, int im) { pdf_image *image; pdf_check_handle(p, im, pdc_imagehandle); if (pdf_get_thumb_id(p) != PDC_BAD_ID) pdc_error(p->pdc, PDF_E_IMAGE_THUMB, 0, 0, 0, 0); image = &p->images[im]; if (image->strips > 1) pdc_error(p->pdc, PDF_E_IMAGE_THUMB_MULTISTRIP, pdc_errprintf(p->pdc, "%d", im), 0, 0, 0); if (image->width > MAX_THUMBNAIL_SIZE || image->height > MAX_THUMBNAIL_SIZE) pdc_error(p->pdc, PDF_E_IMAGE_THUMB_SIZE, pdc_errprintf(p->pdc, "%d", im), pdc_errprintf(p->pdc, "%d", MAX_THUMBNAIL_SIZE), 0, 0); if (image->colorspace != (int) DeviceGray && image->colorspace != (int) DeviceRGB && image->colorspace != (int) Indexed) pdc_error(p->pdc, PDF_E_IMAGE_THUMB_CS, pdc_errprintf(p->pdc, "%d", im), 0, 0, 0); /* Add the image to the thumbnail key of the current page. */ pdf_set_thumb_id(p, p->xobjects[image->no].obj_id); }