1 /*
2  * tumble: build a PDF file from image files
3  *
4  * Copyright 2004 Daniel Gloeckner
5  *
6  * Derived from tumble_jpeg.c written 2003 by Eric Smith <spacewar@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.  Note that permission is
11  * not granted to redistribute this program under the terms of any
12  * other version of the General Public License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
22  *
23  *  2009-03-13 [JDB] New module to add PNG image support.
24  */
25 
26 
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <strings.h>  /* strcasecmp() is a BSDism */
32 
33 
34 #include "semantics.h"
35 #include "tumble.h"
36 #include "bitblt.h"
37 #include "pdf.h"
38 #include "tumble_input.h"
39 
40 
41 static FILE *png_f;
42 
43 static struct {
44   uint32_t palent;
45   uint8_t bpp;
46   uint8_t color;
47   char pal[256*3];
48 } cinfo;
49 
50 
match_png_suffix(char * suffix)51 static bool match_png_suffix (char *suffix)
52 {
53   return (strcasecmp (suffix, ".png") == 0);
54 }
55 
close_png_input_file(void)56 static bool close_png_input_file (void)
57 {
58   return (1);
59 }
60 
61 #define BENUM(p) (((p)[0]<<24)+((p)[1]<<16)+((p)[2]<<8)+(p)[3])
62 
open_png_input_file(FILE * f,char * name)63 static bool open_png_input_file (FILE *f, char *name)
64 {
65   const char sig [8]="\211PNG\r\n\032\n";
66   uint8_t buf [8];
67   int l;
68 
69   l = fread (buf, 1, sizeof (sig), f);
70   if (l != sizeof (sig) || memcmp(buf,sig,sizeof(sig))) {
71     rewind(f);
72     return 0;
73   }
74 
75   png_f = f;
76 
77   return 1;
78 }
79 
80 
last_png_input_page(void)81 static bool last_png_input_page (void)
82 {
83   return 1;
84 }
85 
86 
get_png_image_info(int image,input_attributes_t input_attributes,image_info_t * image_info)87 static bool get_png_image_info (int image,
88 				 input_attributes_t input_attributes,
89 				 image_info_t *image_info)
90 {
91   uint8_t buf [20], unit;
92   uint32_t width,height,xppu,yppu;
93   size_t l;
94   bool seen_IHDR,seen_PLTE,seen_pHYs;
95 
96   seen_IHDR=seen_PLTE=seen_pHYs=false;
97   memset(&cinfo,0,sizeof(cinfo));
98   unit=0;
99   xppu=yppu=1;
100 
101   for(;;)
102   {
103     l = fread (buf, 1, 8, png_f);
104     if(l != 8)
105       return 0;
106     l=BENUM(buf);
107     if(!memcmp(buf+4,"IHDR",4)) {
108       if(seen_IHDR || l!=13)
109 	return 0;
110       seen_IHDR=true;
111       l = fread (buf, 1, 17, png_f);
112       if(l!=17)
113 	return 0;
114       width=BENUM(buf);
115       height=BENUM(buf+4);
116       cinfo.bpp=buf[8];
117       cinfo.color=buf[9];
118       if(buf[8]>8 || buf[10] || buf[11] || buf[12])
119 	return 0;
120       continue;
121     }
122     if(!seen_IHDR)
123       return 0;
124     if(!memcmp(buf+4,"PLTE",4)) {
125       size_t i;
126       if(seen_PLTE || l>256*3 || l%3 || !cinfo.color)
127 	return 0;
128       seen_PLTE=true;
129       i = fread (cinfo.pal, 1, l, png_f);
130       if(i != l)
131 	return 0;
132       cinfo.palent=l/3;
133       fseek(png_f,4,SEEK_CUR);
134     } else if(!memcmp(buf+4,"pHYs",4)) {
135       if(seen_pHYs || l!=9)
136 	return 0;
137       seen_pHYs=true;
138       l = fread (buf, 1, 13, png_f);
139       if(l != 13)
140 	return 0;
141       xppu=BENUM(buf);
142       yppu=BENUM(buf+4);
143       unit=buf[8];
144     } else if(!memcmp(buf+4,"IDAT",4)) {
145       fseek(png_f,-8,SEEK_CUR);
146       break;
147     } else {
148       fseek(png_f,l+4,SEEK_CUR);
149     }
150   }
151   if(cinfo.color==3 && !seen_PLTE)
152     return 0;
153 
154 #ifdef DEBUG_JPEG
155   printf ("color type: %d\n", cinfo.color);
156   printf ("bit depth: %d\n", cinfo.bpp);
157   printf ("density unit: %d\n", unit);
158   printf ("x density: %d\n", xppu);
159   printf ("y density: %d\n", yppu);
160   printf ("width: %d\n", width);
161   printf ("height: %d\n", height);
162 #endif
163 
164   switch (cinfo.color)
165     {
166     case 0:
167       image_info->color = 0;
168       break;
169     case 2:
170     case 3:
171       image_info->color = 1;
172       break;
173     default:
174       fprintf (stderr, "PNG color type %d not supported\n", cinfo.color);
175       return (0);
176     }
177   image_info->width_samples = width;
178   image_info->height_samples = height;
179 
180   switch (unit==1)
181   {
182     case 1:
183       image_info->width_points = ((image_info->width_samples * POINTS_PER_INCH) /
184 				  (xppu * 0.0254));
185       image_info->height_points = ((image_info->height_samples * POINTS_PER_INCH) /
186 				   (yppu * 0.0254));
187       break;
188     case 0:
189       /* assume 300 DPI - not great, but what else can we do? */
190       image_info->width_points = (image_info->width_samples * POINTS_PER_INCH) / 300.0;
191       image_info->height_points = ((double) yppu * image_info->height_samples * POINTS_PER_INCH) / ( 300.0 * xppu);
192       break;
193     default:
194       fprintf (stderr, "PNG pHYs unit %d not supported\n", unit);
195   }
196 
197   return 1;
198 }
199 
200 
process_png_image(int image,input_attributes_t input_attributes,image_info_t * image_info,pdf_page_handle page,position_t position)201 static bool process_png_image (int image,  /* range 1 .. n */
202 			       input_attributes_t input_attributes,
203 			       image_info_t *image_info,
204 			       pdf_page_handle page,
205 			       position_t position)
206 {
207   pdf_write_png_image (page,
208 		       position.x, position.y,
209 		       image_info->width_points,
210 		       image_info->height_points,
211 		       cinfo.color,
212 		       cinfo.color==3?cinfo.pal:NULL,
213 		       cinfo.palent,
214 		       cinfo.bpp,
215 		       image_info->width_samples,
216 		       image_info->height_samples,
217 		       input_attributes.transparency,
218 		       png_f);
219 
220   return (1);
221 }
222 
223 
224 input_handler_t png_handler =
225   {
226     match_png_suffix,
227     open_png_input_file,
228     close_png_input_file,
229     last_png_input_page,
230     get_png_image_info,
231     process_png_image
232   };
233 
234 
init_png_handler(void)235 void init_png_handler (void)
236 {
237   install_input_handler (& png_handler);
238 }
239