3 % Copyright
1996-2006 Han The Thanh
<thanh@@pdftex.org
>
4 % Copyright
2006-2011 Taco Hoekwater
<taco@@luatex.org
>
6 % This file is part of LuaTeX.
8 % LuaTeX is free software
; you can redistribute it and
/or modify it under
9 % the terms of the GNU General Public License as published by the Free
10 % Software Foundation
; either version
2 of the License
, or
(at your
11 % option
) any later version.
13 % LuaTeX is distributed in the hope that it will be useful
, but WITHOUT
14 % ANY WARRANTY
; without even the implied warranty of MERCHANTABILITY or
15 % FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 % License for more details.
18 % You should have received a copy of the GNU General Public License along
19 % with LuaTeX
; if not
, see
<http
://www.gnu.org
/licenses
/>.
25 #include
"image/image.h"
26 #include
"image/writejpg.h"
29 #define JPG_GRAY
1 /* Gray color space
, use
/DeviceGray
*/
30 #define JPG_RGB
3 /* RGB color space
, use
/DeviceRGB
*/
31 #define JPG_CMYK
4 /* CMYK color space
, use
/DeviceCMYK
*/
34 M_SOF0
= 0xc0, /* baseline DCT
*/
35 M_SOF1
= 0xc1, /* extended sequential DCT
*/
36 M_SOF2
= 0xc2, /* progressive DCT
*/
37 M_SOF3
= 0xc3, /* lossless
(sequential
) */
39 M_SOF5
= 0xc5, /* differential sequential DCT
*/
40 M_SOF6
= 0xc6, /* differential progressive DCT
*/
41 M_SOF7
= 0xc7, /* differential lossless
(sequential
) */
43 M_JPG
= 0xc8, /* reserved for JPEG extensions
*/
44 M_SOF9
= 0xc9, /* extended sequential DCT
*/
45 M_SOF10
= 0xca, /* progressive DCT
*/
46 M_SOF11
= 0xcb, /* lossless
(sequential
) */
48 M_SOF13
= 0xcd, /* differential sequential DCT
*/
49 M_SOF14
= 0xce, /* differential progressive DCT
*/
50 M_SOF15
= 0xcf, /* differential lossless
(sequential
) */
52 M_DHT
= 0xc4, /* define Huffman table
(s
) */
54 M_DAC
= 0xcc, /* define arithmetic conditioning table
*/
56 M_RST0
= 0xd0, /* restart
*/
57 M_RST1
= 0xd1, /* restart
*/
58 M_RST2
= 0xd2, /* restart
*/
59 M_RST3
= 0xd3, /* restart
*/
60 M_RST4
= 0xd4, /* restart
*/
61 M_RST5
= 0xd5, /* restart
*/
62 M_RST6
= 0xd6, /* restart
*/
63 M_RST7
= 0xd7, /* restart
*/
65 M_SOI
= 0xd8, /* start of image
*/
66 M_EOI
= 0xd9, /* end of image
*/
67 M_SOS
= 0xda, /* start of scan
*/
68 M_DQT
= 0xdb, /* define quantization tables
*/
69 M_DNL
= 0xdc, /* define number of lines
*/
70 M_DRI
= 0xdd, /* define restart interval
*/
71 M_DHP
= 0xde, /* define hierarchical progression
*/
72 M_EXP
= 0xdf, /* expand reference image
(s
) */
74 M_APP0
= 0xe0, /* application marker
, used for JFIF
*/
75 M_APP1
= 0xe1, /* application marker
*/
76 M_APP2
= 0xe2, /* application marker
*/
77 M_APP3
= 0xe3, /* application marker
*/
78 M_APP4
= 0xe4, /* application marker
*/
79 M_APP5
= 0xe5, /* application marker
*/
80 M_APP6
= 0xe6, /* application marker
*/
81 M_APP7
= 0xe7, /* application marker
*/
82 M_APP8
= 0xe8, /* application marker
*/
83 M_APP9
= 0xe9, /* application marker
*/
84 M_APP10
= 0xea, /* application marker
*/
85 M_APP11
= 0xeb, /* application marker
*/
86 M_APP12
= 0xec, /* application marker
*/
87 M_APP13
= 0xed, /* application marker
*/
88 M_APP14
= 0xee, /* application marker
, used by Adobe
*/
89 M_APP15
= 0xef, /* application marker
*/
91 M_JPG0
= 0xf0, /* reserved for JPEG extensions
*/
92 M_JPG13
= 0xfd, /* reserved for JPEG extensions
*/
93 M_COM
= 0xfe, /* comment
*/
95 M_TEM
= 0x01, /* temporary use
*/
97 M_ERROR
= 0x100 /* dummy marker
, internal use only
*/
101 static unsigned char myget_unsigned_byte
(FILE *file
)
105 return
(unsigned char
) ch
;
109 static unsigned short myget_unsigned_pair
(FILE *file
)
111 unsigned short pair
= myget_unsigned_byte
(file
);
112 pair
= (pair
<< 8) | myget_unsigned_byte
(file
);
117 static unsigned int read_exif_bytes
(unsigned char
**p
, int n
, int b
)
119 unsigned int rval
= 0;
120 unsigned char
*pp
= *p
;
124 rval
+= *pp
++; rval
<<= 8;
125 rval
+= *pp
++; rval
<<= 8;
127 rval
+= *pp
++; rval
<<= 8;
135 rval
+= *--pp
; rval
<<= 8;
136 rval
+= *--pp
; rval
<<= 8;
138 rval
+= *--pp
; rval
<<= 8;
148 static void read_APP1_Exif
(FILE *fp
, unsigned short length
, int
*xx
, int
*yy
)
150 /* this doesn't save the data
, just reads the tags we need
*/
151 /* based on info from http
://www.exif.org
/Exif2-2.PDF
*/
152 unsigned char
*buffer
= (unsigned char
*)xmalloc
(length
);
153 unsigned char
*p
, *rp
;
154 unsigned char
*tiff_header
;
157 int num_fields
, tag
, type
;
158 int value
= 0, num
= 0, den
= 0; /* silence uninitialized warnings
*/
161 double res_unit
= 1.0;
163 ret_len
= fread
(buffer
, length
, 1, fp
);
167 while
((p
< buffer
+ length
) && (*p == 0))
170 if
((*p
== 'M'
) && (*(p+1) == 'M'))
172 else if
((*p
== 'I'
) && (*(p+1) == 'I'))
177 i
= read_exif_bytes
(&p, 2, bigendian);
180 i
= read_exif_bytes
(&p, 4, bigendian);
182 num_fields
= read_exif_bytes
(&p, 2, bigendian);
183 while
(num_fields--
> 0) {
184 tag
= read_exif_bytes
(&p, 2, bigendian);
185 type
= read_exif_bytes
(&p, 2, bigendian);
186 read_exif_bytes
(&p, 4, bigendian);
193 value
= read_exif_bytes
(&p, 2, bigendian);
198 value
= read_exif_bytes
(&p, 4, bigendian);
200 case
5: /* rational
*/
201 case
10: /* srational
*/
202 value
= read_exif_bytes
(&p, 4, bigendian);
203 rp
= tiff_header
+ value
;
204 num
= read_exif_bytes
(&rp, 4, bigendian);
205 den
= read_exif_bytes
(&rp, 4, bigendian);
207 case
7: /* undefined
*/
217 case
282: /* x res
*/
221 case
283: /* y res
*/
225 case
296: /* res unit
*/
237 *xx
= (int
)(xres
* res_unit
);
238 *yy
= (int
)(yres
* res_unit
);
246 Contrary to pdf where several parallel usage can happen
(epdf
, tex
, lua
) with
247 bitmaps we care less about keeping files open. So
, we can keep files open in
248 the img lib but then they are closed after inclusion anyway.
253 static void close_and_cleanup_jpg
(image_dict
* idict
)
255 /* if one of then is not
NULL we already cleaned up
*/
256 if
(img_file
(idict
) != NULL) {
257 xfclose
(img_file
(idict
), img_filepath
(idict
));
258 img_file
(idict
) = NULL;
260 if
(img_jpg_ptr
(idict
) != NULL) {
261 xfree
(img_jpg_ptr
(idict
));
266 void flush_jpg_info
(image_dict
* idict
)
268 close_and_cleanup_jpg
(idict
);
272 void read_jpg_info
(image_dict
* idict
)
275 unsigned short appmk
, length
;
276 unsigned char jpg_id
[] = "JFIF";
277 if
(img_type
(idict
) != IMG_TYPE_JPG
) {
278 normal_error
("readjpg","conflicting image dictionary");
280 if
(img_file
(idict
) != NULL) {
281 normal_error
("readjpg","image data already read");
283 img_totalpages
(idict
) = 1;
284 img_pagenum
(idict
) = 1;
285 img_xres
(idict
) = img_yres
(idict
) = 0;
286 img_file
(idict
) = xfopen
(img_filepath
(idict
), FOPEN_RBIN_MODE
);
287 if
(img_file
(idict
) == NULL) {
288 normal_error
("readjpg","unable to read image file");
290 img_jpg_ptr
(idict
) = xtalloc
(1, jpg_img_struct
);
291 xfseek
(img_file
(idict
), 0, SEEK_END
, img_filepath
(idict
));
292 img_jpg_ptr
(idict
)->length
= xftell
(img_file
(idict
), img_filepath
(idict
));
293 xfseek
(img_file
(idict
), 0, SEEK_SET
, img_filepath
(idict
));
294 if
((unsigned int
) read2bytes
(img_file
(idict
)) != 0xFFD8) {
295 normal_error
("readjpg","no header found");
297 /* currently JFIF and Exif files allow extracting |img_xres| and |img_yres|
*/
298 appmk
= read2bytes
(img_file
(idict
));
299 if
(appmk
== 0xFFE0) {
301 (void
) read2bytes
(img_file
(idict
));
302 for
(i
= 0; i
< 5; i
++) {
303 if
(xgetc
(img_file
(idict
)) != jpg_id
[i
])
308 (void
) read2bytes
(img_file
(idict
));
309 units
= xgetc
(img_file
(idict
));
310 img_xres
(idict
) = (int
) read2bytes
(img_file
(idict
));
311 img_yres
(idict
) = (int
) read2bytes
(img_file
(idict
));
314 /* pixels per inch
*/
315 if
((img_xres
(idict
) == 1) ||
(img_yres
(idict
) == 1)) {
316 formatted_warning
("readjpg","unusual resolution of %ddpi by %ddpi", img_xres
(idict
), img_yres
(idict
));
321 img_xres
(idict
) = (int
) ((double
) img_xres
(idict
) * 2.54);
322 img_yres
(idict
) = (int
) ((double
) img_yres
(idict
) * 2.54);
325 img_xres
(idict
) = img_yres
(idict
) = 0;
329 /* if either xres or yres is
0 but the other isn't
, set it to the value of the other
*/
330 if
((img_xres
(idict
) == 0) && (img_yres(idict) != 0)) {
331 img_xres
(idict
) = img_yres
(idict
);
333 if
((img_yres
(idict
) == 0) && (img_xres(idict) != 0)) {
334 img_yres
(idict
) = img_xres
(idict
);
336 } else if
(appmk
== 0xFFE1) {
338 FILE *fp
= img_file
(idict
);
342 length
= myget_unsigned_pair
(fp
) - 2;
344 if
(fread
(app_sig
, sizeof
(char
), 5, fp
) != 5)
347 if
(!memcmp
(app_sig
, "Exif\000", 5)) {
348 read_APP1_Exif
(fp
, length
, &xxres, &yyres);
351 img_xres
(idict
) = xxres
;
352 img_yres
(idict
) = yyres
;
354 xfseek
(img_file
(idict
), 0, SEEK_SET
, img_filepath
(idict
));
356 if
(feof
(img_file
(idict
))) {
357 normal_error
("readjpg","premature file end");
358 } else if
(fgetc
(img_file
(idict
)) != 0xFF) {
359 normal_error
("readjpg","no marker found");
361 i
= xgetc
(img_file
(idict
));
363 case M_SOF3
: /* lossless
*/
366 case M_SOF7
: /* lossless
*/
369 case M_SOF11
: /* lossless
*/
372 case M_SOF15
: /* lossless
*/
373 formatted_error
("readjpg","unsupported compression SOF_%d", i
- M_SOF0
);
376 if
(img_pdfminorversion
(idict
) <= 2) {
377 normal_error
("readjpg","progressive DCT with PDF-1.2 is not permitted");
381 (void
) read2bytes
(img_file
(idict
)); /* read segment length
*/
382 img_colordepth
(idict
) = xgetc
(img_file
(idict
));
383 img_ysize
(idict
) = (int
) read2bytes
(img_file
(idict
));
384 img_xsize
(idict
) = (int
) read2bytes
(img_file
(idict
));
385 img_jpg_color
(idict
) = xgetc
(img_file
(idict
));
386 xfseek
(img_file
(idict
), 0, SEEK_SET
, img_filepath
(idict
));
387 switch
(img_jpg_color
(idict
)) {
389 img_procset
(idict
) |
= PROCSET_IMAGE_B
;
392 img_procset
(idict
) |
= PROCSET_IMAGE_C
;
395 img_procset
(idict
) |
= PROCSET_IMAGE_C
;
398 formatted_error
("readjpg","unsupported color space %i", (int
) img_jpg_color
(idict
));
401 So we can optionally keep open a file in img.
403 if
(! img_keepopen
(idict
)) {
404 close_and_cleanup_jpg
(idict
);
407 /* ignore markers without parameters
*/
421 /* skip variable length markers
*/
422 xfseek
(img_file
(idict
), (int
) read2bytes
(img_file
(idict
)) - 2, SEEK_CUR
, img_filepath
(idict
));
426 normal_error
("readjpg","unknown fatal error");
430 static void reopen_jpg
(image_dict
* idict
)
432 int width
= img_xsize
(idict
);
433 int height
= img_ysize
(idict
);
434 int xres
= img_xres
(idict
);
435 int yres
= img_yres
(idict
);
437 we need to make sure that the file kept open
439 img_keepopen
(idict
) = 1;
440 read_jpg_info
(idict
);
441 if
(width
!= img_xsize
(idict
) || height
!= img_ysize
(idict
) || xres
!= img_xres
(idict
) || yres
!= img_yres
(idict
)) {
442 normal_error
("writejpg","image dimensions have changed");
447 void write_jpg
(PDF pdf
, image_dict
* idict
)
450 if
(img_file
(idict
) == NULL) {
453 pdf_begin_obj
(pdf
, img_objnum
(idict
), OBJSTM_NEVER
);
455 pdf_dict_add_name
(pdf
, "Type", "XObject");
456 pdf_dict_add_name
(pdf
, "Subtype", "Image");
457 pdf_dict_add_img_filename
(pdf
, idict
);
458 if
(img_attr
(idict
) != NULL && strlen(img_attr(idict)) > 0) {
459 pdf_printf
(pdf
, "\n%s\n", img_attr
(idict
));
461 pdf_dict_add_int
(pdf
, "Width", (int
) img_xsize
(idict
));
462 pdf_dict_add_int
(pdf
, "Height", (int
) img_ysize
(idict
));
463 pdf_dict_add_int
(pdf
, "BitsPerComponent", (int
) img_colordepth
(idict
));
464 pdf_dict_add_int
(pdf
, "Length", (int
) img_jpg_ptr
(idict
)->length
);
465 if
(img_colorspace
(idict
) != 0) {
466 pdf_dict_add_ref
(pdf
, "ColorSpace", (int
) img_colorspace
(idict
));
468 switch
(img_jpg_color
(idict
)) {
470 pdf_dict_add_name
(pdf
, "ColorSpace", "DeviceGray");
473 pdf_dict_add_name
(pdf
, "ColorSpace", "DeviceRGB");
476 pdf_dict_add_name
(pdf
, "ColorSpace", "DeviceCMYK");
477 pdf_add_name
(pdf
, "Decode");
478 pdf_begin_array
(pdf
);
490 formatted_error
("writejpg","unsupported JPEG color space %i", (int
) img_jpg_color
(idict
));
493 pdf_dict_add_name
(pdf
, "Filter", "DCTDecode");
495 pdf_begin_stream
(pdf
);
496 l
= (size_t
) img_jpg_ptr
(idict
)->length
;
497 xfseek
(img_file
(idict
), 0, SEEK_SET
, img_filepath
(idict
));
498 if
(read_file_to_buf
(pdf
, img_file
(idict
), l
) != l
) {
499 normal_error
("writejpg","fread failed");
504 close_and_cleanup_jpg
(idict
);