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