1 /***************************************************************************
2 * Copyright (C) 2005 by Dominik Seichter *
3 * domseichter@web.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU Library General Public License as *
7 * published by the Free Software Foundation; either version 2 of the *
8 * License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU Library General Public *
16 * License along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 * *
20 * In addition, as a special exception, the copyright holders give *
21 * permission to link the code of portions of this program with the *
22 * OpenSSL library under certain conditions as described in each *
23 * individual source file, and distribute linked combinations *
24 * including the two. *
25 * You must obey the GNU General Public License in all respects *
26 * for all of the code used other than OpenSSL. If you modify *
27 * file(s) with this exception, you may extend this exception to your *
28 * version of the file(s), but you are not obligated to do so. If you *
29 * do not wish to do so, delete this exception statement from your *
30 * version. If you delete this exception statement from all source *
31 * files in the program, then also delete it here. *
32 ***************************************************************************/
33
34 #include "PdfImage.h"
35
36 #include "base/PdfDefinesPrivate.h"
37
38 #include "base/PdfArray.h"
39 #include "base/PdfColor.h"
40 #include "base/PdfStream.h"
41 #include "base/PdfFiltersPrivate.h"
42
43 #include <stdio.h>
44 #include <wchar.h>
45 #include <sstream>
46
47 // TIFF and JPEG headers already included through "base/PdfFiltersPrivate.h",
48 // although in opposite order (first JPEG, then TIFF), if available of course
49
50 #ifdef PODOFO_HAVE_PNG_LIB
51 #include <png.h>
52 #endif /// PODOFO_HAVE_PNG_LIB
53
54 // <windows.h> defines an annoying GetObject macro that changes uses of GetObject to
55 // GetObjectA . Since macros aren't scope and namespace aware that breaks our code.
56 // Since we won't be using the Win32 resource manager API here, just undefine it.
57 #if defined(GetObject)
58 #undef GetObject
59 #endif
60
61 using namespace std;
62
63 namespace PoDoFo {
64
PdfImage(PdfVecObjects * pParent,const char * pszPrefix)65 PdfImage::PdfImage( PdfVecObjects* pParent, const char* pszPrefix )
66 : PdfXObject( "Image", pParent, pszPrefix )
67 {
68 m_rRect = PdfRect();
69
70 this->SetImageColorSpace( ePdfColorSpace_DeviceRGB );
71 }
72
PdfImage(PdfDocument * pParent,const char * pszPrefix)73 PdfImage::PdfImage( PdfDocument* pParent, const char* pszPrefix )
74 : PdfXObject( "Image", pParent, pszPrefix )
75 {
76 m_rRect = PdfRect();
77
78 this->SetImageColorSpace( ePdfColorSpace_DeviceRGB );
79 }
80
PdfImage(PdfObject * pObject)81 PdfImage::PdfImage( PdfObject* pObject )
82 : PdfXObject( "Image", pObject )
83 {
84 m_rRect.SetHeight( static_cast<double>(this->GetObject()->MustGetIndirectKey( "Height" )->GetNumber()) );
85 m_rRect.SetWidth ( static_cast<double>(this->GetObject()->MustGetIndirectKey( "Width" )->GetNumber()) );
86 }
87
~PdfImage()88 PdfImage::~PdfImage()
89 {
90
91 }
92
93 /* Example: { "JPEG", "TIFF", NULL }
94 *
95 * \returns a zero terminates list of all supported image formats
96 */
GetSupportedFormats()97 const char** PdfImage::GetSupportedFormats()
98 {
99 static const char* ppszFormats[] = {
100 #ifdef PODOFO_HAVE_JPEG_LIB
101 "JPEG",
102 #endif // PODOFO_HAVE_JPEG_LIB
103 #ifdef PODOFO_HAVE_PNG_LIB
104 "PNG",
105 #endif // PODOFO_HAVE_PNG_LIB
106 #ifdef PODOFO_HAVE_TIFF_LIB
107 "TIFF",
108 #endif // PODOFO_HAVE_TIFF_LIB
109 NULL
110 };
111
112 return ppszFormats;
113 }
114
SetImageColorSpace(EPdfColorSpace eColorSpace,const PdfArray * indexedData)115 void PdfImage::SetImageColorSpace( EPdfColorSpace eColorSpace, const PdfArray *indexedData )
116 {
117 if (eColorSpace == ePdfColorSpace_Indexed) {
118 PODOFO_RAISE_LOGIC_IF( !indexedData, "PdfImage::SetImageColorSpace: indexedData cannot be NULL for Indexed color space." );
119
120 PdfArray array(*indexedData);
121
122 array.insert(array.begin(), ColorspaceToName( eColorSpace ));
123 this->GetObject()->GetDictionary().AddKey( PdfName("ColorSpace"), array );
124 } else {
125 this->GetObject()->GetDictionary().AddKey( PdfName("ColorSpace"), ColorspaceToName( eColorSpace ) );
126 }
127 }
128
SetImageICCProfile(PdfInputStream * pStream,long lColorComponents,EPdfColorSpace eAlternateColorSpace)129 void PdfImage::SetImageICCProfile( PdfInputStream* pStream, long lColorComponents, EPdfColorSpace eAlternateColorSpace )
130 {
131 // Check lColorComponents for a valid value
132 if( lColorComponents != 1 &&
133 lColorComponents != 3 &&
134 lColorComponents != 4 )
135 {
136 PODOFO_RAISE_ERROR_INFO( ePdfError_ValueOutOfRange, "SetImageICCProfile lColorComponents must be 1,3 or 4!" );
137 }
138
139 // Create a colorspace object
140 PdfObject* pIccObject = this->GetObject()->GetOwner()->CreateObject();
141 pIccObject->GetDictionary().AddKey( PdfName("Alternate"), ColorspaceToName( eAlternateColorSpace ) );
142 pIccObject->GetDictionary().AddKey( PdfName("N"), static_cast<pdf_int64>(lColorComponents) );
143 pIccObject->GetStream()->Set( pStream );
144
145 // Add the colorspace to our image
146 PdfArray array;
147 array.push_back( PdfName("ICCBased") );
148 array.push_back( pIccObject->Reference() );
149 this->GetObject()->GetDictionary().AddKey( PdfName("ColorSpace"), array );
150 }
151
SetImageSoftmask(const PdfImage * pSoftmask)152 void PdfImage::SetImageSoftmask( const PdfImage* pSoftmask )
153 {
154 GetObject()->GetDictionary().AddKey( "SMask", pSoftmask->GetObject()->Reference() );
155 }
156
SetImageData(unsigned int nWidth,unsigned int nHeight,unsigned int nBitsPerComponent,PdfInputStream * pStream)157 void PdfImage::SetImageData( unsigned int nWidth, unsigned int nHeight,
158 unsigned int nBitsPerComponent, PdfInputStream* pStream )
159 {
160 TVecFilters vecFlate;
161 vecFlate.push_back( ePdfFilter_FlateDecode );
162
163 this->SetImageData( nWidth, nHeight, nBitsPerComponent, pStream, vecFlate );
164 }
165
SetImageData(unsigned int nWidth,unsigned int nHeight,unsigned int nBitsPerComponent,PdfInputStream * pStream,const TVecFilters & vecFilters)166 void PdfImage::SetImageData( unsigned int nWidth, unsigned int nHeight,
167 unsigned int nBitsPerComponent, PdfInputStream* pStream,
168 const TVecFilters & vecFilters )
169 {
170 m_rRect.SetWidth( nWidth );
171 m_rRect.SetHeight( nHeight );
172
173 this->GetObject()->GetDictionary().AddKey( "Width", PdfVariant( static_cast<pdf_int64>(nWidth) ) );
174 this->GetObject()->GetDictionary().AddKey( "Height", PdfVariant( static_cast<pdf_int64>(nHeight) ) );
175 this->GetObject()->GetDictionary().AddKey( "BitsPerComponent", PdfVariant( static_cast<pdf_int64>(nBitsPerComponent) ) );
176
177 PdfVariant var;
178 m_rRect.ToVariant( var );
179 this->GetObject()->GetDictionary().AddKey( "BBox", var );
180
181 this->GetObject()->GetStream()->Set( pStream, vecFilters );
182 }
183
SetImageDataRaw(unsigned int nWidth,unsigned int nHeight,unsigned int nBitsPerComponent,PdfInputStream * pStream)184 void PdfImage::SetImageDataRaw( unsigned int nWidth, unsigned int nHeight,
185 unsigned int nBitsPerComponent, PdfInputStream* pStream )
186 {
187 m_rRect.SetWidth( nWidth );
188 m_rRect.SetHeight( nHeight );
189
190 this->GetObject()->GetDictionary().AddKey( "Width", PdfVariant( static_cast<pdf_int64>(nWidth) ) );
191 this->GetObject()->GetDictionary().AddKey( "Height", PdfVariant( static_cast<pdf_int64>(nHeight) ) );
192 this->GetObject()->GetDictionary().AddKey( "BitsPerComponent", PdfVariant( static_cast<pdf_int64>(nBitsPerComponent) ) );
193
194 PdfVariant var;
195 m_rRect.ToVariant( var );
196 this->GetObject()->GetDictionary().AddKey( "BBox", var );
197
198 this->GetObject()->GetStream()->SetRawData( pStream, -1 );
199 }
200
LoadFromFile(const char * pszFilename)201 void PdfImage::LoadFromFile( const char* pszFilename )
202 {
203 if( pszFilename && strlen( pszFilename ) > 3 )
204 {
205 const char* pszExtension = pszFilename + strlen( pszFilename ) - 3;
206
207 #ifdef PODOFO_HAVE_TIFF_LIB
208 if( PoDoFo::compat::strncasecmp( pszExtension, "tif", 3 ) == 0 ||
209 PoDoFo::compat::strncasecmp( pszExtension, "iff", 3 ) == 0 ) // "tiff"
210 {
211 LoadFromTiff( pszFilename );
212 return;
213 }
214 #endif
215
216 #ifdef PODOFO_HAVE_JPEG_LIB
217 if( PoDoFo::compat::strncasecmp( pszExtension, "jpg", 3 ) == 0 )
218 {
219 LoadFromJpeg( pszFilename );
220 return;
221 }
222 #endif
223
224 #ifdef PODOFO_HAVE_PNG_LIB
225 if( PoDoFo::compat::strncasecmp( pszExtension, "png", 3 ) == 0 )
226 {
227 LoadFromPng( pszFilename );
228 return;
229 }
230 #endif
231
232 }
233 PODOFO_RAISE_ERROR_INFO( ePdfError_UnsupportedImageFormat, pszFilename );
234 }
235
236 #ifdef _WIN32
LoadFromFile(const wchar_t * pszFilename)237 void PdfImage::LoadFromFile( const wchar_t* pszFilename )
238 {
239 if( pszFilename && wcslen( pszFilename ) > 3 )
240 {
241 const wchar_t* pszExtension = pszFilename + wcslen( pszFilename ) - 3;
242
243 #ifdef PODOFO_HAVE_TIFF_LIB
244 #if TIFFLIB_VERSION >= 20120922 // TiffOpenW needs at least version 4.0.3
245 if( _wcsnicmp( pszExtension, L"tif", 3 ) == 0 ||
246 _wcsnicmp( pszExtension, L"iff", 3 ) == 0 ) // "tiff"
247 {
248 LoadFromTiff( pszFilename );
249 return;
250 }
251 #endif
252 #endif
253
254 #ifdef PODOFO_HAVE_JPEG_LIB
255 if( _wcsnicmp( pszExtension, L"jpg", 3 ) == 0 )
256 {
257 LoadFromJpeg( pszFilename );
258 return;
259 }
260 #endif
261
262 #ifdef PODOFO_HAVE_PNG_LIB
263 if( _wcsnicmp( pszExtension, L"png", 3 ) == 0 )
264 {
265 LoadFromPng( pszFilename );
266 return;
267 }
268 #endif
269 }
270
271 PdfError e( ePdfError_UnsupportedImageFormat, __FILE__, __LINE__ );
272 e.SetErrorInformation( pszFilename );
273 throw e;
274 }
275 #endif // _WIN32
276
LoadFromData(const unsigned char * pData,pdf_long dwLen)277 void PdfImage::LoadFromData(const unsigned char* pData, pdf_long dwLen)
278 {
279 if (dwLen > 4) {
280 unsigned char magic[4];
281 memcpy(magic, pData, 4);
282
283 #ifdef PODOFO_HAVE_TIFF_LIB
284 if((magic[0] == 0x4d &&
285 magic[1] == 0x4d &&
286 magic[2] == 0x00 &&
287 magic[3] == 0x2a) ||
288 (magic[0] == 0x49 &&
289 magic[1] == 0x49 &&
290 magic[2] == 0x2a &&
291 magic[3] == 0x00))
292 {
293 LoadFromTiffData(pData, dwLen);
294 return;
295 }
296 #endif
297
298 #ifdef PODOFO_HAVE_JPEG_LIB
299 if( magic[0] == 0xff &&
300 magic[1] == 0xd8 )
301 {
302 LoadFromJpegData(pData, dwLen);
303 return;
304 }
305 #endif
306
307 #ifdef PODOFO_HAVE_PNG_LIB
308 if( magic[0] == 0x89 &&
309 magic[1] == 0x50 &&
310 magic[2] == 0x4e &&
311 magic[3] == 0x47 )
312 {
313 LoadFromPngData(pData, dwLen);
314 return;
315 }
316 #endif
317
318 }
319 PODOFO_RAISE_ERROR_INFO( ePdfError_UnsupportedImageFormat, "Unknown magic number" );
320 }
321
322 #ifdef PODOFO_HAVE_JPEG_LIB
323
LoadFromJpeg(const char * pszFilename)324 void PdfImage::LoadFromJpeg( const char* pszFilename )
325 {
326 /* Constructor will throw exception */
327 PdfFileInputStream stream( pszFilename );
328 LoadFromJpegHandle( &stream );
329 }
330
331 #ifdef _WIN32
LoadFromJpeg(const wchar_t * pszFilename)332 void PdfImage::LoadFromJpeg( const wchar_t* pszFilename )
333 {
334 PdfFileInputStream stream( pszFilename );
335 LoadFromJpegHandle( &stream );
336 }
337 #endif // _WIN32
338
LoadFromJpegHandle(PdfFileInputStream * pInStream)339 void PdfImage::LoadFromJpegHandle( PdfFileInputStream* pInStream )
340 {
341 struct jpeg_decompress_struct cinfo;
342 struct jpeg_error_mgr jerr;
343
344 cinfo.err = jpeg_std_error(&jerr);
345 jerr.error_exit = &JPegErrorExit;
346 jerr.emit_message = &JPegErrorOutput;
347
348 jpeg_create_decompress(&cinfo);
349
350 jpeg_stdio_src(&cinfo, pInStream->GetHandle());
351
352 if( jpeg_read_header(&cinfo, TRUE) <= 0 )
353 {
354 (void) jpeg_destroy_decompress(&cinfo);
355
356 PODOFO_RAISE_ERROR( ePdfError_UnexpectedEOF );
357 }
358
359 jpeg_start_decompress(&cinfo);
360
361 m_rRect.SetWidth( cinfo.output_width );
362 m_rRect.SetHeight( cinfo.output_height );
363
364 // I am not sure wether this switch is fully correct.
365 // it should handle all cases though.
366 // Index jpeg files might look strange as jpeglib+
367 // returns 1 for them.
368 switch( cinfo.output_components )
369 {
370 case 3:
371 this->SetImageColorSpace( ePdfColorSpace_DeviceRGB );
372 break;
373 case 4:
374 {
375 this->SetImageColorSpace( ePdfColorSpace_DeviceCMYK );
376 // The jpeg-doc ist not specific in this point, but cmyk's seem to be stored
377 // in a inverted fashion. Fix by attaching a decode array
378 PdfArray decode;
379 decode.push_back( 1.0 );
380 decode.push_back( 0.0 );
381 decode.push_back( 1.0 );
382 decode.push_back( 0.0 );
383 decode.push_back( 1.0 );
384 decode.push_back( 0.0 );
385 decode.push_back( 1.0 );
386 decode.push_back( 0.0 );
387
388 this->GetObject()->GetDictionary().AddKey( PdfName("Decode"), decode );
389 }
390 break;
391 default:
392 this->SetImageColorSpace( ePdfColorSpace_DeviceGray );
393 break;
394 }
395
396 // Set the filters key to DCTDecode
397 this->GetObject()->GetDictionary().AddKey( PdfName::KeyFilter, PdfName( "DCTDecode" ) );
398 // Do not apply any filters as JPEG data is already DCT encoded.
399 fseeko( pInStream->GetHandle(), 0L, SEEK_SET );
400 this->SetImageDataRaw( cinfo.output_width, cinfo.output_height, 8, pInStream );
401
402 (void) jpeg_destroy_decompress(&cinfo);
403 }
404
LoadFromJpegData(const unsigned char * pData,pdf_long dwLen)405 void PdfImage::LoadFromJpegData(const unsigned char* pData, pdf_long dwLen)
406 {
407 struct jpeg_decompress_struct cinfo;
408 struct jpeg_error_mgr jerr;
409
410 cinfo.err = jpeg_std_error(&jerr);
411 jerr.error_exit = &JPegErrorExit;
412 jerr.emit_message = &JPegErrorOutput;
413
414 jpeg_create_decompress(&cinfo);
415
416 jpeg_memory_src(&cinfo, pData, dwLen);
417
418 if( jpeg_read_header(&cinfo, TRUE) <= 0 )
419 {
420 (void) jpeg_destroy_decompress(&cinfo);
421
422 PODOFO_RAISE_ERROR( ePdfError_UnexpectedEOF );
423 }
424
425 jpeg_start_decompress(&cinfo);
426
427 m_rRect.SetWidth( cinfo.output_width );
428 m_rRect.SetHeight( cinfo.output_height );
429
430 // I am not sure wether this switch is fully correct.
431 // it should handle all cases though.
432 // Index jpeg files might look strange as jpeglib+
433 // returns 1 for them.
434 switch( cinfo.output_components )
435 {
436 case 3:
437 this->SetImageColorSpace( ePdfColorSpace_DeviceRGB );
438 break;
439 case 4:
440 {
441 this->SetImageColorSpace( ePdfColorSpace_DeviceCMYK );
442 // The jpeg-doc ist not specific in this point, but cmyk's seem to be stored
443 // in a inverted fashion. Fix by attaching a decode array
444 PdfArray decode;
445 decode.push_back( 1.0 );
446 decode.push_back( 0.0 );
447 decode.push_back( 1.0 );
448 decode.push_back( 0.0 );
449 decode.push_back( 1.0 );
450 decode.push_back( 0.0 );
451 decode.push_back( 1.0 );
452 decode.push_back( 0.0 );
453
454 this->GetObject()->GetDictionary().AddKey( PdfName("Decode"), decode );
455 }
456 break;
457 default:
458 this->SetImageColorSpace( ePdfColorSpace_DeviceGray );
459 break;
460 }
461
462 // Set the filters key to DCTDecode
463 this->GetObject()->GetDictionary().AddKey( PdfName::KeyFilter, PdfName( "DCTDecode" ) );
464
465 PdfMemoryInputStream fInpStream( (const char*)pData, (pdf_long) dwLen);
466 this->SetImageDataRaw( cinfo.output_width, cinfo.output_height, 8, &fInpStream );
467
468 (void) jpeg_destroy_decompress(&cinfo);
469 }
470 #endif // PODOFO_HAVE_JPEG_LIB
471
472 #ifdef PODOFO_HAVE_TIFF_LIB
TIFFErrorWarningHandler(const char *,const char *,va_list)473 static void TIFFErrorWarningHandler(const char*, const char*, va_list)
474 {
475
476 }
477
LoadFromTiffHandle(void * hInHandle)478 void PdfImage::LoadFromTiffHandle(void* hInHandle) {
479
480 TIFF* hInTiffHandle = (TIFF*)hInHandle;
481
482 int32 row, width, height;
483 uint16 samplesPerPixel, bitsPerSample;
484 uint16* sampleInfo;
485 uint16 extraSamples;
486 uint16 planarConfig, photoMetric, orientation;
487 int32 resolutionUnit;
488
489 TIFFGetField(hInTiffHandle, TIFFTAG_IMAGEWIDTH, &width);
490 TIFFGetField(hInTiffHandle, TIFFTAG_IMAGELENGTH, &height);
491 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
492 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
493 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_PLANARCONFIG, &planarConfig);
494 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_PHOTOMETRIC, &photoMetric);
495 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_EXTRASAMPLES, &extraSamples, &sampleInfo);
496 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_ORIENTATION, &orientation);
497
498 resolutionUnit = 0;
499 float resX;
500 float resY;
501 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_XRESOLUTION, &resX);
502 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_YRESOLUTION, &resY);
503 TIFFGetFieldDefaulted(hInTiffHandle, TIFFTAG_RESOLUTIONUNIT, &resolutionUnit);
504
505 int colorChannels = samplesPerPixel - extraSamples;
506
507 int bitsPixel = bitsPerSample * samplesPerPixel;
508
509 // TODO: implement special cases
510 if( TIFFIsTiled(hInTiffHandle) )
511 {
512 TIFFClose(hInTiffHandle);
513 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
514 }
515
516 if ( planarConfig != PLANARCONFIG_CONTIG && colorChannels != 1 )
517 {
518 TIFFClose(hInTiffHandle);
519 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
520 }
521
522 if ( orientation != ORIENTATION_TOPLEFT )
523 {
524 TIFFClose(hInTiffHandle);
525 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
526 }
527
528 switch(photoMetric)
529 {
530 case PHOTOMETRIC_MINISBLACK:
531 {
532 if( bitsPixel == 1 )
533 {
534 PdfArray decode;
535 decode.insert( decode.end(), PdfVariant( static_cast<pdf_int64>(0) ) );
536 decode.insert( decode.end(), PdfVariant( static_cast<pdf_int64>(1) ) );
537 this->GetObject()->GetDictionary().AddKey( PdfName("Decode"), decode );
538 this->GetObject()->GetDictionary().AddKey( PdfName("ImageMask"), PdfVariant( true ) );
539 this->GetObject()->GetDictionary().RemoveKey( PdfName("ColorSpace") );
540 }
541 else if ( bitsPixel == 8 || bitsPixel == 16)
542 SetImageColorSpace(ePdfColorSpace_DeviceGray);
543 else
544 {
545 TIFFClose(hInTiffHandle);
546 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
547 }
548 }
549 break;
550
551 case PHOTOMETRIC_MINISWHITE:
552 {
553 if( bitsPixel == 1 )
554 {
555 PdfArray decode;
556 decode.insert( decode.end(), PdfVariant( static_cast<pdf_int64>(1) ) );
557 decode.insert( decode.end(), PdfVariant( static_cast<pdf_int64>(0) ) );
558 this->GetObject()->GetDictionary().AddKey( PdfName("Decode"), decode );
559 this->GetObject()->GetDictionary().AddKey( PdfName("ImageMask"), PdfVariant( true ) );
560 this->GetObject()->GetDictionary().RemoveKey( PdfName("ColorSpace") );
561 }
562 else if ( bitsPixel == 8 || bitsPixel == 16)
563 SetImageColorSpace(ePdfColorSpace_DeviceGray);
564 else
565 {
566 TIFFClose(hInTiffHandle);
567 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
568 }
569 }
570 break;
571
572 case PHOTOMETRIC_RGB:
573 if ( bitsPixel != 24 )
574 {
575 TIFFClose(hInTiffHandle);
576 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
577 }
578 SetImageColorSpace(ePdfColorSpace_DeviceRGB);
579 break;
580
581 case PHOTOMETRIC_SEPARATED:
582 if( bitsPixel != 32)
583 {
584 TIFFClose(hInTiffHandle);
585 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
586 }
587 SetImageColorSpace(ePdfColorSpace_DeviceCMYK);
588 break;
589
590 case PHOTOMETRIC_PALETTE:
591 {
592 int numColors = (1 << bitsPixel);
593
594 PdfArray decode;
595 decode.insert( decode.end(), PdfVariant( static_cast<pdf_int64>(0) ) );
596 decode.insert( decode.end(), PdfVariant( static_cast<pdf_int64>(numColors-1) ) );
597 this->GetObject()->GetDictionary().AddKey( PdfName("Decode"), decode );
598
599 uint16 * rgbRed;
600 uint16 * rgbGreen;
601 uint16 * rgbBlue;
602 TIFFGetField(hInTiffHandle, TIFFTAG_COLORMAP, &rgbRed, &rgbGreen, &rgbBlue);
603
604 char *datap = new char[numColors*3];
605
606 for ( int clr = 0; clr < numColors; clr++ )
607 {
608 datap[3*clr+0] = rgbRed[clr]/257;
609 datap[3*clr+1] = rgbGreen[clr]/257;
610 datap[3*clr+2] = rgbBlue[clr]/257;
611 }
612 PdfMemoryInputStream stream( datap, numColors*3 );
613
614 // Create a colorspace object
615 PdfObject* pIdxObject = this->GetObject()->GetOwner()->CreateObject();
616 pIdxObject->GetStream()->Set( &stream );
617
618 // Add the colorspace to our image
619 PdfArray array;
620 array.push_back( PdfName("Indexed") );
621 array.push_back( PdfName("DeviceRGB") );
622 array.push_back( static_cast<pdf_int64>(numColors-1) );
623 array.push_back( pIdxObject->Reference() );
624 this->GetObject()->GetDictionary().AddKey( PdfName("ColorSpace"), array );
625
626 delete[] datap;
627 }
628 break;
629
630 default:
631 TIFFClose(hInTiffHandle);
632 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
633 break;
634 }
635
636 int32 scanlineSize = TIFFScanlineSize(hInTiffHandle);
637 long bufferSize = scanlineSize * height;
638 char *buffer = new char[bufferSize];
639 if( !buffer )
640 {
641 TIFFClose(hInTiffHandle);
642 PODOFO_RAISE_ERROR( ePdfError_OutOfMemory );
643 }
644
645 for(row = 0; row < height; row++)
646 {
647 if(TIFFReadScanline(hInTiffHandle,
648 &buffer[row * scanlineSize],
649 row) == (-1))
650 {
651 TIFFClose(hInTiffHandle);
652 PODOFO_RAISE_ERROR( ePdfError_UnsupportedImageFormat );
653 }
654 }
655
656 PdfMemoryInputStream stream(buffer, bufferSize);
657
658 SetImageData(static_cast<unsigned int>(width),
659 static_cast<unsigned int>(height),
660 static_cast<unsigned int>(bitsPerSample),
661 &stream);
662
663 delete[] buffer;
664
665 TIFFClose(hInTiffHandle);
666 }
667
LoadFromTiff(const char * pszFilename)668 void PdfImage::LoadFromTiff( const char* pszFilename )
669 {
670 TIFFSetErrorHandler(TIFFErrorWarningHandler);
671 TIFFSetWarningHandler(TIFFErrorWarningHandler);
672
673 if( !pszFilename )
674 {
675 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
676 }
677
678 TIFF* hInfile = TIFFOpen(pszFilename, "rb");
679
680 if( !hInfile )
681 {
682 PODOFO_RAISE_ERROR_INFO( ePdfError_FileNotFound, pszFilename );
683 }
684
685 LoadFromTiffHandle(hInfile);
686 }
687
688 #ifdef _WIN32
LoadFromTiff(const wchar_t * pszFilename)689 void PdfImage::LoadFromTiff( const wchar_t* pszFilename )
690 {
691 #if TIFFLIB_VERSION >= 20120922 // TiffOpenW needs at least version 4.0.3
692 TIFFSetErrorHandler(TIFFErrorWarningHandler);
693 TIFFSetWarningHandler(TIFFErrorWarningHandler);
694
695 if( !pszFilename )
696 {
697 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
698 }
699
700 TIFF* hInfile = TIFFOpenW(pszFilename, "rb");
701
702 if( !hInfile )
703 {
704 PdfError e( ePdfError_FileNotFound, __FILE__, __LINE__ );
705 e.SetErrorInformation( pszFilename );
706 throw e;
707 }
708
709 LoadFromTiffHandle(hInfile);
710 #else
711 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
712 #endif // TIFFLIB_VERSION
713 }
714 #endif // _WIN32
715
716 struct tiffData
717 {
tiffDataPoDoFo::tiffData718 tiffData(const unsigned char* data, tsize_t size):_data(data), _pos(0), _size(size) {}
719
readPoDoFo::tiffData720 tsize_t read(tdata_t data, tsize_t length)
721 {
722 tsize_t bytesRead = 0;
723 if (length > _size - static_cast<tsize_t>(_pos))
724 {
725 memcpy(data, &_data[_pos], _size - _pos);
726 bytesRead = _size - _pos;
727 _pos = _size;
728 }
729 else
730 {
731 memcpy(data, &_data[_pos], length);
732 bytesRead = length;
733 _pos += length;
734 }
735 return bytesRead;
736 }
737
sizePoDoFo::tiffData738 toff_t size()
739 {
740 return _size;
741 }
742
seekPoDoFo::tiffData743 toff_t seek(toff_t pos, int whence)
744 {
745 if (pos == 0xFFFFFFFF) {
746 return 0xFFFFFFFF;
747 }
748 switch(whence)
749 {
750 case SEEK_SET:
751 if (static_cast<tsize_t>(pos) > _size)
752 {
753 _pos = _size;
754 }
755 else
756 {
757 _pos = pos;
758 }
759 break;
760 case SEEK_CUR:
761 if (static_cast<tsize_t>(pos + _pos) > _size)
762 {
763 _pos = _size;
764 }
765 else
766 {
767 _pos += pos;
768 }
769 break;
770 case SEEK_END:
771 if (static_cast<tsize_t>(pos) > _size)
772 {
773 _pos = 0;
774 }
775 else
776 {
777 _pos = _size - pos;
778 }
779 break;
780 }
781 return _pos;
782 }
783
784 private:
785 const unsigned char* _data;
786 toff_t _pos;
787 tsize_t _size;
788 };
tiff_Read(thandle_t st,tdata_t buffer,tsize_t size)789 tsize_t tiff_Read(thandle_t st, tdata_t buffer, tsize_t size)
790 {
791 tiffData* data = (tiffData*)st;
792 return data->read(buffer, size);
793 };
tiff_Write(thandle_t,tdata_t,tsize_t)794 tsize_t tiff_Write(thandle_t /*st*/, tdata_t /*buffer*/, tsize_t /*size*/)
795 {
796 return 0;
797 };
tiff_Close(thandle_t)798 int tiff_Close(thandle_t)
799 {
800 return 0;
801 };
tiff_Seek(thandle_t st,toff_t pos,int whence)802 toff_t tiff_Seek(thandle_t st, toff_t pos, int whence)
803 {
804 tiffData* data = (tiffData*)st;
805 return data->seek(pos, whence);
806 };
tiff_Size(thandle_t st)807 toff_t tiff_Size(thandle_t st)
808 {
809 tiffData* data = (tiffData*)st;
810 return data->size();
811 };
tiff_Map(thandle_t,tdata_t *,toff_t *)812 int tiff_Map(thandle_t, tdata_t*, toff_t*)
813 {
814 return 0;
815 };
tiff_Unmap(thandle_t,tdata_t,toff_t)816 void tiff_Unmap(thandle_t, tdata_t, toff_t)
817 {
818 return;
819 };
LoadFromTiffData(const unsigned char * pData,pdf_long dwLen)820 void PdfImage::LoadFromTiffData(const unsigned char* pData, pdf_long dwLen)
821 {
822 TIFFSetErrorHandler(TIFFErrorWarningHandler);
823 TIFFSetWarningHandler(TIFFErrorWarningHandler);
824
825 if( !pData )
826 {
827 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
828 }
829
830 tiffData data(pData, dwLen);
831 TIFF* hInHandle = TIFFClientOpen("Memory", "r", (thandle_t)&data,
832 tiff_Read, tiff_Write, tiff_Seek, tiff_Close, tiff_Size,
833 tiff_Map, tiff_Unmap);
834 if( !hInHandle )
835 {
836 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
837 }
838 LoadFromTiffHandle(hInHandle);
839 }
840 #endif // PODOFO_HAVE_TIFF_LIB
841 #ifdef PODOFO_HAVE_PNG_LIB
LoadFromPng(const char * pszFilename)842 void PdfImage::LoadFromPng( const char* pszFilename )
843 {
844 PdfFileInputStream stream( pszFilename );
845 LoadFromPngHandle( &stream );
846 }
847
848 #ifdef _WIN32
LoadFromPng(const wchar_t * pszFilename)849 void PdfImage::LoadFromPng( const wchar_t* pszFilename )
850 {
851 PdfFileInputStream stream( pszFilename );
852 LoadFromPngHandle( &stream );
853 }
854 #endif // _WIN32
855
LoadFromPngContent(png_structp pPng,png_infop pInfo,PdfImage * image)856 static void LoadFromPngContent(png_structp pPng, png_infop pInfo, PdfImage *image)
857 {
858 png_set_sig_bytes(pPng, 8);
859 png_read_info(pPng, pInfo);
860
861 // Begin
862 png_uint_32 width;
863 png_uint_32 height;
864 int depth;
865 int color_type;
866 int interlace;
867
868 png_get_IHDR (pPng, pInfo,
869 &width, &height, &depth,
870 &color_type, &interlace, NULL, NULL);
871
872 /* convert palette/gray image to rgb */
873 /* expand gray bit depth if needed */
874 if (color_type == PNG_COLOR_TYPE_GRAY) {
875 #if PNG_LIBPNG_VER >= 10209
876 png_set_expand_gray_1_2_4_to_8 (pPng);
877 #else
878 png_set_gray_1_2_4_to_8 (pPng);
879 #endif
880 } else if (color_type != PNG_COLOR_TYPE_PALETTE && depth < 8) {
881 png_set_packing(pPng);
882 }
883
884 /* transform transparency to alpha */
885 if (color_type != PNG_COLOR_TYPE_PALETTE && png_get_valid (pPng, pInfo, PNG_INFO_tRNS))
886 png_set_tRNS_to_alpha (pPng);
887
888 if (depth == 16)
889 png_set_strip_16(pPng);
890
891 if (interlace != PNG_INTERLACE_NONE)
892 png_set_interlace_handling(pPng);
893
894 //png_set_filler (pPng, 0xff, PNG_FILLER_AFTER);
895
896 /* recheck header after setting EXPAND options */
897 png_read_update_info(pPng, pInfo);
898 png_get_IHDR (pPng, pInfo,
899 &width, &height, &depth,
900 &color_type, &interlace, NULL, NULL);
901 // End //
902
903 // Read the file
904 if( setjmp(png_jmpbuf(pPng)) )
905 {
906 png_destroy_read_struct(&pPng, &pInfo, (png_infopp)NULL);
907 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
908 }
909
910 size_t lRowLen = png_get_rowbytes(pPng, pInfo);
911 size_t lLen = lRowLen * height;
912 char* pBuffer = static_cast<char*>(podofo_calloc(lLen, sizeof(char)));
913 if (!pBuffer)
914 {
915 PODOFO_RAISE_ERROR(ePdfError_OutOfMemory);
916 }
917
918 png_bytepp pRows = static_cast<png_bytepp>(podofo_calloc(height, sizeof(png_bytep)));
919 if (!pRows)
920 {
921 PODOFO_RAISE_ERROR(ePdfError_OutOfMemory);
922 }
923
924 for(unsigned int y=0; y<height; y++)
925 {
926 pRows[y] = reinterpret_cast<png_bytep>(pBuffer + y * lRowLen);
927 }
928
929 png_read_image(pPng, pRows);
930
931 png_bytep paletteTrans;
932 int numTransColors;
933 if (color_type & PNG_COLOR_MASK_ALPHA ||
934 (color_type == PNG_COLOR_TYPE_PALETTE && png_get_valid(pPng, pInfo, PNG_INFO_tRNS) && png_get_tRNS(pPng, pInfo, &paletteTrans, &numTransColors, NULL)))
935 {
936 // Handle alpha channel and create smask
937 char *smask = static_cast<char*>(podofo_calloc(height, width));
938 png_uint_32 smaskIndex = 0;
939 if (color_type == PNG_COLOR_TYPE_PALETTE) {
940 for (png_uint_32 r = 0; r < height; r++) {
941 png_bytep row = pRows[r];
942 for (png_uint_32 c = 0; c < width; c++) {
943 png_byte color;
944 if (depth == 8) {
945 color = row[c];
946 } else if (depth == 4) {
947 color = c % 2 ? row[c / 2] >> 4 : row[c / 2] & 0xF;
948 } else if (depth == 2) {
949 color = (row[c / 4] >> c % 4 * 2) & 3;
950 } else if (depth == 1) {
951 color = (row[c / 4] >> c % 8) & 1;
952 }
953 smask[smaskIndex++] = color < numTransColors ? paletteTrans[color] : 0xFF;
954 }
955 }
956 } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
957 for (png_uint_32 r = 0; r < height; r++) {
958 png_bytep row = pRows[r];
959 for (png_uint_32 c = 0; c < width; c++) {
960 memmove(pBuffer + 3 * smaskIndex, row + 4 * c, 3); // 3 byte for rgb
961 smask[smaskIndex++] = row[c * 4 + 3]; // 4th byte for alpha
962 }
963 }
964 lLen = 3 * width * height;
965 } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
966 for (png_uint_32 r = 0; r < height; r++) {
967 png_bytep row = pRows[r];
968 for (png_uint_32 c = 0; c < width; c++) {
969 pBuffer[smaskIndex] = row[c * 2]; // 1 byte for gray
970 smask[smaskIndex++] = row[c * 2 + 1]; // 2nd byte for alpha
971 }
972 }
973 lLen = width * height;
974 }
975 PdfMemoryInputStream smaskstream(smask, width * height);
976 PdfImage smakeImage(image->GetObject()->GetOwner());
977 smakeImage.SetImageColorSpace(ePdfColorSpace_DeviceGray);
978 smakeImage.SetImageData(width, height, 8, &smaskstream);
979 image->SetImageSoftmask(&smakeImage);
980 podofo_free(smask);
981 }
982
983 // Set color space
984 if (color_type == PNG_COLOR_TYPE_PALETTE) {
985 png_color *pColors;
986 int numColors;
987 png_get_PLTE(pPng, pInfo, &pColors, &numColors);
988
989 char *datap = new char[numColors * 3];
990 for (int i = 0; i < numColors; i++, pColors++)
991 {
992 datap[3 * i + 0] = pColors->red;
993 datap[3 * i + 1] = pColors->green;
994 datap[3 * i + 2] = pColors->blue;
995 }
996 PdfMemoryInputStream stream(datap, numColors * 3);
997 PdfObject* pIdxObject = image->GetObject()->GetOwner()->CreateObject();
998 pIdxObject->GetStream()->Set(&stream);
999
1000 PdfArray array;
1001 array.push_back(PdfName("DeviceRGB"));
1002 array.push_back(static_cast<pdf_int64>(numColors - 1));
1003 array.push_back(pIdxObject->Reference());
1004 image->SetImageColorSpace(ePdfColorSpace_Indexed, &array);
1005 } else if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
1006 image->SetImageColorSpace(ePdfColorSpace_DeviceGray);
1007 } else {
1008 image->SetImageColorSpace(ePdfColorSpace_DeviceRGB);
1009 }
1010
1011 // Set the image data and flate compress it
1012 PdfMemoryInputStream stream( pBuffer, lLen );
1013 image->SetImageData( width, height, depth, &stream );
1014
1015 podofo_free(pBuffer);
1016 podofo_free(pRows);
1017
1018 png_destroy_read_struct(&pPng, &pInfo, (png_infopp)NULL);
1019 }
1020
LoadFromPngHandle(PdfFileInputStream * pInStream)1021 void PdfImage::LoadFromPngHandle( PdfFileInputStream* pInStream )
1022 {
1023 FILE* hFile = pInStream->GetHandle();
1024 png_byte header[8];
1025 if( fread( header, 1, 8, hFile ) != 8 ||
1026 png_sig_cmp( header, 0, 8 ) )
1027 {
1028 PODOFO_RAISE_ERROR_INFO( ePdfError_UnsupportedImageFormat, "The file could not be recognized as a PNG file." );
1029 }
1030
1031 png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1032 if( !pPng )
1033 {
1034 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1035 }
1036
1037 png_infop pInfo = png_create_info_struct(pPng);
1038 if( !pInfo )
1039 {
1040 png_destroy_read_struct(&pPng, (png_infopp)NULL, (png_infopp)NULL);
1041 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1042 }
1043
1044 if( setjmp(png_jmpbuf(pPng)) )
1045 {
1046 png_destroy_read_struct(&pPng, &pInfo, (png_infopp)NULL);
1047 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1048 }
1049
1050 png_init_io(pPng, hFile);
1051
1052 LoadFromPngContent(pPng, pInfo, this);
1053 }
1054
1055 struct pngData
1056 {
pngDataPoDoFo::pngData1057 pngData(const unsigned char* data, png_size_t size):_data(data), _pos(0), _size(size) {}
1058
readPoDoFo::pngData1059 void read(png_bytep data, png_size_t length)
1060 {
1061 if (length > _size - _pos)
1062 {
1063 memcpy(data, &_data[_pos], _size - _pos);
1064 _pos = _size;
1065 }
1066 else
1067 {
1068 memcpy(data, &_data[_pos], length);
1069 _pos += length;
1070 }
1071 }
1072
1073 private:
1074 const unsigned char* _data;
1075 png_size_t _pos;
1076 png_size_t _size;
1077 };
pngReadData(png_structp pngPtr,png_bytep data,png_size_t length)1078 void pngReadData(png_structp pngPtr, png_bytep data, png_size_t length)
1079 {
1080 pngData* a = (pngData*)png_get_io_ptr(pngPtr);
1081 a->read(data, length);
1082 }
1083
LoadFromPngData(const unsigned char * pData,pdf_long dwLen)1084 void PdfImage::LoadFromPngData(const unsigned char* pData, pdf_long dwLen)
1085 {
1086 if( !pData )
1087 {
1088 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1089 }
1090
1091 pngData data(pData, dwLen);
1092 png_byte header[8];
1093 data.read(header, 8);
1094 if( png_sig_cmp(header, 0, 8) )
1095 {
1096 PODOFO_RAISE_ERROR_INFO( ePdfError_UnsupportedImageFormat, "The file could not be recognized as a PNG file." );
1097 }
1098
1099 png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1100 if( !pPng )
1101 {
1102 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1103 }
1104
1105 png_infop pInfo = png_create_info_struct(pPng);
1106 if( !pInfo )
1107 {
1108 png_destroy_read_struct(&pPng, (png_infopp)NULL, (png_infopp)NULL);
1109 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1110 }
1111
1112 if( setjmp(png_jmpbuf(pPng)) )
1113 {
1114 png_destroy_read_struct(&pPng, &pInfo, (png_infopp)NULL);
1115 PODOFO_RAISE_ERROR( ePdfError_InvalidHandle );
1116 }
1117
1118 png_set_read_fn(pPng, (png_voidp)&data, pngReadData);
1119
1120 LoadFromPngContent(pPng, pInfo, this);
1121 }
1122 #endif // PODOFO_HAVE_PNG_LIB
1123
ColorspaceToName(EPdfColorSpace eColorSpace)1124 PdfName PdfImage::ColorspaceToName( EPdfColorSpace eColorSpace )
1125 {
1126 return PdfColor::GetNameForColorSpace( eColorSpace ).GetName();
1127 }
1128
SetImageChromaKeyMask(pdf_int64 r,pdf_int64 g,pdf_int64 b,pdf_int64 threshold)1129 void PdfImage::SetImageChromaKeyMask(pdf_int64 r, pdf_int64 g, pdf_int64 b, pdf_int64 threshold)
1130 {
1131 PdfArray array;
1132 array.push_back(r - threshold);
1133 array.push_back(r + threshold);
1134 array.push_back(g - threshold);
1135 array.push_back(g + threshold);
1136 array.push_back(b - threshold);
1137 array.push_back(b + threshold);
1138
1139 this->GetObject()->GetDictionary().AddKey( "Mask", array);
1140 }
1141
SetInterpolate(bool bValue)1142 void PdfImage::SetInterpolate(bool bValue)
1143 {
1144 this->GetObject()->GetDictionary().AddKey( "Interpolate", PdfVariant(bValue));
1145 }
1146 };
1147