diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/luatexdir/image | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/image')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/epdf.h | 183 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/image.h | 242 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/pdftoepdf.c | 1114 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/pdftoepdf.h | 39 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writeimg.c | 820 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writeimg.h | 52 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejbig2.c | 861 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejbig2.h | 35 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejp2.c | 302 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejp2.h | 30 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejpg.c | 609 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejpg.h | 30 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writepng.c | 645 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writepng.h | 32 |
14 files changed, 4994 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/image/epdf.h b/Build/source/texk/web2c/luatexdir/image/epdf.h new file mode 100644 index 00000000000..737682d791a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/epdf.h @@ -0,0 +1,183 @@ +/* epdf.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2015 Taco Hoekwater <taco@luatex.org> + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +/* this is the common header file for C++ sources pdftoepdf.c and lepdflib.c */ + +#ifndef EPDF_H +# define EPDF_H + +/*extern "C" {*/ + +#ifdef HAVE_CONFIG_H +#include <w2c/config.h> +#endif + +/*}*/ + +# include <stdlib.h> +# include <math.h> +# include <stddef.h> +# include <stdio.h> +# include <string.h> +# include <kpathsea/c-ctype.h> +# include <kpathsea/simpletypes.h> +# include <sys/stat.h> +# include <dirent.h> + +/*extern "C" { */ + +# include <kpathsea/c-auto.h> + +extern char *xstrdup(const char *); + +typedef enum { FE_FAIL, FE_RETURN_NULL } file_error_mode; + +/* the following code is extremly ugly but needed for including web2c/config.h */ + +/*typedef const char *const_string;*/ /* including kpathsea/types.h doesn't work on some systems */ + +# define KPATHSEA_CONFIG_H /* avoid including other kpathsea header files */ + +/* from web2c/config.h */ + +# ifdef CONFIG_H /* CONFIG_H has been defined by some xpdf */ +# undef CONFIG_H /* header file */ +# endif + +# include <c-auto.h> /* define SIZEOF_LONG */ + +# include "image.h" +# include "utils/avlstuff.h" +# include "pdf/pdftypes.h" + +# include "lua.h" +# include "lauxlib.h" + +# include "pplib.h" + + +/* pdfgen.w */ + +extern int ten_pow[10]; +__attribute__ ((format(printf, 2, 3))) +extern void pdf_printf(PDF, const char *fmt, ...); +extern void pdf_begin_obj(PDF, int, int); +extern void pdf_end_obj(PDF); +extern void pdf_begin_dict(PDF); +extern void pdf_end_dict(PDF); +extern void pdf_begin_array(PDF); +extern void pdf_end_array(PDF); +extern void pdf_add_null(PDF); +extern void pdf_add_bool(PDF, int i); +extern void pdf_add_int(PDF, int i); +extern void pdf_add_real(PDF, double d); +extern void pdf_add_ref(PDF, int num); +extern void pdf_add_name(PDF, const char *name); +extern void pdf_dict_add_streaminfo(PDF); +extern void pdf_begin_stream(PDF); +extern void pdf_end_stream(PDF); +extern void pdf_room(PDF, int); +extern void pdf_out_block(PDF pdf, const char *s, size_t n); + +extern void pdf_dict_add_int(PDF, const char *key, int i); +extern void pdf_dict_add_ref(PDF, const char *key, int num); +extern void pdf_dict_add_name(PDF, const char *key, const char *val); +extern void pdf_dict_add_streaminfo(PDF); + +/* Conflict with pdfgen.h */ +/*# define pdf_out(pdf, A) do { pdf_room(pdf, 1); *(pdf->buf->p++) = A; } while (0)*/ +/*# define pdf_quick_out(pdf,A) *(pdf->buf->p++)=(unsigned char)(A) */ + +# define pdf_puts(pdf, s) pdf_out_block((pdf), (s), strlen(s)) + +/* pdfpage.w */ + +extern void print_pdffloat(PDF pdf, pdffloat f); + +/* pdftables.w */ + +extern int pdf_create_obj(PDF pdf, int t, int i); + +/* pdftoepdf.c */ + +extern void read_pdf_info(image_dict *); +extern void flush_pdf_info(image_dict *); + +extern void write_epdf(PDF, image_dict *, int suppress_optional_info); +extern int write_epdf_object(PDF, image_dict *, int n); + +extern void unrefPdfDocument(char *); +extern void unrefMemStreamPdfDocument(char *); + +extern void epdf_free(void); + +/* writeimg.w */ + +extern void pdf_dict_add_img_filename(PDF pdf, image_dict * idict); + +/* utils.w */ + +/*extern char *convertStringToPDFString(const char *in, int len);*/ + +/* lepdflib.w */ + +int luaopen_epdf(lua_State * L); + +# include "luatex-common.h" + +/*}*/ + +typedef struct InObj InObj; + +struct InObj { + /* int version; */ /* old object number in included PDF */ + int objnum; /* old version number in included PDF, we can do without */ + int num; /* new object number in output PDF */ + InObj *next; /* next entry in list of indirect objects */ +} ; + + +typedef struct avl_table avl_table; + +struct PdfDocument { + char *file_path; /* full file name including path */ + char *checksum; /* for reopening */ + ppdoc *pdfe; + InObj *inObjList; /* temporary linked list */ + avl_table *ObjMapTree; /* permanent over luatex run */ + int is_mem; + char *memstream; + unsigned int occurences; /* number of references to the PdfDocument; it can be deleted when occurences == 0 */ + unsigned int pc; /* counter to track PDFDoc generation or deletion */ +}; + +typedef struct PdfDocument PdfDocument; + +PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe, const char *userpassword, const char *ownerpassword); + +PdfDocument *refMemStreamPdfDocument(char *docstream, unsigned long long streamsize, const char *file_id); + +#define STREAM_CHECKSUM_SIZE 16 // md5 +#define STRSTREAM_CHECKSUM_SIZE 1+((unsigned int)(log((double)ULONG_MAX)/log(16.0))) // djb2 printable digest as hex stream +#define STREAM_FILE_ID_LEN 2048 // 2048 bytes are enough to make a strong almost-unique name +#define STREAM_URI "data:application/pdf," +#define STREAM_URI_LEN 21 // length of "data:application/pdf," without final '\0' + +#endif /* EPDF_H */ diff --git a/Build/source/texk/web2c/luatexdir/image/image.h b/Build/source/texk/web2c/luatexdir/image/image.h new file mode 100644 index 00000000000..687aa2bdf85 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/image.h @@ -0,0 +1,242 @@ +/* image.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2010 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef IMAGE_H +# define IMAGE_H + +# include <png.h> +# include "pdf/pdftypes.h" /* for scaled_whd */ + +# define JPG_UINT16 unsigned int +# define JPG_UINT32 unsigned long +# define JPG_UINT8 unsigned char + +extern int do_zround(double r); /* from utils.w */ +extern scaled one_hundred_bp; /* from pdfgen.w */ + +# define bp2sp(p) do_zround(p * (one_hundred_bp / 100.0)) +# define sp2bp(i) (i * 100.0 / one_hundred_bp) + +# define TYPE_IMG "image.meta" +# define TYPE_IMG_DICT "image.dict" + +typedef struct { + char *stream; + size_t size; +} pdf_stream_struct; + +typedef struct { + png_structp png_ptr; + png_infop info_ptr; +} png_img_struct; + +typedef struct { + int color_space; /* used color space. See JPG_ constants */ + JPG_UINT32 length; /* length of file/data */ +} jpg_img_struct; + +typedef struct { + int length; /* length of file/data */ +} jp2_img_struct; + +# if 0 +typedef struct { /* currently unused */ +} jb2_img_struct; +# endif + +typedef enum { + DICT_NEW, /* fresh dictionary */ + DICT_FILESCANNED, /* image file scanned */ + DICT_REFERED, /* pdf_refximage_node in node list --> read-only dict */ + DICT_OUTIMG, /* /Im* appears in pagestream */ + DICT_SCHEDULED, /* image dict scheduled for writing (PONR) */ + DICT_WRITTEN /* image dict written to file */ +} dict_state; + +typedef enum { + IMG_TYPE_NONE, + IMG_TYPE_PDF, + IMG_TYPE_PNG, + IMG_TYPE_JPG, + IMG_TYPE_JP2, + IMG_TYPE_JBIG2, + IMG_TYPE_PDFSTREAM, + IMG_TYPE_PDFMEMSTREAM, + IMG_TYPE_SENTINEL +} imgtype_e; + +typedef enum { IMG_KEEPOPEN, IMG_CLOSEINBETWEEN } img_readtype_e; + +typedef enum { + PDF_BOX_SPEC_NONE, + PDF_BOX_SPEC_MEDIA, + PDF_BOX_SPEC_CROP, + PDF_BOX_SPEC_BLEED, + PDF_BOX_SPEC_TRIM, + PDF_BOX_SPEC_ART, + PDF_BOX_SPEC_SENTINEL +} pdfboxspec_e; + +typedef struct { + int objnum; + int index; /* /Im1, /Im2, ... */ + scaled_whd dimen; /* TeX dimensions given to \pdfximage */ + int transform; /* transform given to \pdfximage */ + int x_size; /* dimensions in pixel counts as in JPG/PNG/JBIG2 file */ + int y_size; + int x_orig; /* origin in sp for PDF files */ + int y_orig; + int x_res; /* pixel resolution as in JPG/PNG/JBIG2 file */ + int y_res; + int rotation; /* rotation (multiples of 90 deg.) for PDF files */ + int orientation; /* jpeg orientation, 0=unset, 1=ul, 2=ur, 3=lr, 4=ll, 5-8 flipped */ + int colorspace; /* number of /ColorSpace object */ + int group_ref; /* if it's <=0, the page has no group */ + int total_pages; + int page_num; /* requested page (by number) */ + char *pagename; /* requested page (by name) */ + char *filename; /* requested raw file name */ + char *visiblefilename; /* blocks or overwrites filename as it appears in PDF output */ + char *filepath; /* full file path after kpathsea */ + char *attr; /* additional image dict entries */ + FILE *file; + char *userpassword; + char *ownerpassword; + imgtype_e image_type; + int procset; /* /ProcSet flags */ + int color_depth; /* color depth */ + pdfboxspec_e page_box_spec; /* PDF page box spec.: media/crop/bleed/trim/art */ + int bbox[4]; + dict_state state; + int flags; + int luaref ; + boolean keepopen; + boolean nolength; + boolean notype; + boolean nobbox; + int errorlevel; + int pdfmajorversion; + int pdfminorversion; + union { + pdf_stream_struct *pdfstream; + png_img_struct *png; + jpg_img_struct *jpg; + jp2_img_struct *jp2; + /* jb2_img_struct *jb2; */ + } img_struct; +} image_dict; + +# define img_objnum(N) ((N)->objnum) +# define img_index(N) ((N)->index) +# define img_dimen(N) ((N)->dimen) +# define img_width(N) ((N)->dimen.wd) +# define img_height(N) ((N)->dimen.ht) +# define img_depth(N) ((N)->dimen.dp) +# define img_transform(N) ((N)->transform) +# define img_xsize(N) ((N)->x_size) +# define img_ysize(N) ((N)->y_size) +# define img_xorig(N) ((N)->x_orig) +# define img_yorig(N) ((N)->y_orig) +# define img_xres(N) ((N)->x_res) +# define img_yres(N) ((N)->y_res) +# define img_rotation(N) ((N)->rotation) +# define img_orientation(N) ((N)->orientation) +# define img_colorspace(N) ((N)->colorspace) +# define img_group_ref(N) ((N)->group_ref) +# define img_totalpages(N) ((N)->total_pages) +# define img_pagenum(N) ((N)->page_num) +# define img_pagename(N) ((N)->pagename) +# define img_filename(N) ((N)->filename) +# define img_visiblefilename(N) ((N)->visiblefilename) +# define img_userpassword(N) ((N)->userpassword) +# define img_ownerpassword(N) ((N)->ownerpassword) +# define img_filepath(N) ((N)->filepath) +# define img_attr(N) ((N)->attr) +# define img_file(N) ((N)->file) +# define img_type(N) ((N)->image_type) +# define img_procset(N) ((N)->procset) +# define img_colordepth(N) ((N)->color_depth) +# define img_pagebox(N) ((N)->page_box_spec) +# define img_bbox(N) ((N)->bbox) +# define img_state(N) ((N)->state) +# define img_flags(N) ((N)->flags) +# define img_luaref(N) ((N)->luaref) +# define img_keepopen(N) ((N)->keepopen) +# define img_nobbox(N) ((N)->nobbox) +# define img_nolength(N) ((N)->nolength) +# define img_notype(N) ((N)->notype) +# define img_errorlevel(N) ((N)->errorlevel) +# define img_pdfmajorversion(N) ((N)->pdfmajorversion) +# define img_pdfminorversion(N) ((N)->pdfminorversion) + +# define img_pdfstream_ptr(N) ((N)->img_struct.pdfstream) +# define img_pdfstream_stream(N) ((N)->img_struct.pdfstream->stream) +# define img_pdfstream_size(N) ((N)->img_struct.pdfstream->size) + +# define img_png_ptr(N) ((N)->img_struct.png) +# define img_png_png_ptr(N) ((N)->img_struct.png->png_ptr) +# define img_png_info_ptr(N) ((N)->img_struct.png->info_ptr) + +# define img_jpg_ptr(N) ((N)->img_struct.jpg) +# define img_jpg_color(N) ((N)->img_struct.jpg->color_space) + +# define img_jp2_ptr(N) ((N)->img_struct.jp2) + +# define img_jb2_ptr(N) ((N)->img_struct.jb2) + +# define F_FLAG_BBOX (1 << 0) +# define F_FLAG_GROUP (1 << 1) + +# define img_set_bbox(N) (img_flags(N) |= F_FLAG_BBOX) +# define img_unset_bbox(N) (img_flags(N) &= ~F_FLAG_BBOX) +# define img_is_bbox(N) ((img_flags(N) & F_FLAG_BBOX) != 0) + +# define img_set_group(N) (img_flags(N) |= F_FLAG_GROUP) +# define img_unset_group(N) (img_flags(N) &= ~F_FLAG_GROUP) +# define img_is_group(N) ((img_flags(N) & F_FLAG_GROUP) != 0) + +# define epdf_xsize(a) img_xsize(idict_array[a]) +# define epdf_ysize(a) img_ysize(idict_array[a]) +# define epdf_orig_x(a) img_xorig(idict_array[a]) +# define epdf_orig_y(a) img_yorig(idict_array[a]) + +# define is_pdf_image(a) ((img_type(idict_array[a]) == IMG_TYPE_PDF) || (img_type(idict_array[a]) == IMG_TYPE_PDFMEMSTREAM)) +# define is_png_image(a) (img_type(idict_array[a]) == IMG_TYPE_PNG) + +# define img_is_refered(N) (img_index(N) != -1) + +typedef struct { + scaled_whd dimen; /* requested/actual TeX dimensions */ + int transform; + image_dict *dict; + int dict_ref; /* luaL_ref() reference */ +} image; + +# define img_dict(N) ((N)->dict) +# define img_dictref(N) ((N)->dict_ref) + +# define set_wd_running(N) (img_width(N) = null_flag) +# define set_ht_running(N) (img_height(N) = null_flag) +# define set_dp_running(N) (img_depth(N) = null_flag) +# define is_wd_running(N) (img_width(N) == null_flag) +# define is_ht_running(N) (img_height(N) == null_flag) +# define is_dp_running(N) (img_depth(N) == null_flag) + +#endif diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.c b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.c new file mode 100644 index 00000000000..78dab3a37b3 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.c @@ -0,0 +1,1114 @@ +/* +pdftoepdf.w + +Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> +Copyright 2006-2015 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +*/ + +#define __STDC_FORMAT_MACROS /* for PRId64 etc. */ + +#include "image/epdf.h" +#include "luatexcallbackids.h" + +/* to be sorted out, we cannot include */ + +#define xfree(a) do { free(a); a = NULL; } while (0) + +/* Conflict with pdfgen.h */ + +#ifndef pdf_out + +#define pdf_out(pdf, A) do { pdf_room(pdf, 1); *(pdf->buf->p++) = A; } while (0) + +#define pdf_check_space(pdf) do { \ + if (pdf->cave > 0) { \ + pdf_out(pdf, ' '); \ + pdf->cave = 0; \ + } \ +} while (0) + +#define pdf_set_space(pdf) \ + pdf->cave = 1; + +#define pdf_reset_space(pdf) \ + pdf->cave = 0; + +#endif + +/* Maintain AVL tree of all PDF files for embedding */ + +static avl_table *PdfDocumentTree = NULL; + +/* AVL sort PdfDocument into PdfDocumentTree by file_path */ + +static int CompPdfDocument(const void *pa, const void *pb, void *p ) +{ + return strcmp(((const PdfDocument *) pa)->file_path, ((const PdfDocument *) pb)->file_path); +} + +/* Returns pointer to PdfDocument structure for PDF file. */ + +static PdfDocument *findPdfDocument(char *file_path) +{ + PdfDocument *pdf_doc, tmp; + if (file_path == NULL) { + normal_error("pdf backend","empty filename when loading pdf file"); + } else if (PdfDocumentTree == NULL) { + return NULL; + } + tmp.file_path = file_path; + pdf_doc = (PdfDocument *) avl_find(PdfDocumentTree, &tmp); + return pdf_doc; +} + +#define PDF_CHECKSUM_SIZE 32 + +static char *get_file_checksum(const char *a, file_error_mode fe) +{ + struct stat finfo; + char *ck = NULL; + if (stat(a, &finfo) == 0) { + off_t size = finfo.st_size; + time_t mtime = finfo.st_mtime; + ck = (char *) malloc(PDF_CHECKSUM_SIZE); + if (ck == NULL) + formatted_error("pdf inclusion","out of memory while processing '%s'", a); + snprintf(ck, PDF_CHECKSUM_SIZE, "%" PRIu64 "_%" PRIu64, (uint64_t) size,(uint64_t) mtime); + } else { + switch (fe) { + case FE_FAIL: + formatted_error("pdf inclusion","could not stat() file '%s'", a); + break; + case FE_RETURN_NULL: + if (ck != NULL) + free(ck); + ck = NULL; + break; + default: + assert(0); + } + } + return ck; +} + +static char *get_stream_checksum (const char *str, unsigned long long str_size){ + /* http://www.cse.yorku.ca/~oz/hash.html */ + /* djb2 */ + unsigned long hash ; + char *ck = NULL; + unsigned int i; + hash = 5381; + ck = (char *) malloc(STRSTREAM_CHECKSUM_SIZE+1); + if (ck == NULL) + normal_error("pdf inclusion","out of memory while processing a memstream"); + for(i=0; i<(unsigned int)(str_size); i++) { + hash = ((hash << 5) + hash) + str[i]; /* hash * 33 + str[i] */ + } + snprintf(ck,STRSTREAM_CHECKSUM_SIZE+1,"%lx",hash); + ck[STRSTREAM_CHECKSUM_SIZE]='\0'; + return ck; +} + +/* + Returns pointer to PdfDocument structure for PDF file. + Creates a new PdfDocument structure if it doesn't exist yet. + When fe = FE_RETURN_NULL, the function returns NULL in error case. +*/ + +PdfDocument *refPdfDocument(const char *file_path, file_error_mode fe, const char *userpassword, const char *ownerpassword) +{ + char *checksum, *path_copy; + PdfDocument *pdf_doc; + ppdoc *pdfe = NULL; + int new_flag = 0; + if ((checksum = get_file_checksum(file_path, fe)) == NULL) { + return (PdfDocument *) NULL; + } + path_copy = xstrdup(file_path); + if ((pdf_doc = findPdfDocument(path_copy)) == NULL) { + new_flag = 1; + pdf_doc = (PdfDocument*) xmalloc(sizeof( PdfDocument)); + pdf_doc->file_path = path_copy; + pdf_doc->checksum = checksum; + pdf_doc->pdfe = NULL; + pdf_doc->inObjList = NULL; + pdf_doc->ObjMapTree = NULL; + pdf_doc->occurences = 0; /* 0 = unreferenced */ + pdf_doc->pc = 0; + pdf_doc->is_mem = 0; + } else { + if (strncmp(pdf_doc->checksum, checksum, PDF_CHECKSUM_SIZE) != 0) { + formatted_error("pdf inclusion","file has changed '%s'", file_path); + } + free(checksum); + free(path_copy); + } + if (pdf_doc->pdfe == NULL) { + pdfe = ppdoc_load(file_path); + pdf_doc->pc++; + /* todo: check if we might print the document */ + if (pdfe == NULL) { + switch (fe) { + case FE_FAIL: + normal_error("pdf inclusion","reading image failed"); + break; + case FE_RETURN_NULL: + if (pdf_doc->pdfe != NULL) { + ppdoc_free(pdfe); + pdf_doc->pdfe = NULL; + } + /* delete docName */ + if (new_flag == 1) { + if (pdf_doc->file_path != NULL) + free(pdf_doc->file_path); + if (pdf_doc->checksum != NULL) + free(pdf_doc->checksum); + free(pdf_doc); + } + return (PdfDocument *) NULL; + break; + default: + assert(0); + } + } + if (pdfe != NULL) { + if (ppdoc_crypt_status(pdfe) < 0 && userpassword==NULL) { + formatted_error("pdf inclusion","the pdf file '%s' is encrypted, passwords wrong",file_path); + } + if (ppdoc_crypt_status(pdfe) < 0) { + ppdoc_crypt_pass(pdfe,userpassword,strlen(userpassword),NULL,0); + } + if (ppdoc_crypt_status(pdfe) < 0) { + ppdoc_crypt_pass(pdfe,NULL,0,ownerpassword,strlen(ownerpassword)); + } + if (ppdoc_crypt_status(pdfe) < 0) { + formatted_error("pdf inclusion","the pdf file '%s' is encrypted, provide proper passwords",file_path); + } + } + pdf_doc->pdfe = pdfe; + } + /* PDF file could be opened without problems, checksum ok. */ + if (PdfDocumentTree == NULL) + PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator); + if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) { + avl_probe(PdfDocumentTree, pdf_doc); + } + pdf_doc->occurences++; + return pdf_doc; +} + +/* + Returns pointer to PdfDocument structure for a PDF stream in memory of streamsize + dimension. As before, creates a new PdfDocument structure if it doesn't exist yet + with file_path = file_id +*/ + +PdfDocument *refMemStreamPdfDocument(char *docstream, unsigned long long streamsize,const char *file_id) +{ + char *checksum; + char *file_path; + PdfDocument *pdf_doc; + ppdoc *pdfe = NULL; + size_t cnt = 0; + checksum = get_stream_checksum(docstream, streamsize); + cnt = strlen(file_id); + file_path = (char *) malloc(cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE+1); /* 1 for \0 */ + strcpy(file_path,STREAM_URI); + strcat(file_path,file_id); + strcat(file_path,checksum); + file_path[cnt+STREAM_URI_LEN+STRSTREAM_CHECKSUM_SIZE]='\0'; + if ((pdf_doc = findPdfDocument(file_path)) == NULL) { + /*new_flag = 1;*/ + pdf_doc = (PdfDocument*) xmalloc(sizeof( PdfDocument)); + pdf_doc->file_path = file_path; + pdf_doc->checksum = checksum; + pdf_doc->pdfe = NULL; + pdf_doc->inObjList = NULL; + pdf_doc->ObjMapTree = NULL; + pdf_doc->occurences = 0; /* 0 = unreferenced */ + pdf_doc->pc = 0; + pdf_doc->is_mem = 1; + pdf_doc->memstream = docstream; + } else { + /* As is now, checksum is in file_path, so this check should be useless. */ + if (strncmp(pdf_doc->checksum, checksum, STRSTREAM_CHECKSUM_SIZE) != 0) { + formatted_error("pdf inclusion","stream has changed '%s'", file_path); + } + free(file_path); + free(checksum); + } + if (pdf_doc->pdfe == NULL) { + pdfe = ppdoc_mem(docstream, (size_t) streamsize); + pdf_doc->pc++; + if (pdfe == NULL) { + normal_error("pdf inclusion","reading pdf Stream failed"); + } + pdf_doc->pdfe = pdfe; + } + /* PDF file could be opened without problems, checksum ok. */ + if (PdfDocumentTree == NULL) + PdfDocumentTree = avl_create(CompPdfDocument, NULL, &avl_xallocator); + if ((PdfDocument *) avl_find(PdfDocumentTree, pdf_doc) == NULL) { + avl_probe(PdfDocumentTree, pdf_doc); + } + pdf_doc->occurences++; + return pdf_doc; +} + +/* + AVL sort ObjMap into ObjMapTree by object number and generation keep the ObjMap + struct small, as these are accumulated until the end +*/ + +typedef struct ObjMap ObjMap ; + +struct ObjMap { + /* int version; */ /* not really needed */ + int objnum; + int out_num; +}; + +static int CompObjMap(const void *pa, const void *pb, void *p) +{ + const ObjMap *a = (((const ObjMap *) pa)); + const ObjMap *b = (((const ObjMap *) pb)); + if (a->objnum > b->objnum) + return 1; + else if (a->objnum < b->objnum) + return -1; + /* + else if (a->version == b->version) + return 0; + else if (a->version < b->version) + return -1; + return 1; + */ + return 0; +} + +static ObjMap *findObjMap(PdfDocument * pdf_doc, ppref * in) +{ + ObjMap *obj_map, tmp; + if (pdf_doc->ObjMapTree == NULL) { + return NULL; + } + tmp.objnum = in->number; + /* tmp.version = in->version; */ + obj_map = (ObjMap *) avl_find(pdf_doc->ObjMapTree, &tmp); + return obj_map; +} + +static void addObjMap(PdfDocument * pdf_doc, ppref * in, int out_num) +{ + ObjMap *obj_map = NULL; + if (pdf_doc->ObjMapTree == NULL) { + pdf_doc->ObjMapTree = avl_create(CompObjMap, NULL, &avl_xallocator); + } + obj_map = (ObjMap*)xmalloc(sizeof(ObjMap)); + /* obj_map->version = in->version; */ + obj_map->objnum = in->number; + obj_map->out_num = out_num; + avl_probe(pdf_doc->ObjMapTree, obj_map); +} + +/* + When copying the Resources of the selected page, all objects are + copied recursively top-down. The findObjMap() function checks if an + object has already been copied; if so, instead of copying just the + new object number will be referenced. The ObjMapTree guarantees, + that during the entire LuaTeX run any object from any embedded PDF + file will end up max. once in the output PDF file. Indirect objects + are not fetched during copying, but get a new object number from + LuaTeX and then will be appended into a linked list. +*/ + +static int addInObj(PDF pdf, PdfDocument * pdf_doc, ppref * ref) +{ + ObjMap *obj_map; + InObj *p, *q, *n; + if (ref->number == 0) { + normal_error("pdf inclusion","reference to invalid object (broken pdf)"); + } + if ((obj_map = findObjMap(pdf_doc, ref)) != NULL) { + return obj_map->out_num; + } + n = (InObj*)xmalloc(sizeof(InObj)); + n->objnum = ref->number; + /* n->version = ref->version; */ + n->next = NULL; + n->num = pdf_create_obj(pdf, obj_type_others, 0); + addObjMap(pdf_doc, ref, n->num); + if (pdf_doc->inObjList == NULL) { + pdf_doc->inObjList = n; + } else { + /* + It is important to add new objects at the end of the list, + because new objects are being added while the list is being + written out by writeRefs(). + */ + for (p = pdf_doc->inObjList; p != NULL; p = p->next) + q = p; + q->next = n; + } + return n->num; +} + +static void copyObject(PDF, PdfDocument *, ppobj *); + +static void copyString(PDF pdf, ppstring *str) +{ + pdf_check_space(pdf); + switch (ppstring_type(str)) { + case PPSTRING_PLAIN: + pdf_out(pdf, '('); + pdf_out_block(pdf, ppstring_data(str), ppstring_size(str)); + pdf_out(pdf, ')'); + break; + case PPSTRING_BASE16: + pdf_out(pdf, '<'); + pdf_out_block(pdf, ppstring_data(str), ppstring_size(str)); + pdf_out(pdf, '>'); + break; + case PPSTRING_BASE85: + pdf_out(pdf, '<'); + pdf_out(pdf, '~'); + pdf_out_block(pdf, ppstring_data(str), ppstring_size(str)); + pdf_out(pdf, '~'); + pdf_out(pdf, '>'); + break; + } + pdf_set_space(pdf); +} + +static void copyName(PDF pdf, ppname *name) +{ + pdf_add_name(pdf, ppname_data(name)); +} + +static void copyArray(PDF pdf, PdfDocument * pdf_doc, pparray * array) +{ + int i; + int n = array->size; + pdf_begin_array(pdf); + for (i=0; i<n; ++i) { + copyObject(pdf, pdf_doc, pparray_at(array,i)); + } + pdf_end_array(pdf); +} + +static void copyDict(PDF pdf, PdfDocument * pdf_doc, ppdict *dict) +{ + int i; + int n = dict->size; + pdf_begin_dict(pdf); + for (i=0; i<n; ++i) { + ppname *key = ppdict_key(dict,i); + pdf_add_name(pdf,ppname_data(key)); + copyObject(pdf, pdf_doc, ppdict_at(dict,i)); + } + pdf_end_dict(pdf); +} + +static void copyStreamStream(PDF pdf, ppstream * stream, int decode, int callback_id) +{ + uint8_t *data = NULL; + size_t size = 0; + if (0) { + for (data = ppstream_first(stream, &size, decode); data != NULL; data = ppstream_next(stream, &size)) { + pdf_out_block(pdf, (const char *) data, size); + } + } else { + data = ppstream_all(stream,&size,decode); + if (data != NULL) { + /*tex We only do this when we recompress in which case we fetch the whole stream. */ + if (callback_id == 1) { + callback_id = callback_defined(process_pdf_image_content_callback); + } + if (callback_id) { + char *result = NULL; + run_callback(callback_id, "S->S",(char *) data,&result); + pdf_out_block(pdf, (const char *) (uint8_t *) result, size); + xfree(result); + } else { + pdf_out_block(pdf, (const char *) data, size); + } + } + } + ppstream_done(stream); +} + +static void copyStream(PDF pdf, PdfDocument * pdf_doc, ppstream * stream) +{ + ppdict *dict = stream->dict; /* bug in: stream_dict(stream) */ + if (pdf->compress_level == 0 || pdf->recompress) { + ppobj * obj = ppdict_get_obj (dict, "Filter"); + int known = 0; + if (obj != NULL && obj->type == PPNAME) { + const char *codecs[] = { + "ASCIIHexDecode", "ASCII85Decode", "RunLengthDecode", + "FlateDecode", "LZWDecode", NULL + }; + int k; + for (k = 0; codecs[k] != NULL; k++) { + if (strcmp(ppname_data(obj->name),codecs[k]) == 0) { + known = 1; + break; + } + } + } + if (known) { + /*tex recompress or keep uncompressed */ + const char *ignoredkeys[] = { + "Filter", "DecodeParms", "Length", "DL", NULL + }; + int i; + pdf_begin_dict(pdf); + for (i=0; i<dict->size; ++i) { + ppname *key = ppdict_key(dict,i); + int copy = 1; + int k; + for (k = 0; ignoredkeys[k] != NULL; k++) { + if (strcmp(ppname_data(key),ignoredkeys[k]) == 0) { + copy = 0; + break; + } + } + if (copy) { + pdf_add_name(pdf, ppname_data(key)); + copyObject(pdf, pdf_doc, ppdict_at(dict,i)); + } + } + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream, 1, 0); + pdf_end_stream(pdf); + return ; + } + } + /* copy as-is */ + copyDict(pdf, pdf_doc, dict); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream, 0, 0); + pdf_end_stream(pdf); +} + +static void copyObject(PDF pdf, PdfDocument * pdf_doc, ppobj * obj) +{ + switch (obj->type) { + case PPNULL: + pdf_add_null(pdf); + break; + case PPBOOL: + pdf_add_bool(pdf,obj->integer); /* ppobj_get_bool_value(obj) */ + break; + case PPINT: + pdf_add_int(pdf,obj->integer); /* ppobj_get_int_value(obj) */ + break; + case PPNUM: + pdf_add_real(pdf,obj->number); /* ppobj_get_num_value(obj) */ + break; + case PPNAME: + copyName(pdf, obj->name); + break; + case PPSTRING: + copyString(pdf, obj->string); /* ppobj_get_string(obj) */ + break; + case PPARRAY: + copyArray(pdf, pdf_doc, obj->array); /* ppobj_get_array(obj) */ + break; + case PPDICT: + copyDict(pdf, pdf_doc, obj->dict); /* ppobj_get_dict(obj) */ + break; + case PPSTREAM: + copyStream(pdf, pdf_doc, obj->stream); /* ppobj_get_stream(obj) */ + break; + case PPREF: + pdf_add_ref(pdf, addInObj(pdf, pdf_doc, obj->ref)); /* ppobj_get_ref(obj) */ + break; + default: + break; + } +} + +static void writeRefs(PDF pdf, PdfDocument * pdf_doc) +{ + InObj *r, *n; + ppobj * obj; + ppref * ref ; + ppxref * xref = ppdoc_xref (pdf_doc->pdfe); + for (r = pdf_doc->inObjList; r != NULL;) { + if (xref != NULL) { + ref = ppxref_find(xref, (ppuint) r->objnum); + if (ref != NULL) { + obj = ppref_obj(ref); + if (obj->type == PPSTREAM) { + pdf_begin_obj(pdf, r->num, OBJSTM_NEVER); + } else { + pdf_begin_obj(pdf, r->num, 2); + } + copyObject(pdf, pdf_doc, obj); + pdf_end_obj(pdf); + } else { + formatted_warning("pdf inclusion","ignoring missing object %i, case 1\n",(int) r->objnum); + } + } else { + formatted_warning("pdf inclusion","ignoring missing object %i, case 2\n",(int) r->objnum); + } + n = r->next; + free(r); + r = n; + pdf_doc->inObjList = n; + } +} + + +/* get the pagebox coordinates according to the pagebox_spec */ + +static void somebox(ppdict *page, const char * key, pprect * box) +{ + pprect * r = ppdict_get_box(page, key, box); + if (r != NULL) { + box->lx = r->lx; + box->ly = r->ly; + box->rx = r->rx; + box->ry = r->ry; + } +} + +static void get_pagebox(ppdict * page, int pagebox_spec, pprect * box) +{ + box->lx = box->rx = box->ly = box->ry = 0; + somebox(page,"MediaBox",box); + if (pagebox_spec == PDF_BOX_SPEC_MEDIA) { + return; + } + somebox(page,"CropBox",box); + if (pagebox_spec == PDF_BOX_SPEC_CROP) { + return; + } + switch (pagebox_spec) { + case PDF_BOX_SPEC_BLEED: + somebox(page,"BleedBox",box); + break; + case PDF_BOX_SPEC_TRIM: + somebox(page,"TrimBox",box); + break; + case PDF_BOX_SPEC_ART: + somebox(page,"ArtBox",box); + break; + default: + break; + } +} + +/* + Reads various information about the PDF and sets it up for later inclusion. + This will fail if the PDF version of the PDF is higher than minor_pdf_version_wanted + or page_name is given and can not be found. It makes no sense to give page_name and + page_num. Returns the page number. +*/ + +static ppdict * get_pdf_page_dict(ppdoc *pdfe, int n) +{ + ppref *r; + int i; + for (r=ppdoc_first_page(pdfe), i=1; r != NULL; r = ppdoc_next_page(pdfe), ++i) { + if (i == n) { + return ppref_obj(r)->dict; + } + } + return NULL; +} + +void read_pdf_info(image_dict * idict) +{ + PdfDocument *pdf_doc = NULL; + ppdoc * pdfe = NULL; + ppdict *pageDict, *groupDict; + pprect pagebox; + ppint rotate = 0; + int pdf_major_version_found = 1; + int pdf_minor_version_found = 3; + double xsize, ysize, xorig, yorig; + if (img_type(idict) == IMG_TYPE_PDF) { + pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict)); + } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) { + pdf_doc = findPdfDocument(img_filepath(idict)) ; + if (pdf_doc == NULL ) + normal_error("pdf inclusion", "memstream not initialized"); + if (pdf_doc->pdfe == NULL) + normal_error("pdf inclusion", "memstream document is empty"); + pdf_doc->occurences++; + } else { + normal_error("pdf inclusion","unknown document"); + } + pdfe = pdf_doc->pdfe; + /* + Check PDF version. This works only for PDF 1.x but since any versions of + PDF newer than 1.x will not be backwards compatible to PDF 1.x, we will + then have to changed drastically anyway. + */ + pdf_major_version_found = ppdoc_version_number(pdfe,&pdf_minor_version_found); + if ((100 * pdf_major_version_found + pdf_minor_version_found) > (100 * img_pdfmajorversion(idict) + img_pdfminorversion(idict))) { + const char *msg = "PDF inclusion: found PDF version '%d.%d', but at most version '%d.%d' allowed"; + if (img_errorlevel(idict) > 0) { + formatted_error("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict)); + } else { + formatted_warning("pdf inclusion",msg, pdf_major_version_found, pdf_minor_version_found, img_pdfmajorversion(idict), img_pdfminorversion(idict)); + } + } + img_totalpages(idict) = ppdoc_page_count(pdfe); + if (img_pagename(idict)) { + /* + get page by name is obsolete + */ + normal_error("pdf inclusion","named pages are not supported"); + } else { + /* + get page by number + */ + if (img_pagenum(idict) <= 0 + || img_pagenum(idict) > img_totalpages(idict)) + formatted_error("pdf inclusion","required page '%i' does not exist",(int) img_pagenum(idict)); + } + /* + get the required page + */ + pageDict = get_pdf_page_dict(pdfe,img_pagenum(idict)); + if (pageDict==NULL) { + formatted_error("pdf inclusion","unable to retrive dictionary for page '%i'",(int) img_pagenum(idict)); + } + /* + get the pagebox coordinates (media, crop,...) to use + */ + get_pagebox(pageDict, img_pagebox(idict), &pagebox); + if (pagebox.rx > pagebox.lx) { + xorig = pagebox.lx; + xsize = pagebox.rx - pagebox.lx; + } else { + xorig = pagebox.rx; + xsize = pagebox.lx - pagebox.rx; + } + if (pagebox.ry > pagebox.ly) { + yorig = pagebox.ly; + ysize = pagebox.ry - pagebox.ly; + } else { + yorig = pagebox.ry; + ysize = pagebox.ly - pagebox.ry; + } + /* + The following 4 parameters are raw. Do _not_ modify by /Rotate! + */ + img_xsize(idict) = bp2sp(xsize); + img_ysize(idict) = bp2sp(ysize); + img_xorig(idict) = bp2sp(xorig); + img_yorig(idict) = bp2sp(yorig); + /* + Handle /Rotate parameter. Only multiples of 90 deg. are allowed (PDF Ref. v1.3, + p. 78). We also accept negative angles. Beware: PDF counts clockwise! + */ + if (ppdict_get_int(pageDict, "Rotate", &rotate)) { + switch ((((int)rotate % 360) + 360) % 360) { + case 0: + img_rotation(idict) = 0; + break; + case 90: + img_rotation(idict) = 3; + break; + case 180: + img_rotation(idict) = 2; + break; + case 270: + img_rotation(idict) = 1; + break; + default: + formatted_warning("pdf inclusion","/Rotate parameter in PDF file not multiple of 90 degrees"); + } + } + /* + currently unused info whether PDF contains a /Group + */ + groupDict = ppdict_get_dict(pageDict, "Group"); + if (groupDict != NULL) { + img_set_group(idict); + } + /* + LuaTeX pre 0.85 versions did this: + + if (readtype == IMG_CLOSEINBETWEEN) { + unrefPdfDocument(img_filepath(idict)); + } + + and also unref'd in the finalizer so we got an extra unrefs when garbage was + collected. However it is more efficient to keep the file open so we do that + now. The (slower) alternative is to unref here (which in most cases forcing a + close of the file) but then we must not call flush_pdf_info. + + A close (unref) can be forced by nilling the dict object at the lua end and + forcing a collectgarbage("collect") after that. + + */ + if (! img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } +} + +void flush_pdf_info(image_dict * idict) +{ + if (img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } +} + +/* + Write the current epf_doc. Here the included PDF is copied, so most errors + that can happen during PDF inclusion will arise here. +*/ + +void write_epdf(PDF pdf, image_dict * idict, int suppress_optional_info) +{ + PdfDocument *pdf_doc = NULL; + ppdoc *pdfe = NULL; + ppdict *pageDict, *infoDict; + ppobj *obj, *content, *resources; + pprect pagebox; + int i; + double bbox[4]; + const char *pagedictkeys[] = { + "Group", "LastModified", "Metadata", "PieceInfo", "SeparationInfo", NULL + }; + /* + open PDF file + */ + if (img_type(idict) == IMG_TYPE_PDF) { + pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict)); + } else if (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) { + pdf_doc = findPdfDocument(img_filepath(idict)) ; + pdf_doc->occurences++; + } else { + normal_error("pdf inclusion","unknown document"); + } + pdfe = pdf_doc->pdfe; + pageDict = get_pdf_page_dict(pdfe,img_pagenum(idict)); + if (pageDict==NULL) { + formatted_error("pdf inclusion","unable to retrive dictionary for page '%i'",(int) img_pagenum(idict)); + } + /* + write the Page header + */ + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Form"); + pdf_dict_add_int(pdf, "FormType", 1); + /* + write additional information + */ + pdf_dict_add_img_filename(pdf, idict); + if ((suppress_optional_info & 4) == 0) { + pdf_dict_add_int(pdf, "PTEX.PageNumber", (int) img_pagenum(idict)); + } + if ((suppress_optional_info & 8) == 0) { + infoDict = ppdoc_info(pdfe); + if (infoDict != NULL) { + /* todo : check this + pdf_dict_add_ref(pdf, "PTEX.InfoDict", addInObj(pdf, pdf_doc, infoDict)); + */ + pdf_add_name(pdf, "PTEX.InfoDict"); + copyDict(pdf, pdf_doc, infoDict); + } + } + if (img_is_bbox(idict)) { + bbox[0] = sp2bp(img_bbox(idict)[0]); + bbox[1] = sp2bp(img_bbox(idict)[1]); + bbox[2] = sp2bp(img_bbox(idict)[2]); + bbox[3] = sp2bp(img_bbox(idict)[3]); + } else { + /* + get the pagebox coordinates (media, crop,...) to use. + */ + get_pagebox(pageDict, img_pagebox(idict), &pagebox); + bbox[0] = pagebox.lx; + bbox[1] = pagebox.ly; + bbox[2] = pagebox.rx; + bbox[3] = pagebox.ry; + } + pdf_add_name(pdf, "BBox"); + pdf_begin_array(pdf); + pdf_add_real(pdf, bbox[0]); + pdf_add_real(pdf, bbox[1]); + pdf_add_real(pdf, bbox[2]); + pdf_add_real(pdf, bbox[3]); + pdf_end_array(pdf); + /* + Now all relevant parts of the Page dictionary are copied. Metadata validity + check is needed(as a stream it must be indirect). + */ + obj = ppdict_get_obj(pageDict, "Metadata"); + if (obj != NULL && obj->type != PPREF) { + formatted_warning("pdf inclusion","/Metadata must be indirect object"); + } + /* + copy selected items in Page dictionary + */ + for (i = 0; pagedictkeys[i] != NULL; i++) { + obj = ppdict_rget_obj(pageDict, pagedictkeys[i]); + if (obj != NULL) { + pdf_add_name(pdf, pagedictkeys[i]); + /* + preserves indirection + */ + copyObject(pdf, pdf_doc, obj); + } + } + resources = ppdict_rget_obj(pageDict, "Resources"); + if (resources == NULL) { + /* + If there are no Resources in the Page dict of the embedded page, + try to inherit the Resources from the Pages tree of the embedded + PDF file, climbing up the tree until the Resources are found. + (This fixes a problem with Scribus 1.3.3.14.) + */ + obj = ppdict_rget_obj(pageDict, "Parent"); + while (obj != NULL && obj->type == PPDICT) { + resources = ppdict_rget_obj(obj->dict, "Resources"); + if (resources != NULL) { + break; + } + obj = ppdict_get_obj(obj->dict, "Parent"); + } + } + if (resources != NULL) { + pdf_add_name(pdf, "Resources"); + copyObject(pdf, pdf_doc, resources); + } else { + formatted_warning("pdf inclusion","Page /Resources missing"); + } + /* + User supplied entries. + */ + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) { + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + } + /* + Write the Page contents. + */ + content = ppdict_rget_obj(pageDict, "Contents"); + if (content && content->type == PPSTREAM) { + if (pdf->compress_level == 0 || pdf->recompress) { + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, content->stream, 1, 1); /* decompress */ + } else { + /* copies compressed stream */ + ppstream * stream = content->stream; + ppdict *streamDict = stream->dict; /* */ + obj = ppdict_rget_obj(streamDict, "Length"); + if (obj != NULL) { + pdf_add_name(pdf, "Length"); + copyObject(pdf, pdf_doc, obj); + obj = ppdict_rget_obj(streamDict, "Filter"); + if (obj != NULL) { + pdf_add_name(pdf, "Filter"); + copyObject(pdf, pdf_doc, obj); + /* the next one is irrelevant, only for inline images: */ + /* + obj = ppdict_rget_obj(streamDict, "DecodeParms"); + if (obj != NULL) { + pdf_add_name(pdf, "DecodeParms"); + copyObject(pdf, pdf_doc, obj); + } + */ + } + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream, 0, 0); + } else { + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + copyStreamStream(pdf, stream, 1, 0); + } + } + pdf_end_stream(pdf); + } else if (content && content->type == PPARRAY) { + /* listens to compresslevel */ + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + { + int i; + int b = 0; + int n = content->array->size; + for (i=0; i<n; ++i) { + ppobj *o = pparray_at(content->array,i); + while (o != NULL && o->type == PPREF) { + o = ppref_obj((ppref *) o->ref); + } + if (o != NULL && o->type == PPSTREAM) { + if (b) { + /* + Put a space between streams to be on the safe side (streams + should have a trailing space here, but one never knows) + */ + pdf_out(pdf, ' '); + } else { + b = 1; + } + copyStreamStream(pdf, (ppstream *) o->stream, 1, 0); + } + } + } + pdf_end_stream(pdf); + } else { + /* + the contents are optional, but we need to include an empty stream + */ + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + pdf_end_stream(pdf); + } + pdf_end_obj(pdf); + /* + write out all indirect objects + */ + writeRefs(pdf, pdf_doc); + /* + unrefPdfDocument() must come after freeing whatever is used + + */ + if (! img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } +} + +/* a special simple case of inclusion, e.g. an appearance stream */ + +int write_epdf_object(PDF pdf, image_dict * idict, int n) +{ + int num = 0 ; + if (img_type(idict) != IMG_TYPE_PDF) { + normal_error("pdf inclusion","unknown document"); + } else { + PdfDocument * pdf_doc = refPdfDocument(img_filepath(idict), FE_FAIL, img_userpassword(idict), img_ownerpassword(idict)); + ppdoc * pdfe = pdf_doc->pdfe; + if (ppdoc_xref(pdfe)) { + ppref * ref = ppxref_find(ppdoc_xref(pdfe), (ppuint) n); + if (ref != NULL) { + ppobj *obj; + num = pdf->obj_count++; + obj = ppref_obj(ref); + if (obj->type == PPSTREAM) { + pdf_begin_obj(pdf, num, OBJSTM_NEVER); + } else { + pdf_begin_obj(pdf, num, 2); + } + copyObject(pdf, pdf_doc, obj); + pdf_end_obj(pdf); + writeRefs(pdf, pdf_doc); + } else { + formatted_warning("pdf inclusion","ignoring missing image %i, case 1\n",(int) n); + } + } else { + formatted_warning("pdf inclusion","ignoring missing image %i, case 2\n",(int) n); + } + if (! img_keepopen(idict)) { + unrefPdfDocument(img_filepath(idict)); + } + } + return num; +} + +/* Deallocate a PdfDocument with all its resources. */ + +static void deletePdfDocumentPdfDoc(PdfDocument * pdf_doc) +{ + InObj *r, *n; + /* this may be probably needed for an emergency destroyPdfDocument() */ + for (r = pdf_doc->inObjList; r != NULL; r = n) { + n = r->next; + free(r); + } + if (pdf_doc->pdfe != NULL) { + ppdoc_free(pdf_doc->pdfe); + pdf_doc->pdfe = NULL; + } + if (pdf_doc->memstream != NULL) { + /* pplib does this: free(pdf_doc->memstream); */ + pdf_doc->memstream = NULL; + } + /* pdf_doc->pc++; */ + pdf_doc->pc = 0; +} + +static void destroyPdfDocument(void *pa, void * p) +{ + PdfDocument *pdf_doc = (PdfDocument *) pa; + deletePdfDocumentPdfDoc(pdf_doc); + /* TODO: delete rest of pdf_doc */ +} + +/* + Called when an image has been written and its resources in image_tab are + freed and it's not referenced anymore. +*/ + +void unrefPdfDocument(char *file_path) +{ + PdfDocument *pdf_doc = findPdfDocument(file_path); + if (pdf_doc == NULL) { + /* we're ok */ + } else if (pdf_doc->occurences > 0) { + pdf_doc->occurences--; + if (pdf_doc->occurences == 0) { + deletePdfDocumentPdfDoc(pdf_doc); + } + } else { + /* + We either have a mismatch in ref and unref or we're somehow out of sync + which can happen when we mess with the same file in lua and tex. + */ + formatted_warning("pdf inclusion","there can be a mismatch in opening and closing file '%s'",file_path); + } +} + +/* + For completeness, but it isn't currently used (unreferencing is done by mean + of file_path. +*/ + +void unrefMemStreamPdfDocument(char *file_id) +{ + (void) unrefPdfDocument(file_id); + +} + +/* + Called when PDF embedding system is finalized. We now deallocate all remaining + PdfDocuments. +*/ + +void epdf_free(void) +{ + if (PdfDocumentTree != NULL) + avl_destroy(PdfDocumentTree, destroyPdfDocument); + PdfDocumentTree = NULL; +} diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.h b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.h new file mode 100644 index 00000000000..cc8f0e15041 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.h @@ -0,0 +1,39 @@ +/* pdftoepdf.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2011 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef PDFTOEPDF_H +# define PDFTOEPDF_H + +# include "image.h" + +void read_pdf_info(image_dict *); +void flush_pdf_info(image_dict *); +/* flush_pdfstream_info(image_dict *); */ /* somehow doesn't work due to xfree macro */ +void unrefPdfDocument(char *); +void unrefMemStreamPdfDocument(char *); +void write_epdf(PDF, image_dict *, int suppress_optional_info); +int write_epdf_object(PDF, image_dict *, int n); + +/* epdf.c --- this should go in an own header file */ + +extern void epdf_free(void); + +#endif /* PDFTOEPDF_H */ diff --git a/Build/source/texk/web2c/luatexdir/image/writeimg.c b/Build/source/texk/web2c/luatexdir/image/writeimg.c new file mode 100644 index 00000000000..f544e6b6814 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writeimg.c @@ -0,0 +1,820 @@ +/* + +writeimg.c + +Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> +Copyright 2006-2012 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include <kpathsea/c-auto.h> +#include <kpathsea/c-memstr.h> + +#include "image/image.h" +#include "image/writejpg.h" +#include "image/writejp2.h" +#include "image/writepng.h" +#include "image/writejbig2.h" + +#include "lua.h" +#include "lauxlib.h" + +/*tex + +The function |readimage| performs some basic initializations. Then it looks at +the file extension to determine the image type and calls specific code/functions. +The main disadvantage is that standard file extensions have to be used, otherwise +pdfTeX is not able to detect the correct image type. The patch now looks at the +file header first regardless of the file extension. This is implemented in +function |check_type_by_header|. If this check fails, the traditional test of +standard file extension is tried, done in function |check_type_by_extension|. + +The magic headers are as follows: + +\startitemize + \startitem + \type {png}: 89 50 4E 47 0D 0A 1A 0A or |"\137PNG\013\010\026\010"| + \stopitem + \startitem + \type {jpg}: FF D8 FF or |"\255\216\255"|. + \stopitem + \startitem + \type {jp2}: 00 00 00 0C 6A 50 20 20 0D 0A or |"\000\000\000\012\106\080\032\032\013\010"| + \stopitem + \startitem + \type {pdf}: |"%PDF"| somewhere in the beginning + \stopitem +\stopitemize + +Functions |check_type_by_header| and |check_type_by_extension|: |img_type(img)| +is set to |IMG_TYPE_NONE| by |new_image_dict()|. Both functions try to detect a +type and set |img_type(img)|. Thus a value other than |IMG_TYPE_NONE| indicates +that a type has been found. + +*/ + +#define HEADER_JPG "\xFF\xD8" +#define HEADER_PNG "\x89PNG\r\n\x1A\n" +#define HEADER_JBIG2 "\x97\x4A\x42\x32\x0D\x0A\x1A\x0A" +#define HEADER_JP2 "\x6A\x50\x20\x20" +#define HEADER_PDF "%PDF-" + +#define MAX_HEADER (sizeof(HEADER_PNG)-1) + +#define HEADER_PDF_MEMSTREAM "data:application/pdf," +#define LEN_PDF_MEMSTREAM 21 + +static void check_type_by_header(image_dict * idict) +{ + int i; + FILE *file = NULL; + char header[MAX_HEADER]; + char prefix[LEN_PDF_MEMSTREAM+1]; + if (idict == NULL) + return; + if (img_type(idict) != IMG_TYPE_NONE) + return; + /*tex Here we read the and also check for a memstream object. */ + if (!img_filepath(idict) || !FOPEN_RBIN_MODE) { + normal_error("pdf backend","reading image file failed"); + } + file = fopen(img_filepath(idict), FOPEN_RBIN_MODE); + if (file == NULL) { + /*tex We check the prefix of img_filepath(idict). */ + for (i = 0; (unsigned) i < LEN_PDF_MEMSTREAM; i++) { + prefix[i] = (char) (img_filepath(idict)[i]); + } + prefix[LEN_PDF_MEMSTREAM]='\0'; + if (strncmp(prefix, HEADER_PDF_MEMSTREAM, LEN_PDF_MEMSTREAM) == 0) { + img_type(idict) = IMG_TYPE_PDFMEMSTREAM; + return; + } else { + formatted_error("pdf backend","reading image file '%s' failed",img_filepath(idict)); + } + } + /*tex Do we have a valid file but perhaps unsupported? */ + for (i = 0; (unsigned) i < MAX_HEADER; i++) { + header[i] = (char) xgetc(file); + if (feof(file)) { + normal_error("pdf backend","reading image file failed"); + } + } + xfclose(file, img_filepath(idict)); + /*tex Further tests: */ + if (strncmp(header, HEADER_JPG, sizeof(HEADER_JPG) - 1) == 0) + img_type(idict) = IMG_TYPE_JPG; + else if (strncmp(header + 4, HEADER_JP2, sizeof(HEADER_JP2) - 1) == 0) + img_type(idict) = IMG_TYPE_JP2; + else if (strncmp(header, HEADER_PNG, sizeof(HEADER_PNG) - 1) == 0) + img_type(idict) = IMG_TYPE_PNG; + else if (strncmp(header, HEADER_JBIG2, sizeof(HEADER_JBIG2) - 1) == 0) + img_type(idict) = IMG_TYPE_JBIG2; + else if (strncmp(header, HEADER_PDF, sizeof(HEADER_PDF) - 1) == 0) + img_type(idict) = IMG_TYPE_PDF; +} + +static void check_type_by_extension(image_dict * idict) +{ + char *image_suffix; + if (idict == NULL) + return; + if (img_type(idict) != IMG_TYPE_NONE) + return; + if ((image_suffix = strrchr(img_filename(idict), '.')) == 0) + img_type(idict) = IMG_TYPE_NONE; + else if (strcasecmp(image_suffix, ".png") == 0) + img_type(idict) = IMG_TYPE_PNG; + else if (strcasecmp(image_suffix, ".jpg") == 0 || + strcasecmp(image_suffix, ".jpeg") == 0) + img_type(idict) = IMG_TYPE_JPG; + else if (strcasecmp(image_suffix, ".jp2") == 0) + img_type(idict) = IMG_TYPE_JP2; + else if (strcasecmp(image_suffix, ".jbig2") == 0 || + strcasecmp(image_suffix, ".jb2") == 0) + img_type(idict) = IMG_TYPE_JBIG2; + else if (strcasecmp(image_suffix, ".pdf") == 0) + img_type(idict) = IMG_TYPE_PDF; +} + +void new_img_pdfstream_struct(image_dict * p) +{ + img_pdfstream_ptr(p) = xtalloc(1, pdf_stream_struct); + img_pdfstream_stream(p) = NULL; + img_pdfstream_size(p) = 0; +} + +image *new_image(void) +{ + image *p = xtalloc(1, image); + set_wd_running(p); + set_ht_running(p); + set_dp_running(p); + img_transform(p) = 0; + img_dict(p) = NULL; + img_dictref(p) = LUA_NOREF; + return p; +} + +image_dict *new_image_dict(void) +{ + image_dict *p = xtalloc(1, image_dict); + memset(p, 0, sizeof(image_dict)); + set_wd_running(p); + set_ht_running(p); + set_dp_running(p); + img_transform(p) = 0; + img_pagenum(p) = 1; + img_type(p) = IMG_TYPE_NONE; + img_pagebox(p) = PDF_BOX_SPEC_MEDIA; + img_unset_bbox(p); + img_unset_group(p); + img_state(p) = DICT_NEW; + /*tex A value of -1 means unused while the used counts from 0 */ + img_index(p) = -1; + img_luaref(p) = 0; + img_errorlevel(p) = pdf_inclusion_errorlevel; + fix_pdf_version(static_pdf); + img_pdfmajorversion(p) = pdf_major_version; + img_pdfminorversion(p) = pdf_minor_version; + return p; +} + +static void free_dict_strings(image_dict * p) +{ + xfree(img_filename(p)); + xfree(img_filepath(p)); + xfree(img_attr(p)); + xfree(img_pagename(p)); +} + +void free_image_dict(image_dict * p) +{ + if (ini_version) { + /*tex The image may be \.{\\dump}ed to a format. */ + return; + } + /*tex Called from limglib.c. */ + switch (img_type(p)) { + case IMG_TYPE_PDFMEMSTREAM: + case IMG_TYPE_PDF: + flush_pdf_info(p); + break; + case IMG_TYPE_PNG: + flush_png_info(p); + break; + case IMG_TYPE_JPG: + flush_jpg_info(p); + break; + case IMG_TYPE_JP2: + flush_jp2_info(p); + break; + case IMG_TYPE_JBIG2: + flush_jbig2_info(p); + break; + case IMG_TYPE_PDFSTREAM: + if (img_pdfstream_ptr(p) != NULL) { + xfree(img_pdfstream_stream(p)); + xfree(img_pdfstream_ptr(p)); + } + break; + case IMG_TYPE_NONE: + break; + default: + normal_error("pdf backend","unknown image type"); + } + free_dict_strings(p); + xfree(p); +} + +void read_img(image_dict * idict) +{ + char *filepath = NULL; + int callback_id; + if (img_filename(idict) == NULL) { + normal_error("pdf backend","image file name missing"); + } + callback_id = callback_defined(find_image_file_callback); + if (img_filepath(idict) == NULL) { + if (callback_id > 0) { + /*tex We always callback, also for a mem stream. */ + if (run_callback(callback_id, "S->S", img_filename(idict),&filepath)) { + if (filepath && (strlen(filepath) > 0)) { + img_filepath(idict) = strdup(filepath); + } + } + } + if (img_filepath(idict) == NULL && (strstr(img_filename(idict),"data:application/pdf,") != NULL)) { + /*tex We need to check here for a pdf memstream. */ + img_filepath(idict) = strdup(img_filename(idict)); + } else if (callback_id == 0) { + /*tex Otherwise we use kpse but only when we don't callback. */ + img_filepath(idict) = kpse_find_file(img_filename(idict), kpse_tex_format, true); + } + if (img_filepath(idict) == NULL) { + /*tex In any case we need a name. */ + formatted_error("pdf backend","cannot find image file '%s'", img_filename(idict)); + } + } + recorder_record_input(img_filepath(idict)); + /*tex A few type checks. */ + check_type_by_header(idict); + check_type_by_extension(idict); + /*tex Now we're ready to read the image. */ + switch (img_type(idict)) { + case IMG_TYPE_PDFMEMSTREAM: + case IMG_TYPE_PDF: + read_pdf_info(idict); + break; + case IMG_TYPE_PNG: + read_png_info(idict); + break; + case IMG_TYPE_JPG: + read_jpg_info(idict); + break; + case IMG_TYPE_JP2: + read_jp2_info(idict); + break; + case IMG_TYPE_JBIG2: + read_jbig2_info(idict); + break; + default: + img_type(idict) = IMG_TYPE_NONE; + if (pdf_ignore_unknown_images) { + normal_warning("pdf backend","internal error: ignoring unknown image type"); + } else { + normal_error("pdf backend","internal error: unknown image type"); + } + break; + } + cur_file_name = NULL; + if (img_type(idict) == IMG_TYPE_NONE) { + img_state(idict) = DICT_NEW; + } else if (img_state(idict) < DICT_FILESCANNED) { + img_state(idict) = DICT_FILESCANNED; + } +} + +static image_dict *read_image(char *file_name, int page_num, char *page_name, int colorspace, int page_box, char *user_password, char *owner_password, char *visible_filename) +{ + image *a = new_image(); + image_dict *idict = img_dict(a) = new_image_dict(); + static_pdf->ximage_count++; + img_objnum(idict) = pdf_create_obj(static_pdf, obj_type_ximage, static_pdf->ximage_count); + img_index(idict) = static_pdf->ximage_count; + set_obj_data_ptr(static_pdf, img_objnum(idict), img_index(idict)); + idict_to_array(idict); + img_colorspace(idict) = colorspace; + img_pagenum(idict) = page_num; + img_pagename(idict) = page_name; + img_userpassword(idict) = user_password; + img_ownerpassword(idict) = owner_password; + img_visiblefilename(idict) = visible_filename; + if (file_name == NULL) { + normal_error("pdf backend","no image filename given"); + } + cur_file_name = file_name; + img_filename(idict) = file_name; + img_pagebox(idict) = page_box; + read_img(idict); + return idict; +} + +/*tex + + There can be several page boxes. Normally the cropbox is used. + +*/ + +static pdfboxspec_e scan_pdf_box_spec(void) +{ + if (scan_keyword("mediabox")) + return PDF_BOX_SPEC_MEDIA; + else if (scan_keyword("cropbox")) + return PDF_BOX_SPEC_CROP; + else if (scan_keyword("bleedbox")) + return PDF_BOX_SPEC_BLEED; + else if (scan_keyword("trimbox")) + return PDF_BOX_SPEC_TRIM; + else if (scan_keyword("artbox")) + return PDF_BOX_SPEC_ART; + else + return PDF_BOX_SPEC_NONE; +} + +void scan_pdfximage(PDF pdf) +{ + scaled_whd alt_rule; + image_dict *idict; + int transform = 0, page = 1, pagebox, colorspace = 0; + char *named = NULL, *attr = NULL, *file_name = NULL, *user = NULL, *owner = NULL, *visible = NULL; + alt_rule = scan_alt_rule(); + if (scan_keyword("attr")) { + scan_toks(false, true); + attr = tokenlist_to_cstring(def_ref, true, NULL); + delete_token_ref(def_ref); + } + if (scan_keyword("named")) { + scan_toks(false, true); + if (0) { + named = tokenlist_to_cstring(def_ref, true, NULL); + page = 0; + } else { + normal_warning("pdf backend","named pages are not supported, using page 1"); + page = 1; + } + delete_token_ref(def_ref); + } else if (scan_keyword("page")) { + scan_int(); + page = cur_val; + } + if (scan_keyword("userpassword")) { + scan_toks(false, true); + user = tokenlist_to_cstring(def_ref, true, NULL); + delete_token_ref(def_ref); + } + if (scan_keyword("ownerpassword")) { + scan_toks(false, true); + owner = tokenlist_to_cstring(def_ref, true, NULL); + delete_token_ref(def_ref); + } + if (scan_keyword("visiblefilename")) { + scan_toks(false, true); + visible = tokenlist_to_cstring(def_ref, true, NULL); + delete_token_ref(def_ref); + } + if (scan_keyword("colorspace")) { + scan_int(); + colorspace = cur_val; + } + pagebox = scan_pdf_box_spec(); + if (pagebox == PDF_BOX_SPEC_NONE) { + pagebox = pdf_pagebox; + if (pagebox == PDF_BOX_SPEC_NONE) + pagebox = PDF_BOX_SPEC_CROP; + } + scan_toks(false, true); + file_name = tokenlist_to_cstring(def_ref, true, NULL); + if (file_name == NULL) { + normal_error("pdf backend","no image filename given"); + } + delete_token_ref(def_ref); + idict = read_image(file_name, page, named, colorspace, pagebox, user, owner, visible); + img_attr(idict) = attr; + img_dimen(idict) = alt_rule; + img_transform(idict) = transform; + last_saved_image_index = img_objnum(idict); + last_saved_image_pages = img_totalpages(idict); +} + +void scan_pdfrefximage(PDF pdf) +{ + /*tex One could scan transform as well. */ + int transform = 0; + /*tex Begin of experiment. */ + int open = 0; + /*tex End of experiment. */ + image_dict *idict; + /*tex This scans |<rule spec>| to |alt_rule|. */ + scaled_whd alt_rule, dim; + alt_rule = scan_alt_rule(); + /*tex Begin of experiment. */ + if (scan_keyword("keepopen")) { + open = 1; + } + /*tex End of experiment. */ + scan_int(); + check_obj_type(pdf, obj_type_ximage, cur_val); + tail_append(new_rule(image_rule)); + idict = idict_array[obj_data_ptr(pdf, cur_val)]; + /*tex Begin of experiment, */ + if (open) { + /*tex So we keep the original value when no close is given. */ + idict->keepopen = 1; + } + /*tex End of experiment. */ + if (img_state(idict) == DICT_NEW) { + normal_warning("image","don't rely on the image data to be okay"); + width(tail_par) = 0; + height(tail_par) = 0; + depth(tail_par) = 0; + } else { + if (alt_rule.wd != null_flag || alt_rule.ht != null_flag || alt_rule.dp != null_flag) { + dim = scale_img(idict, alt_rule, transform); + } else { + dim = scale_img(idict, img_dimen(idict), img_transform(idict)); + } + width(tail_par) = dim.wd; + height(tail_par) = dim.ht; + depth(tail_par) = dim.dp; + rule_transform(tail_par) = transform; + rule_index(tail_par) = img_index(idict); + } +} + +/* + The |tex_scale| function follows a sequence of decisions: + + \starttyping + wd ht dp : res = tex; + wd ht -- + wd -- dp + wd -- -- + -- ht dp + -- ht -- + -- -- dp + -- -- -- : res = nat; + \stoptyping + +*/ + +scaled_whd tex_scale(scaled_whd nat, scaled_whd tex) +{ + scaled_whd res; + if (!is_running(tex.wd) && !is_running(tex.ht) && !is_running(tex.dp)) { + /*tex width, height, and depth specified */ + res = tex; + } else if (!is_running(tex.wd)) { + /*tex max. 2 dimensions are specified */ + res.wd = tex.wd; + if (!is_running(tex.ht)) { + res.ht = tex.ht; + /*tex width and height specified */ + res.dp = ext_xn_over_d(tex.ht, nat.dp, nat.ht); + } else if (!is_running(tex.dp)) { + res.dp = tex.dp; + /*tex width and depth specified */ + res.ht = ext_xn_over_d(tex.wd, nat.ht + nat.dp, nat.wd) - tex.dp; + } else { + /*tex only width specified */ + res.ht = ext_xn_over_d(tex.wd, nat.ht, nat.wd); + res.dp = ext_xn_over_d(tex.wd, nat.dp, nat.wd); + } + } else if (!is_running(tex.ht)) { + res.ht = tex.ht; + if (!is_running(tex.dp)) { + res.dp = tex.dp; + /*tex height and depth specified */ + res.wd = ext_xn_over_d(tex.ht + tex.dp, nat.wd, nat.ht + nat.dp); + } else { + /*tex only height specified */ + res.wd = ext_xn_over_d(tex.ht, nat.wd, nat.ht); + res.dp = ext_xn_over_d(tex.ht, nat.dp, nat.ht); + } + } else if (!is_running(tex.dp)) { + res.dp = tex.dp; + /*tex only depth specified */ + res.ht = nat.ht - (tex.dp - nat.dp); + res.wd = nat.wd; + } else { + /*tex nothing specified */ + res = nat; + } + return res; +} + +/*tex + +Within |scale_img| only image width and height matter; the offsets and +positioning are not interesting here. But one needs rotation info to swap width +and height. |img_rotation| comes from the optional |/Rotate| key in the PDF file. + +*/ + +scaled_whd scale_img(image_dict * idict, scaled_whd alt_rule, int transform) +{ + /*tex size and resolution of image */ + int x, y, xr, yr, tmp; + /*tex natural size corresponding to image resolution */ + scaled_whd nat; + int default_res; + nat.dp = 0; + nat.wd = 0; + nat.ht = 0; + if (img_nobbox(idict)) { + if (img_is_bbox(idict)) { + x = img_xsize(idict) = img_bbox(idict)[2] - img_bbox(idict)[0]; + y = img_ysize(idict) = img_bbox(idict)[3] - img_bbox(idict)[1]; + img_xorig(idict) = img_bbox(idict)[0]; + img_yorig(idict) = img_bbox(idict)[1]; + nat.wd = x; + nat.ht = y; + } else { + normal_error("pdf backend","use boundingbox to pass dimensions"); + } + } else { + if ((img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM + || img_type(idict) == IMG_TYPE_PDFSTREAM) && img_is_bbox(idict)) { + /*tex dimensions from image.bbox */ + x = img_xsize(idict) = img_bbox(idict)[2] - img_bbox(idict)[0]; + y = img_ysize(idict) = img_bbox(idict)[3] - img_bbox(idict)[1]; + img_xorig(idict) = img_bbox(idict)[0]; + img_yorig(idict) = img_bbox(idict)[1]; + } else { + /*tex dimensions, resolutions from image file */ + x = img_xsize(idict); + y = img_ysize(idict); + } + xr = img_xres(idict); + yr = img_yres(idict); + if (x <= 0 || y <= 0 || xr < 0 || yr < 0) + normal_error("pdf backend","invalid image dimensions"); + if (xr > 65535 || yr > 65535) { + xr = 0; + yr = 0; + normal_warning("pdf backend","too large image resolution ignored"); + } + if (((transform - img_rotation(idict)) & 1) == 1) { + tmp = x; + x = y; + y = tmp; + tmp = xr; + xr = yr; + yr = tmp; + } + /*tex always for images */ + if (img_type(idict) == IMG_TYPE_PDF || img_type(idict) == IMG_TYPE_PDFMEMSTREAM + || img_type(idict) == IMG_TYPE_PDFSTREAM) { + nat.wd = x; + nat.ht = y; + } else { + default_res = fix_int(pdf_image_resolution, 0, 65535); + if (default_res > 0 && (xr == 0 || yr == 0)) { + xr = default_res; + yr = default_res; + } + if (xr > 0 && yr > 0) { + nat.wd = ext_xn_over_d(one_hundred_inch, x, 100 * xr); + nat.ht = ext_xn_over_d(one_hundred_inch, y, 100 * yr); + } else { + nat.wd = ext_xn_over_d(one_hundred_inch, x, 7200); + nat.ht = ext_xn_over_d(one_hundred_inch, y, 7200); + } + } + } + return tex_scale(nat, alt_rule); +} + +void write_img(PDF pdf, image_dict * idict) +{ + if (img_state(idict) < DICT_WRITTEN) { + report_start_file(filetype_image, img_filepath(idict)); + switch (img_type(idict)) { + case IMG_TYPE_PNG: + write_png(pdf, idict); + break; + case IMG_TYPE_JPG: + write_jpg(pdf, idict); + break; + case IMG_TYPE_JP2: + write_jp2(pdf, idict); + break; + case IMG_TYPE_JBIG2: + write_jbig2(pdf, idict); + break; + case IMG_TYPE_PDFMEMSTREAM: + case IMG_TYPE_PDF: + write_epdf(pdf, idict,(int) pdf_suppress_optional_info); + break; + case IMG_TYPE_PDFSTREAM: + write_pdfstream(pdf, idict); + break; + default: + normal_error("pdf backend","internal error: writing unknown image type"); + } + report_stop_file(filetype_image); + if (img_type(idict) == IMG_TYPE_PNG) { + write_additional_png_objects(pdf); + } + } + if (img_state(idict) < DICT_WRITTEN) + img_state(idict) = DICT_WRITTEN; +} + +int write_img_object(PDF pdf, image_dict * idict, int n) +{ + return write_epdf_object(pdf, idict, n); +} + +void pdf_write_image(PDF pdf, int n) +{ + if (pdf->draftmode == 0) + write_img(pdf, idict_array[obj_data_ptr(pdf, n)]); +} + +void check_pdfstream_dict(image_dict * idict) +{ + if (!img_is_bbox(idict) && !img_nobbox(idict)) { + normal_error("pdf backend","image.stream: no bbox given"); + } + if (img_state(idict) < DICT_FILESCANNED) { + img_state(idict) = DICT_FILESCANNED; + } +} + +void write_pdfstream(PDF pdf, image_dict * idict) +{ + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + if (!img_notype(idict)) { + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Form"); + pdf_dict_add_int(pdf, "FormType", 1); + } + if (!img_nobbox(idict)) { + pdf_add_name(pdf, "BBox"); + pdf_begin_array(pdf); + pdf_add_real(pdf, sp2bp(img_bbox(idict)[0])); + pdf_add_real(pdf, sp2bp(img_bbox(idict)[1])); + pdf_add_real(pdf, sp2bp(img_bbox(idict)[2])); + pdf_add_real(pdf, sp2bp(img_bbox(idict)[3])); + pdf_end_array(pdf); + } + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) { + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + } + if (!img_nolength(idict)) { + pdf_dict_add_streaminfo(pdf); + } + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + if (img_pdfstream_stream(idict) != NULL) { + pdf_out_block(pdf, (const char *) img_pdfstream_stream(idict), img_pdfstream_size(idict)); + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); +} + +idict_entry *idict_ptr, *idict_array = NULL; +size_t idict_limit; + +void idict_to_array(image_dict * idict) +{ + if (idict_ptr - idict_array == 0) { + /*tex align to count from 1 */ + alloc_array(idict, 1, SMALL_BUF_SIZE); + idict_ptr++; + } + alloc_array(idict, 1, SMALL_BUF_SIZE); + *idict_ptr = idict; + idict_ptr++; +} + +void pdf_dict_add_img_filename(PDF pdf, image_dict * idict) +{ + char *p; + if ((pdf_image_addfilename > 0) && ((pdf_suppress_optional_info & 2) == 0)) { + /*tex + For now |PTEX.FileName| is only used for \PDF, but we're prepared + for \JPG, \PNG, ... + */ + if (! ( (img_type(idict) == IMG_TYPE_PDF) || (img_type(idict) == IMG_TYPE_PDFMEMSTREAM) )) + return; + if (img_visiblefilename(idict) != NULL) { + if (strlen(img_visiblefilename(idict)) == 0) { + /*tex empty string blocks PTEX.FileName output */ + return; + } else { + p = img_visiblefilename(idict); + } + } else { + /*tex unset so let's use the default */ + p = img_filepath(idict); + } + /*tex write additional information */ + pdf_add_name(pdf, "PTEX.FileName"); + pdf_printf(pdf, " (%s)", convertStringToPDFString(p, strlen(p))); + } +} + +/*tex + +To allow the use of box resources inside saved boxes in -ini mode, the +information in the array has to be (un)dumped with the format. The next two +routines take care of that. + +Most of the work involved in setting up the images is simply executed again. This +solves the many possible errors resulting from the split in two separate runs. + +There was only one problem remaining: The |pdfversion| and +|pdfinclusionerrorlevel| can have changed inbetween the call to |readimage| and +dump time. + +Some of the dumped values are really type int, not integer,but since the macro +falls back to |generic_dump| anyway, that does not matter. + +We might drop this feature as it makes no sense to store images in the format. + +*/ + +#define dumpinteger generic_dump +#define undumpinteger generic_undump + +/*tex + +(Un)dumping a string means dumping the allocation size, followed by the bytes. +The trailing \.{\\0} is dumped as well, because that makes the code simpler. The +rule specification ends up in |alt_rule|. + +*/ + +scaled_whd scan_alt_rule(void) +{ + boolean loop; + scaled_whd alt_rule; + alt_rule.wd = null_flag; + alt_rule.ht = null_flag; + alt_rule.dp = null_flag; + do { + loop = false; + if (scan_keyword("width")) { + scan_normal_dimen(); + alt_rule.wd = cur_val; + loop = true; + } else if (scan_keyword("height")) { + scan_normal_dimen(); + alt_rule.ht = cur_val; + loop = true; + } else if (scan_keyword("depth")) { + scan_normal_dimen(); + alt_rule.dp = cur_val; + loop = true; + } + } while (loop); + return alt_rule; +} + +/*tex + + This copy a file of arbitrary size to the buffer and flushed as needed. + +*/ + +size_t read_file_to_buf(PDF pdf, FILE * f, size_t len) +{ + size_t i, j, k = 0; + while (len > 0) { + i = (size_t) (len > pdf->buf->size) ? (size_t) pdf->buf->size : len; + pdf_room(pdf, (int) i); + j = fread(pdf->buf->p, 1, i, f); + pdf->buf->p += j; + k += j; + len -= j; + if (i != j) + break; + } + return k; +} diff --git a/Build/source/texk/web2c/luatexdir/image/writeimg.h b/Build/source/texk/web2c/luatexdir/image/writeimg.h new file mode 100644 index 00000000000..96771decd35 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writeimg.h @@ -0,0 +1,52 @@ +/* writeimg.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2012 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef WRITEIMG_H +# define WRITEIMG_H + +# include "dvi/dvigen.h" +# include "image.h" +# include "pdf/pdfpage.h" + +typedef image_dict *idict_entry; +extern idict_entry *idict_array; + +void new_img_pdfstream_struct(image_dict *); +image *new_image(void); +image_dict *new_image_dict(void); +void free_image_dict(image_dict * p); +void read_img(image_dict * p); +void scan_pdfximage(PDF pdf); +void scan_pdfrefximage(PDF pdf); +scaled_whd tex_scale(scaled_whd nat, scaled_whd tex); +scaled_whd scale_img(image_dict *, scaled_whd, int); +void write_img(PDF, image_dict *); +int write_img_object(PDF, image_dict *, int n); +void pdf_write_image(PDF pdf, int n); +void check_pdfstream_dict(image_dict *); +void write_pdfstream(PDF, image_dict *); +void idict_to_array(image_dict *); +void dumpimagemeta(void); +void undumpimagemeta(PDF, int, int); +scaled_whd scan_alt_rule(void); +size_t read_file_to_buf(PDF pdf, FILE * f, size_t len); +void pdf_dict_add_img_filename(PDF pdf, image_dict * idict); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/image/writejbig2.c b/Build/source/texk/web2c/luatexdir/image/writejbig2.c new file mode 100644 index 00000000000..77a1ddb07ca --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejbig2.c @@ -0,0 +1,861 @@ +/* + +writejbig2.c + +Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> +Copyright 2006-2013 Taco Hoekwater <taco@luatex.org> +Copyright 2003-2013 Hartmut Henkel <hartmut@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +/*tex + +This is experimental JBIG2 image support to pdfTeX. JBIG2 image decoding is part +of Adobe PDF-1.4, and requires Acroread 5.0 or later. + +References +========== + +* 14492 FCD: Information technology -- coded representation of picture +and audio information -- lossy/lossless coding of bi-level images / +JBIG committee, 1999 July 16. This JBIG2 Working Draft is available from +http://www.jpeg.org/public/fcd14492.pdf. The references in the C-code +correspond to the sections of this document. + +* PDF Reference, 5th edition, version 1.6, 1985--2005 Adobe Systems +Incorporated. Available online: + +http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf + +News +==== + +31 May 2006: no need to wait for |endoffileflag| in sequential access +organization. + +10 May 2006: |ygetc()| for some catching of broken JBIG2 files; modify to +accept Example 3.4 from PDFRef 5th ed. with short end-of-file segment. + +09 May 2006: |pages_maketree()| and |segments_maketree()| by AVL tree, +some cleaning up. + +06 May 2006: File list replaced by AVL tree; |new_fileinfo()|, +|new_pageinfo()|. + +04 May 2006: Updated for pdftex-1.40-beta-20060213. + +08 Jan. 2003: Added |flushjbig2page0objects()| function. Now at the end +of the pdfTeX run all pending page0 objects are written out. + +08 Jan. 2003: Release on private webpage. + +04 Jan. 2003: Completely rewritten. Now with some data structures. +Rudimentary local file and image bookkeeping. Multiple image inclusion +from one JBIG2 file. Only required page0 segments are marked for +inclusion. + +13 Nov. 2002: pdfcrypting removed. + +08 Dec. 2002: bug in page 0 stream writing repaired. +Strategy for multiple page inclusion from same JBIG2 file: When writing +1st image, create fresh PDF object for page 0, and include any page +0 segments from complete file (even if these segments are not needed +for image). When writing next image, check by filename comparison if +PDF object for page 0 of this JBIG2 file has already been written. This +can only remember the file name for the direct predecessor JBIG2 image +(but images of other types might come inbetween). If such page 0 PDF +object exists, reference it. Else create fresh one. + +09 Dec. 2002: JBIG2 seg. page numbers > 0 are now set to 1, see PDF Ref. + +*/ + +#undef DEBUG + +#include "ptexlib.h" +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include "image/image.h" + +/*tex Table 7.3: Segment types */ + +#define M_SymbolDictionary 0 +#define M_IntermediateTextRegion 4 +#define M_ImmediateTextRegion 6 +#define M_ImmediateLosslessTextRegion 7 +#define M_PatternDictionary 16 +#define M_IntermediateHalftoneRegion 20 +#define M_ImmediateHalftoneRegion 22 +#define M_ImmediateLosslessHalftoneRegion 23 +#define M_IntermediateGenericRegion 36 +#define M_ImmediateGenericRegion 38 +#define M_ImmediateLosslessGenericRegion 39 +#define M_IntermediateGenericRefinementRegion 40 +#define M_ImmediateGenericRefinementRegion 42 +#define M_ImmediateLosslessGenericRefinementRegion 43 +#define M_PageInformation 48 +#define M_EndOfPage 49 +#define M_EndOfStripe 50 +#define M_EndOfFile 51 +#define M_Profiles 52 +#define M_Tables 53 +#define M_Extension 62 + +typedef enum { INITIAL, HAVEINFO, WRITEPDF } PHASE; + +typedef struct _LITEM { + struct _LITEM *prev; + struct _LITEM *next; + void *d; +} LITEM; + +typedef struct _LIST { + LITEM *first; + LITEM *last; + struct avl_table *tree; +} LIST; + +typedef struct _SEGINFO { + unsigned long segnum; + boolean isrefered; + boolean refers; + /*tex Set by |readseghdr|: */ + unsigned int seghdrflags; + boolean pageassocsizeflag; + unsigned int reftosegcount; + unsigned int countofrefered; + unsigned int fieldlen; + unsigned int segnumwidth; + long segpage; + unsigned long segdatalen; + unsigned long hdrstart; + unsigned long hdrend; + unsigned long datastart; + unsigned long dataend; + /*tex Set by |checkseghdrflags|: */ + boolean endofstripeflag; + boolean endofpageflag; + boolean pageinfoflag; + boolean endoffileflag; +} SEGINFO; + +typedef struct _PAGEINFO { + LIST segments; + unsigned long pagenum; + unsigned int width; + unsigned int height; + unsigned int xres; + unsigned int yres; + unsigned int pagesegmentflags; + unsigned int stripinginfo; + unsigned int stripedheight; +} PAGEINFO; + +typedef struct _FILEINFO { + FILE *file; + char *filepath; + long filesize; + /*tex Not including |page0|: */ + LIST pages; + LIST page0; + /*tex Set by |readfilehdr| */ + unsigned int filehdrflags; + boolean sequentialaccess; + unsigned long numofpages; + /*tex Set by |get_jbig2_info| */ + unsigned long streamstart; + unsigned long pdfpage0objnum; + PHASE phase; +} FILEINFO; + +static struct avl_table *file_tree = NULL; + +static int comp_file_entry(const void *pa, const void *pb, void *p) +{ + (void) p; + return strcmp(((const FILEINFO *) pa)->filepath,((const FILEINFO *) pb)->filepath); +} + +static int comp_page_entry(const void *pa, const void *pb, void *p) +{ + (void) p; + return (int) (((const PAGEINFO *) pa)->pagenum - ((const PAGEINFO *) pb)->pagenum); +} + +static int comp_segment_entry(const void *pa, const void *pb, void *p) +{ + (void) p; + return (int) (((const SEGINFO *) pa)->segnum - ((const SEGINFO *) pb)->segnum); +} + +static int ygetc(FILE * stream) +{ + int c = getc(stream); + if (c < 0) { + if (c == EOF) + normal_error("readjbig2","premature end file"); + else + normal_error("readjbig2","can't happen"); + } + return c; +} + +static void initlinkedlist(LIST * lp) +{ + lp->first = NULL; + lp->last = NULL; + lp->tree = NULL; +} + +static LIST *litem_append(LIST * lp) +{ + LITEM *ip; + ip = xtalloc(1, LITEM); + if (lp->first == NULL) { + lp->first = ip; + ip->prev = NULL; + } else { + lp->last->next = ip; + ip->prev = lp->last; + } + lp->last = ip; + ip->next = NULL; + ip->d = NULL; + return lp; +} + +static FILEINFO *new_fileinfo(void) +{ + FILEINFO *fip; + fip = xtalloc(1, FILEINFO); + fip->file = NULL; + fip->filepath = NULL; + fip->filesize = 0; + initlinkedlist(&(fip->pages)); + initlinkedlist(&(fip->page0)); + fip->filehdrflags = 0; + fip->sequentialaccess = false; + fip->numofpages = 0; + fip->streamstart = 0; + fip->pdfpage0objnum = 0; + fip->phase = INITIAL; + return fip; +} + +static PAGEINFO *new_pageinfo(void) +{ + PAGEINFO *pip; + pip = xtalloc(1, PAGEINFO); + initlinkedlist(&(pip->segments)); + pip->pagenum = 0; + pip->width = 0; + pip->height = 0; + pip->xres = 0; + pip->yres = 0; + pip->pagesegmentflags = 0; + pip->stripinginfo = 0; + pip->stripedheight = 0; + return pip; +} + +static void init_seginfo(SEGINFO * sip) +{ + sip->segnum = 0; + sip->isrefered = false; + sip->refers = false; + sip->seghdrflags = 0; + sip->pageassocsizeflag = false; + sip->reftosegcount = 0; + sip->countofrefered = 0; + sip->fieldlen = 0; + sip->segnumwidth = 0; + sip->segpage = 0; + sip->segdatalen = 0; + sip->hdrstart = 0; + sip->hdrend = 0; + sip->datastart = 0; + sip->dataend = 0; + sip->endofstripeflag = false; + sip->endofpageflag = false; + sip->pageinfoflag = false; + sip->endoffileflag = false; +} + +static void pages_maketree(LIST * plp) +{ + LITEM *ip; + void **aa; + assert(plp->tree == NULL); + plp->tree = avl_create(comp_page_entry, NULL, &avl_xallocator); + assert(plp->tree != NULL); + for (ip = plp->first; ip != NULL; ip = ip->next) { + aa = avl_probe(plp->tree, (PAGEINFO *) ip->d); + assert(aa != NULL); + } +} + +static void segments_maketree(LIST * slp) +{ + LITEM *ip; + void **aa; + assert(slp->tree == NULL); + slp->tree = avl_create(comp_segment_entry, NULL, &avl_xallocator); + assert(slp->tree != NULL); + for (ip = slp->first; ip != NULL; ip = ip->next) { + aa = avl_probe(slp->tree, (SEGINFO *) ip->d); + assert(aa != NULL); + } +} + +static PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum) +{ + PAGEINFO tmp; + tmp.pagenum = pagenum; + assert(plp->tree != NULL); + return (PAGEINFO *) avl_find(plp->tree, &tmp); +} + +static SEGINFO *find_seginfo(LIST * slp, unsigned long segnum) +{ + SEGINFO tmp; + tmp.segnum = segnum; + assert(slp->tree != NULL); + return (SEGINFO *) avl_find(slp->tree, &tmp); +} + +unsigned int read2bytes(FILE * f) +{ + unsigned int c = (unsigned int) ygetc(f); + return (c << 8) + (unsigned int) ygetc(f); +} + +unsigned int read4bytes(FILE * f) +{ + unsigned int l = read2bytes(f); + return (l << 16) + read2bytes(f); +} + +static unsigned long getstreamlen(LITEM * slip, boolean refer) +{ + SEGINFO *sip; + unsigned long len = 0; + for (; slip != NULL; slip = slip->next) { + sip = slip->d; + if (refer || sip->isrefered) + len += sip->hdrend - sip->hdrstart + sip->dataend - sip->datastart; + } + return len; +} + +static void readfilehdr(FILEINFO * fip) +{ + unsigned int i; + /*tex Annex D.4: File header syntax */ + /*tex Annex D.4.1: ID string */ + unsigned char jbig2_id[] = { 0x97, 'J', 'B', '2', 0x0d, 0x0a, 0x1a, 0x0a }; + xfseek(fip->file, 0, SEEK_SET, fip->filepath); + for (i = 0; i < 8; i++) + if (ygetc(fip->file) != jbig2_id[i]) + normal_error("readjbig2","ID string missing"); + /*tex Annex D.4.2: File header flags */ + fip->filehdrflags = (unsigned int) ygetc(fip->file); + fip->sequentialaccess = (fip->filehdrflags & 0x01) ? true : false; + if (fip->sequentialaccess) { + /*tex Annex D.1 vs. Annex D.2 */ + xfseek(fip->file, 0, SEEK_END, fip->filepath); + fip->filesize = (long) xftello(fip->file, fip->filepath); + xfseek(fip->file, 9, SEEK_SET, fip->filepath); + } + /*tex Annex D.4.3: Number of pages */ + if (( !(fip->filehdrflags >> 1)) & 0x01) { + /*tex The known number of pages: */ + fip->numofpages = read4bytes(fip->file); + } + /*tex End of file header */ +} + +static void checkseghdrflags(SEGINFO * sip) +{ + sip->endofstripeflag = false; + sip->endofpageflag = false; + sip->pageinfoflag = false; + sip->endoffileflag = false; + /*tex Table 7.3: Segment types */ + switch (sip->seghdrflags & 0x3f) { + case M_SymbolDictionary: + case M_IntermediateTextRegion: + case M_ImmediateTextRegion: + case M_ImmediateLosslessTextRegion: + case M_PatternDictionary: + case M_IntermediateHalftoneRegion: + case M_ImmediateHalftoneRegion: + case M_ImmediateLosslessHalftoneRegion: + case M_IntermediateGenericRegion: + case M_ImmediateGenericRegion: + case M_ImmediateLosslessGenericRegion: + case M_IntermediateGenericRefinementRegion: + case M_ImmediateGenericRefinementRegion: + case M_ImmediateLosslessGenericRefinementRegion: + break; + case M_PageInformation: + sip->pageinfoflag = true; + break; + case M_EndOfPage: + sip->endofpageflag = true; + break; + case M_EndOfStripe: + sip->endofstripeflag = true; + break; + case M_EndOfFile: + sip->endoffileflag = true; + break; + case M_Profiles: + case M_Tables: + case M_Extension: + break; + default: + normal_error("readjbig2","unknown segment type file"); + break; + } +} + +/*tex + + For first reading of file; return value tells if header been read. + +*/ + +static boolean readseghdr(FILEINFO * fip, SEGINFO * sip) +{ + unsigned int i; + sip->hdrstart = xftell(fip->file, fip->filepath); + if (fip->sequentialaccess && sip->hdrstart == (unsigned) fip->filesize) { + /*tex No endoffileflag is ok for sequential access. */ + return false; + } + /*tex Table 7.2.2: Segment number */ + sip->segnum = read4bytes(fip->file); + /*tex Table 7.2.3: Segment header flags */ + sip->seghdrflags = (unsigned int) ygetc(fip->file); + checkseghdrflags(sip); + if (fip->sequentialaccess && sip->endoffileflag) { + /* + Accept shorter segment, makes it compliant with Example 3.4 of + PDFRef. 5th ed. + */ + return true; + } + sip->pageassocsizeflag = ((sip->seghdrflags >> 6) & 0x01) ? true : false; + /*tex Table 7.2.4: Referred-to segment count and retention flags */ + sip->reftosegcount = (unsigned int) ygetc(fip->file); + sip->countofrefered = sip->reftosegcount >> 5; + if (sip->countofrefered < 5) + sip->fieldlen = 1; + else { + sip->fieldlen = 5 + sip->countofrefered / 8; + xfseek(fip->file, sip->fieldlen - 1, SEEK_CUR, fip->filepath); + } + /*tex Table 7.2.5: Referred-to segment numbers */ + if (sip->segnum <= 256) + sip->segnumwidth = 1; + else if (sip->segnum <= 65536) + sip->segnumwidth = 2; + else + sip->segnumwidth = 4; + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: + (void) ygetc(fip->file); + break; + case 2: + (void) read2bytes(fip->file); + break; + case 4: + (void) read4bytes(fip->file); + break; + } + } + /*tex Table 7.2.6: Segment page association */ + if (sip->pageassocsizeflag) + sip->segpage = read4bytes(fip->file); + else + sip->segpage = ygetc(fip->file); + /*tex Table 7.2.7: Segment data length */ + sip->segdatalen = read4bytes(fip->file); + sip->hdrend = (unsigned long) xftello(fip->file, fip->filepath); + /*tex End of segment header. */ + return true; +} + +static void checkseghdr(FILEINFO * fip, SEGINFO * sip); + +static void markpage0seg(FILEINFO * fip, unsigned long referedseg) +{ + PAGEINFO *pip; + SEGINFO *sip; + pip = fip->page0.first->d; + sip = find_seginfo(&(pip->segments), referedseg); + if (sip != NULL) { + if (!sip->refers && sip->countofrefered > 0) + checkseghdr(fip, sip); + sip->isrefered = true; + } +} + +/*tex + + For writing, marks refered page0 segments, sets segpage larger than + zero to one. + +*/ + +static void writeseghdr(PDF pdf, FILEINFO * fip, SEGINFO * sip) +{ + unsigned int i; + unsigned long referedseg = 0; + /*tex Table 7.2.2: Segment number */ + /*tex Table 7.2.3: Segment header flags */ + /*tex Table 7.2.4: Referred-to segment count and retention flags */ + for (i = 0; i < 5 + sip->fieldlen; i++) + pdf_out(pdf, ygetc(fip->file)); + /*tex Table 7.2.5: Referred-to segment numbers */ + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: + referedseg = (unsigned long) ygetc(fip->file); + pdf_out(pdf, referedseg); + break; + case 2: + referedseg = read2bytes(fip->file); + pdf_out(pdf, (referedseg >> 8) & 0xff); + pdf_out(pdf, referedseg & 0xff); + break; + case 4: + referedseg = read4bytes(fip->file); + pdf_out(pdf, (referedseg >> 24) & 0xff); + pdf_out(pdf, (referedseg >> 16) & 0xff); + pdf_out(pdf, (referedseg >> 8) & 0xff); + pdf_out(pdf, referedseg & 0xff); + break; + } + if (fip->page0.last != NULL && !sip->refers) + markpage0seg(fip, referedseg); + } + if (sip->countofrefered > 0) + sip->refers = true; + /*tex Table 7.2.6: Segment page association */ + if (sip->pageassocsizeflag) + for (i = 0; i < 3; i++) { + (void) ygetc(fip->file); + pdf_out(pdf, 0); + } + (void) ygetc(fip->file); + pdf_out(pdf, (unsigned char) ((sip->segpage > 0) ? 1 : 0)); + /*tex Table 7.2.7: Segment data length */ + for (i = 0; i < 4; i++) + pdf_out(pdf, ygetc(fip->file)); + /* End of segment header. */ +} + +/*tex + + For recursive marking of refered page0 segments: + +*/ + +static void checkseghdr(FILEINFO * fip, SEGINFO * sip) +{ + unsigned int i; + unsigned long referedseg = 0; + /*tex Table 7.2.2: Segment number */ + /*tex Table 7.2.3: Segment header flags */ + /*tex Table 7.2.4: Referred-to segment count and retention flags */ + xfseek(fip->file, 5 + sip->fieldlen, SEEK_CUR, fip->filepath); + /*tex Table 7.2.5: Referred-to segment numbers */ + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: + referedseg = (unsigned long) ygetc(fip->file); + break; + case 2: + referedseg = read2bytes(fip->file); + break; + case 4: + referedseg = read4bytes(fip->file); + break; + } + if (!sip->refers) + markpage0seg(fip, referedseg); + } + if (sip->countofrefered > 0) + sip->refers = true; + /*tex Table 7.2.6: Segment page association */ + /*tex Table 7.2.7: Segment data length */ + if (sip->pageassocsizeflag) + xfseek(fip->file, 8, SEEK_CUR, fip->filepath); + else + xfseek(fip->file, 5, SEEK_CUR, fip->filepath); + /*tex End of segment header. */ +} + +static unsigned long findstreamstart(FILEINFO * fip) +{ + SEGINFO tmp; + /*tex Table D.2: Random-access organisation */ + do { + /*tex Find random-access stream start. */ + (void) readseghdr(fip, &tmp); + } while (!tmp.endoffileflag); + fip->streamstart = tmp.hdrend; + readfilehdr(fip); + return fip->streamstart; +} + +static void rd_jbig2_info(FILEINFO * fip) +{ + /*tex For sequential-access only: */ + unsigned long seekdist = 0; + /*tex For random-access only: */ + unsigned long streampos = 0; + unsigned long currentpage = 0; + boolean sipavail = false; + PAGEINFO *pip; + SEGINFO *sip = NULL; + LIST *plp, *slp; + fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE); + readfilehdr(fip); + if (!fip->sequentialaccess) { + /*tex Table D.2: Random-access organisation */ + streampos = findstreamstart(fip); + } + while (true) { + /*tex Loop over segments: */ + if (!sipavail) { + sip = xtalloc(1, SEGINFO); + sipavail = true; + } + init_seginfo(sip); + if (!readseghdr(fip, sip) || sip->endoffileflag) + break; + if (sip->segpage > 0) { + if (sip->segpage > (int) currentpage) { + plp = litem_append(&(fip->pages)); + plp->last->d = new_pageinfo(); + currentpage = (unsigned long) sip->segpage; + } + pip = fip->pages.last->d; + } else { + if (fip->page0.last == NULL) { + plp = litem_append(&(fip->page0)); + plp->last->d = new_pageinfo(); + } + pip = fip->page0.last->d; + } + if (!sip->endofpageflag) { + slp = litem_append(&(pip->segments)); + slp->last->d = sip; + sipavail = false; + } + if (!fip->sequentialaccess) + sip->datastart = streampos; + else + sip->datastart = sip->hdrend; + sip->dataend = sip->datastart + sip->segdatalen; + if (!fip->sequentialaccess && (sip->pageinfoflag || sip->endofstripeflag)) + xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath); + seekdist = sip->segdatalen; + /*tex Table 7.4.8: Page information segment syntax */ + if (sip->pageinfoflag) { + pip->pagenum = (unsigned long) sip->segpage; + pip->width = read4bytes(fip->file); + pip->height = read4bytes(fip->file); + pip->xres = read4bytes(fip->file); + pip->yres = read4bytes(fip->file); + pip->pagesegmentflags = (unsigned) ygetc(fip->file); + /*tex Table 7.4.8.6: Page striping information */ + pip->stripinginfo = read2bytes(fip->file); + seekdist -= 19; + } + if (sip->endofstripeflag) { + pip->stripedheight = read4bytes(fip->file); + seekdist -= 4; + } + if (!fip->sequentialaccess + && (sip->pageinfoflag || sip->endofstripeflag)) + xfseeko(fip->file, (off_t) sip->hdrend, SEEK_SET, fip->filepath); + if (!fip->sequentialaccess) + streampos += sip->segdatalen; + if (fip->sequentialaccess) + xfseeko(fip->file, (off_t) seekdist, SEEK_CUR, fip->filepath); + if (sip->endofpageflag && currentpage && (pip->stripinginfo >> 15)) + pip->height = pip->stripedheight; + } + fip->phase = HAVEINFO; + if (sipavail) + xfree(sip); + xfclose(fip->file, fip->filepath); +} + +static void wr_jbig2(PDF pdf, image_dict * idict, FILEINFO * fip, unsigned long page) +{ + LITEM *slip; + PAGEINFO *pip; + SEGINFO *sip; + unsigned long i; + if (page > 0) { + assert(idict != NULL); + pip = find_pageinfo(&(fip->pages), page); + assert(pip != NULL); + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Image"); + pdf_dict_add_img_filename(pdf, idict); + pdf_dict_add_int(pdf, "Width", pip->width); + pdf_dict_add_int(pdf, "Height", pip->height); + pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray"); + pdf_dict_add_int(pdf, "BitsPerComponent", 1); + pdf_dict_add_int(pdf, "Length", getstreamlen(pip->segments.first, true)); + pdf_add_name(pdf, "Filter"); + pdf_begin_array(pdf); + pdf_add_name(pdf, "JBIG2Decode"); + pdf_end_array(pdf); + if (fip->page0.last != NULL) { + if (fip->pdfpage0objnum == 0) { + fip->pdfpage0objnum = (unsigned long) pdf_create_obj(pdf, obj_type_others, 0); + } + pdf_add_name(pdf, "DecodeParms"); + pdf_begin_array(pdf); + pdf_begin_dict(pdf); + pdf_dict_add_ref(pdf, "JBIG2Globals", fip->pdfpage0objnum); + pdf_end_dict(pdf); + pdf_end_array(pdf); + } + pdf_end_dict(pdf); + } else { + assert(idict == NULL); + pip = find_pageinfo(&(fip->page0), page); + assert(pip != NULL); + pdf_begin_obj(pdf, (int) fip->pdfpage0objnum, OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_int(pdf, "Length", getstreamlen(pip->segments.first, false)); + pdf_end_dict(pdf); + } + pdf_begin_stream(pdf); + fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE); + for (slip = pip->segments.first; slip != NULL; slip = slip->next) { + /*tex Loop over page segments. */ + sip = slip->d; + if (sip->isrefered || page > 0) { + xfseeko(fip->file, (off_t) sip->hdrstart, SEEK_SET, fip->filepath); + /*tex Mark refered-to page 0 segments, change segpages > 1 to 1. */ + writeseghdr(pdf, fip, sip); + xfseeko(fip->file, (off_t) sip->datastart, SEEK_SET, fip->filepath); + for (i = sip->datastart; i < sip->dataend; i++) + pdf_out(pdf, ygetc(fip->file)); + } + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); + xfclose(fip->file, fip->filepath); +} + +boolean supported_jbig2(image_dict * idict) +{ + if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) < 4) { + normal_error("readjbig2","you need to generate at least PDF 1.4"); + return false; + } else { + return true; + } +} + +void flush_jbig2_info(image_dict * idict) +{ + /*tex Todo (or not). */ +} + +void read_jbig2_info(image_dict * idict) +{ + FILEINFO *fip, tmp; + PAGEINFO *pip; + /*tex Already set probably, see other |read_|. */ + img_type(idict) = IMG_TYPE_JBIG2; + if (! supported_jbig2(idict)) { + /*tex Already an error seen? */ + } + if (img_pagenum(idict) < 1) { + normal_error("readjbig2","page must be > 0"); + } + if (file_tree == NULL) { + file_tree = avl_create(comp_file_entry, NULL, &avl_xallocator); + } + tmp.filepath = img_filepath(idict); + fip = (FILEINFO *) avl_find(file_tree, &tmp); + if (fip == NULL) { + fip = new_fileinfo(); + fip->filepath = xstrdup(img_filepath(idict)); + avl_probe(file_tree, fip); + } + if (fip->phase == INITIAL) { + rd_jbig2_info(fip); + pages_maketree(&(fip->pages)); + if (fip->page0.last != NULL) { + pages_maketree(&(fip->page0)); + pip = fip->page0.first->d; + segments_maketree(&(pip->segments)); + } + } + pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict)); + if (pip == NULL) { + formatted_error("readjbig2","page %d not found in image file",(int) img_pagenum(idict)); + } + img_totalpages(idict) = (int) fip->numofpages; + img_xsize(idict) = (int) pip->width; + img_ysize(idict) = (int) pip->height; + img_xres(idict) = (int) (pip->xres * 0.0254 + 0.5); + img_yres(idict) = (int) (pip->yres * 0.0254 + 0.5); + img_colordepth(idict) = 1; +} + +void write_jbig2(PDF pdf, image_dict * idict) +{ + FILEINFO *fip, tmp; + PAGEINFO *pip; + tmp.filepath = img_filepath(idict); + fip = (FILEINFO *) avl_find(file_tree, &tmp); + /*tex Don't write before |rd_jbig2_info()| call. */ + pip = find_pageinfo(&(fip->pages), (unsigned long) img_pagenum(idict)); + assert(pip != NULL); + wr_jbig2(pdf, idict, fip, pip->pagenum); + img_file(idict) = NULL; +} + +void flush_jbig2_page0_objects(PDF pdf) +{ + FILEINFO *fip; + struct avl_traverser t; + if (file_tree != NULL) { + avl_t_init(&t, file_tree); + for (fip = avl_t_first(&t, file_tree); fip != NULL; + fip = avl_t_next(&t)) { + if (fip->page0.last != NULL) + /*tex |NULL|: page0 */ + wr_jbig2(pdf, NULL, fip, 0); + } + } +} diff --git a/Build/source/texk/web2c/luatexdir/image/writejbig2.h b/Build/source/texk/web2c/luatexdir/image/writejbig2.h new file mode 100644 index 00000000000..e2c907bf687 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejbig2.h @@ -0,0 +1,35 @@ +/* writejbig2.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef WRITEJBIG2_H +# define WRITEJBIG2_H + +# include "image.h" + +unsigned int read2bytes(FILE * f); +unsigned int read4bytes(FILE * f); + +boolean supported_jbig2(image_dict *); +void read_jbig2_info(image_dict *); +void flush_jbig2_info(image_dict *); +void write_jbig2(PDF, image_dict *); +void flush_jbig2_page0_objects(PDF); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/image/writejp2.c b/Build/source/texk/web2c/luatexdir/image/writejp2.c new file mode 100644 index 00000000000..0ea0017084a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejp2.c @@ -0,0 +1,302 @@ +/* + +writejp2.c + +Copyright 2011-2013 Taco Hoekwater <taco@luatex.org> +Copyright 2011-2013 Hartmut Henkel <hartmut@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +/*tex + + Basic JPEG~2000 image support. Section and Table references below: + Information technology --- JPEG~2000 image coding system: Core coding system. + ISO/IEC 15444-1, Second edition, 2004-09-15, file |15444-1annexi.pdf|. + +*/ + +#include "ptexlib.h" +#include <math.h> +#include <assert.h> +#include "image/image.h" +#include "image/writejp2.h" +#include "image/writejbig2.h" + +/*tex Table 1.2: Defined boxes */ + +#define BOX_JP 0x6A502020 +#define BOX_FTYP 0x66747970 +#define BOX_JP2H 0x6a703268 +#define BOX_IHDR 0x69686472 +#define BOX_BPCC 0x62706363 +#define BOX_COLR 0x636D6170 +#define BOX_CDEF 0x63646566 +#define BOX_RES 0x72657320 +#define BOX_RESC 0x72657363 +#define BOX_RESD 0x72657364 +#define BOX_JP2C 0x6A703263 + +/*tex Table 1.4: Box definition */ + +typedef struct { + uint64_t lbox; + unsigned int tbox; +} hdr_struct; + +static uint64_t read8bytes(FILE * f) +{ + uint64_t l = read4bytes(f); + l = (l << 32) + read4bytes(f); + return l; +} + +static hdr_struct read_boxhdr(image_dict * idict) +{ + hdr_struct hdr; + hdr.lbox = read4bytes(img_file(idict)); + hdr.tbox = read4bytes(img_file(idict)); + if (hdr.lbox == 1) + hdr.lbox = read8bytes(img_file(idict)); + if (hdr.lbox == 0 && hdr.tbox != BOX_JP2C) { + normal_error("readjp2","LBox == 0"); + } + return hdr; +} + +/*tex Table 1.5.3.1: Image Header box */ + +static void scan_ihdr(image_dict * idict) +{ + unsigned int height, width; + unsigned char bpc; + height = read4bytes(img_file(idict)); + width = read4bytes(img_file(idict)); + img_ysize(idict) = (int) height; + img_xsize(idict) = (int) width; + (void) read2bytes(img_file(idict)); /* nc */ + bpc = (unsigned char) xgetc(img_file(idict)); + img_colordepth(idict) = bpc + 1; + (void) xgetc(img_file(idict)); /* c */ + (void) xgetc(img_file(idict)); /* unkc */ + (void) xgetc(img_file(idict)); /* ipr */ +} + +/*tex Table 1.5.3.7.1: Capture Resolution box */ + +/*tex Table 1.5.3.7.2: Default Display Resolution box */ + +static void scan_resc_resd(image_dict * idict) +{ + unsigned int vr_n, vr_d, hr_n, hr_d; + unsigned char vr_e, hr_e; + double hr_, vr_; + vr_n = read2bytes(img_file(idict)); + vr_d = read2bytes(img_file(idict)); + hr_n = read2bytes(img_file(idict)); + hr_d = read2bytes(img_file(idict)); + vr_e = (unsigned char) xgetc(img_file(idict)); + hr_e = (unsigned char) xgetc(img_file(idict)); + hr_ = ((double) hr_n / hr_d) * exp(hr_e * log(10.0)) * 0.0254; + vr_ = ((double) vr_n / vr_d) * exp(vr_e * log(10.0)) * 0.0254; + img_xres(idict) = (int) (hr_ + 0.5); + img_yres(idict) = (int) (vr_ + 0.5); +} + +/*tex Table 1.5.3.7: Resolution box (superbox) */ + +static void scan_res(image_dict * idict, uint64_t epos_s) +{ + hdr_struct hdr; + uint64_t spos, epos; + epos = xftell(img_file(idict), img_filepath(idict)); + while (1) { + spos = epos; + hdr = read_boxhdr(idict); + epos = spos + hdr.lbox; + switch (hdr.tbox) { + case (BOX_RESC): + /*tex arbitrary: let BOX_RESD have precedence */ + if (img_xres(idict) == 0 && img_yres(idict) == 0) { + scan_resc_resd(idict); + if (xftell(img_file(idict), img_filepath(idict)) != (long)epos) + normal_error("readjp2","resc box size inconsistent"); + } + break; + case (BOX_RESD): + scan_resc_resd(idict); + if (xftell(img_file(idict), img_filepath(idict)) != (long)epos) + normal_error("readjp2","resd box size inconsistent"); + break; + default:; + } + if (epos > epos_s) + normal_error("readjp2","res box size inconsistent"); + if (epos == epos_s) + break; + xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); + } +} + +/*tex Table 1.5.3: JP2 Header box (superbox) */ + +static boolean scan_jp2h(image_dict * idict, uint64_t epos_s) +{ + boolean ihdr_found = false; + hdr_struct hdr; + uint64_t spos, epos; + epos = xftell(img_file(idict), img_filepath(idict)); + while (1) { + spos = epos; + hdr = read_boxhdr(idict); + epos = spos + hdr.lbox; + switch (hdr.tbox) { + case (BOX_IHDR): + scan_ihdr(idict); + if (xftell(img_file(idict), img_filepath(idict)) != (long)epos) + normal_error("readjp2","ihdr box size inconsistent"); + ihdr_found = true; + break; + case (BOX_RES): + scan_res(idict, epos); + break; + default:; + } + if (epos > epos_s) + normal_error("readjp2","jp2h box size inconsistent"); + if (epos == epos_s) + break; + xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); + } + return ihdr_found; +} + +static void close_and_cleanup_jp2(image_dict * idict) +{ + /*tex If one of then is not NULL we already cleaned up. */ + if (img_file(idict) != NULL) { + xfclose(img_file(idict), img_filepath(idict)); + img_file(idict) = NULL; + } + if (img_jp2_ptr(idict) != NULL) { + xfree(img_jp2_ptr(idict)); + } +} + +void flush_jp2_info(image_dict * idict) +{ + close_and_cleanup_jp2(idict); +} + +void read_jp2_info(image_dict * idict) +{ + boolean ihdr_found = false; + hdr_struct hdr; + uint64_t spos, epos; + if (img_type(idict) != IMG_TYPE_JP2) { + normal_error("readjp2","conflicting image dictionary"); + } + if (img_file(idict) != NULL) { + normal_error("readjp2","image data already read"); + } + img_totalpages(idict) = 1; + img_pagenum(idict) = 1; + img_xres(idict) = img_yres(idict) = 0; + img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); + img_jp2_ptr(idict) = xtalloc(1, jp2_img_struct); + xfseek(img_file(idict), 0, SEEK_END, img_filepath(idict)); + img_jp2_ptr(idict)->length = (int) xftell(img_file(idict), img_filepath(idict)); + xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); + if (sizeof(uint64_t) < 8) { + normal_error("readjp2","size problem"); + } + spos = epos = 0; + /*tex Table 1.5.1: JPEG 2000 Signature box */ + hdr = read_boxhdr(idict); + epos = spos + hdr.lbox; + xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); + /*tex Table 1.5.2: File Type box */ + spos = epos; + hdr = read_boxhdr(idict); + if (hdr.tbox != BOX_FTYP) { + normal_error("readjp2","missing ftyp box"); + } + epos = spos + hdr.lbox; + xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); + while (!ihdr_found) { + spos = epos; + hdr = read_boxhdr(idict); + epos = spos + hdr.lbox; + switch (hdr.tbox) { + case BOX_JP2H: + ihdr_found = scan_jp2h(idict, epos); + break; + case BOX_JP2C: + if (!ihdr_found) + normal_error("readjp2","no ihdr box found"); + break; + default:; + } + xfseek(img_file(idict), (long) epos, SEEK_SET, img_filepath(idict)); + } + if (! img_keepopen(idict)) { + close_and_cleanup_jp2(idict); + } +} + +static void reopen_jp2(image_dict * idict) +{ + int width, height, xres, yres; + width = img_xsize(idict); + height = img_ysize(idict); + xres = img_xres(idict); + yres = img_yres(idict); + /*tex We need to make sure that the file kept open. */ + img_keepopen(idict) = 1; + read_jp2_info(idict); + if (width != img_xsize(idict) || height != img_ysize(idict) + || xres != img_xres(idict) || yres != img_yres(idict)) { + normal_error("writejp2","image dimensions have changed"); + } +} + +void write_jp2(PDF pdf, image_dict * idict) +{ + long unsigned l; + if (img_file(idict) == NULL) { + reopen_jp2(idict); + } + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Image"); + pdf_dict_add_img_filename(pdf, idict); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + pdf_dict_add_int(pdf, "Width", (int) img_xsize(idict)); + pdf_dict_add_int(pdf, "Height", (int) img_ysize(idict)); + pdf_dict_add_int(pdf, "Length", (int) img_jp2_ptr(idict)->length); + pdf_dict_add_name(pdf, "Filter", "JPXDecode"); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + l = (long unsigned int) img_jp2_ptr(idict)->length; + xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); + if (read_file_to_buf(pdf, img_file(idict), l) != l) + normal_error("writejp2","fread failed"); + pdf_end_stream(pdf); + pdf_end_obj(pdf); + /*tex We always:*/ + close_and_cleanup_jp2(idict); +} diff --git a/Build/source/texk/web2c/luatexdir/image/writejp2.h b/Build/source/texk/web2c/luatexdir/image/writejp2.h new file mode 100644 index 00000000000..aa8248fd25a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejp2.h @@ -0,0 +1,30 @@ +/* writejp2.h + + Copyright 2011 Taco Hoekwater <taco@luatex.org> + Copyright 2011 Hartmut Henkel <hartmut@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef WRITEJP2_H +# define WRITEJP2_H + +# include "image.h" + +void read_jp2_info(image_dict *); +void flush_jp2_info(image_dict *); +void write_jp2(PDF, image_dict *); + +#endif /* WRITEJP2_H */ diff --git a/Build/source/texk/web2c/luatexdir/image/writejpg.c b/Build/source/texk/web2c/luatexdir/image/writejpg.c new file mode 100644 index 00000000000..f58dc2aa642 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejpg.c @@ -0,0 +1,609 @@ +/* + +writejpg.w + +Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> +Copyright 2006-2011 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include <assert.h> +#include "image/image.h" +#include "image/writejpg.h" + +#define JPG_GRAY 1 /* Gray color space, use /DeviceGray */ +#define JPG_RGB 3 /* RGB color space, use /DeviceRGB */ +#define JPG_CMYK 4 /* CMYK color space, use /DeviceCMYK */ + +typedef enum { + M_SOF0 = 0xc0, /* baseline DCT */ + M_SOF1 = 0xc1, /* extended sequential DCT */ + M_SOF2 = 0xc2, /* progressive DCT */ + M_SOF3 = 0xc3, /* lossless (sequential) */ + + M_SOF5 = 0xc5, /* differential sequential DCT */ + M_SOF6 = 0xc6, /* differential progressive DCT */ + M_SOF7 = 0xc7, /* differential lossless (sequential) */ + + M_JPG = 0xc8, /* reserved for JPEG extensions */ + M_SOF9 = 0xc9, /* extended sequential DCT */ + M_SOF10 = 0xca, /* progressive DCT */ + M_SOF11 = 0xcb, /* lossless (sequential) */ + + M_SOF13 = 0xcd, /* differential sequential DCT */ + M_SOF14 = 0xce, /* differential progressive DCT */ + M_SOF15 = 0xcf, /* differential lossless (sequential) */ + + M_DHT = 0xc4, /* define Huffman table(s) */ + + M_DAC = 0xcc, /* define arithmetic conditioning table */ + + M_RST0 = 0xd0, /* restart */ + M_RST1 = 0xd1, /* restart */ + M_RST2 = 0xd2, /* restart */ + M_RST3 = 0xd3, /* restart */ + M_RST4 = 0xd4, /* restart */ + M_RST5 = 0xd5, /* restart */ + M_RST6 = 0xd6, /* restart */ + M_RST7 = 0xd7, /* restart */ + + M_SOI = 0xd8, /* start of image */ + M_EOI = 0xd9, /* end of image */ + M_SOS = 0xda, /* start of scan */ + M_DQT = 0xdb, /* define quantization tables */ + M_DNL = 0xdc, /* define number of lines */ + M_DRI = 0xdd, /* define restart interval */ + M_DHP = 0xde, /* define hierarchical progression */ + M_EXP = 0xdf, /* expand reference image(s) */ + + M_APP0 = 0xe0, /* application marker, used for JFIF */ + M_APP1 = 0xe1, /* application marker */ + M_APP2 = 0xe2, /* application marker */ + M_APP3 = 0xe3, /* application marker */ + M_APP4 = 0xe4, /* application marker */ + M_APP5 = 0xe5, /* application marker */ + M_APP6 = 0xe6, /* application marker */ + M_APP7 = 0xe7, /* application marker */ + M_APP8 = 0xe8, /* application marker */ + M_APP9 = 0xe9, /* application marker */ + M_APP10 = 0xea, /* application marker */ + M_APP11 = 0xeb, /* application marker */ + M_APP12 = 0xec, /* application marker */ + M_APP13 = 0xed, /* application marker */ + M_APP14 = 0xee, /* application marker, used by Adobe */ + M_APP15 = 0xef, /* application marker */ + + M_JPG0 = 0xf0, /* reserved for JPEG extensions */ + M_JPG13 = 0xfd, /* reserved for JPEG extensions */ + M_COM = 0xfe, /* comment */ + + M_TEM = 0x01, /* temporary use */ + + M_ERROR = 0x100 /* dummy marker, internal use only */ +} JPEG_MARKER; + +static unsigned int read_exif_bytes(unsigned char **p, int n, int b) +{ + unsigned int rval = 0; + unsigned char *pp = *p; + if (b) { + switch (n) { + case 4: + rval += *pp++; rval <<= 8; + rval += *pp++; rval <<= 8; + case 2: + rval += *pp++; rval <<= 8; + rval += *pp; + break; + } + } else { + pp += n; + switch (n) { + case 4: + rval += *--pp; rval <<= 8; + rval += *--pp; rval <<= 8; + case 2: + rval += *--pp; rval <<= 8; + rval += *--pp; + break; + } + } + *p += n; + return rval; +} + +/*tex + + The Exif block can contain the data on the resolution in two forms: + XResolution, YResolution and ResolutionUnit (tag 282, 283 and 296) as well as + PixelPerUnitX, PixelPerUnitY and PixelUnit (tag 0x5111, 0x5112 and 0x5110). + Tags 282, 293 and 296 have the priority, with ResolutionUnit set to inch by + default, then tag 0x5110, 0x5111 and 0x5112, where the only valid value for + PixelUnit is 0.0254, and finally the given value xx and yy, choosen if the + Exif x and y resolution are not strictly positive. + + The next one doesn't save the data, just reads the tags we need based on info + from \typ {http://www.exif.org/Exif2-2.PDF}. + +*/ + +static void read_APP1_Exif (FILE *fp, unsigned short length, int *xx, int *yy, int *or) +{ + unsigned char *buffer = (unsigned char *)xmalloc(length); + unsigned char *p, *rp; + unsigned char *tiff_header; + char bigendian; + int i; + int num_fields, tag, type; + /*tex silence uninitialized warnings */ + int value = 0; + unsigned int num = 0; + unsigned int den = 0; + boolean found_x = false; + boolean found_y = false; + int tempx = 0; + int tempy = 0; + double xres = 0; + double yres = 0; + double res_unit = 1.0; + unsigned int xres_ms = 0; + unsigned int yres_ms = 0; + double res_unit_ms = 0; + boolean found_x_ms = false; + boolean found_y_ms = false; + boolean found_res= false; + int orientation = 1; + size_t ret_len; + ret_len = fread(buffer, length, 1, fp); + if (ret_len != 1) + goto err ; + p = buffer; + while ((p < buffer + length) && (*p == 0)) + ++p; + tiff_header = p; + if ((*p == 'M') && (*(p+1) == 'M')) + bigendian = 1; + else if ((*p == 'I') && (*(p+1) == 'I')) + bigendian = 0; + else + goto err; + p += 2; + i = read_exif_bytes(&p, 2, bigendian); + if (i != 42) + goto err; + i = read_exif_bytes(&p, 4, bigendian); + p = tiff_header + i; + num_fields = read_exif_bytes(&p, 2, bigendian); + while (num_fields-- > 0) { + tag = read_exif_bytes(&p, 2, bigendian); + type = read_exif_bytes(&p, 2, bigendian); + read_exif_bytes(&p, 4, bigendian); + switch (type) { + case 1: + /*tex byte */ + value = *p++; + p += 3; + break; + case 3: + /*tex unsigned short */ + case 8: + /*tex signed short */ + value = read_exif_bytes(&p, 2, bigendian); + p += 2; + break; + case 4: + /*tex unsigned long */ + case 9: + /*tex signed long */ + value = read_exif_bytes(&p, 4, bigendian); + break; + case 5: + /*tex rational */ + case 10: + /*tex srational */ + value = read_exif_bytes(&p, 4, bigendian); + rp = tiff_header + value; + num = read_exif_bytes(&rp, 4, bigendian); + den = read_exif_bytes(&rp, 4, bigendian); + break; + case 7: + /*tex undefined */ + value = *p++; + p += 3; + break; + case 2: + /*tex ascii */ + default: + p += 4; + break; + } + switch (tag) { + case 274: + /*tex orientation */ + orientation = value; + break; + case 282: + /*tex x res */ + if (den != 0) { + xres = num / den; + found_x = true; + } + break; + case 283: + /*tex y res */ + if (den != 0) { + yres = num / den; + found_y = true ; + } + break; + case 296: + /*tex res unit */ + switch (value) { + case 2: + res_unit = 1.0; + break; + case 3: + res_unit = 2.54; + break; + default: + res_unit = 0; + break; + } + break; + case 0x5110: + /*tex PixelUnit */ + switch (value) { + case 1: + res_unit_ms = 0.0254; /* Unit is meter */ + break; + default: + res_unit_ms = 0; + } + break; + case 0x5111: + /*tex PixelPerUnitX */ + found_x_ms = true ; + xres_ms = value; + break; + case 0x5112: + /*tex PixelPerUnitY */ + found_y_ms = true ; + yres_ms = value ; + break; + } + } + if (found_x && found_y && res_unit>0) { + found_res = true; + tempx = (int)(xres * res_unit+0.5); + tempy = (int)(yres * res_unit+0.5); + } else if (found_x_ms && found_y_ms && res_unit_ms==0.0254) { + found_res = true; + tempx = (int)(xres_ms * res_unit_ms+0.5); + tempy = (int)(yres_ms * res_unit_ms+0.5); + } + if (found_res) { + if (tempx>0 && tempy>0) { + if ((tempx!=(*xx) || tempy!=(*yy)) && (*xx!=0 && (*yy!=0) ) ) { + formatted_warning("readjpg","Exif resolution %ddpi x %ddpi differs from the input resolution %ddpi x %ddpi",tempx,tempy,*xx,*yy); + } + if (tempx==1 || tempy==1) { + formatted_warning("readjpg","Exif resolution %ddpi x %ddpi looks weird", tempx, tempy); + } + *xx = tempx; + *yy = tempy; + } else { + formatted_warning("readjpg","Bad Exif resolution %ddpi x %ddpi (zero or negative value of a signed integer)",tempx,tempy); + } + } + *or = orientation; +err: + free(buffer); + return; +} + +/*tex + + Contrary to \PDF\ where several parallel usage can happen (\PDF, |TEX, \LUA) + with bitmaps we care less about keeping files open. So, we can keep files + open in the img lib but then they are closed after inclusion anyway. + +*/ + +static void close_and_cleanup_jpg(image_dict * idict) +{ + /*tex if one of then is not NULL we already cleaned up */ + if (img_file(idict) != NULL) { + xfclose(img_file(idict), img_filepath(idict)); + img_file(idict) = NULL; + } + if (img_jpg_ptr(idict) != NULL) { + xfree(img_jpg_ptr(idict)); + } +} + +void flush_jpg_info(image_dict * idict) +{ + close_and_cleanup_jpg(idict); +} + +/*tex + + The jpeg images are scanned for resolution, colorspace, depth, dimensions and + orientation. We need to look at the exif blob for that. The original version + did a quick test for jfif and exif but there can be more blobs later on. The + current approach is to run over the linked list of blobs which is somewhat + less efficient but not noticeable. + +*/ + +void read_jpg_info(image_dict * idict) +{ + int i, position, units = 0; + unsigned short length; + int okay = 0 ; + FILE *fp = img_file(idict); + if (img_type(idict) != IMG_TYPE_JPG) { + normal_error("readjpg","conflicting image dictionary"); + } + if (fp != NULL) { + normal_error("readjpg","image data already read"); + } + fp = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); + img_totalpages(idict) = 1; + img_pagenum(idict) = 1; + img_xres(idict) = img_yres(idict) = 0; + img_file(idict) = fp; + if (fp == NULL) { + normal_error("readjpg","unable to read image file"); + } + img_jpg_ptr(idict) = xtalloc(1, jpg_img_struct); + xfseek(fp, 0, SEEK_END, img_filepath(idict)); + img_jpg_ptr(idict)->length = xftell(fp, img_filepath(idict)); + xfseek(fp, 0, SEEK_SET, img_filepath(idict)); + if ((unsigned int) read2bytes(fp) != 0xFFD8) { + normal_error("readjpg","no header found"); + } + xfseek(fp, 0, SEEK_SET, img_filepath(idict)); + while (1) { + if (feof(fp)) { + if (okay) { + break ; + } else { + normal_error("readjpg","premature file end"); + } + } else if (fgetc(fp) != 0xFF) { + if (okay) { + break ; + } else { + normal_error("readjpg","no marker found"); + } + } + i = xgetc(fp); + position = ftell(fp); + length = 0 ; + switch (i) { + case M_SOF3: + /*tex lossless */ + case M_SOF5: + case M_SOF6: + case M_SOF7: + /*tex lossless */ + case M_SOF9: + case M_SOF10: + case M_SOF11: + /*tex lossless */ + case M_SOF13: + case M_SOF14: + case M_SOF15: + /*tex lossless */ + formatted_error("readjpg","unsupported compression SOF_%d", i - M_SOF0); + break; + case M_SOF2: + if (img_pdfmajorversion(idict) < 2 && img_pdfminorversion(idict) <= 2) { + normal_error("readjpg","progressive DCT with PDF-1.2 is not permitted"); + } + case M_SOF0: + case M_SOF1: + /*tex read segment length */ + length = (int) read2bytes(fp); + img_colordepth(idict) = xgetc(fp); + img_ysize(idict) = (int) read2bytes(fp); + img_xsize(idict) = (int) read2bytes(fp); + img_jpg_color(idict) = xgetc(fp); + switch (img_jpg_color(idict)) { + case JPG_GRAY: + img_procset(idict) |= PROCSET_IMAGE_B; + break; + case JPG_RGB: + img_procset(idict) |= PROCSET_IMAGE_C; + break; + case JPG_CMYK: + img_procset(idict) |= PROCSET_IMAGE_C; + break; + default: + formatted_error("readjpg","unsupported color space %i", (int) img_jpg_color(idict)); + } + okay = 1 ; + break ; + case M_APP0: + { + char app_sig[32]; + length = (int) read2bytes(fp); + if (length > 6) { + if (fread(app_sig, sizeof(char), 5, fp) != 5) + return; + if (!memcmp(app_sig, "JFIF\000", 5)) { + /*tex skip two bytes, compiler is also happy*/ + units = (int) read2bytes(fp); + units = xgetc(fp); + img_xres(idict) = (int) read2bytes(fp); + img_yres(idict) = (int) read2bytes(fp); + switch (units) { + case 1: + /*tex pixels per inch */ + if ((img_xres(idict) == 1) || (img_yres(idict) == 1)) { + formatted_warning("readjpg","unusual resolution of %ddpi by %ddpi", img_xres(idict), img_yres(idict)); + } + break; + case 2: + /*tex pixels per cm */ + img_xres(idict) = (int) ((double) img_xres(idict) * 2.54); + img_yres(idict) = (int) ((double) img_yres(idict) * 2.54); + break; + default: + img_xres(idict) = img_yres(idict) = 0; + break; + } + } + /*tex + If either xres or yres is 0 but the other isn't, set + it to the value of the other. + */ + } + } + break; + case M_APP1: + { + char app_sig[32]; + length = (int) read2bytes(fp); + if (length > 7) { + if (fread(app_sig, sizeof(char), 5, fp) != 5) + return; + if (!memcmp(app_sig, "Exif\000", 5)) { + int xxres = img_xres(idict); + int yyres = img_yres(idict); + int orientation = img_orientation(idict); + read_APP1_Exif(fp, length - 7, &xxres, &yyres, &orientation); + img_xres(idict) = xxres; + img_yres(idict) = yyres; + img_orientation(idict) = orientation; + } + } + } + break; + /*tex ignore markers without parameters */ + case M_SOI: + case M_EOI: + case M_TEM: + case M_RST0: + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + break; + default: + /*tex skip variable length markers */ + length = (int) read2bytes(fp); + break; + } + if (length > 0) { + xfseek(fp, position + length, SEEK_SET, img_filepath(idict)); + } + } + xfseek(fp, 0, SEEK_SET, img_filepath(idict)); + if (! img_keepopen(idict)) { + close_and_cleanup_jpg(idict); + } + if (okay){ + if ((img_xres(idict) == 0) && (img_yres(idict) != 0)) { + img_xres(idict) = img_yres(idict); + } + if ((img_yres(idict) == 0) && (img_xres(idict) != 0)) { + img_yres(idict) = img_xres(idict); + } + } else { + normal_error("readjpg","unknown fatal error"); + } +} + +static void reopen_jpg(image_dict * idict) +{ + int width = img_xsize(idict); + int height = img_ysize(idict); + int xres = img_xres(idict); + int yres = img_yres(idict); + /*tex + we need to make sure that the file kept open + */ + img_keepopen(idict) = 1; + read_jpg_info(idict); + if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) { + normal_error("writejpg","image dimensions have changed"); + } +} + +void write_jpg(PDF pdf, image_dict * idict) +{ + size_t l; + if (img_file(idict) == NULL) { + reopen_jpg(idict); + } + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Image"); + pdf_dict_add_img_filename(pdf, idict); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) { + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + } + pdf_dict_add_int(pdf, "Width", (int) img_xsize(idict)); + pdf_dict_add_int(pdf, "Height", (int) img_ysize(idict)); + pdf_dict_add_int(pdf, "BitsPerComponent", (int) img_colordepth(idict)); + pdf_dict_add_int(pdf, "Length", (int) img_jpg_ptr(idict)->length); + if (img_colorspace(idict) != 0) { + pdf_dict_add_ref(pdf, "ColorSpace", (int) img_colorspace(idict)); + } else { + switch (img_jpg_color(idict)) { + case JPG_GRAY: + pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray"); + break; + case JPG_RGB: + pdf_dict_add_name(pdf, "ColorSpace", "DeviceRGB"); + break; + case JPG_CMYK: + pdf_dict_add_name(pdf, "ColorSpace", "DeviceCMYK"); + pdf_add_name(pdf, "Decode"); + pdf_begin_array(pdf); + pdf_add_int(pdf, 1); + pdf_add_int(pdf, 0); + pdf_add_int(pdf, 1); + pdf_add_int(pdf, 0); + pdf_add_int(pdf, 1); + pdf_add_int(pdf, 0); + pdf_add_int(pdf, 1); + pdf_add_int(pdf, 0); + pdf_end_array(pdf); + break; + default: + formatted_error("writejpg","unsupported JPEG color space %i", (int) img_jpg_color(idict)); + } + } + pdf_dict_add_name(pdf, "Filter", "DCTDecode"); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + l = (size_t) img_jpg_ptr(idict)->length; + xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); + if (read_file_to_buf(pdf, img_file(idict), l) != l) { + normal_error("writejpg","fread failed"); + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); + close_and_cleanup_jpg(idict); +} diff --git a/Build/source/texk/web2c/luatexdir/image/writejpg.h b/Build/source/texk/web2c/luatexdir/image/writejpg.h new file mode 100644 index 00000000000..96bf961246f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejpg.h @@ -0,0 +1,30 @@ +/* writejpg.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef WRITEJPG_H +# define WRITEJPG_H + +# include "image.h" + +void read_jpg_info(image_dict *); +void flush_jpg_info(image_dict *); +void write_jpg(PDF, image_dict *); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/image/writepng.c b/Build/source/texk/web2c/luatexdir/image/writepng.c new file mode 100644 index 00000000000..165e202f4e3 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writepng.c @@ -0,0 +1,645 @@ +/* + +writepng.c + +Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> +Copyright 2006-2013 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include <assert.h> +#include "image/image.h" +#include "image/writepng.h" + +static void close_and_cleanup_png(image_dict * idict) +{ + if (img_file(idict) != NULL) { + xfclose(img_file(idict), img_filepath(idict)); + img_file(idict) = NULL; + } + if (img_png_ptr(idict) != NULL) { + png_destroy_read_struct(&(img_png_png_ptr(idict)), &(img_png_info_ptr(idict)), NULL); + xfree(img_png_ptr(idict)); + } +} + +void flush_png_info(image_dict * idict) +{ + close_and_cleanup_png(idict); +} + +/*tex A dummy function: */ + +static void warn(png_structp png_ptr, png_const_charp msg) +{ + (void)png_ptr; (void)msg; +} + +void read_png_info(image_dict * idict) +{ + png_structp png_p; + png_infop info_p; + if (img_type(idict) != IMG_TYPE_PNG) { + normal_error("readpng","conflicting image dictionary"); + } + if (img_file(idict) != NULL) { + normal_error("readpng","image data already read"); + } + img_totalpages(idict) = 1; + img_pagenum(idict) = 1; + img_xres(idict) = img_yres(idict) = 0; + img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); + img_png_ptr(idict) = xtalloc(1, png_img_struct); + if ((png_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, warn)) == NULL) { + normal_error("readpng","png_create_read_struct() failed"); + } + img_png_png_ptr(idict) = png_p; + if ((info_p = png_create_info_struct(png_p)) == NULL) { + normal_error("readpng","png_create_info_struct() failed"); + } + img_png_info_ptr(idict) = info_p; + if (setjmp(png_jmpbuf(png_p))) { + normal_error("readpng","internal error"); + } +#if PNG_LIBPNG_VER >= 10603 + /*tex ignore possibly incorrect CMF bytes */ + png_set_option(png_p, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON); +#endif + png_init_io(png_p, img_file(idict)); + png_read_info(png_p, info_p); + /*tex resolution support */ + img_xsize(idict) = (int) png_get_image_width(png_p, info_p); + img_ysize(idict) = (int) png_get_image_height(png_p, info_p); + if (png_get_valid(png_p, info_p, PNG_INFO_pHYs)) { + img_xres(idict) = round(0.0254 * (double) png_get_x_pixels_per_meter(png_p, info_p)); + img_yres(idict) = round(0.0254 * (double) png_get_y_pixels_per_meter(png_p, info_p)); + } + switch (png_get_color_type(png_p, info_p)) { + case PNG_COLOR_TYPE_PALETTE: + img_procset(idict) |= PROCSET_IMAGE_C | PROCSET_IMAGE_I; + break; + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + img_procset(idict) |= PROCSET_IMAGE_B; + break; + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + img_procset(idict) |= PROCSET_IMAGE_C; + break; + default: + formatted_error("readpng","unsupported type of color_type '%i'",(int) png_get_color_type(png_p, info_p)); + } + img_colordepth(idict) = png_get_bit_depth(png_p, info_p); + /*tex So we can optionally keep open a file in |img|. */ + if (! img_keepopen(idict)) { + close_and_cleanup_png(idict); + } +} + +#define write_gray_pixel_16(r) \ + if (j % 4 == 0 || j % 4 == 1) \ + pdf_quick_out(pdf, *r++); \ + else \ + smask[smask_ptr++] = *r++ \ + +#define write_gray_pixel_8(r) \ + if (j % 2 == 0) \ + pdf_quick_out(pdf, *r++); \ + else \ + smask[smask_ptr++] = *r++ + +#define write_rgb_pixel_16(r) \ + if (!(j % 8 == 6 || j % 8 == 7)) \ + pdf_quick_out(pdf, *r++); \ + else \ + smask[smask_ptr++] = *r++ + +#define write_rgb_pixel_8(r) \ + if (j % 4 != 3) \ + pdf_quick_out(pdf, *r++); \ + else \ + smask[smask_ptr++] = *r++ + +#define write_simple_pixel(r) pdf_quick_out(pdf,*r++) + +#define write_noninterlaced(outmac) \ + for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { \ + png_read_row(png_p, row, NULL); \ + r = row; \ + k = (size_t) png_get_rowbytes(png_p, info_p); \ + while (k > 0) { \ + l = (k > pdf->buf->size) ? pdf->buf->size : k; \ + pdf_room(pdf, l); \ + for (j = 0; j < l; j++) { \ + outmac; \ + } \ + k -= l; \ + } \ + } + +#define write_interlaced(outmac) \ + for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { \ + row = rows[i]; \ + k = (size_t) png_get_rowbytes(png_p, info_p); \ + while (k > 0) { \ + l = (k > pdf->buf->size) ? pdf->buf->size : k; \ + pdf_room(pdf, l); \ + for (j = 0; j < l; j++) { \ + outmac; \ + } \ + k -= l; \ + } \ + xfree(rows[i]); \ + } + +static void write_palette_streamobj(PDF pdf, int palette_objnum, png_colorp palette, int num_palette) +{ + int i; + if (palette_objnum == 0) + return; + assert(palette != NULL); + pdf_begin_obj(pdf, palette_objnum, OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + for (i = 0; i < num_palette; i++) { + pdf_room(pdf, 3); + pdf_quick_out(pdf, palette[i].red); + pdf_quick_out(pdf, palette[i].green); + pdf_quick_out(pdf, palette[i].blue); + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); +} + +static void write_smask_streamobj(PDF pdf, image_dict * idict, int smask_objnum, png_bytep smask, int smask_size) +{ + int i; + png_structp png_p = img_png_png_ptr(idict); + png_infop info_p = img_png_info_ptr(idict); + png_byte bitdepth = png_get_bit_depth(png_p, info_p); + pdf_begin_obj(pdf, smask_objnum, OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Image"); + pdf_dict_add_img_filename(pdf, idict); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + pdf_dict_add_int(pdf, "Width", (int) png_get_image_width(png_p, info_p)); + pdf_dict_add_int(pdf, "Height", (int) png_get_image_height(png_p, info_p)); + pdf_dict_add_int(pdf, "BitsPerComponent", (bitdepth == 16 ? 8 : bitdepth)); + pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray"); + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + for (i = 0; i < smask_size; i++) { + if (i % 8 == 0) + pdf_room(pdf, 8); + pdf_quick_out(pdf, smask[i]); + if (bitdepth == 16) + i++; + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); +} + +static void write_png_gray(PDF pdf, image_dict * idict) +{ + int i; + size_t j, k, l; + png_structp png_p = img_png_png_ptr(idict); + png_infop info_p = img_png_info_ptr(idict); + png_bytep row, r, *rows; + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { + row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); + write_noninterlaced(write_simple_pixel(r)); + xfree(row); + } else { + if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) { + formatted_warning("pngwrite","large interlaced bitmap might cause out of memory"); + } + rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep); + for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { + rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); + } + png_read_image(png_p, rows); + write_interlaced(write_simple_pixel(row)); + xfree(rows); + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); +} + +static void write_png_gray_alpha(PDF pdf, image_dict * idict) +{ + int i; + size_t j, k, l; + png_structp png_p = img_png_png_ptr(idict); + png_infop info_p = img_png_info_ptr(idict); + png_bytep row, r, *rows; + int smask_objnum = 0; + png_bytep smask; + int smask_ptr = 0; + int smask_size = 0; + smask_objnum = pdf_create_obj(pdf, obj_type_others, 0); + pdf_dict_add_ref(pdf, "SMask", (int) smask_objnum); + smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 2) * png_get_image_height(png_p, info_p)); + smask = xtalloc((unsigned) smask_size, png_byte); + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { + row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); + if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { + write_noninterlaced(write_gray_pixel_16(r)); + } else { + write_noninterlaced(write_gray_pixel_8(r)); + } + xfree(row); + } else { + if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) { + formatted_warning("pngwrite","large interlaced bitmap might cause out of memory"); + } + rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep); + for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { + rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); + } + png_read_image(png_p, rows); + if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { + write_interlaced(write_gray_pixel_16(row)); + } else { + write_interlaced(write_gray_pixel_8(row)); + } + xfree(rows); + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); + write_smask_streamobj(pdf, idict, smask_objnum, smask, smask_size); + xfree(smask); +} + +static void write_png_rgb_alpha(PDF pdf, image_dict * idict) +{ + int i; + size_t j, k, l; + png_structp png_p = img_png_png_ptr(idict); + png_infop info_p = img_png_info_ptr(idict); + png_bytep row, r, *rows; + int smask_objnum = 0; + png_bytep smask; + int smask_ptr = 0; + int smask_size = 0; + smask_objnum = pdf_create_obj(pdf, obj_type_others, 0); + pdf_dict_add_ref(pdf, "SMask", (int) smask_objnum); + smask_size = (int) ((png_get_rowbytes(png_p, info_p) / 4) * png_get_image_height(png_p, info_p)); + smask = xtalloc((unsigned) smask_size, png_byte); + pdf_dict_add_streaminfo(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) { + row = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); + if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { + write_noninterlaced(write_rgb_pixel_16(r)); + } else { + write_noninterlaced(write_rgb_pixel_8(r)); + } + xfree(row); + } else { + if (png_get_image_height(png_p, info_p) * png_get_rowbytes(png_p, info_p) >= 10240000L) { + formatted_warning("pngwrite","large interlaced bitmap might cause out of memory"); + } + rows = xtalloc(png_get_image_height(png_p, info_p), png_bytep); + for (i = 0; i < (int) png_get_image_height(png_p, info_p); i++) { + rows[i] = xtalloc(png_get_rowbytes(png_p, info_p), png_byte); + } + png_read_image(png_p, rows); + if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor != 0)) { + write_interlaced(write_rgb_pixel_16(row)); + } else { + write_interlaced(write_rgb_pixel_8(row)); + } + xfree(rows); + } + pdf_end_stream(pdf); + pdf_end_obj(pdf); + write_smask_streamobj(pdf, idict, smask_objnum, smask, smask_size); + xfree(smask); +} + +/*tex + +The |copy_png| code is cheerfully gleaned from Thomas Merz' PDFlib, file +|p_png.c| ``SPNG - Simple PNG''. The goal is to use pdf's native FlateDecode +support, if that is possible. Only a subset of the png files allows this, but for +these it greatly improves inclusion speed. + +In the ``PNG Copy'' mode only the IDAT chunks are copied; all other chunks from +the PNG file are discarded. If there are any other chunks in the PNG file, which +might influence the visual appearance of the image, or if image processing like +gamma change is requested, the ``PNG Copy'' function must be skipped; therefore +the lengthy tests. + +*/ + +static int spng_getint(FILE * f) +{ + unsigned char buf[4]; + if (fread(buf, 1, 4, f) != 4) { + normal_error("writepng", "reading chunk type failed"); + } + return ((((((int) buf[0] << 8) + buf[1]) << 8) + buf[2]) << 8) + buf[3]; +} + +#define SPNG_CHUNK_IDAT 0x49444154 +#define SPNG_CHUNK_IEND 0x49454E44 + +static void copy_png(PDF pdf, image_dict * idict) +{ + int type, streamlength = 0, idat = 0; + size_t len; + boolean endflag = false; + FILE *f; + png_structp png_p; + png_infop info_p; + assert(idict != NULL); + png_p = img_png_png_ptr(idict); + info_p = img_png_info_ptr(idict); + f = (FILE *) png_get_io_ptr(png_p); + /*tex 1st pass to find overall stream /Length */ + if (fseek(f, 8, SEEK_SET) != 0) + normal_error("writepng", "fseek in file failed"); + do { + len = spng_getint(f); + type = spng_getint(f); + switch (type) { + case SPNG_CHUNK_IEND: + endflag = true; + break; + case SPNG_CHUNK_IDAT: + streamlength += len; + default: + if (fseek(f, len + 4, SEEK_CUR) != 0) { + normal_error("writepng", "fseek in file failed"); + } + } + } while (endflag == false); + pdf_dict_add_int(pdf, "Length", streamlength); + pdf_dict_add_name(pdf, "Filter", "FlateDecode"); + pdf_add_name(pdf, "DecodeParms"); + pdf_begin_dict(pdf); + pdf_dict_add_int(pdf, "Colors", png_get_color_type(png_p,info_p) == PNG_COLOR_TYPE_RGB ? 3 : 1); + pdf_dict_add_int(pdf, "Columns", png_get_image_width(png_p, info_p)); + pdf_dict_add_int(pdf, "BitsPerComponent", png_get_bit_depth(png_p, info_p)); + pdf_dict_add_int(pdf, "Predictor", 10); + pdf_end_dict(pdf); + pdf_end_dict(pdf); + pdf_begin_stream(pdf); + /*tex 2nd pass to copy data */ + endflag = false; + if (fseek(f, 8, SEEK_SET) != 0) + normal_error("writepng", "fseek in file failed"); + do { + len = spng_getint(f); + type = spng_getint(f); + switch (type) { + case SPNG_CHUNK_IDAT: + /*tex do copy */ + if (idat == 2) { + normal_error("writepng", "IDAT chunk sequence broken"); + } + idat = 1; + if (read_file_to_buf(pdf, f, len) != len) { + normal_error("writepng", "fread failed"); + } else if (fseek(f, 4, SEEK_CUR) != 0) { + normal_error("writepng", "fseek in file failed"); + } + break; + case SPNG_CHUNK_IEND: + /*tex done */ + endflag = true; + break; + default: + if (idat == 1) + idat = 2; + if (fseek(f, len + 4, SEEK_CUR) != 0) + normal_error("writepng", "fseek in file failed"); + } + } while (endflag == false); + pdf_end_stream(pdf); + pdf_end_obj(pdf); +} + +static void reopen_png(image_dict * idict) +{ + int width, height, xres, yres; + /*tex A consistency check: */ + width = img_xsize(idict); + height = img_ysize(idict); + xres = img_xres(idict); + yres = img_yres(idict); + /*tex We need to ake sure that the file kept open. */ + img_keepopen(idict) = 1; + read_png_info(idict); + if (width != img_xsize(idict) || height != img_ysize(idict) || xres != img_xres(idict) || yres != img_yres(idict)) { + normal_error("writepng", "image dimensions have changed"); + } +} + +void write_png(PDF pdf, image_dict * idict) +{ +#ifndef PNG_FP_1 + /*tex for libpng < 1.5.0 */ +# define PNG_FP_1 100000 +#endif + int num_palette, palette_objnum = 0; + boolean png_copy = true; + double gamma = 0.0; + png_fixed_point int_file_gamma = 0; + png_structp png_p; + png_infop info_p; + png_colorp palette; + assert(idict != NULL); + if (img_file(idict) == NULL) + reopen_png(idict); + assert(img_png_ptr(idict) != NULL); + png_p = img_png_png_ptr(idict); + info_p = img_png_info_ptr(idict); + /*tex simple transparency support */ + if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_p); + png_copy = false; + } + /*tex alpha channel support */ + if (pdf->minor_version < 4 + && png_get_color_type(png_p, info_p) | PNG_COLOR_MASK_ALPHA) { + png_set_strip_alpha(png_p); + png_copy = false; + } + /*tex 16 bit depth support */ + if (pdf->minor_version < 5) + pdf->image_hicolor = 0; + if ((png_get_bit_depth(png_p, info_p) == 16) && (pdf->image_hicolor == 0)) { + png_set_strip_16(png_p); + png_copy = false; + } + /*tex gamma support */ + if (png_get_valid(png_p, info_p, PNG_INFO_gAMA)) { + png_get_gAMA(png_p, info_p, &gamma); + png_get_gAMA_fixed(png_p, info_p, &int_file_gamma); + } + if (pdf->image_apply_gamma) { + if (png_get_valid(png_p, info_p, PNG_INFO_gAMA)) + png_set_gamma(png_p, (pdf->gamma / 1000.0), gamma); + else + png_set_gamma(png_p, (pdf->gamma / 1000.0), (1000.0 / pdf->image_gamma)); + png_copy = false; + } + /*tex reset structure */ + (void) png_set_interlace_handling(png_p); + png_read_update_info(png_p, info_p); + pdf_begin_obj(pdf, img_objnum(idict), OBJSTM_NEVER); + pdf_begin_dict(pdf); + pdf_dict_add_name(pdf, "Type", "XObject"); + pdf_dict_add_name(pdf, "Subtype", "Image"); + pdf_dict_add_img_filename(pdf, idict); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) { + pdf_printf(pdf, "\n%s\n", img_attr(idict)); + } + pdf_dict_add_int(pdf, "Width", (int) png_get_image_width(png_p, info_p)); + pdf_dict_add_int(pdf, "Height", (int) png_get_image_height(png_p, info_p)); + pdf_dict_add_int(pdf, "BitsPerComponent", (int) png_get_bit_depth(png_p, info_p)); + if (img_colorspace(idict) != 0) { + pdf_dict_add_ref(pdf, "ColorSpace", (int) img_colorspace(idict)); + } else { + switch (png_get_color_type(png_p, info_p)) { + case PNG_COLOR_TYPE_PALETTE: + png_get_PLTE(png_p, info_p, &palette, &num_palette); + palette_objnum = pdf_create_obj(pdf, obj_type_others, 0); + pdf_add_name(pdf, "ColorSpace"); + pdf_begin_array(pdf); + pdf_add_name(pdf, "Indexed"); + pdf_add_name(pdf, "DeviceRGB"); + /*tex hival */ + pdf_add_int(pdf, (int) (num_palette - 1)); + /*tex lookup */ + pdf_add_ref(pdf, (int) palette_objnum); + pdf_end_array(pdf); + break; + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + pdf_dict_add_name(pdf, "ColorSpace", "DeviceGray"); + break; + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + pdf_dict_add_name(pdf, "ColorSpace", "DeviceRGB"); + break; + default: + formatted_error("writepng", "unsupported color_type '%i'", png_get_color_type(png_p, info_p)); + } + } + if (png_copy + && pdf->minor_version > 1 + && png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE + && (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY + || png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB) + && !pdf->image_apply_gamma + && (!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1) + && !png_get_valid(png_p, info_p, PNG_INFO_cHRM) + && !png_get_valid(png_p, info_p, PNG_INFO_iCCP) + && !png_get_valid(png_p, info_p, PNG_INFO_sBIT) + && !png_get_valid(png_p, info_p, PNG_INFO_sRGB) + && !png_get_valid(png_p, info_p, PNG_INFO_bKGD) + && !png_get_valid(png_p, info_p, PNG_INFO_hIST) + && !png_get_valid(png_p, info_p, PNG_INFO_tRNS) + && !png_get_valid(png_p, info_p, PNG_INFO_sPLT)) { + copy_png(pdf, idict); + } else { + if (img_errorlevel(idict) > 1) { + if (!png_copy) + normal_warning("pngcopy","failed"); + if (!(pdf->minor_version > 1)) + formatted_warning("pngcopy","skipped because minorversion is '%d'", pdf->minor_version); + if (!(png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE)) + normal_warning("pngcopy","skipped because of interlacing"); + if (!((png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_GRAY) + || (png_get_color_type(png_p, info_p) == PNG_COLOR_TYPE_RGB))) + normal_warning("pngcopy","skipped because of colortype"); + if (pdf->image_apply_gamma) + normal_warning("pngcopy","skipped because of gamma (1)"); + if (!(!png_get_valid(png_p, info_p, PNG_INFO_gAMA) || int_file_gamma == PNG_FP_1)) + normal_warning("pngcopy","skipped because of gamma (2)"); + if (png_get_valid(png_p, info_p, PNG_INFO_cHRM)) + normal_warning("pngcopy","skipped because of cHRM"); + if (png_get_valid(png_p, info_p, PNG_INFO_iCCP)) + normal_warning("pngcopy","skipped because of iCCP"); + if (png_get_valid(png_p, info_p, PNG_INFO_sBIT)) + normal_warning("pngcopy","skipped because of sBIT"); + if (png_get_valid(png_p, info_p, PNG_INFO_sRGB)) + normal_warning("pngcopy","skipped because of sRGB"); + if (png_get_valid(png_p, info_p, PNG_INFO_bKGD)) + normal_warning("pngcopy","skipped because of bKGD"); + if (png_get_valid(png_p, info_p, PNG_INFO_hIST)) + normal_warning("pngcopy","skipped because of hIST"); + if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) + normal_warning("pngcopy","skipped because of tRNS"); + if (png_get_valid(png_p, info_p, PNG_INFO_sPLT)) + normal_warning("pngcopy","skipped because of sPLT"); + } + switch (png_get_color_type(png_p, info_p)) { + case PNG_COLOR_TYPE_PALETTE: + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_RGB: + write_png_gray(pdf, idict); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (pdf->minor_version >= 4) { + write_png_gray_alpha(pdf, idict); + } else + write_png_gray(pdf, idict); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (pdf->minor_version >= 4) { + write_png_rgb_alpha(pdf, idict); + } else + write_png_gray(pdf, idict); + break; + default: + assert(0); + } + } + write_palette_streamobj(pdf, palette_objnum, palette, num_palette); + /*tex always */ + close_and_cleanup_png(idict); +} + + +/*tex + + Called after the xobject generated by |write_png| has been finished; used to + write out additional objects. + +*/ + +void write_additional_png_objects(PDF pdf) +{ + (void) pdf; + return; +} diff --git a/Build/source/texk/web2c/luatexdir/image/writepng.h b/Build/source/texk/web2c/luatexdir/image/writepng.h new file mode 100644 index 00000000000..2af2a3fa2fb --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writepng.h @@ -0,0 +1,32 @@ +/* writepng.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2011 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef WRITEPNG_H +# define WRITEPNG_H + +# include "image.h" + +void read_png_info(image_dict *); +void flush_png_info(image_dict *); + +void write_additional_png_objects(PDF); +void write_png(PDF, image_dict *); + +#endif |