diff options
author | Martin Schröder <martin@oneiros.de> | 2008-03-24 15:47:15 +0000 |
---|---|---|
committer | Martin Schröder <martin@oneiros.de> | 2008-03-24 15:47:15 +0000 |
commit | 0b5550c241229764120d133af80a0cbab5937ddd (patch) | |
tree | 81309c18d6f0deba7407813cf15b9add2db375d1 /Build/source/texk/web2c/luatexdir/image | |
parent | 51f55df6a36ce376952a1f2b30bdb4dd0e06548d (diff) |
First step of importing luatex (up to r1121)
git-svn-id: svn://tug.org/texlive/trunk@7124 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/image')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/.indent.pro | 12 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/epdf.c | 107 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/epdf.h | 125 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/image.h | 223 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc | 1008 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writeimg.c | 677 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejbig2.c | 749 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejbig2.h | 151 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writejpg.c | 277 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/image/writepng.c | 635 |
10 files changed, 3964 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/image/.indent.pro b/Build/source/texk/web2c/luatexdir/image/.indent.pro new file mode 100644 index 00000000000..e9e690f1ba8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/.indent.pro @@ -0,0 +1,12 @@ +/* $Id$ */ +--k-and-r-style +--blank-lines-after-procedures +--line-length 80 +--procnames-start-lines +--no-space-after-function-call-names +--dont-break-procedure-type +--space-after-cast +/* this does not work in indent 2.2.6 :-( */ +--no-tabs +/* this is new in indent 2.2.9 */ +--preprocessor-indentation 2 diff --git a/Build/source/texk/web2c/luatexdir/image/epdf.c b/Build/source/texk/web2c/luatexdir/image/epdf.c new file mode 100644 index 00000000000..071181f17bd --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/epdf.c @@ -0,0 +1,107 @@ +/* +Copyright (c) 1996-2002 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. + +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: epdf.c 1012 2008-02-14 00:00:57Z oneiros $ +*/ + +#include "ptexlib.h" + + +#include <kpathsea/c-vararg.h> +#include <kpathsea/c-proto.h> +#include <string.h> + +extern void epdf_check_mem(void); +extern void register_fd_entry(fd_entry *); + + +int is_subsetable(fm_entry * fm) +{ + assert(is_included(fm)); + return is_subsetted(fm); +} + +fd_entry *epdf_create_fontdescriptor(fm_entry * fm) +{ + fd_entry *fd; + if ((fd = lookup_fd_entry(fm->ff_name, fm->slant, fm->extend)) == NULL) { + fm->in_use = true; + fd = new_fd_entry(); + fd->fm = fm; + register_fd_entry(fd); + fd->fd_objnum = pdf_new_objnum(); + assert(fm->ps_name != NULL); + fd->fontname = xstrdup(fm->ps_name); /* just fallback */ + /* preset_fontmetrics (fo->fd, f); */ + fd->gl_tree = avl_create(comp_string_entry, NULL, &avl_xallocator); + assert(fd->gl_tree != NULL); + } + return fd; +} + +integer get_fd_objnum(fd_entry * fd) +{ + assert(fd->fd_objnum != 0); + return fd->fd_objnum; +} + +integer get_fn_objnum(fd_entry * fd) +{ + if (fd->fn_objnum == 0) + fd->fn_objnum = pdf_new_objnum(); + return fd->fn_objnum; +} + +/*********************************************************************** + * Mark glyphs used by embedded PDF file: + * 1. build fontdescriptor, if not yet existing + * 2. mark glyphs directly there + * + * Input charset from xpdf is a string of glyph names including + * leading slashes, but without blanks between them, like: /a/b/c +***********************************************************************/ + +void epdf_mark_glyphs(fd_entry * fd, char *charset) +{ + char *p, *q, *s; + char *glyph; + void **aa; + if (charset == NULL) + return; + assert(fd != NULL); + for (s = charset + 1, q = charset + strlen(charset); s < q; s = p + 1) { + for (p = s; *p != '\0' && *p != '/'; p++); + *p = '\0'; + if ((char *) avl_find(fd->gl_tree, s) == NULL) { + glyph = xstrdup(s); + aa = avl_probe(fd->gl_tree, glyph); + assert(aa != NULL); + } + } +} + +void embed_whole_font(fd_entry * fd) +{ + fd->all_glyphs = true; +} + +void epdf_free(void) +{ + epdf_check_mem(); +} 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..2359d53d3db --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/epdf.h @@ -0,0 +1,125 @@ +/* +Copyright (c) 1996-2006 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. + +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: epdf.h 1031 2008-02-14 23:03:20Z hhenkel $ +*/ + +extern "C" { + +#include <kpathsea/c-auto.h> + + extern char *xstrdup(const char *); + +/* the following code is extremly ugly but needed for including web2c/config.h */ + +#include <kpathsea/c-proto.h> /* define P?H macros */ + + 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 <web2c/c-auto.h> /* define SIZEOF_LONG */ +#include <web2c/config.h> /* define type integer */ + +#include <web2c/luatexdir/ptexmac.h> +#include "openbsd-compat.h" +#include "image.h" + +/* #-define pdfbufsize pdfbufmax */ + + extern integer epdf_page_box; + extern void *epdf_xref; + + extern integer pdf_box_spec_media; + extern integer pdf_box_spec_crop; + extern integer pdf_box_spec_bleed; + extern integer pdf_box_spec_trim; + extern integer pdf_box_spec_art; + + extern integer pdf_stream_length; + extern integer pdf_ptr; + extern integer pool_ptr; + typedef unsigned char eight_bits; + extern eight_bits *pdf_buf; + extern integer pdf_buf_size; + extern integer pdf_os_mode; + extern eight_bits pdf_last_byte; + + extern char notdef[]; + + extern integer fixed_replace_font; + + extern int is_subsetable(struct fm_entry *); + extern struct fm_entry *lookup_fontmap(char *); + extern integer get_fontfile(struct fm_entry *); + extern integer get_fontname(struct fm_entry *); + extern integer pdf_new_objnum(void); + extern void read_pdf_info(image_dict *, integer, integer); + extern void embed_whole_font(struct fd_entry *); + extern void epdf_check_mem(void); + extern void epdf_delete(image_dict *); + extern void epdf_free(void); + __attribute__ ((format(printf, 1, 2))) + extern void pdf_printf(const char *fmt, ...); + extern void pdf_puts(const char *); + extern void pdf_begin_stream(void); + extern void pdf_end_obj(void); + extern void pdf_end_stream(void); + extern void pdf_flush(void); + __attribute__ ((noreturn, format(printf, 1, 2))) + extern void pdftex_fail(const char *fmt, ...); + __attribute__ ((format(printf, 1, 2))) + extern void pdftex_warn(const char *fmt, ...); + __attribute__ ((format(printf, 1, 2))) + extern void tex_printf(const char *, ...); + extern void write_epdf(image_dict *); + extern void zpdf_begin_dict(integer, bool); + extern void zpdf_begin_obj(integer, bool); + extern void zpdf_create_obj(integer, integer); + extern void zpdf_new_dict(integer, integer, bool); + extern void zpdf_os_get_os_buf(integer); + +/* epdf.c */ + extern void epdf_mark_glyphs(struct fd_entry *, char *); + extern struct fd_entry *epdf_create_fontdescriptor(struct fm_entry *); + extern int get_fd_objnum(struct fd_entry *); + extern int get_fn_objnum(struct fd_entry *); + +/* write_enc.c */ + extern void epdf_write_enc(char **, integer); + +/* write_enc.c */ + extern pdf_img_struct *new_pdf_img_struct(void); + +/* utils.c */ + extern char *convertStringToPDFString(char *in, int len); + extern char *stripzeros(char *a); + +/* config.c */ + extern integer cfgpar(integer); + +/* avlstuff.c */ + extern void avl_put_obj(integer, integer); + extern integer avl_find_obj(integer, integer, integer); +} 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..cf7d4f0dd88 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/image.h @@ -0,0 +1,223 @@ +/* +Copyright (c) 1996-2002 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: image.h 1100 2008-03-09 13:40:48Z hhenkel $ +*/ + +#ifndef IMAGE_H +# define IMAGE_H + +# include <../libpng/png.h> + +# define JPG_UINT16 unsigned int +# define JPG_UINT32 unsigned long +# define JPG_UINT8 unsigned char + +# define IMAGE_COLOR_B 1 +# define IMAGE_COLOR_C 2 +# define IMAGE_COLOR_I 4 + +extern integer zround(double); /* from zround.c */ +# define bp2int(p) zround(p * (one_hundred_bp / 100.0)) + +# define TYPE_IMG "image" +# define TYPE_IMG_DICT "image-dict" + +# define scaled integer + +/**********************************************************************/ + +typedef struct { + float width; + float height; + float orig_x; + float orig_y; + void *doc; +} pdf_img_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 { /* currently unused */ +} jb2_img_struct; + +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 */ + DICT_WRITTEN /* image dict written to file */ +} dict_state; + +typedef enum { IMAGE_TYPE_NONE, IMAGE_TYPE_PDF, IMAGE_TYPE_PNG, IMAGE_TYPE_JPG, + IMAGE_TYPE_JBIG2, IMAGE_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 { + integer objnum; + integer index; /* /Im1, /Im2, ... */ + integer x_size; /* dimensions in pixel counts as in JPG/PNG/JBIG2 file */ + integer y_size; + integer x_res; /* pixel resolution as in JPG/PNG/JBIG2 file */ + integer y_res; + integer colorspace; /* number of /ColorSpace object */ + integer total_pages; + integer page_num; /* requested page (by number) */ + char *pagename; /* requested page (by name) */ + char *filename; /* requested raw file name */ + char *filepath; /* full file path after kpathsea */ + char *attr; /* additional image dict entries */ + FILE *file; + imgtype_e image_type; + int color_space; /* used color space. See JPG_ constants */ + int color_depth; /* color depth */ + pdfboxspec_e page_box_spec; /* PDF page box spec.: media/crop/bleed/trim/art */ + dict_state state; + union { + pdf_img_struct *pdf; + png_img_struct *png; + jpg_img_struct *jpg; + jb2_img_struct *jb2; + } img_struct; +} image_dict; + +# define img_objnum(N) ((N)->objnum) +# define img_index(N) ((N)->index) +# define img_xsize(N) ((N)->x_size) +# define img_ysize(N) ((N)->y_size) +# define img_xres(N) ((N)->x_res) +# define img_yres(N) ((N)->y_res) +# define img_colorspace(N) ((N)->colorspace) +# 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_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_color(N) ((N)->color_space) +# define img_colordepth(N) ((N)->color_depth) +# define img_pagebox(N) ((N)->page_box_spec) +# define img_state(N) ((N)->state) + +# define img_pdf_ptr(N) ((N)->img_struct.pdf) +# define img_pdf_width(N) ((N)->img_struct.pdf->width) +# define img_pdf_height(N) ((N)->img_struct.pdf->height) +# define img_pdf_orig_x(N) ((N)->img_struct.pdf->orig_x) +# define img_pdf_orig_y(N) ((N)->img_struct.pdf->orig_y) +# define img_pdf_doc(N) ((N)->img_struct.pdf->doc) + +# 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_jb2_ptr(N) ((N)->img_struct.jb2) + +/**********************************************************************/ + +typedef struct { + integer width; /* requested/actual TeX dimensions */ + integer height; + integer depth; + integer transform; + integer flags; + image_dict *dict; + int dict_ref; /* luaL_ref() reference */ +} image; + +# define img_width(N) ((N)->width) +# define img_height(N) ((N)->height) +# define img_depth(N) ((N)->depth) +# define img_transform(N) ((N)->transform) +# define img_flags(N) ((N)->flags) +# define img_dict(N) ((N)->dict) +# define img_dictref(N) ((N)->dict_ref) + +# define F_FLAG_SCALED 0x01 +# define F_FLAG_REFERED 0x02 + +# define img_flags(N) ((N)->flags) +# define img_set_scaled(N) (img_flags(N) |= F_FLAG_SCALED) +# define img_set_refered(N) (img_flags(N) |= F_FLAG_REFERED) +# define img_unset_scaled(N) (img_flags(N) &= ~F_FLAG_SCALED) +# define img_unset_refered(N) (img_flags(N) &= ~F_FLAG_REFERED) +# define img_is_scaled(N) ((img_flags(N) & F_FLAG_SCALED) != 0) +# define img_is_refered(N) ((img_flags(N) & F_FLAG_REFERED) != 0) + +# 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) + +/**********************************************************************/ + +/* writeimg.c */ + +image *new_image(); +image_dict *new_image_dict(); +void init_image(image *); +void init_image_dict(image_dict *); +void scale_img(image *); +integer img_to_array(image *); +void delete_image(image *); +void free_image_dict(image_dict * p); +void read_img(image_dict *, integer, integer); + +/* writepng.c */ + +# ifndef boolean /* TODO: from where to get the original definition? */ +# define boolean int +# endif + +void read_png_info(image_dict *, img_readtype_e); +void read_jpg_info(image_dict *, img_readtype_e); +void read_jbig2_info(image_dict *); +void read_pdf_info(image_dict *, integer, integer); +void write_img(image_dict *); +void write_png(image_dict *); +void write_jpg(image_dict *); +void write_jbig2(image_dict *); +void write_epdf(image_dict *); + +/* pdftoepdf.cc */ + +void epdf_delete(image_dict * idict); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc new file mode 100644 index 00000000000..c5270eb58bd --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/pdftoepdf.cc @@ -0,0 +1,1008 @@ +/* +Copyright 1996-2006 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. + +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: pdftoepdf.cc 1058 2008-02-19 21:13:58Z hhenkel $ +*/ + +#include <stdlib.h> +#include <math.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <aconf.h> +#include <GString.h> +#include <gmem.h> +#include <gfile.h> +#include <config.h> +#include <assert.h> +#include "Object.h" +#include "Stream.h" +#include "Array.h" +#include "Dict.h" +#include "XRef.h" +#include "Catalog.h" +#include "Link.h" +#include "Page.h" +#include "GfxFont.h" +#include "PDFDoc.h" +#include "GlobalParams.h" +#include "Error.h" + +#include "epdf.h" + +#define one_hundred_bp 6578176 /* 7227 * 65536 / 72 */ + +// This file is mostly C and not very much C++; it's just used to interface +// the functions of xpdf, which happens to be written in C++. + +// Used flags below: +// PDFTEX_COPY_PAGEGROUP +// If pdfTeX should copy a page group (a new feature in PDF 1.4 for +// transparency) of an included file. The current support for this is +// most likely broken. pdfTeX will at least give a warning if this flag +// is not set. Surprisingly Acrobat and Jaws display files without a +// page group correctly, so it might be safe to not set the flag. +// See also PDFTEX_COPY_PAGEGROUP_NOFAIL. +// PDFTEX_COPY_PAGEGROUP_NOFAIL +// If set to false, pdfTeX will treat a page group in an included file +// as an error and abort gracefully. This is only evaluated if +// PDFTEX_COPY_PAGEGROUP is set. + +// The prefix "PTEX" for the PDF keys is special to pdfTeX; +// this has been registered with Adobe by Hans Hagen. + +#define pdfkeyprefix "PTEX" + +// PdfObject encapsulates the xpdf Object type, +// and properly frees its resources on destruction. +// Use obj-> to access members of the Object, +// and &obj to get a pointer to the object. +// It is no longer necessary to call Object::free explicitely. + +class PdfObject { + public: + PdfObject() { // nothing + } ~PdfObject() { + iObject.free(); + } + Object *operator->() { + return &iObject; + } + Object *operator&() { + return &iObject; + } + private: // no copying or assigning + PdfObject(const PdfObject &); + void operator=(const PdfObject &); + public: + Object iObject; +}; + +// When copying the Resources of the selected page, all objects are copied +// recusively top-down. Indirect objects however are not fetched during +// copying, but get a new object number from pdfTeX and then will be +// appended into a linked list. Duplicates are checked and removed from the +// list of indirect objects during appending. + +enum InObjType { + objFont, + objFontDesc, + objOther +}; + +struct InObj { + Ref ref; // ref in original PDF + InObjType type; // object type + InObj *next; // next entry in list of indirect objects + integer num; // new object number in output PDF + fd_entry *fd; // pointer to /FontDescriptor object structure + integer enc_objnum; // Encoding for objFont + int written; // has it been written to output PDF? +}; + +struct UsedEncoding { + integer enc_objnum; + GfxFont *font; + UsedEncoding *next; +}; + +static InObj *inObjList; +static UsedEncoding *encodingList; +static GBool isInit = gFalse; + +// -------------------------------------------------------------------- +// Maintain list of open embedded PDF files +// -------------------------------------------------------------------- + +struct PdfDocument { + char *file_name; + PDFDoc *doc; + XRef *xref; + InObj *inObjList; + int occurences; // number of references to the document; the doc can be + // deleted when this is negative + PdfDocument *next; +}; + +static PdfDocument *pdfDocuments = 0; + +static XRef *xref = 0; + +// Returns pointer to PdfDocument record for PDF file. +// Creates a new record if it doesn't exist yet. +// xref is made current for the document. + +static PdfDocument *find_add_document(char *file_name) +{ + PdfDocument *p = pdfDocuments; + while (p && strcmp(p->file_name, file_name) != 0) + p = p->next; + if (p) { + xref = p->xref; + (p->occurences)++; +#ifdef DEBUG + fprintf(stderr, "\npdfTeX Debug: Incrementing %s (%d)\n", p->file_name, + p->occurences); +#endif + return p; + } + p = new PdfDocument; + p->file_name = xstrdup(file_name); + p->xref = xref = 0; + p->occurences = 0; +#ifdef DEBUG + fprintf(stderr, "\npdfTeX Debug: Creating %s (%d)\n", p->file_name, + p->occurences); +#endif + GString *docName = new GString(p->file_name); + p->doc = new PDFDoc(docName); // takes ownership of docName + if (!p->doc->isOk() || !p->doc->okToPrint()) { + pdftex_fail("xpdf: reading PDF image failed"); + } + p->inObjList = 0; + p->next = pdfDocuments; + pdfDocuments = p; + return p; +} + +// Deallocate a PdfDocument with all its resources + +static void delete_document(PdfDocument * pdf_doc) +{ + PdfDocument **p = &pdfDocuments; + while (*p && *p != pdf_doc) + p = &((*p)->next); + // should not happen: + if (!*p) + return; + // unlink from list + *p = pdf_doc->next; + // free pdf_doc's resources + InObj *r, *n; + for (r = pdf_doc->inObjList; r != 0; r = n) { + n = r->next; + delete r; + } + xref = pdf_doc->xref; + delete pdf_doc->doc; + xfree(pdf_doc->file_name); + delete pdf_doc; +} + +// Replacement for +// Object *initDict(Dict *dict1){ initObj(objDict); dict = dict1; return this; } + +static void initDictFromDict(PdfObject & obj, Dict * dict) +{ + obj->initDict(xref); + for (int i = 0, l = dict->getLength(); i < l; i++) { + Object obj1; + obj->dictAdd(copyString(dict->getKey(i)), dict->getValNF(i, &obj1)); + } +} + +// -------------------------------------------------------------------- + +static int addEncoding(GfxFont * gfont) +{ + UsedEncoding *n; + n = new UsedEncoding; + n->next = encodingList; + encodingList = n; + n->font = gfont; + n->enc_objnum = pdf_new_objnum(); + return n->enc_objnum; +} + +#define addFont(ref, fd, enc_objnum) \ + addInObj(objFont, ref, fd, enc_objnum) + +// addFontDesc is only used to avoid writing the original FontDescriptor +// from the PDF file. + +#define addFontDesc(ref, fd) \ + addInObj(objFontDesc, ref, fd, 0) + +#define addOther(ref) \ + addInObj(objOther, ref, 0, 0) + +static int addInObj(InObjType type, Ref ref, fd_entry * fd, integer e) +{ + InObj *p, *q, *n = new InObj; + if (ref.num == 0) + pdftex_fail("PDF inclusion: invalid reference"); + n->ref = ref; + n->type = type; + n->next = 0; + n->fd = fd; + n->enc_objnum = e; + n->written = 0; + if (inObjList == 0) + inObjList = n; + else { + for (p = inObjList; p != 0; p = p->next) { + if (p->ref.num == ref.num && p->ref.gen == ref.gen) { + delete n; + return p->num; + } + q = p; + } + // 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. + q->next = n; + } + if (type == objFontDesc) + n->num = get_fd_objnum(fd); + else + n->num = pdf_new_objnum(); + return n->num; +} + +static void copyObject(Object *); + +static void copyName(char *s) +{ + pdf_puts("/"); + for (; *s != 0; s++) { + if (isdigit(*s) || isupper(*s) || islower(*s) || *s == '_' || + *s == '.' || *s == '-') + pdfout(*s); + else + pdf_printf("#%.2X", *s & 0xFF); + } +} + +static void copyDictEntry(Object * obj, int i) +{ + PdfObject obj1; + copyName(obj->dictGetKey(i)); + pdf_puts(" "); + obj->dictGetValNF(i, &obj1); + copyObject(&obj1); + pdf_puts("\n"); +} + +static void copyDict(Object * obj) +{ + int i, l; + if (!obj->isDict()) + pdftex_fail("PDF inclusion: invalid dict type <%s>", + obj->getTypeName()); + for (i = 0, l = obj->dictGetLength(); i < l; ++i) + copyDictEntry(obj, i); +} + +static void copyFontDict(Object * obj, InObj * r) +{ + int i, l; + char *key; + if (!obj->isDict()) + pdftex_fail("PDF inclusion: invalid dict type <%s>", + obj->getTypeName()); + pdf_puts("<<\n"); + assert(r->type == objFont); // FontDescriptor is in fd_tree + for (i = 0, l = obj->dictGetLength(); i < l; ++i) { + key = obj->dictGetKey(i); + if (strncmp("FontDescriptor", key, strlen("FontDescriptor")) == 0 + || strncmp("BaseFont", key, strlen("BaseFont")) == 0 + || strncmp("Encoding", key, strlen("Encoding")) == 0) + continue; // skip original values + copyDictEntry(obj, i); + } + // write new FontDescriptor, BaseFont, and Encoding + pdf_printf("/FontDescriptor %d 0 R\n", (int) get_fd_objnum(r->fd)); + pdf_printf("/BaseFont %d 0 R\n", (int) get_fn_objnum(r->fd)); + pdf_printf("/Encoding %d 0 R\n", (int) r->enc_objnum); + pdf_puts(">>"); +} + +static void copyStream(Stream * str) +{ + int c; + str->reset(); + while ((c = str->getChar()) != EOF) + pdfout(c); + pdf_last_byte = pdf_buf[pdf_ptr - 1]; +} + +static void copyProcSet(Object * obj) +{ + int i, l; + PdfObject procset; + if (!obj->isArray()) + pdftex_fail("PDF inclusion: invalid ProcSet array type <%s>", + obj->getTypeName()); + pdf_puts("/ProcSet [ "); + for (i = 0, l = obj->arrayGetLength(); i < l; ++i) { + obj->arrayGetNF(i, &procset); + if (!procset->isName()) + pdftex_fail("PDF inclusion: invalid ProcSet entry type <%s>", + procset->getTypeName()); + copyName(procset->getName()); + pdf_puts(" "); + } + pdf_puts("]\n"); +} + +#define REPLACE_TYPE1C true + +static void copyFont(char *tag, Object * fontRef) +{ + PdfObject fontdict, subtype, basefont, fontdescRef, fontdesc, charset, + fontfile, ffsubtype; + GfxFont *gfont; + fd_entry *fd; + fm_entry *fontmap; + // Check whether the font has already been embedded before analysing it. + InObj *p; + Ref ref = fontRef->getRef(); + for (p = inObjList; p; p = p->next) { + if (p->ref.num == ref.num && p->ref.gen == ref.gen) { + copyName(tag); + pdf_printf(" %d 0 R ", (int) p->num); + return; + } + } + // Only handle included Type1 (and Type1C) fonts; anything else will be copied. + // Type1C fonts are replaced by Type1 fonts, if REPLACE_TYPE1C is true. + if (fixed_replace_font && fontRef->fetch(xref, &fontdict)->isDict() + && fontdict->dictLookup("Subtype", &subtype)->isName() + && !strcmp(subtype->getName(), "Type1") + && fontdict->dictLookup("BaseFont", &basefont)->isName() + && fontdict->dictLookupNF("FontDescriptor", &fontdescRef)->isRef() + && fontdescRef->fetch(xref, &fontdesc)->isDict() + && (fontdesc->dictLookup("FontFile", &fontfile)->isStream() + || (REPLACE_TYPE1C + && fontdesc->dictLookup("FontFile3", &fontfile)->isStream() + && fontfile->streamGetDict()->lookup("Subtype", + &ffsubtype)->isName() + && !strcmp(ffsubtype->getName(), "Type1C"))) + && (fontmap = lookup_fontmap(basefont->getName())) != NULL) { + fd = epdf_create_fontdescriptor(fontmap); + if (fontdesc->dictLookup("CharSet", &charset) && + charset->isString() && is_subsetable(fontmap)) + epdf_mark_glyphs(fd, charset->getString()->getCString()); + else + embed_whole_font(fd); + addFontDesc(fontdescRef->getRef(), fd); + copyName(tag); + gfont = GfxFont::makeFont(xref, tag, fontRef->getRef(), + fontdict->getDict()); + pdf_printf(" %d 0 R ", addFont(fontRef->getRef(), fd, + addEncoding(gfont))); + } else { + copyName(tag); + pdf_puts(" "); + copyObject(fontRef); + } +} + +static void copyFontResources(Object * obj) +{ + PdfObject fontRef; + int i, l; + if (!obj->isDict()) + pdftex_fail("PDF inclusion: invalid font resources dict type <%s>", + obj->getTypeName()); + pdf_puts("/Font << "); + for (i = 0, l = obj->dictGetLength(); i < l; ++i) { + obj->dictGetValNF(i, &fontRef); + if (fontRef->isRef()) + copyFont(obj->dictGetKey(i), &fontRef); + else + pdftex_fail("PDF inclusion: invalid font in reference type <%s>", + fontRef->getTypeName()); + } + pdf_puts(">>\n"); +} + +static void copyOtherResources(Object * obj, char *key) +{ + // copies all other resources (write_epdf handles Fonts and ProcSets), + // but gives a warning if an object is not a dictionary. + + if (!obj->isDict()) + //FIXME: Write the message only to the log file + pdftex_warn("PDF inclusion: invalid other resource which is no dict" + " (key '%s', type <%s>); copying it anyway.", + key, obj->getTypeName()); + copyName(key); + pdf_puts(" "); + copyObject(obj); +} + +// Function onverts double to string; very small and very large numbers +// are NOT converted to scientific notation. +// n must be a number or real conforming to the implementation limits +// of PDF as specified in appendix C.1 of the PDF Ref. +// These are: +// maximum value of ints is +2^32 +// maximum value of reals is +2^15 +// smalles values of reals is 1/(2^16) + +static char *convertNumToPDF(double n) +{ + static const int precision = 6; + static const int fact = (int) 1E6; // must be 10^precision + static const double epsilon = 0.5E-6; // 2epsilon must be 10^-precision + static char buf[64]; + // handle very small values: return 0 + if (fabs(n) < epsilon) { + buf[0] = '0'; + buf[1] = '\0'; + } else { + char ints[64]; + int bindex = 0, sindex = 0; + int ival, fval; + // handle the sign part if n is negative + if (n < 0) { + buf[bindex++] = '-'; + n = -n; + } + n += epsilon; // for rounding + // handle the integer part, simply with sprintf + ival = (int) floor(n); + n -= ival; + sprintf(ints, "%d", ival); + while (ints[sindex] != 0) + buf[bindex++] = ints[sindex++]; + // handle the fractional part up to 'precision' digits + fval = (int) floor(n * fact); + if (fval) { + // set a dot + buf[bindex++] = '.'; + sindex = bindex + precision; + buf[sindex--] = '\0'; + // fill up trailing zeros with the string terminator NULL + while (((fval % 10) == 0) && (sindex >= bindex)) { + buf[sindex--] = '\0'; + fval /= 10; + } + // fill up the fractional part back to front + while (sindex >= bindex) { + buf[sindex--] = (fval % 10) + '0'; + fval /= 10; + } + } else + buf[bindex++] = 0; + } + return (char *) buf; +} + +static void copyObject(Object * obj) +{ + PdfObject obj1; + int i, l, c; + Ref ref; + char *p; + GString *s; + if (obj->isBool()) { + pdf_printf("%s", obj->getBool()? "true" : "false"); + } else if (obj->isInt()) { + pdf_printf("%i", obj->getInt()); + } else if (obj->isReal()) { + pdf_printf("%s", convertNumToPDF(obj->getReal())); + } else if (obj->isNum()) { + pdf_printf("%s", convertNumToPDF(obj->getNum())); + } else if (obj->isString()) { + s = obj->getString(); + p = s->getCString(); + l = s->getLength(); + if (strlen(p) == (unsigned int) l) { + pdf_puts("("); + for (; *p != 0; p++) { + c = (unsigned char) *p; + if (c == '(' || c == ')' || c == '\\') + pdf_printf("\\%c", c); + else if (c < 0x20 || c > 0x7F) + pdf_printf("\\%03o", c); + else + pdfout(c); + } + pdf_puts(")"); + } else { + pdf_puts("<"); + for (i = 0; i < l; i++) { + c = s->getChar(i) & 0xFF; + pdf_printf("%.2x", c); + } + pdf_puts(">"); + } + } else if (obj->isName()) { + copyName(obj->getName()); + } else if (obj->isNull()) { + pdf_puts("null"); + } else if (obj->isArray()) { + pdf_puts("["); + for (i = 0, l = obj->arrayGetLength(); i < l; ++i) { + obj->arrayGetNF(i, &obj1); + if (!obj1->isName()) + pdf_puts(" "); + copyObject(&obj1); + } + pdf_puts("]"); + } else if (obj->isDict()) { + pdf_puts("<<\n"); + copyDict(obj); + pdf_puts(">>"); + } else if (obj->isStream()) { + initDictFromDict(obj1, obj->streamGetDict()); + obj->streamGetDict()->incRef(); + pdf_puts("<<\n"); + copyDict(&obj1); + pdf_puts(">>\n"); + pdf_puts("stream\n"); + copyStream(obj->getStream()->getBaseStream()); + if (pdf_last_byte != '\n') + pdf_puts("\n"); + pdf_puts("endstream"); // can't simply write pdf_end_stream() + } else if (obj->isRef()) { + ref = obj->getRef(); + if (ref.num == 0) { + pdftex_warn + ("PDF inclusion: reference to invalid object was replaced by <null>"); + pdf_puts("null"); + } else + pdf_printf("%d 0 R", addOther(ref)); + } else { + pdftex_fail("PDF inclusion: type <%s> cannot be copied", + obj->getTypeName()); + } +} + +static void writeRefs() +{ + InObj *r; + for (r = inObjList; r != 0; r = r->next) { + if (!r->written) { + Object obj1; + r->written = 1; + xref->fetch(r->ref.num, r->ref.gen, &obj1); + if (r->type == objFont) { + assert(!obj1.isStream()); + zpdf_begin_obj(r->num, 2); // \pdfobjcompresslevel = 2 is for this + copyFontDict(&obj1, r); + pdf_puts("\n"); + pdf_end_obj(); + } else if (r->type != objFontDesc) { // /FontDescriptor is written via write_fontdescriptor() + if (obj1.isStream()) + zpdf_begin_obj(r->num, 0); + else + zpdf_begin_obj(r->num, 2); // \pdfobjcompresslevel = 2 is for this + copyObject(&obj1); + pdf_puts("\n"); + pdf_end_obj(); + } + obj1.free(); + } + } +} + +static void writeEncodings() +{ + UsedEncoding *r, *n; + char *glyphNames[256], *s; + int i; + for (r = encodingList; r != 0; r = r->next) { + for (i = 0; i < 256; i++) { + if (r->font->isCIDFont()) { + pdftex_warn + ("PDF inclusion: CID font included, encoding maybe wrong"); + } + if ((s = ((Gfx8BitFont *) r->font)->getCharName(i)) != 0) + glyphNames[i] = s; + else + glyphNames[i] = notdef; + } + epdf_write_enc(glyphNames, r->enc_objnum); + } + for (r = encodingList; r != 0; r = n) { + n = r->next; + delete r->font; + delete r; + } +} + +// get the pagebox according to the pagebox_spec + +static PDFRectangle *get_pagebox(Page * page, integer pagebox_spec) +{ + if (pagebox_spec == pdf_box_spec_media) + return page->getMediaBox(); + else if (pagebox_spec == pdf_box_spec_crop) + return page->getCropBox(); + else if (pagebox_spec == pdf_box_spec_bleed) + return page->getBleedBox(); + else if (pagebox_spec == pdf_box_spec_trim) + return page->getTrimBox(); + else if (pagebox_spec == pdf_box_spec_art) + return page->getArtBox(); + else + pdftex_fail("PDF inclusion: unknown value of pagebox spec (%i)", + (int) pagebox_spec); + return page->getMediaBox(); // to make the compiler happy +} + + +// 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. + +void +read_pdf_info(image_dict * idict, integer minor_pdf_version_wanted, + integer pdf_inclusion_errorlevel) +{ + PdfDocument *pdf_doc; + Page *page; + int rotate; + PDFRectangle *pagebox; + float pdf_version_found, pdf_version_wanted; + assert(idict != NULL); + assert(img_type(idict) == IMAGE_TYPE_PDF); + assert(img_pdf_ptr(idict) == NULL); + img_pdf_ptr(idict) = new_pdf_img_struct(); + // initialize + if (!isInit) { + globalParams = new GlobalParams(); + globalParams->setErrQuiet(gFalse); + isInit = gTrue; + } + // open PDF file + pdf_doc = find_add_document(img_filepath(idict)); + img_pdf_doc(idict) = pdf_doc; + + // 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, pdfTeX will + // then have to changed drastically anyway. + pdf_version_found = pdf_doc->doc->getPDFVersion(); + pdf_version_wanted = 1 + (minor_pdf_version_wanted * 0.1); + if (pdf_version_found > pdf_version_wanted) { + char msg[] = + "PDF inclusion: found PDF version <%.1f>, but at most version <%.1f> allowed"; + if (pdf_inclusion_errorlevel > 0) { + pdftex_fail(msg, pdf_version_found, pdf_version_wanted); + } else { + pdftex_warn(msg, pdf_version_found, pdf_version_wanted); + } + } + img_totalpages(idict) = pdf_doc->doc->getCatalog()->getNumPages(); + if (img_pagename(idict)) { + // get page by name + GString name(img_pagename(idict)); + LinkDest *link = pdf_doc->doc->findDest(&name); + if (link == 0 || !link->isOk()) + pdftex_fail("PDF inclusion: invalid destination <%s>", + img_pagename(idict)); + Ref ref = link->getPageRef(); + img_pagenum(idict) = + pdf_doc->doc->getCatalog()->findPage(ref.num, ref.gen); + if (img_pagenum(idict) == 0) + pdftex_fail("PDF inclusion: destination is not a page <%s>", + img_pagename(idict)); + delete link; + } else { + // get page by number + if (img_pagenum(idict) <= 0 + || img_pagenum(idict) > img_totalpages(idict)) + pdftex_fail("PDF inclusion: required page <%i> does not exist", + (int) img_pagenum(idict)); + } + // get the required page + page = pdf_doc->doc->getCatalog()->getPage(img_pagenum(idict)); + + // get the pagebox (media, crop...) to use. + pagebox = get_pagebox(page, img_pagebox(idict)); + if (pagebox->x2 > pagebox->x1) { + img_pdf_orig_x(idict) = pagebox->x1; + img_pdf_width(idict) = pagebox->x2 - pagebox->x1; + } else { + img_pdf_orig_x(idict) = pagebox->x2; + img_pdf_width(idict) = pagebox->x1 - pagebox->x2; + } + if (pagebox->y2 > pagebox->y1) { + img_pdf_orig_y(idict) = pagebox->y1; + img_pdf_height(idict) = pagebox->y2 - pagebox->y1; + } else { + img_pdf_orig_y(idict) = pagebox->y2; + img_pdf_height(idict) = pagebox->y1 - pagebox->y2; + } + + rotate = page->getRotate(); + // handle page rotation and adjust dimens as needed + if (rotate != 0) { + if (rotate % 90 == 0) { + // handle only the simple case: multiple of 90s. + // these are the only values allowed according to the + // reference (v1.3, p. 78). + // 180 needs no special treatment here + register float f; + switch (rotate) { + case 90: + f = img_pdf_height(idict); + img_pdf_height(idict) = img_pdf_width(idict); + img_pdf_width(idict) = f; + break; + case 270: + f = img_pdf_height(idict); + img_pdf_height(idict) = img_pdf_width(idict); + img_pdf_width(idict) = f; + break; + } + } + } + pdf_doc->xref = pdf_doc->doc->getXRef(); + img_xsize(idict) = bp2int(img_pdf_width(idict)); + img_ysize(idict) = bp2int(img_pdf_height(idict)); +} + +// writes 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(image_dict * idict) +{ + Page *page; + PdfObject contents, obj1, obj2; + PdfObject group, metadata, pieceinfo, separationInfo; + Object info; + char *key; + char s[256]; + int i, l; + int rotate; + double scale[6] = { 0, 0, 0, 0, 0, 0 }; + bool writematrix = false; + PdfDocument *pdf_doc = (PdfDocument *) img_pdf_doc(idict); + (pdf_doc->occurences)--; +#ifdef DEBUG + fprintf(stderr, "\npdfTeX Debug: Decrementing %s (%d)\n", + pdf_doc->file_name, pdf_doc->occurences); +#endif + xref = pdf_doc->xref; + inObjList = pdf_doc->inObjList; + encodingList = 0; + page = pdf_doc->doc->getCatalog()->getPage(img_pagenum(idict)); + rotate = page->getRotate(); + PDFRectangle *pagebox; + // write the Page header + pdf_puts("/Type /XObject\n/Subtype /Form\n"); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf("%s\n", img_attr(idict)); + pdf_puts("/FormType 1\n"); + + // write additional information + pdf_printf("/%s.FileName (%s)\n", pdfkeyprefix, + convertStringToPDFString(pdf_doc->file_name, + strlen(pdf_doc->file_name))); + pdf_printf("/%s.PageNumber %i\n", pdfkeyprefix, (int) img_pagenum(idict)); + pdf_doc->doc->getDocInfoNF(&info); + if (info.isRef()) { + // the info dict must be indirect (PDF Ref p. 61) + pdf_printf("/%s.InfoDict ", pdfkeyprefix); + pdf_printf("%d 0 R\n", addOther(info.getRef())); + } + // get the pagebox (media, crop...) to use. + pagebox = get_pagebox(page, img_pagebox(idict)); + + // handle page rotation + if (rotate != 0) { + if (rotate % 90 == 0) { + // this handles only the simple case: multiple of 90s but these + // are the only values allowed according to the reference + // (v1.3, p. 78). + // the image is rotated around its center. + // the /Rotate key is clockwise while the matrix is + // counterclockwise :-% + tex_printf(", page is rotated %d degrees", rotate); + switch (rotate) { + case 90: + scale[1] = -1; + scale[2] = 1; + scale[4] = pagebox->x1 - pagebox->y1; + scale[5] = pagebox->y1 + pagebox->x2; + writematrix = true; + break; + case 180: + scale[0] = scale[3] = -1; + scale[4] = pagebox->x1 + pagebox->x2; + scale[5] = pagebox->y1 + pagebox->y2; + writematrix = true; + break; // width and height are exchanged + case 270: + scale[1] = 1; + scale[2] = -1; + scale[4] = pagebox->x1 + pagebox->y2; + scale[5] = pagebox->y1 - pagebox->x1; + writematrix = true; + break; + } + if (writematrix) { // The matrix is only written if the image is rotated. + sprintf(s, "/Matrix [%.8f %.8f %.8f %.8f %.8f %.8f]\n", + scale[0], + scale[1], scale[2], scale[3], scale[4], scale[5]); + pdf_printf(stripzeros(s)); + } + } + } + + sprintf(s, "/BBox [%.8f %.8f %.8f %.8f]\n", + pagebox->x1, pagebox->y1, pagebox->x2, pagebox->y2); + pdf_printf(stripzeros(s)); + + // write the page Group if it's there + if (page->getGroup() != NULL) { +#if PDFTEX_COPY_PAGEGROUP +# if PDFTEX_COPY_PAGEGROUP_NOFAIL + // FIXME: This will most likely produce incorrect PDFs :-( + initDictFromDict(group, page->getGroup()); + if (group->dictGetLength() > 0) { + pdf_puts("/Group "); + copyObject(&group); + pdf_puts("\n"); + } +# else + // FIXME: currently we don't know how to handle Page Groups so we abort gracefully :-( + pdftex_fail + ("PDF inclusion: Page Group detected which pdfTeX can't handle. Sorry."); +# endif +#else + // FIXME: currently we don't know how to handle Page Groups so we at least give a warning :-( + pdftex_warn + ("PDF inclusion: Page Group detected which pdfTeX can't handle. Ignoring it."); +#endif + } + // write the page Metadata if it's there + if (page->getMetadata() != NULL) { + metadata->initStream(page->getMetadata()); + pdf_puts("/Metadata "); + copyObject(&metadata); + pdf_puts("\n"); + } + // write the page PieceInfo if it's there + if (page->getPieceInfo() != NULL) { + initDictFromDict(pieceinfo, page->getPieceInfo()); + if (pieceinfo->dictGetLength() > 0) { + pdf_puts("/PieceInfo "); + copyObject(&pieceinfo); + pdf_puts("\n"); + } + } + // write the page SeparationInfo if it's there + if (page->getSeparationInfo() != NULL) { + initDictFromDict(separationInfo, page->getSeparationInfo()); + if (separationInfo->dictGetLength() > 0) { + pdf_puts("/SeparationInfo "); + copyObject(&separationInfo); + pdf_puts("\n"); + } + } + // write the Resources dictionary + if (page->getResourceDict() == NULL) { + // Resources can be missing (files without them have been spotted + // in the wild). This violates the PDF Ref., which claims they are + // required, but all RIPs accept them. + // We "replace" them with empty /Resources, although in form xobjects + // /Resources are not required. + pdftex_warn + ("PDF inclusion: no /Resources detected. Replacing with empty /Resources."); + pdf_puts("/Resources <<>>\n"); + } else { + initDictFromDict(obj1, page->getResourceDict()); + page->getResourceDict()->incRef(); + if (!obj1->isDict()) + pdftex_fail("PDF inclusion: invalid resources dict type <%s>", + obj1->getTypeName()); + pdf_puts("/Resources <<\n"); + for (i = 0, l = obj1->dictGetLength(); i < l; ++i) { + obj1->dictGetVal(i, &obj2); + key = obj1->dictGetKey(i); + if (strcmp("Font", key) == 0) + copyFontResources(&obj2); + else if (strcmp("ProcSet", key) == 0) + copyProcSet(&obj2); + else + copyOtherResources(&obj2, key); + } + pdf_puts(">>\n"); + } + // write the page contents + page->getContents(&contents); + if (contents->isStream()) { + initDictFromDict(obj1, contents->streamGetDict()); + contents->streamGetDict()->incRef(); + copyDict(&obj1); + pdf_puts(">>\nstream\n"); + copyStream(contents->getStream()->getBaseStream()); + pdf_end_stream(); + } else if (contents->isArray()) { + pdf_begin_stream(); + for (i = 0, l = contents->arrayGetLength(); i < l; ++i) { + Object contentsobj; + copyStream((contents->arrayGet(i, &contentsobj))->getStream()); + contentsobj.free(); + } + pdf_end_stream(); + } else { // the contents are optional, but we need to include an empty stream + pdf_begin_stream(); + pdf_end_stream(); + } + // write out all indirect objects + writeRefs(); + // write out all used encodings (and delete list) + writeEncodings(); + // save object list, xref + pdf_doc->inObjList = inObjList; + pdf_doc->xref = xref; +} + +// Called when an image has been written and it's resources in image_tab are +// freed and it's not referenced anymore. + +void epdf_delete(image_dict * idict) +{ + PdfDocument *pdf_doc = (PdfDocument *) img_pdf_doc(idict); + xref = pdf_doc->xref; + if (pdf_doc->occurences < 0) { +#ifdef DEBUG + fprintf(stderr, "\npdfTeX Debug: Deleting %s\n", pdf_doc->file_name); +#endif + delete_document(pdf_doc); + } + img_pdf_doc(idict) = NULL; +} + +// Called when PDF embedding system is finalized. +// Now deallocate all remaining PdfDocuments. + +void epdf_check_mem() +{ + if (isInit) { + PdfDocument *p, *n; + for (p = pdfDocuments; p; p = n) { + n = p->next; + delete_document(p); + } + // see above for globalParams + delete globalParams; + } +} 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..66eb459bc0c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writeimg.c @@ -0,0 +1,677 @@ +/* +Copyright (c) 1996-2002, 2005 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: writeimg.c 1119 2008-03-22 11:27:02Z hhenkel $ +*/ + +#include <assert.h> +#include "ptexlib.h" +#include <kpathsea/c-auto.h> +#include <kpathsea/c-memstr.h> + +#include "image.h" + +#include <../lua51/lua.h> +#include <../lua51/lauxlib.h> + +extern void pdf_print_real(integer m, integer d); + +#define obj_aux(A) obj_tab[(A)].int4 + +/**********************************************************************/ +/* + Patch ImageTypeDetection 2003/02/08 by Heiko Oberdiek. + + 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". + + Magic headers: + + * "PNG (Portable Network Graphics) Specification", Version 1.2 + (http://www.libpng.org/pub/png): + + | 3.1. PNG file signature + | + | The first eight bytes of a PNG file always contain the following + | (decimal) values: + | + | 137 80 78 71 13 10 26 10 + + Translation to C: "\x89PNG\r\n\x1A\n" + + * "JPEG File Interchange Format", Version 1.02: + + | o you can identify a JFIF file by looking for the following + | sequence: X'FF', SOI X'FF', APP0, <2 bytes to be skipped>, + | "JFIF", X'00'. + + Function "check_type_by_header" only looks at the first two bytes: + "\xFF\xD8" + + * ISO/IEC JTC 1/SC 29/WG 1 + (ITU-T SG8) + Coding of Still Pictures + Title: 14492 FCD + Source: JBIG Committee + Project: JTC 1.29.10 + Status: Final Committee Draft + + | D.4.1, ID string + | + | This is an 8-byte sequence containing 0x97 0x4A 0x42 0x32 0x0D 0x0A + | 0x1A 0x0A. + + * "PDF Reference", third edition: + * The first line should contain "%PDF-1.0" until "%PDF-1.4" + (section 3.4.1 "File Header"). + * The "implementation notes" say: + + | 3.4.1, File Header + | 12. Acrobat viewers require only that the header appear + | somewhere within the first 1024 bytes of the file. + | 13. Acrobat viewers will also accept a header of the form + | %!PS-Adobe-N.n PDF-M.m + + The check in function "check_type_by_header" only implements + the first issue. The implementation notes are not considered. + Therefore files with garbage at start of file must have the + standard extension. + + Functions "check_type_by_header" and "check_type_by_extension": + img_type(img) is set to IMAGE_TYPE_NONE by new_image_dict(). + Both functions try to detect a type and set img_type(img). + Thus a value other than IMAGE_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_PDF "%PDF-1." +#define MAX_HEADER (sizeof(HEADER_PNG)-1) + +static void check_type_by_header(image_dict * idict) +{ + int i; + FILE *file = NULL; + char header[MAX_HEADER]; + + assert(idict != NULL); + if (img_type(idict) != IMAGE_TYPE_NONE) /* nothing to do */ + return; + /* read the header */ + file = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); + for (i = 0; (unsigned) i < MAX_HEADER; i++) { + header[i] = xgetc(file); + if (feof(file)) + pdftex_fail("reading image file failed"); + } + xfclose(file, img_filepath(idict)); + /* tests */ + if (strncmp(header, HEADER_JPG, sizeof(HEADER_JPG) - 1) == 0) + img_type(idict) = IMAGE_TYPE_JPG; + else if (strncmp(header, HEADER_PNG, sizeof(HEADER_PNG) - 1) == 0) + img_type(idict) = IMAGE_TYPE_PNG; + else if (strncmp(header, HEADER_JBIG2, sizeof(HEADER_JBIG2) - 1) == 0) + img_type(idict) = IMAGE_TYPE_JBIG2; + else if (strncmp(header, HEADER_PDF, sizeof(HEADER_PDF) - 1) == 0) + img_type(idict) = IMAGE_TYPE_PDF; +} + +static void check_type_by_extension(image_dict * idict) +{ + char *image_suffix; + + assert(idict != NULL); + if (img_type(idict) != IMAGE_TYPE_NONE) /* nothing to do */ + return; + /* tests */ + if ((image_suffix = strrchr(img_filename(idict), '.')) == 0) + img_type(idict) = IMAGE_TYPE_NONE; + else if (strcasecmp(image_suffix, ".png") == 0) + img_type(idict) = IMAGE_TYPE_PNG; + else if (strcasecmp(image_suffix, ".jpg") == 0 || + strcasecmp(image_suffix, ".jpeg") == 0) + img_type(idict) = IMAGE_TYPE_JPG; + else if (strcasecmp(image_suffix, ".jbig2") == 0 || + strcasecmp(image_suffix, ".jb2") == 0) + img_type(idict) = IMAGE_TYPE_JBIG2; + else if (strcasecmp(image_suffix, ".pdf") == 0) + img_type(idict) = IMAGE_TYPE_PDF; +} + +/**********************************************************************/ + +pdf_img_struct *new_pdf_img_struct() +{ + pdf_img_struct *p; + p = xtalloc(1, pdf_img_struct); + p->width = 0; + p->height = 0; + p->orig_x = 0; + p->orig_y = 0; + p->doc = NULL; + return p; +} + +void init_image(image * p) +{ + assert(p != NULL); + set_wd_running(p); + set_ht_running(p); + set_dp_running(p); + img_transform(p) = 0; + img_flags(p) = 0; + img_unset_refered(p); /* wd/ht/dp may be modified */ + img_unset_scaled(p); + img_dict(p) = NULL; + img_dictref(p) = LUA_NOREF; +} + +image *new_image() +{ + image *p = xtalloc(1, image); + init_image(p); + return p; +} + +void init_image_dict(image_dict * p) +{ + assert(p != NULL); + img_objnum(p) = 0; + img_index(p) = 0; + img_xsize(p) = 0; + img_ysize(p) = 0; + img_xres(p) = 0; + img_yres(p) = 0; + img_colorspace(p) = 0; + img_totalpages(p) = 0; + img_pagenum(p) = 1; + img_pagename(p) = NULL; + img_filename(p) = NULL; + img_filepath(p) = NULL; + img_attr(p) = NULL; + img_file(p) = NULL; + img_type(p) = IMAGE_TYPE_NONE; + img_color(p) = 0; + img_colordepth(p) = 0; + img_pagebox(p) = PDF_BOX_SPEC_MEDIA; + img_state(p) = DICT_NEW; + img_pdf_ptr(p) = NULL; /* union */ +} + +image_dict *new_image_dict() +{ + image_dict *p = xtalloc(1, image_dict); + init_image_dict(p); + return p; +} + +void free_dict_strings(image_dict * p) +{ + if (img_filename(p) != NULL) + xfree(img_filename(p)); + img_filename(p) = NULL; + if (img_filepath(p) != NULL) + xfree(img_filepath(p)); + img_filepath(p) = NULL; + if (img_attr(p) != NULL) + xfree(img_attr(p)); + img_attr(p) = NULL; + if (img_pagename(p) != NULL) + xfree(img_pagename(p)); + img_pagename(p) = NULL; +} + +void free_image_dict(image_dict * p) +{ + free_dict_strings(p); + if (img_type(p) == IMAGE_TYPE_PDF) + epdf_delete(p); + assert(img_file(p) == NULL); + xfree(p); +} + +/**********************************************************************/ + +void pdf_print_resname_prefix() +{ + if (pdf_resname_prefix != 0) + pdf_printf(makecstring(pdf_resname_prefix)); +} + +void read_img(image_dict * idict, integer pdf_minor_version, + integer pdf_inclusion_errorlevel) +{ + char *filepath; + int callback_id; + assert(idict != NULL); + if (img_filename(idict) == NULL) + pdftex_fail("image file name missing"); + callback_id = callback_defined(find_image_file_callback); + if (img_filepath(idict) == NULL) { + if (callback_id > 0 + && run_callback(callback_id, "S->S", img_filename(idict), + &filepath)) { + if (filepath && (strlen(filepath) > 0)) + img_filepath(idict) = strdup(filepath); + } else + img_filepath(idict) = + kpse_find_file(img_filename(idict), kpse_tex_format, true); + } + if (img_filepath(idict) == NULL) + pdftex_fail("cannot find image file"); + /* type checks */ + check_type_by_header(idict); + check_type_by_extension(idict); + /* read image */ + switch (img_type(idict)) { + case IMAGE_TYPE_PDF: + read_pdf_info(idict, pdf_minor_version, pdf_inclusion_errorlevel); + break; + case IMAGE_TYPE_PNG: + read_png_info(idict, IMG_CLOSEINBETWEEN); + break; + case IMAGE_TYPE_JPG: + read_jpg_info(idict, IMG_CLOSEINBETWEEN); + break; + case IMAGE_TYPE_JBIG2: + if (pdf_minor_version < 4) { + pdftex_fail + ("JBIG2 images only possible with at least PDF 1.4; you are generating PDF 1.%i", + (int) pdf_minor_version); + } + read_jbig2_info(idict); + break; + default: + pdftex_fail("internal error: unknown image type"); + } + cur_file_name = NULL; + if (img_state(idict) < DICT_FILESCANNED) + img_state(idict) = DICT_FILESCANNED; +} + +void scale_img(image * img) +{ + integer x, y, xr, yr; /* size and resolution of image */ + scaled w, h; /* indeed size corresponds to image resolution */ + integer default_res; + assert(img != NULL); + image_dict *idict = img_dict(img); + assert(idict != NULL); + x = img_xsize(idict); /* dimensions, resolutions from image file */ + y = img_ysize(idict); + xr = img_xres(idict); + yr = img_yres(idict); + if ((img_transform(img) & 1) == 1) { + int tmp = x; + x = y; + y = tmp; + tmp = xr; + xr = yr; + yr = tmp; + } + if (xr > 65535 || yr > 65535) { + xr = 0; + yr = 0; + pdftex_warn("ext1: too large image resolution ignored"); + } + if (x <= 0 || y <= 0 || xr < 0 || yr < 0) + pdftex_fail("ext1: invalid image dimensions"); + if (img_type(idict) == IMAGE_TYPE_PDF) { + w = x; + h = y; + } else { + default_res = fix_int(get_pdf_image_resolution(), 0, 65535); + if (default_res > 0 && (xr == 0 || yr == 0)) { + xr = default_res; + yr = default_res; + } + if (is_wd_running(img) && is_ht_running(img)) { + if (xr > 0 && yr > 0) { + w = ext_xn_over_d(one_hundred_inch, x, 100 * xr); + h = ext_xn_over_d(one_hundred_inch, y, 100 * yr); + } else { + w = ext_xn_over_d(one_hundred_inch, x, 7200); + h = ext_xn_over_d(one_hundred_inch, y, 7200); + } + } + } + if (is_wd_running(img) && is_ht_running(img) && is_dp_running(img)) { + img_width(img) = w; + img_height(img) = h; + img_depth(img) = 0; + } else if (is_wd_running(img)) { + /* image depth or height is explicitly specified */ + if (is_ht_running(img)) { + /* image depth is explicitly specified */ + img_width(img) = ext_xn_over_d(h, x, y); + img_height(img) = h - img_depth(img); + } else if (is_dp_running(img)) { + /* image height is explicitly specified */ + img_width(img) = ext_xn_over_d(img_height(img), x, y); + img_depth(img) = 0; + } else { + /* both image depth and height are explicitly specified */ + img_width(img) = + ext_xn_over_d(img_height(img) + img_depth(img), x, y); + } + } else { + /* image width is explicitly specified */ + if (is_ht_running(img) && is_dp_running(img)) { + /* both image depth and height are not specified */ + img_height(img) = ext_xn_over_d(img_width(img), y, x); + img_depth(img) = 0; + } + /* image depth is explicitly specified */ + else if (is_ht_running(img)) { + img_height(img) = + ext_xn_over_d(img_width(img), y, x) - img_depth(img); + } + /* image height is explicitly specified */ + else if (is_dp_running(img)) { + img_depth(img) = 0; + } + /* else both image depth and height are explicitly specified */ + } + img_set_scaled(img); +} + +void out_img(image * img, scaled hpos, scaled vpos) +{ + float a[7]; /* transformation matrix (todo: indices should be reduced!) */ + int r; /* number of digits after the decimal point */ + assert(img != 0); + image_dict *idict = img_dict(img); + assert(idict != 0); + scaled wd = img_width(img); + scaled ht = img_height(img); + scaled dp = img_depth(img); + if (img_type(idict) == IMAGE_TYPE_PDF) { + a[1] = wd * 1.0e6 / img_xsize(idict); + a[2] = 0; + a[3] = 0; + a[4] = (ht + dp) * 1.0e6 / img_ysize(idict); + a[5] = hpos - + (float) wd *bp2int(img_pdf_orig_x(idict)) / img_xsize(idict); + a[6] = vpos - + (float) ht *bp2int(img_pdf_orig_y(idict)) / img_ysize(idict); + r = 6; + } else { + a[1] = wd * 1.0e6 / one_hundred_bp; + a[2] = 0; + a[3] = 0; + a[4] = (ht + dp) * 1.0e6 / one_hundred_bp; + a[5] = hpos; + a[6] = vpos; + r = 4; + } + if ((img_transform(img) & 1) == 1) { + if (ht == -dp) + pdftex_fail("image transform: division by zero (height == -depth)"); + if (wd == 0) + pdftex_fail("image transform: division by zero (width == 0)"); + } + switch (img_transform(img) & 7) { + case 0: /* unrotated */ + break; + case 1: /* rot. 90 deg. (counterclockwise) */ + a[2] = a[1] * (ht + dp) / wd; + a[1] = 0; + a[3] = -a[4] * wd / (ht + dp); + a[4] = 0; + a[5] += wd; + break; + case 2: /* rot. 180 deg. */ + a[1] = -a[1]; + a[4] = -a[4]; + a[5] += wd; + a[6] += ht + dp; + break; + case 3: /* rot. 270 deg. */ + a[2] = -a[1] * (ht + dp) / wd; + a[1] = 0; + a[3] = a[4] * wd / (ht + dp); + a[4] = 0; + a[6] += ht + dp; + break; + case 4: /* mirrored, unrotated */ + a[1] = -a[1]; + a[5] += wd; + break; + case 5: /* mirrored, then rot. 90 deg. */ + a[2] = -a[1] * (ht + dp) / wd; + a[1] = 0; + a[3] = -a[4] * wd / (ht + dp); + a[4] = 0; + a[5] += wd; + a[6] += ht + dp; + break; + case 6: /* mirrored, then rot. 180 deg. */ + a[4] = -a[4]; + a[6] += ht + dp; + break; + case 7: /* mirrored, then rot. 270 deg. */ + a[2] = a[1] * (ht + dp) / wd; + a[1] = 0; + a[3] = a[4] * wd / (ht + dp); + a[4] = 0; + break; + default: + assert(0); + } + pdf_end_text(); + pdf_printf("q\n"); + pdf_print_real((integer) a[1], r); + pdfout(' '); + pdf_print_real((integer) a[2], r); + pdfout(' '); + pdf_print_real((integer) a[3], r); + pdfout(' '); + pdf_print_real((integer) a[4], r); + pdfout(' '); + pdf_print_bp((integer) a[5]); + pdfout(' '); + pdf_print_bp((integer) a[6]); + pdf_printf(" cm\n/Im"); + pdf_print_int(img_index(idict)); + pdf_print_resname_prefix(); + pdf_printf(" Do\nQ\n"); + if (img_state(idict) < DICT_OUTIMG) + img_state(idict) = DICT_OUTIMG; +} + +void write_img(image_dict * idict) +{ + assert(idict != NULL); + if (img_state(idict) < DICT_WRITTEN) { + if (tracefilenames) + tex_printf(" <%s", img_filepath(idict)); + switch (img_type(idict)) { + case IMAGE_TYPE_PNG: + write_png(idict); + break; + case IMAGE_TYPE_JPG: + write_jpg(idict); + break; + case IMAGE_TYPE_JBIG2: + write_jbig2(idict); + break; + case IMAGE_TYPE_PDF: + write_epdf(idict); + break; + default: + pdftex_fail("internal error: unknown image type"); + } + if (tracefilenames) + tex_printf(">"); + } + if (img_state(idict) < DICT_WRITTEN) + img_state(idict) = DICT_WRITTEN; +} + +/**********************************************************************/ + +typedef image *img_entry; +/* define img_ptr, img_array, & img_limit */ +define_array(img); /* array of pointers to image structures */ + +integer img_to_array(image * img) +{ + assert(img != NULL); + alloc_array(img, 1, SMALL_BUF_SIZE); + *img_ptr = img; + return img_ptr++ - img_array; /* now img is read-only */ +} + +/**********************************************************************/ +/* stuff to be accessible from TeX */ + +integer read_image(integer objnum, integer index, strnumber filename, + integer page_num, + strnumber attr, + strnumber page_name, + integer colorspace, integer page_box, + integer pdf_minor_version, integer pdf_inclusion_errorlevel) +{ + integer ref; + image *a = new_image(); + ref = img_to_array(a); + image_dict *idict = img_dict(a) = new_image_dict(); + assert(idict != NULL); + img_objnum(idict) = objnum; + img_index(idict) = index; + /* img_xsize, img_ysize, img_xres, img_yres set by read_img() */ + img_colorspace(idict) = colorspace; + img_pagenum(idict) = page_num; + /* img_totalpages set by read_img() */ + if (page_name != 0) + img_pagename(idict) = xstrdup(makecstring(page_name)); + cur_file_name = makecfilename(filename); + assert(cur_file_name != NULL); + img_filename(idict) = xstrdup(cur_file_name); + if (attr != 0) + img_attr(idict) = xstrdup(makecstring(attr)); + img_pagebox(idict) = page_box; + read_img(idict, pdf_minor_version, pdf_inclusion_errorlevel); + img_unset_scaled(a); + img_set_refered(a); + return ref; +} + +void set_image_dimensions(integer ref, integer wd, integer ht, integer dp) +{ + image *a = img_array[ref]; + img_width(a) = wd; + img_height(a) = ht; + img_depth(a) = dp; +} + +void scale_image(integer ref) +{ + scale_img(img_array[ref]); +} + +void out_image(integer ref, scaled hpos, scaled vpos) +{ + image *a = img_array[ref]; + out_img(a, hpos, vpos); +} + +void write_image(integer ref) +{ + write_img(img_dict(img_array[ref])); +} + +integer image_pages(integer ref) +{ + return img_totalpages(img_dict(img_array[ref])); +} + +integer image_colordepth(integer ref) +{ + return img_colordepth(img_dict(img_array[ref])); +} + +integer epdf_orig_x(integer ref) +{ + return bp2int(img_pdf_orig_x(img_dict(img_array[ref]))); +} + +integer epdf_orig_y(integer ref) +{ + return bp2int(img_pdf_orig_y(img_dict(img_array[ref]))); +} + +integer image_objnum(integer ref) +{ + return img_objnum(img_dict(img_array[ref])); +} + +integer image_index(integer ref) +{ + return img_index(img_dict(img_array[ref])); +} + +integer image_width(integer ref) +{ + return img_width(img_array[ref]); +} + +integer image_height(integer ref) +{ + return img_height(img_array[ref]); +} + +integer image_depth(integer ref) +{ + return img_depth(img_array[ref]); +} + +void update_image_procset(integer ref) +{ + pdf_image_procset |= img_color(img_dict(img_array[ref])); +} + +/**********************************************************************/ + +boolean check_image_b(integer procset) +{ + return procset & IMAGE_COLOR_B; +} + +boolean check_image_c(integer procset) +{ + return procset & IMAGE_COLOR_C; +} + +boolean check_image_i(integer procset) +{ + return procset & IMAGE_COLOR_I; +} 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..d50ed88c485 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejbig2.c @@ -0,0 +1,749 @@ +/*********************************************************************** +Copyright (c) 2002-2006 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. + +pdfTeX 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. + +pdfTeX 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 General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA + +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. + +$Id: writejbig2.c 1027 2008-02-14 21:01:03Z hhenkel $ +***********************************************************************/ + +#include "writejbig2.h" +#undef DEBUG + +/**********************************************************************/ + +struct avl_table *file_tree = NULL; + +static int comp_file_entry(const void *pa, const void *pb, 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) +{ + return ((const PAGEINFO *) pa)->pagenum - ((const PAGEINFO *) pb)->pagenum; +} + +static int comp_segment_entry(const void *pa, const void *pb, void *p) +{ + return ((const SEGINFO *) pa)->segnum - ((const SEGINFO *) pb)->segnum; +} + +/**********************************************************************/ + +int ygetc(FILE * stream) +{ + int c = getc(stream); + if (c < 0) { + if (c == EOF) + pdftex_fail("getc() failed; premature end of JBIG2 image file"); + else + pdftex_fail("getc() failed (can't happen)"); + } + return c; +} + +/**********************************************************************/ + +FILEINFO *new_fileinfo() +{ + 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; +} + +PAGEINFO *new_pageinfo() +{ + 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; +} + +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; +} + +/**********************************************************************/ + +void initlinkedlist(LIST * lp) +{ + lp->first = NULL; + lp->last = NULL; + lp->tree = NULL; +} + +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; +} + +/**********************************************************************/ + +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); + } +} + +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); + } +} + +/**********************************************************************/ + +PAGEINFO *find_pageinfo(LIST * plp, unsigned long pagenum) +{ + PAGEINFO tmp; + tmp.pagenum = pagenum; + assert(plp->tree != NULL); + return (PAGEINFO *) avl_find(plp->tree, &tmp); +} + +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 = ygetc(f); + return (c << 8) + ygetc(f); +} + +unsigned long read4bytes(FILE * f) +{ + unsigned int l = read2bytes(f); + return (l << 16) + read2bytes(f); +} + +/**********************************************************************/ + +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; +} + +/**********************************************************************/ + +void readfilehdr(FILEINFO * fip) +{ + unsigned int i; + /* Annex D.4 File header syntax */ + /* 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]) + pdftex_fail + ("readfilehdr(): reading JBIG2 image file failed: ID string missing"); + /* Annex D.4.2 File header flags */ + fip->filehdrflags = ygetc(fip->file); + fip->sequentialaccess = (fip->filehdrflags & 0x01) ? true : false; + if (fip->sequentialaccess) { /* Annex D.1 vs. Annex D.2 */ + xfseek(fip->file, 0, SEEK_END, fip->filepath); + fip->filesize = xftell(fip->file, fip->filepath); + xfseek(fip->file, 9, SEEK_SET, fip->filepath); + } + /* Annex D.4.3 Number of pages */ + if (!(fip->filehdrflags >> 1) & 0x01) /* known number of pages */ + fip->numofpages = read4bytes(fip->file); + /* --- at end of file header --- */ +} + +/**********************************************************************/ +/* for first reading of file; return value tells if header been read */ + +boolean readseghdr(FILEINFO * fip, SEGINFO * sip) +{ + unsigned int i; + sip->hdrstart = xftell(fip->file, fip->filepath); + if (fip->sequentialaccess && sip->hdrstart == fip->filesize) + return false; /* no endoffileflag is ok for sequentialaccess */ +#ifdef DEBUG + printf("\nhdrstart %d\n", sip->hdrstart); +#endif + /* 7.2.2 Segment number */ + sip->segnum = read4bytes(fip->file); +#ifdef DEBUG + printf(" segnum %d\n", sip->segnum); +#endif + /* 7.2.3 Segment header flags */ + sip->seghdrflags = ygetc(fip->file); +#ifdef DEBUG + printf(" hdrflags %d\n", sip->seghdrflags & 0x3f); +#endif + checkseghdrflags(sip); + if (fip->sequentialaccess && sip->endoffileflag) /* accept shorter segment, */ + return true; /* makes it compliant with Example 3.4 of PDFRef. 5th ed. */ + sip->pageassocsizeflag = ((sip->seghdrflags >> 6) & 0x01) ? true : false; + /* 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); + } + /* 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; + } + } + /* 7.2.6 Segment page association */ + if (sip->pageassocsizeflag) + sip->segpage = read4bytes(fip->file); + else + sip->segpage = ygetc(fip->file); + /* 7.2.7 Segment data length */ + sip->segdatalen = read4bytes(fip->file); + sip->hdrend = xftell(fip->file, fip->filepath); + /* ---- at end of segment header ---- */ + return true; +} + +/**********************************************************************/ +/* for writing, marks refered page0 segments, sets segpage > 0 to 1 */ + +void writeseghdr(FILEINFO * fip, SEGINFO * sip) +{ + unsigned int i; + unsigned long referedseg = 0; + /* 7.2.2 Segment number */ + /* 7.2.3 Segment header flags */ + /* 7.2.4 Referred-to segment count and retention flags */ + for (i = 0; i < 5 + sip->fieldlen; i++) + pdfout(ygetc(fip->file)); + /* 7.2.5 Referred-to segment numbers */ + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: + referedseg = ygetc(fip->file); + pdfout(referedseg); + break; + case 2: + referedseg = read2bytes(fip->file); + pdfout((referedseg >> 8) & 0xff); + pdfout(referedseg & 0xff); + break; + case 4: + referedseg = read4bytes(fip->file); + pdfout((referedseg >> 24) & 0xff); + pdfout((referedseg >> 16) & 0xff); + pdfout((referedseg >> 8) & 0xff); + pdfout(referedseg & 0xff); + break; + } + if (fip->page0.last != NULL && !sip->refers) + markpage0seg(fip, referedseg); + } + if (sip->countofrefered > 0) + sip->refers = true; + /* 7.2.6 Segment page association */ + if (sip->pageassocsizeflag) + for (i = 0; i < 3; i++) { + (void) ygetc(fip->file); + pdfout(0); + } + (void) ygetc(fip->file); + pdfout((sip->segpage > 0) ? 1 : 0); + /* 7.2.7 Segment data length */ + for (i = 0; i < 4; i++) + pdfout(ygetc(fip->file)); + /* ---- at end of segment header ---- */ +} + +/**********************************************************************/ +/* for recursive marking of refered page0 segments */ + +void checkseghdr(FILEINFO * fip, SEGINFO * sip) +{ + unsigned int i; + unsigned long referedseg = 0; + /* 7.2.2 Segment number */ + /* 7.2.3 Segment header flags */ + /* 7.2.4 Referred-to segment count and retention flags */ + xfseek(fip->file, 5 + sip->fieldlen, SEEK_CUR, fip->filepath); + /* 7.2.5 Referred-to segment numbers */ + for (i = 0; i < sip->countofrefered; i++) { + switch (sip->segnumwidth) { + case 1: + referedseg = 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; + /* 7.2.6 Segment page association */ + /* 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); + /* ---- at end of segment header ---- */ +} + +/**********************************************************************/ + +void checkseghdrflags(SEGINFO * sip) +{ + sip->endofstripeflag = false; + sip->endofpageflag = false; + sip->pageinfoflag = false; + sip->endoffileflag = false; + /* 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: + pdftex_fail + ("checkseghdrflags(): unknown segment type in JBIG2 image file"); + break; + } +} + +/**********************************************************************/ + +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; + } +} + +/**********************************************************************/ + +unsigned long findstreamstart(FILEINFO * fip) +{ + SEGINFO tmp; + assert(!fip->sequentialaccess); /* D.2 Random-access organisation */ + do /* find random-access stream start */ + (void) readseghdr(fip, &tmp); + while (!tmp.endoffileflag); + fip->streamstart = tmp.hdrend; + readfilehdr(fip); + return fip->streamstart; +} + +/**********************************************************************/ + +void rd_jbig2_info(FILEINFO * fip) +{ + unsigned long seekdist = 0; /* for sequential-access only */ + unsigned long streampos = 0; /* for random-access only */ + 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) /* D.2 Random-access organisation */ + streampos = findstreamstart(fip); + while (true) { /* 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 > currentpage) { + plp = litem_append(&(fip->pages)); + plp->last->d = new_pageinfo(); + currentpage = 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)) + xfseek(fip->file, sip->datastart, SEEK_SET, fip->filepath); + seekdist = sip->segdatalen; + /* 7.4.8 Page information segment syntax */ + if (sip->pageinfoflag) { + pip->pagenum = sip->segpage; + pip->width = read4bytes(fip->file); + pip->height = read4bytes(fip->file); + pip->xres = read4bytes(fip->file); + pip->yres = read4bytes(fip->file); + pip->pagesegmentflags = ygetc(fip->file); + /* 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)) + xfseek(fip->file, sip->hdrend, SEEK_SET, fip->filepath); + if (!fip->sequentialaccess) + streampos += sip->segdatalen; + if (fip->sequentialaccess) + xfseek(fip->file, 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); +} + +/**********************************************************************/ + +void wr_jbig2(FILEINFO * fip, unsigned long page) +{ + LITEM *slip; + PAGEINFO *pip; + SEGINFO *sip; + unsigned long i; + if (page > 0) { + pip = find_pageinfo(&(fip->pages), page); + assert(pip != NULL); + pdf_puts("/Type /XObject\n/Subtype /Image\n"); + pdf_printf("/Width %i\n", pip->width); + pdf_printf("/Height %i\n", pip->height); + pdf_puts("/ColorSpace /DeviceGray\n"); + pdf_puts("/BitsPerComponent 1\n"); + pdf_printf("/Length %lu\n", getstreamlen(pip->segments.first, true)); + pdf_puts("/Filter [/JBIG2Decode]\n"); + if (fip->page0.last != NULL) { + if (fip->pdfpage0objnum == 0) { + pdf_create_obj(0, 0); + fip->pdfpage0objnum = obj_ptr; + } + pdf_printf("/DecodeParms [<< /JBIG2Globals %lu 0 R >>]\n", + fip->pdfpage0objnum); + } + } else { + pip = find_pageinfo(&(fip->page0), page); + assert(pip != NULL); + pdf_begin_dict(fip->pdfpage0objnum, 0); + pdf_printf("/Length %lu\n", getstreamlen(pip->segments.first, false)); + } + pdf_puts(">>\n"); + pdf_puts("stream\n"); + fip->file = xfopen(fip->filepath, FOPEN_RBIN_MODE); + for (slip = pip->segments.first; slip != NULL; slip = slip->next) { /* loop over page segments */ + sip = slip->d; + if (sip->isrefered || page > 0) { + xfseek(fip->file, sip->hdrstart, SEEK_SET, fip->filepath); + /* mark refered-to page 0 segments, change segpages > 1 to 1 */ + writeseghdr(fip, sip); + xfseek(fip->file, sip->datastart, SEEK_SET, fip->filepath); + for (i = sip->datastart; i < sip->dataend; i++) + pdfout(ygetc(fip->file)); + } + } + pdf_end_stream(); + xfclose(fip->file, fip->filepath); +} + +/**********************************************************************/ + +void read_jbig2_info(image_dict * idict) +{ + FILEINFO *fip, tmp; + PAGEINFO *pip; + void **aa; + assert(idict != NULL); + img_type(idict) = IMAGE_TYPE_JBIG2; + if (img_pagenum(idict) < 1) + pdftex_fail + ("read_jbig2_info(): page %d not in JBIG2 image file; page must be > 0", + (int) img_pagenum(idict)); + if (file_tree == NULL) { + file_tree = avl_create(comp_file_entry, NULL, &avl_xallocator); + assert(file_tree != NULL); + } + tmp.filepath = img_filepath(idict); + fip = (FILEINFO *) avl_find(file_tree, &tmp); + if (fip == NULL) { + fip = new_fileinfo(); + fip->filepath = xstrdup(img_filepath(idict)); + aa = avl_probe(file_tree, fip); + assert(aa != NULL); + } + 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), img_pagenum(idict)); + if (pip == NULL) + pdftex_fail("read_jbig2_info(): page %d not found in JBIG2 image file", + (int) img_pagenum(idict)); + img_xsize(idict) = pip->width; + img_ysize(idict) = 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(image_dict * idict) +{ + FILEINFO *fip, tmp; + PAGEINFO *pip; + assert(idict != NULL); + assert(file_tree != NULL); + tmp.filepath = img_filepath(idict); + fip = (FILEINFO *) avl_find(file_tree, &tmp); + assert(fip != NULL); + assert(fip->phase == HAVEINFO); /* don't write before rd_jbig2_info() call */ + pip = find_pageinfo(&(fip->pages), img_pagenum(idict)); + assert(pip != NULL); + wr_jbig2(fip, pip->pagenum); + img_file(idict) = NULL; +} + +/**********************************************************************/ + +void flush_jbig2_page0_objects() +{ + 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) + wr_jbig2(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..c1bec32d859 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejbig2.h @@ -0,0 +1,151 @@ +/*********************************************************************** +Copyright (c) 2002-2006 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. + +pdfTeX 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. + +pdfTeX 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 General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with pdfTeX; if not, write to the Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA + +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. + +$Id: writejbig2.h 1012 2008-02-14 00:00:57Z oneiros $ +***********************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include "ptexlib.h" +#include "ptexmac.h" +#include "image.h" + +/* 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; /* data */ +} LITEM; + +typedef struct _LIST { + LITEM *first; + LITEM *last; + struct avl_table *tree; +} LIST; + +typedef struct _SEGINFO { + unsigned long segnum; + boolean isrefered; + boolean refers; + unsigned int seghdrflags; /* set by readseghdr() */ + boolean pageassocsizeflag; /* set by readseghdr() */ + unsigned int reftosegcount; /* set by readseghdr() */ + unsigned int countofrefered; /* set by readseghdr() */ + unsigned int fieldlen; /* set by readseghdr() */ + unsigned int segnumwidth; /* set by readseghdr() */ + long segpage; /* set by readseghdr() */ + unsigned long segdatalen; /* set by readseghdr() */ + unsigned long hdrstart; /* set by readseghdr() */ + unsigned long hdrend; /* set by readseghdr() */ + unsigned long datastart; + unsigned long dataend; + boolean endofstripeflag; /* set by checkseghdrflags() */ + boolean endofpageflag; /* set by checkseghdrflags() */ + boolean pageinfoflag; /* set by checkseghdrflags() */ + boolean endoffileflag; /* set by checkseghdrflags() */ +} SEGINFO; + +typedef struct _PAGEINFO { + LIST segments; /* segments associated with page */ + 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; + LIST pages; /* not including page0 */ + LIST page0; + unsigned int filehdrflags; /* set by readfilehdr() */ + boolean sequentialaccess; /* set by readfilehdr() */ + unsigned long numofpages; /* set by readfilehdr() */ + unsigned long streamstart; /* set by get_jbig2_info() */ + unsigned long pdfpage0objnum; + PHASE phase; +} FILEINFO; + +/**********************************************************************/ + +static int comp_file_entry(const void *, const void *, void *); +static int comp_page_entry(const void *, const void *, void *); +static int comp_segment_entry(const void *, const void *, void *); +int ygetc(FILE *); +FILEINFO *new_fileinfo(); +PAGEINFO *new_pageinfo(); +void initseginfo(SEGINFO *); +void initlinkedlist(LIST *); +LIST *litem_append(LIST *); +void pages_maketree(LIST *); +void segments_maketree(LIST *); +PAGEINFO *find_pageinfo(LIST *, unsigned long); +SEGINFO *find_seginfo(LIST *, unsigned long); +unsigned int read2bytes(FILE *); +unsigned long read4bytes(FILE *); +unsigned long getstreamlen(LITEM *, boolean); +void readfilehdr(FILEINFO *); +boolean readseghdr(FILEINFO *, SEGINFO *); +void writeseghdr(FILEINFO *, SEGINFO *); +void checkseghdr(FILEINFO *, SEGINFO *); +void checkseghdrflags(SEGINFO * sip); +void markpage0seg(FILEINFO *, unsigned long); +unsigned long findstreamstart(FILEINFO *); +void rd_jbig2_info(FILEINFO *); +void wr_jbig2(FILEINFO *, unsigned long); +void read_jbig2_info(image_dict *); +void write_jbig2(image_dict *); +void flushjbig2page0objects(); + +/**********************************************************************/ 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..422c29470fb --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writejpg.c @@ -0,0 +1,277 @@ +/* +Copyright (c) 1996-2006 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: writejpg.c 1100 2008-03-09 13:40:48Z hhenkel $ +*/ + +#include <assert.h> +#include "ptexlib.h" +#include "image.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 { /* JPEG marker codes */ + 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 */ + + M_JPG = 0xc8, /* 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 */ + + M_DHT = 0xc4, /* define Huffman tables */ + + 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 JPG_UINT16 read2bytes(FILE * f) +{ + int c = xgetc(f); + return (c << 8) + xgetc(f); +} + +void close_and_cleanup_jpg(image_dict * idict) +{ + assert(idict != NULL); + assert(img_file(idict) != NULL); + assert(img_filepath(idict) != NULL); + xfclose(img_file(idict), img_filepath(idict)); + img_file(idict) = NULL; + assert(img_jpg_ptr(idict) != NULL); + xfree(img_jpg_ptr(idict)); + img_jpg_ptr(idict) = NULL; +} + +void read_jpg_info(image_dict * idict, img_readtype_e readtype) +{ + int i, units = 0; + unsigned char jpg_id[] = "JFIF"; + assert(idict != NULL); + assert(img_type(idict) == IMAGE_TYPE_JPG); + img_totalpages(idict) = 1; + img_pagenum(idict) = 1; + img_xres(idict) = img_yres(idict) = 0; + assert(img_file(idict) == NULL); + img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); + assert(img_jpg_ptr(idict) == NULL); + img_jpg_ptr(idict) = xtalloc(1, jpg_img_struct); + xfseek(img_file(idict), 0, SEEK_END, img_filepath(idict)); + img_jpg_ptr(idict)->length = xftell(img_file(idict), img_filepath(idict)); + xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); + if (read2bytes(img_file(idict)) != 0xFFD8) + pdftex_fail("reading JPEG image failed (no JPEG header found)"); + /* currently only true JFIF files allow extracting img_xres and img_yres */ + if (read2bytes(img_file(idict)) == 0xFFE0) { /* check for JFIF */ + (void) read2bytes(img_file(idict)); + for (i = 0; i < 5; i++) { + if (xgetc(img_file(idict)) != jpg_id[i]) + break; + } + if (i == 5) { /* it's JFIF */ + read2bytes(img_file(idict)); + units = xgetc(img_file(idict)); + img_xres(idict) = read2bytes(img_file(idict)); + img_yres(idict) = read2bytes(img_file(idict)); + switch (units) { + case 1: + break; /* pixels per inch */ + case 2: + img_xres(idict) *= 2.54; + img_yres(idict) *= 2.54; + break; /* pixels per cm */ + default: + img_xres(idict) = img_yres(idict) = 0; + break; + } + } + } + xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); + while (1) { + if (feof(img_file(idict))) + pdftex_fail("reading JPEG image failed (premature file end)"); + if (fgetc(img_file(idict)) != 0xFF) + pdftex_fail("reading JPEG image failed (no marker found)"); + switch (xgetc(img_file(idict))) { + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + pdftex_fail("unsupported type of compression"); + case M_SOF2: + if (fixed_pdf_minor_version <= 2) + pdftex_fail("cannot use progressive DCT with PDF-1.2"); + case M_SOF0: + case M_SOF1: + case M_SOF3: + (void) read2bytes(img_file(idict)); /* read segment length */ + img_colordepth(idict) = xgetc(img_file(idict)); + img_ysize(idict) = read2bytes(img_file(idict)); + img_xsize(idict) = read2bytes(img_file(idict)); + img_jpg_color(idict) = xgetc(img_file(idict)); + xfseek(img_file(idict), 0, SEEK_SET, img_filepath(idict)); + switch (img_jpg_color(idict)) { + case JPG_GRAY: + img_color(idict) = IMAGE_COLOR_B; + break; + case JPG_RGB: + img_color(idict) = IMAGE_COLOR_C; + break; + case JPG_CMYK: + img_color(idict) = IMAGE_COLOR_C; + break; + default: + pdftex_fail("Unsupported color space %i", + (int) img_jpg_color(idict)); + } + if (readtype == IMG_CLOSEINBETWEEN) + close_and_cleanup_jpg(idict); + return; + case M_SOI: /* ignore markers without parameters */ + 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: /* skip variable length markers */ + xfseek(img_file(idict), read2bytes(img_file(idict)) - 2, + SEEK_CUR, img_filepath(idict)); + break; + } + } + assert(0); +} + +static void reopen_jpg(image_dict * idict) +{ + integer width, height, xres, yres; + width = img_xsize(idict); + height = img_ysize(idict); + xres = img_xres(idict); + yres = img_yres(idict); + read_jpg_info(idict, IMG_KEEPOPEN); + if (width != img_xsize(idict) || height != img_ysize(idict) + || xres != img_xres(idict) || yres != img_yres(idict)) + pdftex_fail("writejpg: image dimensions have changed"); +} + +void write_jpg(image_dict * idict) +{ + long unsigned l; + FILE *f; + assert(idict != NULL); + if (img_file(idict) == NULL) + reopen_jpg(idict); + assert(img_jpg_ptr(idict) != NULL); + pdf_puts("/Type /XObject\n/Subtype /Image\n"); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf("%s\n", img_attr(idict)); + pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n/Length %i\n", + (int) img_xsize(idict), + (int) img_ysize(idict), + (int) img_colordepth(idict), (int) img_jpg_ptr(idict)->length); + pdf_puts("/ColorSpace "); + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + switch (img_jpg_color(idict)) { + case JPG_GRAY: + pdf_puts("/DeviceGray\n"); + break; + case JPG_RGB: + pdf_puts("/DeviceRGB\n"); + break; + case JPG_CMYK: + pdf_puts("/DeviceCMYK\n/Decode [1 0 1 0 1 0 1 0]\n"); + break; + default: + pdftex_fail("Unsupported color space %i", + (int) img_jpg_color(idict)); + } + } + pdf_puts("/Filter /DCTDecode\n>>\nstream\n"); + for (l = img_jpg_ptr(idict)->length, f = img_file(idict); l > 0; l--) + pdfout(xgetc(f)); + pdf_end_stream(); + close_and_cleanup_jpg(idict); +} 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..7f1269d9943 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/image/writepng.c @@ -0,0 +1,635 @@ +/* +Copyright (c) 1996-2004 Han The Thanh, <thanh@pdftex.org> + +This file is part of pdfTeX. + +pdfTeX 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. + +pdfTeX 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with pdfTeX; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +$Id: writepng.c 1101 2008-03-09 16:12:15Z hhenkel $ +*/ + +#include <assert.h> +#include "ptexlib.h" +#include "image.h" + +void close_and_cleanup_png(image_dict * idict) +{ + assert(idict != NULL); + assert(img_file(idict) != NULL); + assert(img_filepath(idict) != NULL); + xfclose(img_file(idict), img_filepath(idict)); + img_file(idict) = NULL; + assert(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)); + img_png_ptr(idict) = NULL; +} + +void read_png_info(image_dict * idict, img_readtype_e readtype) +{ + double gamma; + png_structp png_p; + png_infop info_p; + assert(idict != NULL); + assert(img_type(idict) == IMAGE_TYPE_PNG); + img_totalpages(idict) = 1; + img_pagenum(idict) = 1; + img_xres(idict) = img_yres(idict) = 0; + assert(img_file(idict) == NULL); + img_file(idict) = xfopen(img_filepath(idict), FOPEN_RBIN_MODE); + assert(img_png_ptr(idict) == NULL); + img_png_ptr(idict) = xtalloc(1, png_img_struct); + if ((png_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL)) == NULL) + pdftex_fail("libpng: png_create_read_struct() failed"); + img_png_png_ptr(idict) = png_p; + if ((info_p = png_create_info_struct(png_p)) == NULL) + pdftex_fail("libpng: png_create_info_struct() failed"); + img_png_info_ptr(idict) = info_p; + if (setjmp(png_p->jmpbuf)) + pdftex_fail("libpng: internal error"); + png_init_io(png_p, img_file(idict)); + png_read_info(png_p, info_p); + /* simple transparency support */ + if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_p); + } + /* alpha channel support */ + if (fixed_pdf_minor_version < 4 && png_p->color_type | PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_p); + /* 16bit depth support */ + if (fixed_pdf_minor_version < 5) + fixed_image_hicolor = 0; + if (info_p->bit_depth == 16 && !fixed_image_hicolor) + png_set_strip_16(png_p); + /* gamma support */ + if (fixed_image_apply_gamma) { + if (png_get_gAMA(png_p, info_p, &gamma)) + png_set_gamma(png_p, (fixed_gamma / 1000.0), gamma); + else + png_set_gamma(png_p, (fixed_gamma / 1000.0), + (1000.0 / fixed_image_gamma)); + } + /* reset structure */ + png_read_update_info(png_p, info_p); + /* resolution support */ + img_xsize(idict) = info_p->width; + img_ysize(idict) = info_p->height; + if (info_p->valid & PNG_INFO_pHYs) { + img_xres(idict) = + round(0.0254 * png_get_x_pixels_per_meter(png_p, info_p)); + img_yres(idict) = + round(0.0254 * png_get_y_pixels_per_meter(png_p, info_p)); + } + switch (info_p->color_type) { + case PNG_COLOR_TYPE_PALETTE: + img_color(idict) = IMAGE_COLOR_C | IMAGE_COLOR_I; + break; + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + img_color(idict) = IMAGE_COLOR_B; + break; + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + img_color(idict) = IMAGE_COLOR_C; + break; + default: + pdftex_fail("unsupported type of color_type <%i>", info_p->color_type); + } + img_colordepth(idict) = info_p->bit_depth; + if (readtype == IMG_CLOSEINBETWEEN) + close_and_cleanup_png(idict); +} + +#define write_gray_pixel_16(r) \ + if (j % 4 == 0||j % 4 == 1) pdf_buf[pdf_ptr++] = *r++; \ + else smask[smask_ptr++] = *r++ + +#define write_gray_pixel_8(r) \ + if (j % 2 == 0) pdf_buf[pdf_ptr++] = *r++; \ + else smask[smask_ptr++] = *r++ + + +#define write_rgb_pixel_16(r) \ + if (!(j % 8 == 6||j % 8 == 7)) pdf_buf[pdf_ptr++] = *r++; \ + else smask[smask_ptr++] = *r++ + +#define write_rgb_pixel_8(r) \ + if (j % 4 != 3) pdf_buf[pdf_ptr++] = *r++; \ + else smask[smask_ptr++] = *r++ + +#define write_simple_pixel(r) pdf_buf[pdf_ptr++] = *r++ + +#define write_noninterlaced(outmac) \ + for (i = 0; (unsigned) i < (int)info_p->height; i++) { \ + png_read_row(png_p, row, NULL); \ + r = row; \ + k = info_p->rowbytes; \ + while(k > 0) { \ + l = (k > pdf_buf_size)? pdf_buf_size : k; \ + pdfroom(l); \ + for (j = 0; j < l; j++) { \ + outmac; \ + } \ + k -= l; \ + } \ + } + +#define write_interlaced(outmac) \ + for (i = 0; (unsigned) i < (int)info_p->height; i++) { \ + row = rows[i]; \ + k = info_p->rowbytes; \ + while(k > 0) { \ + l = (k > pdf_buf_size)? pdf_buf_size : k;\ + pdfroom(l); \ + for (j = 0; j < l; j++) { \ + outmac; \ + } \ + k -= l; \ + } \ + xfree(rows[i]); \ + } + +void write_png_palette(image_dict * idict) +{ + int i, 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; + integer palette_objnum = 0; + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + pdf_create_obj(0, 0); + palette_objnum = obj_ptr; + pdf_printf("[/Indexed /DeviceRGB %i %i 0 R]\n", + (int) (info_p->num_palette - 1), (int) palette_objnum); + } + pdf_begin_stream(); + if (info_p->interlace_type == PNG_INTERLACE_NONE) { + row = xtalloc(info_p->rowbytes, png_byte); + write_noninterlaced(write_simple_pixel(r)); + xfree(row); + } else { + if (info_p->height * info_p->rowbytes >= 10240000L) + pdftex_warn + ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); + rows = xtalloc(info_p->height, png_bytep); + for (i = 0; (unsigned) i < info_p->height; i++) + rows[i] = xtalloc(info_p->rowbytes, png_byte); + png_read_image(png_p, rows); + write_interlaced(write_simple_pixel(row)); + xfree(rows); + } + pdf_end_stream(); + if (palette_objnum > 0) { + pdf_begin_dict(palette_objnum, 0); + pdf_begin_stream(); + for (i = 0; (unsigned) i < info_p->num_palette; i++) { + pdfroom(3); + pdf_buf[pdf_ptr++] = info_p->palette[i].red; + pdf_buf[pdf_ptr++] = info_p->palette[i].green; + pdf_buf[pdf_ptr++] = info_p->palette[i].blue; + } + pdf_end_stream(); + } +} + +void write_png_gray(image_dict * idict) +{ + int i, 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; + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + pdf_puts("/DeviceGray\n"); + } + pdf_begin_stream(); + if (info_p->interlace_type == PNG_INTERLACE_NONE) { + row = xtalloc(info_p->rowbytes, png_byte); + write_noninterlaced(write_simple_pixel(r)); + xfree(row); + } else { + if (info_p->height * info_p->rowbytes >= 10240000L) + pdftex_warn + ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); + rows = xtalloc(info_p->height, png_bytep); + for (i = 0; (unsigned) i < info_p->height; i++) + rows[i] = xtalloc(info_p->rowbytes, png_byte); + png_read_image(png_p, rows); + write_interlaced(write_simple_pixel(row)); + xfree(rows); + } + pdf_end_stream(); +} + +void write_png_gray_alpha(image_dict * idict) +{ + int i, 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; + integer smask_objnum = 0; + png_bytep smask; + integer smask_ptr = 0; + integer smask_size = 0; + int bitdepth; + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + pdf_puts("/DeviceGray\n"); + } + pdf_create_obj(0, 0); + smask_objnum = obj_ptr; + pdf_printf("/SMask %i 0 R\n", (int) smask_objnum); + smask_size = (info_p->rowbytes / 2) * info_p->height; + smask = xtalloc(smask_size, png_byte); + pdf_begin_stream(); + if (info_p->interlace_type == PNG_INTERLACE_NONE) { + row = xtalloc(info_p->rowbytes, png_byte); + if ((info_p->bit_depth == 16) && fixed_image_hicolor) { + write_noninterlaced(write_gray_pixel_16(r)); + } else { + write_noninterlaced(write_gray_pixel_8(r)); + } + xfree(row); + } else { + if (info_p->height * info_p->rowbytes >= 10240000L) + pdftex_warn + ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); + rows = xtalloc(info_p->height, png_bytep); + for (i = 0; (unsigned) i < info_p->height; i++) + rows[i] = xtalloc(info_p->rowbytes, png_byte); + png_read_image(png_p, rows); + if ((info_p->bit_depth == 16) && fixed_image_hicolor) { + write_interlaced(write_gray_pixel_16(row)); + } else { + write_interlaced(write_gray_pixel_8(row)); + } + xfree(rows); + } + pdf_end_stream(); + pdf_flush(); + /* now write the Smask object */ + bitdepth = (int) info_p->bit_depth; + pdf_begin_dict(smask_objnum, 0); + pdf_puts("/Type /XObject\n/Subtype /Image\n"); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf("%s\n", img_attr(idict)); + pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n", + (int) info_p->width, + (int) info_p->height, (bitdepth == 16 ? 8 : bitdepth)); + pdf_puts("/ColorSpace /DeviceGray\n"); + pdf_begin_stream(); + for (i = 0; i < smask_size; i++) { + if (i % 8 == 0) + pdfroom(8); + pdf_buf[pdf_ptr++] = smask[i]; + if (bitdepth == 16) + i++; + } + pdf_end_stream(); + xfree(smask); +} + +void write_png_rgb(image_dict * idict) +{ + int i, 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; + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + pdf_puts("/DeviceRGB\n"); + } + pdf_begin_stream(); + if (info_p->interlace_type == PNG_INTERLACE_NONE) { + row = xtalloc(info_p->rowbytes, png_byte); + write_noninterlaced(write_simple_pixel(r)); + xfree(row); + } else { + if (info_p->height * info_p->rowbytes >= 10240000L) + pdftex_warn + ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); + rows = xtalloc(info_p->height, png_bytep); + for (i = 0; (unsigned) i < info_p->height; i++) + rows[i] = xtalloc(info_p->rowbytes, png_byte); + png_read_image(png_p, rows); + write_interlaced(write_simple_pixel(row)); + xfree(rows); + } + pdf_end_stream(); +} + +void write_png_rgb_alpha(image_dict * idict) +{ + int i, 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; + integer smask_objnum = 0; + png_bytep smask; + integer smask_ptr = 0; + integer smask_size = 0; + int bitdepth; + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + pdf_puts("/DeviceRGB\n"); + } + pdf_create_obj(0, 0); + smask_objnum = obj_ptr; + pdf_printf("/SMask %i 0 R\n", (int) smask_objnum); + smask_size = (info_p->rowbytes / 2) * info_p->height; + smask = xtalloc(smask_size, png_byte); + pdf_begin_stream(); + if (info_p->interlace_type == PNG_INTERLACE_NONE) { + row = xtalloc(info_p->rowbytes, png_byte); + if ((info_p->bit_depth == 16) && fixed_image_hicolor) { + write_noninterlaced(write_rgb_pixel_16(r)); + } else { + write_noninterlaced(write_rgb_pixel_8(r)); + } + xfree(row); + } else { + if (info_p->height * info_p->rowbytes >= 10240000L) + pdftex_warn + ("large interlaced PNG might cause out of memory (use non-interlaced PNG to fix this)"); + rows = xtalloc(info_p->height, png_bytep); + for (i = 0; (unsigned) i < info_p->height; i++) + rows[i] = xtalloc(info_p->rowbytes, png_byte); + png_read_image(png_p, rows); + if ((info_p->bit_depth == 16) && fixed_image_hicolor) { + write_interlaced(write_rgb_pixel_16(row)); + } else { + write_interlaced(write_rgb_pixel_8(row)); + } + xfree(rows); + } + pdf_end_stream(); + pdf_flush(); + /* now write the Smask object */ + if (smask_objnum > 0) { + bitdepth = (int) info_p->bit_depth; + pdf_begin_dict(smask_objnum, 0); + pdf_puts("/Type /XObject\n/Subtype /Image\n"); + if (img_attr(idict) != NULL) + pdf_printf("%s\n", img_attr(idict)); + pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n", + (int) info_p->width, + (int) info_p->height, (bitdepth == 16 ? 8 : bitdepth)); + pdf_puts("/ColorSpace /DeviceGray\n"); + pdf_begin_stream(); + for (i = 0; i < smask_size; i++) { + if (i % 8 == 0) + pdfroom(8); + pdf_buf[pdf_ptr++] = smask[i]; + if (bitdepth == 16) + i++; + } + xfree(smask); + pdf_end_stream(); + } +} + +/**********************************************************************/ +/* + * The |copy_png| function is from Hartmut Henkel. The goal is to use + * pdf's native FlateDecode support if that is possible. + * + * Only a subset of the png files allows this, but when possible it + * greatly improves inclusion speed. + * + * Code cheerfully gleaned from Thomas Merz' PDFlib, + * file p_png.c "SPNG - Simple PNG" + */ + +static int spng_getint(FILE * fp) +{ + unsigned char buf[4]; + if (fread(buf, 1, 4, fp) != 4) + pdftex_fail("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 + +void copy_png(image_dict * idict) +{ + assert(idict != NULL); + png_structp png_p = img_png_png_ptr(idict); + png_infop info_p = img_png_info_ptr(idict); + FILE *fp = (FILE *) png_p->io_ptr; + int i, len, type, streamlength = 0; + boolean endflag = false; + int idat = 0; /* flag to check continuous IDAT chunks sequence */ + /* 1st pass to find overall stream /Length */ + if (fseek(fp, 8, SEEK_SET) != 0) + pdftex_fail("writepng: fseek in PNG file failed"); + do { + len = spng_getint(fp); + type = spng_getint(fp); + switch (type) { + case SPNG_CHUNK_IEND: + endflag = true; + break; + case SPNG_CHUNK_IDAT: + streamlength += len; + default: + if (fseek(fp, len + 4, SEEK_CUR) != 0) + pdftex_fail("writepng: fseek in PNG file failed"); + } + } while (endflag == false); + pdf_printf("/Length %d\n", streamlength); + pdf_printf("/Filter /FlateDecode\n"); + pdf_printf("/DecodeParms << "); + pdf_printf("/Colors %d ", info_p->color_type == 2 ? 3 : 1); + pdf_printf("/Columns %d ", (int) info_p->width); + pdf_printf("/BitsPerComponent %i ", (int) info_p->bit_depth); + pdf_printf("/Predictor %d ", 10); /* actual predictor defined on line basis */ + pdf_printf(">>\n>>\nstream\n"); + /* 2nd pass to copy data */ + endflag = false; + if (fseek(fp, 8, SEEK_SET) != 0) + pdftex_fail("writepng: fseek in PNG file failed"); + do { + len = spng_getint(fp); + type = spng_getint(fp); + switch (type) { + case SPNG_CHUNK_IDAT: /* do copy */ + if (idat == 2) + pdftex_fail("writepng: IDAT chunk sequence broken"); + idat = 1; + while (len > 0) { + i = (len > pdf_buf_size) ? pdf_buf_size : len; + pdfroom(i); + fread(&pdf_buf[pdf_ptr], 1, i, fp); + pdf_ptr += i; + len -= i; + } + if (fseek(fp, 4, SEEK_CUR) != 0) + pdftex_fail("writepng: fseek in PNG file failed"); + break; + case SPNG_CHUNK_IEND: /* done */ + pdf_end_stream(); + endflag = true; + break; + default: + if (idat == 1) + idat = 2; + if (fseek(fp, len + 4, SEEK_CUR) != 0) + pdftex_fail("writepng: fseek in PNG file failed"); + } + } while (endflag == false); +} + +void reopen_png(image_dict * idict) +{ + integer width, height, xres, yres; + width = img_xsize(idict); /* do consistency check */ + height = img_ysize(idict); + xres = img_xres(idict); + yres = img_yres(idict); + read_png_info(idict, IMG_KEEPOPEN); + if (width != img_xsize(idict) || height != img_ysize(idict) + || xres != img_xres(idict) || yres != img_yres(idict)) + pdftex_fail("writepng: image dimensions have changed"); +} + +void write_png(image_dict * idict) +{ + double gamma, checked_gamma; + int i; + integer palette_objnum = 0; + assert(idict != NULL); + if (img_file(idict) == NULL) + reopen_png(idict); + assert(img_png_ptr(idict) != NULL); + png_structp png_p = img_png_png_ptr(idict); + png_infop info_p = img_png_info_ptr(idict); + if (fixed_pdf_minor_version < 5) + fixed_image_hicolor = 0; + pdf_puts("/Type /XObject\n/Subtype /Image\n"); + if (img_attr(idict) != NULL && strlen(img_attr(idict)) > 0) + pdf_printf("%s\n", img_attr(idict)); + pdf_printf("/Width %i\n/Height %i\n/BitsPerComponent %i\n", + (int) info_p->width, + (int) info_p->height, (int) info_p->bit_depth); + pdf_puts("/ColorSpace "); + checked_gamma = 1.0; + if (fixed_image_apply_gamma) { + if (png_get_gAMA(png_p, info_p, &gamma)) { + checked_gamma = (fixed_gamma / 1000.0) * gamma; + } else { + checked_gamma = + (fixed_gamma / 1000.0) * (1000.0 / fixed_image_gamma); + } + } + /* the switching between |info_p| and |png_p| queries has been trial and error. + */ + if (fixed_pdf_minor_version > 1 && info_p->interlace_type == PNG_INTERLACE_NONE && (png_p->transformations == 0 || png_p->transformations == 0x2000) /* gamma */ + &&!(png_p->color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + png_p->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + && (fixed_image_hicolor || (png_p->bit_depth <= 8)) + && (checked_gamma <= 1.01 && checked_gamma > 0.99) + ) { + if (img_colorspace(idict) != 0) { + pdf_printf("%i 0 R\n", (int) img_colorspace(idict)); + } else { + switch (info_p->color_type) { + case PNG_COLOR_TYPE_PALETTE: + pdf_create_obj(0, 0); + palette_objnum = obj_ptr; + pdf_printf("[/Indexed /DeviceRGB %i %i 0 R]\n", + (int) (info_p->num_palette - 1), + (int) palette_objnum); + break; + case PNG_COLOR_TYPE_GRAY: + pdf_puts("/DeviceGray\n"); + break; + default: /* RGB */ + pdf_puts("/DeviceRGB\n"); + }; + } + if (tracefilenames) + tex_printf(" (PNG copy)"); + copy_png(idict); + if (palette_objnum > 0) { + pdf_begin_dict(palette_objnum, 0); + pdf_begin_stream(); + for (i = 0; (unsigned) i < info_p->num_palette; i++) { + pdfroom(3); + pdf_buf[pdf_ptr++] = info_p->palette[i].red; + pdf_buf[pdf_ptr++] = info_p->palette[i].green; + pdf_buf[pdf_ptr++] = info_p->palette[i].blue; + } + pdf_end_stream(); + } + } else { + if (0) { + tex_printf(" PNG copy skipped because: "); + if (fixed_image_apply_gamma && + (checked_gamma > 1.01 || checked_gamma < 0.99)) + tex_printf("gamma delta=%lf ", checked_gamma); + if (png_p->transformations != PNG_TRANSFORM_IDENTITY) + tex_printf("transform=%lu", (long) png_p->transformations); + if ((info_p->color_type != PNG_COLOR_TYPE_GRAY) + && (info_p->color_type != PNG_COLOR_TYPE_RGB) + && (info_p->color_type != PNG_COLOR_TYPE_PALETTE)) + tex_printf("colortype "); + if (fixed_pdf_minor_version <= 1) + tex_printf("version=%d ", (int) fixed_pdf_minor_version); + if (info_p->interlace_type != PNG_INTERLACE_NONE) + tex_printf("interlaced "); + if (info_p->bit_depth > 8) + tex_printf("bitdepth=%d ", info_p->bit_depth); + if (png_get_valid(png_p, info_p, PNG_INFO_tRNS)) + tex_printf("simple transparancy "); + } + switch (info_p->color_type) { + case PNG_COLOR_TYPE_PALETTE: + write_png_palette(idict); + break; + case PNG_COLOR_TYPE_GRAY: + write_png_gray(idict); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (fixed_pdf_minor_version >= 4) + write_png_gray_alpha(idict); + else + write_png_gray(idict); + break; + case PNG_COLOR_TYPE_RGB: + write_png_rgb(idict); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (fixed_pdf_minor_version >= 4) + write_png_rgb_alpha(idict); + else + write_png_rgb(idict); + break; + default: + pdftex_fail("unsupported type of color_type <%i>", + info_p->color_type); + } + } + pdf_flush(); + close_and_cleanup_png(idict); +} |