1 /*
2  * tumble: build a PDF file from image files
3  *
4  * PDF routines
5  * Copyright 2003, 2017 Eric Smith <spacewar@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.  Note that permission is
10  * not granted to redistribute this program under the terms of any
11  * other version of the General Public License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21  *
22  *  2008-12-30 [JDB] Fixed bug wherein "pdf_write_g4_content_callback" called
23  *                   "pdf_stream_printf" to write XObject name without escaping
24  *                   restricted characters.  Now calls "pdf_write_name".
25  *
26  *  2010-09-02 [JDB] Added support for min-is-black TIFF images.
27  */
28 
29 
30 #include <stdbool.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 
37 #include "bitblt.h"
38 #include "pdf.h"
39 #include "pdf_util.h"
40 #include "pdf_prim.h"
41 #include "pdf_private.h"
42 
43 
44 struct pdf_g4_image
45 {
46   double width, height;
47   double x, y;
48   unsigned long Columns;
49   unsigned long Rows;
50   Bitmap *bitmap;
51   char XObject_name [4];
52 };
53 
54 
pdf_write_g4_content_callback(pdf_file_handle pdf_file,struct pdf_obj * stream,void * app_data)55 static void pdf_write_g4_content_callback (pdf_file_handle pdf_file,
56 					   struct pdf_obj *stream,
57 					   void *app_data)
58 {
59   struct pdf_g4_image *image = app_data;
60 
61   /* transformation matrix is: width 0 0 height x y cm */
62   pdf_stream_printf (pdf_file, stream, "q %g 0 0 %g %g %g cm ",
63 		     image->width, image->height,
64 		     image->x, image->y);
65 
66   pdf_write_name (pdf_file, image->XObject_name);
67   pdf_stream_printf (pdf_file, stream, "Do Q\r\n");
68 }
69 
70 
pdf_write_g4_fax_image_callback(pdf_file_handle pdf_file,struct pdf_obj * stream,void * app_data)71 static void pdf_write_g4_fax_image_callback (pdf_file_handle pdf_file,
72 					     struct pdf_obj *stream,
73 					     void *app_data)
74 {
75   struct pdf_g4_image *image = app_data;
76 
77   bitblt_write_g4 (image->bitmap, pdf_file->f);
78 }
79 
80 
pdf_write_g4_fax_image(pdf_page_handle pdf_page,double x,double y,double width,double height,bool negative,Bitmap * bitmap,colormap_t * colormap,rgb_range_t * transparency)81 void pdf_write_g4_fax_image (pdf_page_handle pdf_page,
82 			     double x,
83 			     double y,
84 			     double width,
85 			     double height,
86 			     bool negative,
87 			     Bitmap *bitmap,
88 			     colormap_t *colormap,
89 			     rgb_range_t *transparency)
90 {
91   struct pdf_g4_image *image;
92 
93   struct pdf_obj *stream;
94   struct pdf_obj *stream_dict;
95   struct pdf_obj *decode_parms;
96 
97   struct pdf_obj *content_stream;
98 
99   struct pdf_obj *contents;
100   struct pdf_obj *mask;
101 
102   typedef char MAP_STRING[6];
103 
104   MAP_STRING color_index;
105   static MAP_STRING last_color_index;
106   static struct pdf_obj *color_space;
107 
108 
109   pdf_add_array_elem_unique (pdf_page->procset, pdf_new_name ("ImageB"));
110 
111   image = pdf_calloc (1, sizeof (struct pdf_g4_image));
112 
113   image->width = width;
114   image->height = height;
115   image->x = x;
116   image->y = y;
117 
118   image->bitmap = bitmap;
119   image->Columns = bitmap->rect.max.x - bitmap->rect.min.x;
120   image->Rows = bitmap->rect.max.y - bitmap->rect.min.y;
121 
122   stream_dict = pdf_new_obj (PT_DICTIONARY);
123 
124   stream = pdf_new_ind_ref (pdf_page->pdf_file,
125 			    pdf_new_stream (pdf_page->pdf_file,
126 					    stream_dict,
127 					    & pdf_write_g4_fax_image_callback,
128 					    image));
129 
130   strcpy (& image->XObject_name [0], "Im ");
131   image->XObject_name [2] = pdf_new_XObject (pdf_page, stream);
132 
133   pdf_set_dict_entry (stream_dict, "Type",    pdf_new_name ("XObject"));
134   pdf_set_dict_entry (stream_dict, "Subtype", pdf_new_name ("Image"));
135   pdf_set_dict_entry (stream_dict, "Name",    pdf_new_name (& image->XObject_name [0]));
136   pdf_set_dict_entry (stream_dict, "Width",   pdf_new_integer (image->Columns));
137   pdf_set_dict_entry (stream_dict, "Height",  pdf_new_integer (image->Rows));
138   pdf_set_dict_entry (stream_dict, "BitsPerComponent", pdf_new_integer (1));
139 
140   if (transparency)
141     {
142       mask = pdf_new_obj (PT_ARRAY);
143 
144       pdf_add_array_elem (mask, pdf_new_integer (transparency->red.first));
145       pdf_add_array_elem (mask, pdf_new_integer (transparency->red.last));
146 
147       pdf_set_dict_entry (stream_dict, "Mask", mask);
148     }
149 
150   if (colormap)
151     {
152       color_index [0] = (char) colormap->black_map.red;
153       color_index [1] = (char) colormap->black_map.green;
154       color_index [2] = (char) colormap->black_map.blue;
155       color_index [3] = (char) colormap->white_map.red;
156       color_index [4] = (char) colormap->white_map.green;
157       color_index [5] = (char) colormap->white_map.blue;
158 
159       if ((color_space == NULL) ||
160           (memcmp (color_index, last_color_index, sizeof (MAP_STRING)) != 0))
161 	{
162 	  memcpy (last_color_index, color_index, sizeof (MAP_STRING));
163 
164 	  color_space = pdf_new_obj (PT_ARRAY);
165 	  pdf_add_array_elem (color_space, pdf_new_name ("Indexed"));
166 	  pdf_add_array_elem (color_space, pdf_new_name ("DeviceRGB"));
167 	  pdf_add_array_elem (color_space, pdf_new_integer (1));
168 	  pdf_add_array_elem (color_space, pdf_new_string_n (color_index, 6));
169 
170 	  color_space = pdf_new_ind_ref (pdf_page->pdf_file, color_space);
171 	}
172 
173       pdf_set_dict_entry (stream_dict, "ColorSpace", color_space);
174     }
175   else
176     pdf_set_dict_entry (stream_dict, "ColorSpace", pdf_new_name ("DeviceGray"));
177 
178   decode_parms = pdf_new_obj (PT_DICTIONARY);
179 
180   pdf_set_dict_entry (decode_parms,
181 		      "K",
182 		      pdf_new_integer (-1));
183 
184   pdf_set_dict_entry (decode_parms,
185 		      "Columns",
186 		      pdf_new_integer (image->Columns));
187 
188   pdf_set_dict_entry (decode_parms,
189 		      "Rows",
190 		      pdf_new_integer (image->Rows));
191 
192   if (negative)
193     pdf_set_dict_entry (decode_parms,
194 			"BlackIs1",
195 			pdf_new_bool (true));
196 
197   pdf_stream_add_filter (stream, "CCITTFaxDecode", decode_parms);
198 
199   /* the following will write the stream, using our callback function to
200      get the actual data */
201   pdf_write_ind_obj (pdf_page->pdf_file, stream);
202 
203   content_stream = pdf_new_ind_ref (pdf_page->pdf_file,
204 				    pdf_new_stream (pdf_page->pdf_file,
205 						    pdf_new_obj (PT_DICTIONARY),
206 						    & pdf_write_g4_content_callback,
207 						    image));
208 
209   contents = pdf_get_dict_entry (pdf_page->page_dict, "Contents");
210 
211   if (! contents)
212     contents = pdf_new_obj (PT_ARRAY);
213 
214   pdf_add_array_elem (contents, content_stream);
215   pdf_set_dict_entry (pdf_page->page_dict, "Contents", contents);
216 
217   pdf_write_ind_obj (pdf_page->pdf_file, content_stream);
218 }
219 
220