Mon, 14 Dec 2009 15:51:53 +0000
Patched to add PNG and JP2 support.
Created-By: Daniel Glöckner <daniel-gl at gmx.net>
The attached patch adds PNG and JP2 support to tumble.
PNG:
As the deflated data is directly copied into the PDF, there are some
limitations to the list of supported images:
- bit depth <= 8
- no alpha channel
- no interlace
JP2:
The PDF Reference says JP2 is just a subset of the allowed JPX
format. I don't have a copy of the official standard, so I don't know
what to change to cover JPXes as well.
You'll need the Adobe Acrobat Reader 6 to display those images.
Xpdf and Ghostscript are missing the ColorSpace key in the image
dictionary, which is optional for JPXDecode and IMHO not just a matter
of a few lines of code.
One thing left to do is to change the PDF version to 1.5 if a JP2 file
has been given to tumble - maybe using the Version key in the Catalog
if seeking is not possible.
Using the resolution info in a JP2 (resc/resd boxes) is implemented but
untested. Jasper doesn't write those boxes.
I had to change the string handling to allow black in PNG palettes.
And there was a double free in tumble_input.c which happens when not
using control files.
Daniel
Makefile | file | annotate | diff | revisions | |
pdf.h | file | annotate | diff | revisions | |
pdf_jp2.c | file | annotate | diff | revisions | |
pdf_png.c | file | annotate | diff | revisions | |
pdf_prim.c | file | annotate | diff | revisions | |
pdf_prim.h | file | annotate | diff | revisions | |
tumble.c | file | annotate | diff | revisions | |
tumble_input.c | file | annotate | diff | revisions | |
tumble_input.h | file | annotate | diff | revisions | |
tumble_jp2.c | file | annotate | diff | revisions | |
tumble_png.c | file | annotate | diff | revisions |
1.1 diff -r 9a505be7e7fd -r 301f6f17c364 Makefile 1.2 --- a/Makefile Mon Dec 14 15:44:55 2009 +0000 1.3 +++ b/Makefile Mon Dec 14 15:51:53 2009 +0000 1.4 @@ -65,11 +65,12 @@ 1.5 TARGETS = tumble 1.6 1.7 CSRCS = tumble.c semantics.c \ 1.8 - tumble_input.c tumble_tiff.c tumble_jpeg.c tumble_pbm.c \ 1.9 + tumble_input.c tumble_tiff.c tumble_jpeg.c \ 1.10 + tumble_pbm.c tumble_png.c tumble_jp2.c \ 1.11 bitblt.c bitblt_table_gen.c bitblt_g4.c g4_table_gen.c \ 1.12 pdf.c pdf_util.c pdf_prim.c pdf_name_tree.c \ 1.13 pdf_bookmark.c pdf_page_label.c \ 1.14 - pdf_text.c pdf_g4.c pdf_jpeg.c 1.15 + pdf_text.c pdf_g4.c pdf_jpeg.c pdf_png.c pdf_jp2.c 1.16 OSRCS = scanner.l parser.y 1.17 HDRS = tumble.h tumble_input.h semantics.h bitblt.h bitblt_tables.h \ 1.18 pdf.h pdf_private.h pdf_util.h pdf_prim.h pdf_name_tree.h 1.19 @@ -107,11 +108,12 @@ 1.20 1.21 1.22 TUMBLE_OBJS = tumble.o semantics.o \ 1.23 - tumble_input.o tumble_tiff.o tumble_jpeg.o tumble_pbm.o \ 1.24 + tumble_input.o tumble_tiff.o tumble_jpeg.o \ 1.25 + tumble_pbm.o tumble_png.o tumble_jp2.o \ 1.26 bitblt.o bitblt_g4.o bitblt_tables.o g4_tables.o \ 1.27 pdf.o pdf_util.o pdf_prim.o pdf_name_tree.o \ 1.28 pdf_bookmark.o pdf_page_label.o \ 1.29 - pdf_text.o pdf_g4.o pdf_jpeg.o 1.30 + pdf_text.o pdf_g4.o pdf_jpeg.o pdf_png.o pdf_jp2.o 1.31 1.32 ifdef CTL_LANG 1.33 TUMBLE_OBJS += scanner.o parser.tab.o
2.1 diff -r 9a505be7e7fd -r 301f6f17c364 pdf.h 2.2 --- a/pdf.h Mon Dec 14 15:44:55 2009 +0000 2.3 +++ b/pdf.h Mon Dec 14 15:51:53 2009 +0000 2.4 @@ -92,6 +92,27 @@ 2.5 uint32_t height_samples, 2.6 FILE *f); 2.7 2.8 +void pdf_write_png_image (pdf_page_handle pdf_page, 2.9 + double x, 2.10 + double y, 2.11 + double width, 2.12 + double height, 2.13 + int color, 2.14 + char *pal, 2.15 + int palent, 2.16 + int bpp, 2.17 + uint32_t width_samples, 2.18 + uint32_t height_samples, 2.19 + FILE *f); 2.20 + 2.21 +void pdf_write_jp2_image (pdf_page_handle pdf_page, 2.22 + double x, 2.23 + double y, 2.24 + double width, 2.25 + double height, 2.26 + uint32_t width_samples, 2.27 + uint32_t height_samples, 2.28 + FILE *f); 2.29 2.30 void pdf_set_page_number (pdf_page_handle pdf_page, char *page_number); 2.31
3.1 diff -r 9a505be7e7fd -r 301f6f17c364 pdf_jp2.c 3.2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 +++ b/pdf_jp2.c Mon Dec 14 15:51:53 2009 +0000 3.4 @@ -0,0 +1,161 @@ 3.5 +/* 3.6 + * tumble: build a PDF file from image files 3.7 + * 3.8 + * PDF routines 3.9 + * Copyright 2004 Daniel Gloeckner 3.10 + * 3.11 + * Derived from pdf_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com> 3.12 + * 3.13 + * This program is free software; you can redistribute it and/or modify 3.14 + * it under the terms of the GNU General Public License version 2 as 3.15 + * published by the Free Software Foundation. Note that permission is 3.16 + * not granted to redistribute this program under the terms of any 3.17 + * other version of the General Public License. 3.18 + * 3.19 + * This program is distributed in the hope that it will be useful, 3.20 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 3.21 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 3.22 + * GNU General Public License for more details. 3.23 + * 3.24 + * You should have received a copy of the GNU General Public License 3.25 + * along with this program; if not, write to the Free Software 3.26 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA 3.27 + */ 3.28 + 3.29 + 3.30 +#include <stdbool.h> 3.31 +#include <stdint.h> 3.32 +#include <stdio.h> 3.33 +#include <stdlib.h> 3.34 +#include <string.h> 3.35 + 3.36 + 3.37 +#include "bitblt.h" 3.38 +#include "pdf.h" 3.39 +#include "pdf_util.h" 3.40 +#include "pdf_prim.h" 3.41 +#include "pdf_private.h" 3.42 + 3.43 + 3.44 +struct pdf_jp2_image 3.45 +{ 3.46 + double width, height; 3.47 + double x, y; 3.48 + uint32_t width_samples, height_samples; 3.49 + FILE *f; 3.50 + char XObject_name [4]; 3.51 +}; 3.52 + 3.53 + 3.54 +static void pdf_write_jp2_content_callback (pdf_file_handle pdf_file, 3.55 + struct pdf_obj *stream, 3.56 + void *app_data) 3.57 +{ 3.58 + struct pdf_jp2_image *image = app_data; 3.59 + 3.60 + /* transformation matrix is: width 0 0 height x y cm */ 3.61 + pdf_stream_printf (pdf_file, stream, "q %g 0 0 %g %g %g cm ", 3.62 + image->width, image->height, 3.63 + image->x, image->y); 3.64 + pdf_stream_printf (pdf_file, stream, "/%s Do Q\r\n", 3.65 + image->XObject_name); 3.66 +} 3.67 + 3.68 + 3.69 +#define JPEG_BUFFER_SIZE 8192 3.70 + 3.71 +static void pdf_write_jp2_image_callback (pdf_file_handle pdf_file, 3.72 + struct pdf_obj *stream, 3.73 + void *app_data) 3.74 +{ 3.75 + struct pdf_jp2_image *image = app_data; 3.76 + int rlen, wlen; 3.77 + uint8_t *wp; 3.78 + uint8_t buffer [8192]; 3.79 + 3.80 + while (! feof (image->f)) 3.81 + { 3.82 + rlen = fread (& buffer [0], 1, JPEG_BUFFER_SIZE, image->f); 3.83 + wp = & buffer [0]; 3.84 + while (rlen) 3.85 + { 3.86 + wlen = fwrite (wp, 1, rlen, pdf_file->f); 3.87 + if (feof (pdf_file->f)) 3.88 + pdf_fatal ("unexpected EOF on output file\n"); 3.89 + if (ferror (pdf_file->f)) 3.90 + pdf_fatal ("error on output file\n"); 3.91 + rlen -= wlen; 3.92 + wp += wlen; 3.93 + } 3.94 + if (ferror (image->f)) 3.95 + pdf_fatal ("error on input file\n"); 3.96 + } 3.97 +} 3.98 + 3.99 + 3.100 +void pdf_write_jp2_image (pdf_page_handle pdf_page, 3.101 + double x, 3.102 + double y, 3.103 + double width, 3.104 + double height, 3.105 + uint32_t width_samples, 3.106 + uint32_t height_samples, 3.107 + FILE *f) 3.108 +{ 3.109 + struct pdf_jp2_image *image; 3.110 + 3.111 + struct pdf_obj *stream; 3.112 + struct pdf_obj *stream_dict; 3.113 + 3.114 + struct pdf_obj *content_stream; 3.115 + 3.116 + image = pdf_calloc (1, sizeof (struct pdf_jp2_image)); 3.117 + 3.118 + image->width = width; 3.119 + image->height = height; 3.120 + image->x = x; 3.121 + image->y = y; 3.122 + 3.123 + image->f = f; 3.124 + 3.125 + image->width_samples = width_samples; 3.126 + image->height_samples = height_samples; 3.127 + 3.128 + stream_dict = pdf_new_obj (PT_DICTIONARY); 3.129 + 3.130 + stream = pdf_new_ind_ref (pdf_page->pdf_file, 3.131 + pdf_new_stream (pdf_page->pdf_file, 3.132 + stream_dict, 3.133 + & pdf_write_jp2_image_callback, 3.134 + image)); 3.135 + 3.136 + strcpy (& image->XObject_name [0], "Im "); 3.137 + image->XObject_name [2] = pdf_new_XObject (pdf_page, stream); 3.138 + 3.139 + pdf_set_dict_entry (stream_dict, "Type", pdf_new_name ("XObject")); 3.140 + pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image")); 3.141 +// Name is required in PDF 1.0 but obsoleted in later PDF versions 3.142 +// pdf_set_dict_entry (stream_dict, "Name", pdf_new_name (& image->XObject_name [0])); 3.143 + pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (image->width_samples)); 3.144 + pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (image->height_samples)); 3.145 + 3.146 + // not required for JPXDecode, but 3.147 + pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (8)); 3.148 + 3.149 + pdf_stream_add_filter (stream, "JPXDecode", NULL); 3.150 + 3.151 + /* the following will write the stream, using our callback function to 3.152 + get the actual data */ 3.153 + pdf_write_ind_obj (pdf_page->pdf_file, stream); 3.154 + 3.155 + content_stream = pdf_new_ind_ref (pdf_page->pdf_file, 3.156 + pdf_new_stream (pdf_page->pdf_file, 3.157 + pdf_new_obj (PT_DICTIONARY), 3.158 + & pdf_write_jp2_content_callback, 3.159 + image)); 3.160 + 3.161 + pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); 3.162 + 3.163 + pdf_write_ind_obj (pdf_page->pdf_file, content_stream); 3.164 + 3.165 +}
4.1 diff -r 9a505be7e7fd -r 301f6f17c364 pdf_png.c 4.2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 +++ b/pdf_png.c Mon Dec 14 15:51:53 2009 +0000 4.4 @@ -0,0 +1,200 @@ 4.5 +/* 4.6 + * tumble: build a PDF file from image files 4.7 + * 4.8 + * PDF routines 4.9 + * Copyright 2004 Daniel Gloeckner 4.10 + * 4.11 + * Derived from pdf_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com> 4.12 + * 4.13 + * This program is free software; you can redistribute it and/or modify 4.14 + * it under the terms of the GNU General Public License version 2 as 4.15 + * published by the Free Software Foundation. Note that permission is 4.16 + * not granted to redistribute this program under the terms of any 4.17 + * other version of the General Public License. 4.18 + * 4.19 + * This program is distributed in the hope that it will be useful, 4.20 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 4.21 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 4.22 + * GNU General Public License for more details. 4.23 + * 4.24 + * You should have received a copy of the GNU General Public License 4.25 + * along with this program; if not, write to the Free Software 4.26 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA 4.27 + */ 4.28 + 4.29 + 4.30 +#include <stdbool.h> 4.31 +#include <stdint.h> 4.32 +#include <stdio.h> 4.33 +#include <stdlib.h> 4.34 +#include <string.h> 4.35 + 4.36 + 4.37 +#include "bitblt.h" 4.38 +#include "pdf.h" 4.39 +#include "pdf_util.h" 4.40 +#include "pdf_prim.h" 4.41 +#include "pdf_private.h" 4.42 + 4.43 + 4.44 +struct pdf_png_image 4.45 +{ 4.46 + double width, height; 4.47 + double x, y; 4.48 + bool color; /* false for grayscale */ 4.49 + uint32_t width_samples, height_samples; 4.50 + FILE *f; 4.51 + char XObject_name [4]; 4.52 +}; 4.53 + 4.54 + 4.55 +static void pdf_write_png_content_callback (pdf_file_handle pdf_file, 4.56 + struct pdf_obj *stream, 4.57 + void *app_data) 4.58 +{ 4.59 + struct pdf_png_image *image = app_data; 4.60 + 4.61 + /* transformation matrix is: width 0 0 height x y cm */ 4.62 + pdf_stream_printf (pdf_file, stream, "q %g 0 0 %g %g %g cm ", 4.63 + image->width, image->height, 4.64 + image->x, image->y); 4.65 + pdf_stream_printf (pdf_file, stream, "/%s Do Q\r\n", 4.66 + image->XObject_name); 4.67 +} 4.68 + 4.69 + 4.70 +static void pdf_write_png_image_callback (pdf_file_handle pdf_file, 4.71 + struct pdf_obj *stream, 4.72 + void *app_data) 4.73 +{ 4.74 + struct pdf_png_image *image = app_data; 4.75 + int rlen, wlen; 4.76 + uint8_t *wp; 4.77 + uint8_t buffer [8192]; 4.78 + 4.79 + while (! feof (image->f)) 4.80 + { 4.81 + uint32_t clen; 4.82 + rlen = fread (buffer, 1, 8, image->f); 4.83 + if (rlen != 8) 4.84 + pdf_fatal ("unexpected EOF on input file\n"); 4.85 + clen=(buffer[0]<<24)+(buffer[1]<<16)+(buffer[2]<<8)+buffer[3]; 4.86 + if (!memcmp(buffer+4,"IEND",4)) 4.87 + break; 4.88 + if (memcmp(buffer+4,"IDAT",4)) { 4.89 + fseek(image->f, clen+4, SEEK_CUR); 4.90 + continue; 4.91 + } 4.92 + while (clen) 4.93 + { 4.94 + rlen = fread (buffer, 1, (clen<sizeof(buffer))?clen:sizeof(buffer), image->f); 4.95 + if(!rlen) 4.96 + pdf_fatal ("unexpected EOF on input file\n"); 4.97 + clen -= rlen; 4.98 + wp = buffer; 4.99 + while (rlen) 4.100 + { 4.101 + wlen = fwrite (wp, 1, rlen, pdf_file->f); 4.102 + if (feof (pdf_file->f)) 4.103 + pdf_fatal ("unexpected EOF on output file\n"); 4.104 + if (ferror (pdf_file->f)) 4.105 + pdf_fatal ("error on output file\n"); 4.106 + rlen -= wlen; 4.107 + wp += wlen; 4.108 + } 4.109 + if (ferror (image->f)) 4.110 + pdf_fatal ("error on input file\n"); 4.111 + } 4.112 + fseek(image->f, 4, SEEK_CUR); 4.113 + } 4.114 +} 4.115 + 4.116 + 4.117 +void pdf_write_png_image (pdf_page_handle pdf_page, 4.118 + double x, 4.119 + double y, 4.120 + double width, 4.121 + double height, 4.122 + int color, 4.123 + char *indexed, 4.124 + int palent, 4.125 + int bpp, 4.126 + uint32_t width_samples, 4.127 + uint32_t height_samples, 4.128 + FILE *f) 4.129 +{ 4.130 + struct pdf_png_image *image; 4.131 + 4.132 + struct pdf_obj *stream; 4.133 + struct pdf_obj *stream_dict; 4.134 + struct pdf_obj *flateparams; 4.135 + 4.136 + struct pdf_obj *content_stream; 4.137 + 4.138 + image = pdf_calloc (1, sizeof (struct pdf_png_image)); 4.139 + 4.140 + image->width = width; 4.141 + image->height = height; 4.142 + image->x = x; 4.143 + image->y = y; 4.144 + 4.145 + image->f = f; 4.146 + 4.147 + image->color = color; 4.148 + image->width_samples = width_samples; 4.149 + image->height_samples = height_samples; 4.150 + 4.151 + pdf_add_array_elem_unique (pdf_page->procset, 4.152 + pdf_new_name (palent ? "ImageI" : image->color ? "ImageC" : "ImageB")); 4.153 + 4.154 + stream_dict = pdf_new_obj (PT_DICTIONARY); 4.155 + 4.156 + stream = pdf_new_ind_ref (pdf_page->pdf_file, 4.157 + pdf_new_stream (pdf_page->pdf_file, 4.158 + stream_dict, 4.159 + & pdf_write_png_image_callback, 4.160 + image)); 4.161 + 4.162 + strcpy (& image->XObject_name [0], "Im "); 4.163 + image->XObject_name [2] = pdf_new_XObject (pdf_page, stream); 4.164 + 4.165 + flateparams = pdf_new_obj (PT_DICTIONARY); 4.166 + 4.167 + pdf_set_dict_entry (stream_dict, "Type", pdf_new_name ("XObject")); 4.168 + pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image")); 4.169 +// Name is required in PDF 1.0 but obsoleted in later PDF versions 4.170 +// pdf_set_dict_entry (stream_dict, "Name", pdf_new_name (& image->XObject_name [0])); 4.171 + pdf_set_dict_entry (stream_dict, "Width", pdf_new_integer (image->width_samples)); 4.172 + pdf_set_dict_entry (flateparams, "Columns", pdf_new_integer (image->width_samples)); 4.173 + pdf_set_dict_entry (stream_dict, "Height", pdf_new_integer (image->height_samples)); 4.174 + if(palent) { 4.175 + struct pdf_obj *space; 4.176 + space = pdf_new_obj (PT_ARRAY); 4.177 + pdf_add_array_elem (space, pdf_new_name ("Indexed")); 4.178 + pdf_add_array_elem (space, pdf_new_name ("DeviceRGB")); 4.179 + pdf_add_array_elem (space, pdf_new_integer (palent-1)); 4.180 + pdf_add_array_elem (space, pdf_new_string_n (indexed,3*palent)); 4.181 + pdf_set_dict_entry (stream_dict, "ColorSpace", space); 4.182 + } else 4.183 + pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name (image->color ? "DeviceRGB" : "DeviceGray")); 4.184 + pdf_set_dict_entry (flateparams, "Colors", pdf_new_integer ((!indexed && image->color) ? 3 : 1)); 4.185 + pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (bpp)); 4.186 + pdf_set_dict_entry (flateparams, "BitsPerComponent", pdf_new_integer (bpp)); 4.187 + pdf_set_dict_entry (flateparams, "Predictor", pdf_new_integer (15)); 4.188 + 4.189 + pdf_stream_add_filter (stream, "FlateDecode", flateparams); 4.190 + 4.191 + /* the following will write the stream, using our callback function to 4.192 + get the actual data */ 4.193 + pdf_write_ind_obj (pdf_page->pdf_file, stream); 4.194 + 4.195 + content_stream = pdf_new_ind_ref (pdf_page->pdf_file, 4.196 + pdf_new_stream (pdf_page->pdf_file, 4.197 + pdf_new_obj (PT_DICTIONARY), 4.198 + & pdf_write_png_content_callback, 4.199 + image)); 4.200 + 4.201 + pdf_set_dict_entry (pdf_page->page_dict, "Contents", content_stream); 4.202 + 4.203 + pdf_write_ind_obj (pdf_page->pdf_file, content_stream); 4.204 +}
5.1 diff -r 9a505be7e7fd -r 301f6f17c364 pdf_prim.c 5.2 --- a/pdf_prim.c Mon Dec 14 15:44:55 2009 +0000 5.3 +++ b/pdf_prim.c Mon Dec 14 15:51:53 2009 +0000 5.4 @@ -90,7 +90,10 @@ 5.5 union { 5.6 bool boolean; 5.7 char *name; 5.8 - char *string; 5.9 + struct { 5.10 + char *content; 5.11 + int length; 5.12 + } string; 5.13 long integer; 5.14 double real; 5.15 struct pdf_obj *ind_ref; 5.16 @@ -243,7 +246,18 @@ 5.17 struct pdf_obj *pdf_new_string (char *str) 5.18 { 5.19 struct pdf_obj *obj = pdf_new_obj (PT_STRING); 5.20 - obj->val.string = pdf_strdup (str); 5.21 + obj->val.string.content = pdf_strdup (str); 5.22 + obj->val.string.length = strlen(str); 5.23 + return (obj); 5.24 +} 5.25 + 5.26 + 5.27 +struct pdf_obj *pdf_new_string_n (char *str, int n) 5.28 +{ 5.29 + struct pdf_obj *obj = pdf_new_obj (PT_STRING); 5.30 + obj->val.string.length = n; 5.31 + obj->val.string.content = pdf_calloc (1,n); 5.32 + memcpy(obj->val.string.content, str, n); 5.33 return (obj); 5.34 } 5.35 5.36 @@ -397,7 +411,16 @@ 5.37 return (1); 5.38 return (0); 5.39 case PT_STRING: 5.40 - return (strcmp (o1->val.string, o2->val.string)); 5.41 + { 5.42 + int l; 5.43 + l = o1->val.string.length; 5.44 + if(l > o2->val.string.length) 5.45 + l = o2->val.string.length; 5.46 + l = memcmp (o1->val.string.content, o2->val.string.content, l); 5.47 + if (l) 5.48 + return l; 5.49 + return o1->val.string.length - o2->val.string.length; 5.50 + } 5.51 case PT_NAME: 5.52 return (strcmp (o1->val.name, o2->val.name)); 5.53 default: 5.54 @@ -427,22 +450,51 @@ 5.55 } 5.56 5.57 5.58 -static int string_char_needs_quoting (char c) 5.59 +static int pdf_write_literal_string (pdf_file_handle pdf_file, char *s, int n) 5.60 { 5.61 - return ((c < ' ') || (c > '~') || (c == '\\') || 5.62 - (c == '(') || (c == ')')); 5.63 + int i,p; 5.64 + if(pdf_file) fprintf (pdf_file->f, "("); 5.65 + for (i=p=0;n;n--) { 5.66 + int j,k; 5.67 + k=0; 5.68 + switch(*s){ 5.69 + case '\\': 5.70 + k=1; 5.71 + break; 5.72 + case '(': 5.73 + for(j=k=1;k && j<n;j++) 5.74 + k+=(s[j]=='(')?1:(s[j]==')')?-1:0; 5.75 + p+=!k; 5.76 + break; 5.77 + case ')': 5.78 + if(p) 5.79 + p--; 5.80 + else 5.81 + k=1; 5.82 + break; 5.83 + } 5.84 + if(k) { 5.85 + i++; 5.86 + if(pdf_file) fprintf (pdf_file->f, "\\"); 5.87 + } 5.88 + i++; 5.89 + if(pdf_file) fprintf (pdf_file->f, "%c", *(s++)); 5.90 + } 5.91 + if(pdf_file) fprintf (pdf_file->f, ") "); 5.92 + return i; 5.93 } 5.94 5.95 5.96 -void pdf_write_string (pdf_file_handle pdf_file, char *s) 5.97 +void pdf_write_string (pdf_file_handle pdf_file, char *s, int n) 5.98 { 5.99 - fprintf (pdf_file->f, "("); 5.100 - while (*s) 5.101 - if (string_char_needs_quoting (*s)) 5.102 - fprintf (pdf_file->f, "\\%03o", 0xff & *(s++)); 5.103 - else 5.104 - fprintf (pdf_file->f, "%c", *(s++)); 5.105 - fprintf (pdf_file->f, ") "); 5.106 + if(pdf_write_literal_string (NULL,s,n)<2*n) 5.107 + pdf_write_literal_string (pdf_file,s,n); 5.108 + else { 5.109 + fprintf (pdf_file->f, "<"); 5.110 + for(;n--;) 5.111 + fprintf (pdf_file->f, "%.2X",*(s++)); 5.112 + fprintf (pdf_file->f, "> "); 5.113 + } 5.114 } 5.115 5.116 5.117 @@ -560,7 +612,7 @@ 5.118 pdf_write_name (pdf_file, obj->val.name); 5.119 break; 5.120 case PT_STRING: 5.121 - pdf_write_string (pdf_file, obj->val.string); 5.122 + pdf_write_string (pdf_file, obj->val.string.content, obj->val.string.length); 5.123 break; 5.124 case PT_INTEGER: 5.125 fprintf (pdf_file->f, "%ld ", obj->val.integer);
6.1 diff -r 9a505be7e7fd -r 301f6f17c364 pdf_prim.h 6.2 --- a/pdf_prim.h Mon Dec 14 15:44:55 2009 +0000 6.3 +++ b/pdf_prim.h Mon Dec 14 15:51:53 2009 +0000 6.4 @@ -79,6 +79,8 @@ 6.5 6.6 struct pdf_obj *pdf_new_string (char *str); 6.7 6.8 +struct pdf_obj *pdf_new_string_n (char *str, int n); 6.9 + 6.10 struct pdf_obj *pdf_new_integer (long val); 6.11 6.12 struct pdf_obj *pdf_new_real (double val);
7.1 diff -r 9a505be7e7fd -r 301f6f17c364 tumble.c 7.2 --- a/tumble.c Mon Dec 14 15:44:55 2009 +0000 7.3 +++ b/tumble.c Mon Dec 14 15:51:53 2009 +0000 7.4 @@ -381,6 +381,8 @@ 7.5 init_tiff_handler (); 7.6 init_jpeg_handler (); 7.7 init_pbm_handler (); 7.8 + init_png_handler (); 7.9 + init_jp2_handler (); 7.10 7.11 while (--argc) 7.12 {
8.1 diff -r 9a505be7e7fd -r 301f6f17c364 tumble_input.c 8.2 --- a/tumble_input.c Mon Dec 14 15:44:55 2009 +0000 8.3 +++ b/tumble_input.c Mon Dec 14 15:51:53 2009 +0000 8.4 @@ -117,8 +117,10 @@ 8.5 result = current_input_handler->close_input_file (); 8.6 current_input_handler = NULL; 8.7 } 8.8 - if (in_filename) 8.9 + if (in_filename) { 8.10 free (in_filename); 8.11 + in_filename = NULL; 8.12 + } 8.13 if (in) 8.14 { 8.15 fclose (in);
9.1 diff -r 9a505be7e7fd -r 301f6f17c364 tumble_input.h 9.2 --- a/tumble_input.h Mon Dec 14 15:44:55 2009 +0000 9.3 +++ b/tumble_input.h Mon Dec 14 15:51:53 2009 +0000 9.4 @@ -65,3 +65,5 @@ 9.5 void init_tiff_handler (void); 9.6 void init_jpeg_handler (void); 9.7 void init_pbm_handler (void); 9.8 +void init_png_handler (void); 9.9 +void init_jp2_handler (void);
10.1 diff -r 9a505be7e7fd -r 301f6f17c364 tumble_jp2.c 10.2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.3 +++ b/tumble_jp2.c Mon Dec 14 15:51:53 2009 +0000 10.4 @@ -0,0 +1,275 @@ 10.5 +/* 10.6 + * tumble: build a PDF file from image files 10.7 + * 10.8 + * Copyright 2004 Daniel Gloeckner 10.9 + * 10.10 + * Derived from tumble_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com> 10.11 + * 10.12 + * This program is free software; you can redistribute it and/or modify 10.13 + * it under the terms of the GNU General Public License version 2 as 10.14 + * published by the Free Software Foundation. Note that permission is 10.15 + * not granted to redistribute this program under the terms of any 10.16 + * other version of the General Public License. 10.17 + * 10.18 + * This program is distributed in the hope that it will be useful, 10.19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10.20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10.21 + * GNU General Public License for more details. 10.22 + * 10.23 + * You should have received a copy of the GNU General Public License 10.24 + * along with this program; if not, write to the Free Software 10.25 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA 10.26 + */ 10.27 + 10.28 + 10.29 +#include <stdbool.h> 10.30 +#include <stdint.h> 10.31 +#include <stdio.h> 10.32 +#include <math.h> 10.33 +#include <strings.h> /* strcasecmp() is a BSDism */ 10.34 + 10.35 + 10.36 +#include "semantics.h" 10.37 +#include "tumble.h" 10.38 +#include "bitblt.h" 10.39 +#include "pdf.h" 10.40 +#include "tumble_input.h" 10.41 + 10.42 + 10.43 +static bool match_jp2_suffix (char *suffix) 10.44 +{ 10.45 + return (strcasecmp (suffix, ".jp2") == 0); 10.46 +} 10.47 + 10.48 +static bool close_jp2_input_file (void) 10.49 +{ 10.50 + return (1); 10.51 +} 10.52 + 10.53 +static struct { 10.54 + FILE *f; 10.55 + uint32_t width,height; 10.56 + struct { 10.57 + double VR,HR; 10.58 + } res[2]; 10.59 +} jp2info; 10.60 + 10.61 +struct box { 10.62 + char TBox[4]; 10.63 + bool (*func)(uint64_t size, void *cparam); 10.64 + void *cparam; 10.65 +}; 10.66 + 10.67 +#define BE32(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3]) 10.68 +#define BE16(p) (((p)[2]<<8)+(p)[3]) 10.69 + 10.70 +static bool skipbox(uint64_t size, void *cparam) 10.71 +{ 10.72 + if(size==~0) 10.73 + fseek(jp2info.f,0,SEEK_END); 10.74 + else { 10.75 + if(cparam) 10.76 + size-=*(int *)cparam; 10.77 + fseek(jp2info.f,size,SEEK_CUR); /*FIXME: size is 64bit*/ 10.78 + } 10.79 + return 1; 10.80 +} 10.81 + 10.82 +static bool read_res(uint64_t size, void *cparam) 10.83 +{ 10.84 + int read=0; 10.85 + bool ret=1; 10.86 + if(size>=10) { 10.87 + int i; 10.88 + unsigned char buf[10]; 10.89 + 10.90 + ret=0; 10.91 + i=(int)cparam; 10.92 + read=fread(buf,1,10,jp2info.f); 10.93 + if(read==10) { 10.94 + uint16_t vrn,vrd,hrn,hrd; 10.95 + int8_t vre,hre; 10.96 + vrn=BE16(buf); 10.97 + vrd=BE16(buf+2); 10.98 + hrn=BE16(buf+4); 10.99 + hrd=BE16(buf+6); 10.100 + vre=((signed char *)buf)[8]; 10.101 + hre=((signed char *)buf)[9]; 10.102 + if(vrn && vrd && hrn && hrd) { 10.103 + jp2info.res[i].VR=vrn*pow(10.0,vre)/vrd; 10.104 + jp2info.res[i].HR=hrn*pow(10.0,hre)/hrd; 10.105 + ret=1; 10.106 + } 10.107 + } 10.108 + } 10.109 + skipbox(size,&read); 10.110 + return ret; 10.111 +} 10.112 + 10.113 +static bool read_ihdr(uint64_t size, void *cparam) 10.114 +{ 10.115 + int read=0; 10.116 + bool ret=1; 10.117 + if(size>=8) { 10.118 + unsigned char buf[8]; 10.119 + read=fread(buf,1,8,jp2info.f); 10.120 + if(read==8) { 10.121 + jp2info.height=BE32(buf); 10.122 + jp2info.width=BE32(buf+4); 10.123 + } else 10.124 + ret=0; 10.125 + } 10.126 + skipbox(size,&read); 10.127 + return ret; 10.128 +} 10.129 + 10.130 +static bool superbox(uint64_t size, void *cparam) 10.131 +{ 10.132 + while(size>=8) { 10.133 + static unsigned char buf[12]; 10.134 + int r; 10.135 + uint64_t s; 10.136 + struct box *l; 10.137 + 10.138 + r=fread(buf+4,1,8,jp2info.f); 10.139 + if(r<8) 10.140 + return (size==~0 && r==0); 10.141 + 10.142 + s=BE32(buf+4); 10.143 + if(s==1 && size>=16) { 10.144 + r=fread(buf,1,8,jp2info.f); 10.145 + if(r<8) 10.146 + return 0; 10.147 + s=((uint64_t)BE32(buf)<<32)+BE32(buf+4); 10.148 + } 10.149 + if(s && s<8) 10.150 + return 0; 10.151 + if(size!=~0) { 10.152 + if(!s) 10.153 + return 0; 10.154 + size-=s; 10.155 + } else if(!s) 10.156 + size=0; 10.157 + s=s?s-8:~0; 10.158 + 10.159 + for(l=(struct box *)cparam;l->func;l++) 10.160 + if(!memcmp(l->TBox,buf+8,4)) 10.161 + break; 10.162 + if(l->func) { 10.163 + if(!l->func(s,l->cparam)) 10.164 + return 0; 10.165 + }else 10.166 + if(!skipbox(s,NULL)) 10.167 + return 0; 10.168 + } 10.169 + 10.170 + return size==0; 10.171 +} 10.172 + 10.173 +static const struct box res_boxes[]={ 10.174 + {"resc",read_res,(void *)0}, 10.175 + {"resd",read_res,(void *)1}, 10.176 + {"",NULL,NULL} 10.177 +}; 10.178 + 10.179 +static const struct box jp2h_boxes[]={ 10.180 + {"ihdr",read_ihdr,NULL}, 10.181 + {"res ",superbox,(void *)res_boxes}, 10.182 + {"",NULL,NULL} 10.183 +}; 10.184 + 10.185 +static const struct box root[]={ 10.186 + {"jp2h",superbox,(void *)jp2h_boxes}, 10.187 + {"",NULL,NULL} 10.188 +}; 10.189 + 10.190 +static bool open_jp2_input_file (FILE *f, char *name) 10.191 +{ 10.192 + int s; 10.193 + const char sig[12]="\0\0\0\xCjP \r\n\x87\n"; 10.194 + char buf[12]; 10.195 + 10.196 + memset(&jp2info,0,sizeof(jp2info)); 10.197 + jp2info.f=f; 10.198 + 10.199 + s=fread(buf,1,12,f); 10.200 + rewind(f); 10.201 + return (s==12 && !memcmp(buf,sig,12)); 10.202 +} 10.203 + 10.204 + 10.205 +static bool last_jp2_input_page (void) 10.206 +{ 10.207 + return 1; 10.208 +} 10.209 + 10.210 + 10.211 +static bool get_jp2_image_info (int image, 10.212 + input_attributes_t input_attributes, 10.213 + image_info_t *image_info) 10.214 +{ 10.215 + int i; 10.216 + 10.217 + if(!superbox(~0,(void *)root)) 10.218 + return 0; 10.219 + rewind(jp2info.f); 10.220 + 10.221 +#ifdef DEBUG_JPEG 10.222 + printf ("capture x density: %d pixel/m\n", jp2info.res[0].HR); 10.223 + printf ("capture y density: %d pixel/m\n", jp2info.res[0].VR); 10.224 + printf ("display x density: %d pixel/m\n", jp2info.res[1].HR); 10.225 + printf ("display y density: %d pixel/m\n", jp2info.res[1].VR); 10.226 + printf ("width: %d\n", jp2info.width); 10.227 + printf ("height: %d\n", jp2info.height); 10.228 +#endif 10.229 + 10.230 + image_info->color = 1; 10.231 + image_info->width_samples = jp2info.width; 10.232 + image_info->height_samples = jp2info.height; 10.233 + 10.234 + image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0; 10.235 + image_info->height_points = (image_info->height_samples * POINTS_PER_INCH) / 300.0; 10.236 + 10.237 + for(i=2;i--;) 10.238 + if(jp2info.res[i].VR > 0.0 && jp2info.res[i].HR > 0.0) { 10.239 + image_info->width_points = (image_info->width_samples * POINTS_PER_INCH * 10000)/(jp2info.res[i].HR * 254); 10.240 + image_info->height_points = (image_info->height_samples * POINTS_PER_INCH * 10000)/(jp2info.res[i].VR * 254); 10.241 + break; 10.242 + } 10.243 + 10.244 + return 1; 10.245 +} 10.246 + 10.247 + 10.248 +static bool process_jp2_image (int image, /* range 1 .. n */ 10.249 + input_attributes_t input_attributes, 10.250 + image_info_t *image_info, 10.251 + pdf_page_handle page) 10.252 +{ 10.253 + pdf_write_jp2_image (page, 10.254 + 0, 0, /* x, y */ 10.255 + image_info->width_points, 10.256 + image_info->height_points, 10.257 + image_info->width_samples, 10.258 + image_info->height_samples, 10.259 + jp2info.f); 10.260 + 10.261 + return (1); 10.262 +} 10.263 + 10.264 + 10.265 +input_handler_t jp2_handler = 10.266 + { 10.267 + match_jp2_suffix, 10.268 + open_jp2_input_file, 10.269 + close_jp2_input_file, 10.270 + last_jp2_input_page, 10.271 + get_jp2_image_info, 10.272 + process_jp2_image 10.273 + }; 10.274 + 10.275 + 10.276 +void init_jp2_handler (void) 10.277 +{ 10.278 + install_input_handler (& jp2_handler); 10.279 +}
11.1 diff -r 9a505be7e7fd -r 301f6f17c364 tumble_png.c 11.2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.3 +++ b/tumble_png.c Mon Dec 14 15:51:53 2009 +0000 11.4 @@ -0,0 +1,233 @@ 11.5 +/* 11.6 + * tumble: build a PDF file from image files 11.7 + * 11.8 + * Copyright 2004 Daniel Gloeckner 11.9 + * 11.10 + * Derived from tumble_jpeg.c written 2003 by Eric Smith <eric at brouhaha.com> 11.11 + * 11.12 + * This program is free software; you can redistribute it and/or modify 11.13 + * it under the terms of the GNU General Public License version 2 as 11.14 + * published by the Free Software Foundation. Note that permission is 11.15 + * not granted to redistribute this program under the terms of any 11.16 + * other version of the General Public License. 11.17 + * 11.18 + * This program is distributed in the hope that it will be useful, 11.19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11.20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11.21 + * GNU General Public License for more details. 11.22 + * 11.23 + * You should have received a copy of the GNU General Public License 11.24 + * along with this program; if not, write to the Free Software 11.25 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA 11.26 + */ 11.27 + 11.28 + 11.29 +#include <stdbool.h> 11.30 +#include <stdint.h> 11.31 +#include <stdio.h> 11.32 +#include <strings.h> /* strcasecmp() is a BSDism */ 11.33 + 11.34 + 11.35 +#include "semantics.h" 11.36 +#include "tumble.h" 11.37 +#include "bitblt.h" 11.38 +#include "pdf.h" 11.39 +#include "tumble_input.h" 11.40 + 11.41 + 11.42 +static FILE *png_f; 11.43 + 11.44 +static struct { 11.45 + uint32_t palent; 11.46 + uint8_t bpp; 11.47 + uint8_t color; 11.48 + char pal[256*3]; 11.49 +} cinfo; 11.50 + 11.51 + 11.52 +static bool match_png_suffix (char *suffix) 11.53 +{ 11.54 + return (strcasecmp (suffix, ".png") == 0); 11.55 +} 11.56 + 11.57 +static bool close_png_input_file (void) 11.58 +{ 11.59 + return (1); 11.60 +} 11.61 + 11.62 +#define BENUM(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3]) 11.63 + 11.64 +static bool open_png_input_file (FILE *f, char *name) 11.65 +{ 11.66 + const char sig [8]="\211PNG\r\n\032\n"; 11.67 + uint8_t buf [8]; 11.68 + int l; 11.69 + 11.70 + l = fread (buf, 1, sizeof (sig), f); 11.71 + if (l != sizeof (sig) || memcmp(buf,sig,sizeof(sig))) { 11.72 + rewind(f); 11.73 + return 0; 11.74 + } 11.75 + 11.76 + png_f = f; 11.77 + 11.78 + return 1; 11.79 +} 11.80 + 11.81 + 11.82 +static bool last_png_input_page (void) 11.83 +{ 11.84 + return 1; 11.85 +} 11.86 + 11.87 + 11.88 +static bool get_png_image_info (int image, 11.89 + input_attributes_t input_attributes, 11.90 + image_info_t *image_info) 11.91 +{ 11.92 + uint8_t buf [20], unit; 11.93 + uint32_t width,height,xppu,yppu; 11.94 + size_t l; 11.95 + bool seen_IHDR,seen_PLTE,seen_pHYs; 11.96 + 11.97 + seen_IHDR=seen_PLTE=seen_pHYs=false; 11.98 + memset(&cinfo,0,sizeof(cinfo)); 11.99 + unit=0; 11.100 + xppu=yppu=1; 11.101 + 11.102 + for(;;) 11.103 + { 11.104 + l = fread (buf, 1, 8, png_f); 11.105 + if(l != 8) 11.106 + return 0; 11.107 + l=BENUM(buf); 11.108 + if(!memcmp(buf+4,"IHDR",4)) { 11.109 + if(seen_IHDR || l!=13) 11.110 + return 0; 11.111 + seen_IHDR=true; 11.112 + l = fread (buf, 1, 17, png_f); 11.113 + if(l!=17) 11.114 + return 0; 11.115 + width=BENUM(buf); 11.116 + height=BENUM(buf+4); 11.117 + cinfo.bpp=buf[8]; 11.118 + cinfo.color=buf[9]; 11.119 + if(buf[8]>8 || buf[10] || buf[11] || buf[12]) 11.120 + return 0; 11.121 + continue; 11.122 + } 11.123 + if(!seen_IHDR) 11.124 + return 0; 11.125 + if(!memcmp(buf+4,"PLTE",4)) { 11.126 + size_t i; 11.127 + if(seen_PLTE || l>256*3 || l%3 || !cinfo.color) 11.128 + return 0; 11.129 + seen_PLTE=true; 11.130 + i = fread (cinfo.pal, 1, l, png_f); 11.131 + if(i != l) 11.132 + return 0; 11.133 + cinfo.palent=l/3; 11.134 + fseek(png_f,4,SEEK_CUR); 11.135 + } else if(!memcmp(buf+4,"pHYs",4)) { 11.136 + if(seen_pHYs || l!=9) 11.137 + return 0; 11.138 + seen_pHYs=true; 11.139 + l = fread (buf, 1, 13, png_f); 11.140 + if(l != 13) 11.141 + return 0; 11.142 + xppu=BENUM(buf); 11.143 + yppu=BENUM(buf+4); 11.144 + unit=buf[8]; 11.145 + } else if(!memcmp(buf+4,"IDAT",4)) { 11.146 + fseek(png_f,-8,SEEK_CUR); 11.147 + break; 11.148 + } else { 11.149 + fseek(png_f,l+4,SEEK_CUR); 11.150 + } 11.151 + } 11.152 + if(cinfo.color==3 && !seen_PLTE) 11.153 + return 0; 11.154 + 11.155 +#ifdef DEBUG_JPEG 11.156 + printf ("color type: %d\n", cinfo.color); 11.157 + printf ("bit depth: %d\n", cinfo.bpp); 11.158 + printf ("density unit: %d\n", unit); 11.159 + printf ("x density: %d\n", xppu); 11.160 + printf ("y density: %d\n", yppu); 11.161 + printf ("width: %d\n", width); 11.162 + printf ("height: %d\n", height); 11.163 +#endif 11.164 + 11.165 + switch (cinfo.color) 11.166 + { 11.167 + case 0: 11.168 + image_info->color = 0; 11.169 + break; 11.170 + case 2: 11.171 + case 3: 11.172 + image_info->color = 1; 11.173 + break; 11.174 + default: 11.175 + fprintf (stderr, "PNG color type %d not supported\n", cinfo.color); 11.176 + return (0); 11.177 + } 11.178 + image_info->width_samples = width; 11.179 + image_info->height_samples = height; 11.180 + 11.181 + switch (unit==1) 11.182 + { 11.183 + case 1: 11.184 + image_info->width_points = ((image_info->width_samples * POINTS_PER_INCH) / 11.185 + (xppu * 0.0254)); 11.186 + image_info->height_points = ((image_info->height_samples * POINTS_PER_INCH) / 11.187 + (yppu * 0.0254)); 11.188 + break; 11.189 + case 0: 11.190 + /* assume 300 DPI - not great, but what else can we do? */ 11.191 + image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0; 11.192 + image_info->height_points = ((double) yppu * image_info->height_samples * POINTS_PER_INCH) / ( 300.0 * xppu); 11.193 + break; 11.194 + default: 11.195 + fprintf (stderr, "PNG pHYs unit %d not supported\n", unit); 11.196 + } 11.197 + 11.198 + return 1; 11.199 +} 11.200 + 11.201 + 11.202 +static bool process_png_image (int image, /* range 1 .. n */ 11.203 + input_attributes_t input_attributes, 11.204 + image_info_t *image_info, 11.205 + pdf_page_handle page) 11.206 +{ 11.207 + pdf_write_png_image (page, 11.208 + 0, 0, /* x, y */ 11.209 + image_info->width_points, 11.210 + image_info->height_points, 11.211 + cinfo.color, 11.212 + cinfo.color==3?cinfo.pal:NULL, 11.213 + cinfo.palent, 11.214 + cinfo.bpp, 11.215 + image_info->width_samples, 11.216 + image_info->height_samples, 11.217 + png_f); 11.218 + 11.219 + return (1); 11.220 +} 11.221 + 11.222 + 11.223 +input_handler_t png_handler = 11.224 + { 11.225 + match_png_suffix, 11.226 + open_png_input_file, 11.227 + close_png_input_file, 11.228 + last_png_input_page, 11.229 + get_png_image_info, 11.230 + process_png_image 11.231 + }; 11.232 + 11.233 + 11.234 +void init_png_handler (void) 11.235 +{ 11.236 + install_input_handler (& png_handler); 11.237 +}