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