summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/image
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/luatexdir/image
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (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.h183
-rw-r--r--Build/source/texk/web2c/luatexdir/image/image.h242
-rw-r--r--Build/source/texk/web2c/luatexdir/image/pdftoepdf.c1114
-rw-r--r--Build/source/texk/web2c/luatexdir/image/pdftoepdf.h39
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writeimg.c820
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writeimg.h52
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejbig2.c861
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejbig2.h35
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejp2.c302
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejp2.h30
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejpg.c609
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writejpg.h30
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writepng.c645
-rw-r--r--Build/source/texk/web2c/luatexdir/image/writepng.h32
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