Update suitable examples and tests to use blank mode
[shapes.git] / source / simplepdfo.cc
blob29e18a78376ad6c5d7702487f6868340fa719959
1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * any later version.
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2008, 2014 Henrik Tidefelt
19 #include <iomanip>
21 #include "simplepdfo.h"
22 #include "simplepdfi.h"
23 #include "pdfversion.h"
24 #include "shapesexceptions.h"
25 #include "texlabelmanager.h"
26 #include "shapestypes.h"
27 #include "globals.h"
28 #include "config.h"
30 #ifdef HAVE_OPENSSL
31 extern "C" {
32 #include <openssl/sha.h>
34 #endif
36 using namespace std;
38 using namespace SimplePDF;
40 PDF_Resources::PDF_Resources( )
41 : counter( 0 ),
42 xobject( new PDF_Dictionary ),
43 graphicsStates( new PDF_Dictionary ),
44 colorSpaces( new PDF_Dictionary ),
45 fonts( new PDF_Dictionary ),
46 shadings( new PDF_Dictionary ),
47 procSetsVector( new PDF_Vector )
48 { }
50 PDF_Resources::~PDF_Resources( )
51 { }
53 void
54 PDF_Resources::writeTo( std::ostream & os, SimplePDF::PDF_xref * xref, const RefCountPtr< const PDF_Object > & self ) const
56 RefCountPtr< PDF_Dictionary > dic( new PDF_Dictionary );
57 if( ! xobject->dic.empty( ) )
59 (*dic)[ "XObject" ] = SimplePDF::indirect( xobject, & Shapes::Kernel::theIndirectObjectCount );
61 if( ! graphicsStates->dic.empty( ) )
63 (*dic)[ "ExtGState" ] = SimplePDF::indirect( graphicsStates, & Shapes::Kernel::theIndirectObjectCount );
65 if( ! colorSpaces->dic.empty( ) )
67 (*dic)[ "ColorSpaces" ] = SimplePDF::indirect( colorSpaces, & Shapes::Kernel::theIndirectObjectCount );
69 if( !fonts->dic.empty( ) )
71 (*dic)[ "Font" ] = SimplePDF::indirect( fonts, & Shapes::Kernel::theIndirectObjectCount );
73 if( ! procSetsVector->vec.empty( ) )
75 (*dic)[ "ProcSet" ] = procSetsVector;
77 if( ! shadings->dic.empty( ) )
79 (*dic)[ "Shading" ] = SimplePDF::indirect( shadings, & Shapes::Kernel::theIndirectObjectCount );
81 dic->writeTo( os, xref, dic );
84 const SimplePDF::PDF_Name &
85 PDF_Resources::nameof( const RefCountPtr< PDF_Object > & obj, ReverseMap * reverseMap, RefCountPtr< PDF_Dictionary > * dic, const char * prefix, size_t * counter )
87 ReverseMap::iterator i = reverseMap->find( obj.getPtr( ) );
88 if( i == reverseMap->end( ) )
90 ostringstream reference;
91 reference << prefix << *counter ;
92 ++*counter;
94 (*dic)->dic[ reference.str( ) ] = obj;
95 reverseMap->insert( ReverseMap::value_type( obj.getPtr( ), PDF_Name( reference.str( ) ) ) );
96 i = reverseMap->find( obj.getPtr( ) );
98 return i->second;
101 const PDF_Name &
102 SimplePDF::PDF_Resources::nameofXObject( const RefCountPtr< PDF_Object > & obj )
104 return nameof( obj, & reverse_xobject, & xobject, "x", & counter );
107 const PDF_Name &
108 SimplePDF::PDF_Resources::nameofGraphicsState( const RefCountPtr< PDF_Object > & obj )
110 return nameof( obj, & reverse_graphicsStates, & graphicsStates, "g", & counter );
113 const PDF_Name &
114 SimplePDF::PDF_Resources::nameofColorSpace( const RefCountPtr< PDF_Object > & obj )
116 return nameof( obj, & reverse_colorSpaces, & colorSpaces, "c", & counter );
119 const PDF_Name &
120 SimplePDF::PDF_Resources::nameofFont( const RefCountPtr< PDF_Object > & obj )
122 return nameof( obj, & reverse_fonts, & fonts, "f", & counter );
125 const PDF_Name &
126 SimplePDF::PDF_Resources::nameofShading( const RefCountPtr< PDF_Object > & obj )
128 return nameof( obj, & reverse_shadings, & shadings, "s", & counter );
132 void
133 SimplePDF::PDF_Resources::requireProcedureSet( ProcSet procSet )
135 if( procSets.find( procSet ) == procSets.end( ) )
137 switch( procSet )
139 case PROC_SET_PDF:
140 procSetsVector->vec.push_back( SimplePDF::newName( "PDF" ) );
141 break;
142 case PROC_SET_TEXT:
143 procSetsVector->vec.push_back( SimplePDF::newName( "Text" ) );
144 break;
145 case PROC_SET_IMAGE_GRAY:
146 procSetsVector->vec.push_back( SimplePDF::newName( "ImageB" ) );
147 break;
148 case PROC_SET_IMAGE_COLOR:
149 procSetsVector->vec.push_back( SimplePDF::newName( "ImageC" ) );
150 break;
151 case PROC_SET_IMAGE_INDEXED:
152 procSetsVector->vec.push_back( SimplePDF::newName( "ImageI" ) );
153 break;
154 default:
155 throw "SimplePDF::PDF_Resources: Internal error: An unexpected procedure set was requested.";
157 procSets.insert( procSets.begin( ), procSet );
161 SimplePDF::DocumentInfo::DocumentInfo( )
162 : finalized_( false ),
163 info_( new PDF_Dictionary ),
164 i_info_( NullPtr< PDF_Indirect_out >( ) )
166 (*info_)[ "Producer" ] = newString( "Shapes" );
169 void
170 SimplePDF::DocumentInfo::addExtensionAuthorString( const std::string & str )
172 if( finalized_ )
174 throw "Internal error: DocumentInfo::addExtensionAuthorString: Already finalized.";
176 extensionAuthorStrings.push_back( str );
179 bool
180 SimplePDF::DocumentInfo::addInfo( const char * key, const RefCountPtr< PDF_Object > & datum )
182 if( finalized_ )
184 throw "Internal error: DocumentInfo::addInfo: Already finalized.";
187 if( info_->hasKey( key ) )
189 return false;
191 (*info_)[ key ] = datum;
192 return true;
196 RefCountPtr< SimplePDF::PDF_Indirect_out >
197 SimplePDF::DocumentInfo::getIndirect( size_t * indirectObjectCounter )
199 if( ! finalized_ )
201 if( ! extensionAuthorStrings.empty( ) )
203 std::ostringstream oss;
204 typedef typeof extensionAuthorStrings ListType;
205 ListType::const_iterator i = extensionAuthorStrings.begin( );
206 oss << *i ;
207 ++i;
208 for( ; i != extensionAuthorStrings.end( ); ++i )
210 oss << "; " << *i ;
212 (*info_)[ "ExtensionAuthors" ] = newString( oss.str( ).c_str( ) );
214 finalized_ = true;
215 i_info_ = SimplePDF::indirect( info_, indirectObjectCounter );
217 return i_info_;
222 SimplePDF::PDF_out::PDF_out( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_root, const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_info, const RefCountPtr< const char > & firstPageLabel )
223 : i_root_( i_root ),
224 i_info_( i_info ),
225 firstPageLabel_( firstPageLabel )
228 SimplePDF::PDF_out::~PDF_out( )
231 RefCountPtr< const char >
232 SimplePDF::PDF_out::getFirstPageLabel( ) const
234 return firstPageLabel_;
237 #ifdef HAVE_OPENSSL
238 /* Helper function that takes a value in the range 0..15 and returns the char
239 * representing its value in hex form.
241 char
242 hexChar(unsigned char val)
244 return ( val < 10 ) ? ( val + '0' ) : ( val + ( 'A' - 10 ) );
247 RefCountPtr< PDF_HexString >
248 SimplePDF::PDF_out::fileIdentifier( std::streampos osp ) const
250 /* I first checked the return values from the SHA256_* functions here,
251 * but then I had a look at crypto/sha/sha256.c in the OpenSSL sources
252 * and found that the return values are ignored in their code. In addition
253 * to that, I couldn't find any documentation of what those returned ints
254 * are supposed to mean, so I decided to do the OpenSSL thing and just
255 * ignore them.
257 * To be able to verify that the hash, the information we add to the hash is
258 * separated by UNIX style line endings ('\n').
260 SHA256_CTX ctx;
261 SHA256_Init( & ctx ); /* Ignore return value. */
264 /* Add approximate file size. */
265 std::ostringstream oss;
266 oss << osp << "\n" ;
267 std::string data = oss.str( );
268 SHA256_Update( & ctx, static_cast< const void * >( data.c_str( ) ), data.size( ) ); /* Ignore return value. */
272 /* Add information from Info dict. */
273 RefCountPtr< PDF_Dictionary > info = i_info_->obj.down_cast< PDF_Dictionary >( );
274 if( info == NullPtr< PDF_Dictionary >( ) )
275 throw "Failed to read Info dictionary.";
276 for( PDF_Dictionary::DicType::const_iterator i = info->dic.begin( ); i != info->dic.end( ); ++i ){
277 RefCountPtr< PDF_LiteralString > val = i->second.down_cast< PDF_LiteralString >( );
278 if( val != NullPtr< PDF_LiteralString >( ) ){
279 std::string data = val->str( ) + "\n";
280 SHA256_Update( & ctx, static_cast< const void * >( data.c_str( ) ), data.size( ) ); /* Ignore return value. */
285 unsigned char * md = new unsigned char[ ctx.md_len ];
286 RefCountPtr< unsigned char > mdMem( md ); /* Take care of deallocation. */
287 SHA256_Final( md, & ctx ); /* Ignore return value. */
289 char * mdHex = new char[ 2 * ctx.md_len + 1 ];
290 RefCountPtr< char > mdHexMem( mdHex ); /* Take care of deallocation. */
291 char * dst = mdHex;
292 const unsigned char * end = md + ctx.md_len;
293 for( const unsigned char * src = md; src != end; ++src ){
294 *dst++ = hexChar( (*src) >> 4 );
295 *dst++ = hexChar( (*src) & 0x0F );
297 *dst = '\0';
299 RefCountPtr< PDF_HexString > res( new PDF_HexString( mdHex ) );
300 return res;
302 #endif
304 void
305 SimplePDF::PDF_out::writeFile( std::ostream & os, SimplePDF::PDF_Version & pdfVersion ) const
307 std::streamoff os_start = static_cast< streamoff >( os.tellp( ) );
308 SimplePDF::PDF_xref my_xref;
312 os << std::fixed ;
313 os << "%" << pdfVersion.maxRequestVersionString( ) << endl
314 << "%"
315 << static_cast< char >( 129 ) << static_cast< char >( 130 )
316 << static_cast< char >( 131 ) << static_cast< char >( 132 )
317 << " Treat as binary" << endl ;
319 my_xref.enqueue( i_root_ );
320 my_xref.enqueue( i_info_ );
321 my_xref.writeRecursive( os );
323 streamoff xref( static_cast< streamoff >( os.tellp( ) ) );
324 my_xref.writeTable( os );
326 os << "trailer" << endl ;
327 RefCountPtr< PDF_Dictionary > trailer( new PDF_Dictionary );
328 trailer->dic[ "Size" ] = newInt( my_xref.size( ) );
329 trailer->dic[ "Root" ] = i_root_;
330 trailer->dic[ "Info" ] = i_info_;
331 #ifdef HAVE_OPENSSL
332 RefCountPtr< PDF_HexString > fileID = fileIdentifier( os.tellp( ) ); /* This is an approximation of the final file size. */
333 RefCountPtr< PDF_Vector > id( new PDF_Vector );
334 id->vec.push_back( fileID ); /* Permanent identifier. */
335 id->vec.push_back( fileID ); /* Changing identifier, identical to permanent identifier since this is the initial version. */
336 trailer->dic[ "ID" ] = id;
337 std::streampos trailerpos = os.tellp( );
338 #endif
339 trailer->writeTo( os, & my_xref, trailer ); /* This will not affect the xref, since the indirect objects were put in queue before calling writeTable. */
341 os << endl << "startxref" << endl ;
342 os << xref - os_start << endl ;
343 os << "%%EOF" << endl ;
345 #ifdef HAVE_OPENSSL
346 /* Now that the file size is known, we try to go back and overwrite the file trailer.
347 * We know that just changing the information that goes into the ID will not change
348 * the size of the trailer.
350 fileID = fileIdentifier( os.tellp( ) );
351 id->vec[ 0 ] = fileID;
352 id->vec[ 1 ] = fileID;
353 os.seekp( trailerpos ); /* If this fails, we're done. */
354 if( os.good( ) ){
355 trailer->writeTo( os, & my_xref, trailer );
357 #endif
359 catch( const char * ball )
361 std::ostringstream msg;
362 msg << "SimplePDF::PDF_out::writeFile: Caught (char*) ball at top level:" << endl
363 << " " << ball << endl ;
364 throw msg.str( );
366 catch( const string & ball )
368 std::ostringstream msg;
369 msg << "SimplePDF::PDF_out::writeFile: Caught (string) ball at top level:" << endl
370 << " " << ball << endl ;
371 throw msg.str( );
373 catch( ... )
375 throw "SimplePDF::PDF_out::writeFile: Caught (...) ball at top level.";
380 RefCountPtr<PDF_Object>
381 SimplePDF::newName( const char * str )
383 return RefCountPtr<PDF_Name>( new PDF_Name( str ) );
386 RefCountPtr<PDF_Object>
387 SimplePDF::newString( const char * str )
389 return RefCountPtr< PDF_String >( new PDF_LiteralString( str ) );
392 RefCountPtr<PDF_Object>
393 SimplePDF::newInt( PDF_Int::ValueType val )
395 return RefCountPtr<PDF_Int>( new PDF_Int( val ) );
398 RefCountPtr<PDF_Object>
399 SimplePDF::newBoolean( PDF_Boolean::ValueType val )
401 if( val )
403 return theTrue;
405 return theFalse;
408 RefCountPtr<PDF_Object>
409 SimplePDF::newFloat( PDF_Float::ValueType val )
411 return RefCountPtr<PDF_Float>( new PDF_Float( val ) );
415 RefCountPtr<PDF_Object> SimplePDF::theTrue( new PDF_Boolean( true ) );
416 RefCountPtr<PDF_Object> SimplePDF::theFalse( new PDF_Boolean( false ) );
419 SimplePDF::OutlineItem::OutlineItem( const RefCountPtr< PDF_Object > & destination, const RefCountPtr< const char > & title, bool isOpen, bool fontBold, bool fontItalic, const Shapes::Concrete::RGB & color )
420 : destination_( destination ), title_( title ), isOpen_( isOpen ), fontBold_( fontBold ), fontItalic_( fontItalic ), color_( color )
423 SimplePDF::OutlineItem::~OutlineItem( )
426 void
427 SimplePDF::OutlineItem::addKid( const RefCountPtr< OutlineItem > & kid )
429 kids_.push_back( kid );
432 bool
433 SimplePDF::OutlineItem::hasKids( ) const
435 return ! kids_.empty( );
438 RefCountPtr< SimplePDF::PDF_Indirect_out >
439 SimplePDF::OutlineItem::getTopIndirectDictionary( SimplePDF::PDF_Version & pdfVersion ) const
441 RefCountPtr< SimplePDF::PDF_Dictionary > res( new SimplePDF::PDF_Dictionary );
442 RefCountPtr< SimplePDF::PDF_Indirect_out > i_res = SimplePDF::indirect( res, & Shapes::Kernel::theIndirectObjectCount );
443 res->dic[ "Type" ] = SimplePDF::newName( "Outlines" );
445 if( ! kids_.empty( ) )
447 size_t openCount = 0;
449 typedef typeof kids_ ListType;
450 ListType::const_iterator i = kids_.begin( );
452 RefCountPtr< SimplePDF::PDF_Dictionary > newKid( new SimplePDF::PDF_Dictionary );
453 newKid->dic[ "Parent" ] = i_res;
454 RefCountPtr< SimplePDF::PDF_Indirect_out > i_newKid = SimplePDF::indirect( newKid, & Shapes::Kernel::theIndirectObjectCount );
455 openCount += (*i)->fillInDictionary( newKid, i_newKid, pdfVersion );
457 RefCountPtr< SimplePDF::PDF_Indirect_out > i_first = i_newKid;
459 ++i;
460 for( ; i != kids_.end( ); ++i )
462 RefCountPtr< SimplePDF::PDF_Dictionary > lastKid = newKid;
463 newKid = RefCountPtr< SimplePDF::PDF_Dictionary >( new SimplePDF::PDF_Dictionary );
464 newKid->dic[ "Prev" ] = i_newKid;
465 i_newKid = SimplePDF::indirect( newKid, & Shapes::Kernel::theIndirectObjectCount );
466 lastKid->dic[ "Next" ] = i_newKid;
467 newKid->dic[ "Parent" ] = i_res;
468 openCount += (*i)->fillInDictionary( newKid, i_newKid, pdfVersion );
471 res->dic[ "First" ] = i_first;
472 res->dic[ "Last" ] = i_newKid;
474 if( openCount > 0 )
476 res->dic[ "Count" ] = SimplePDF::newInt( openCount );
480 return i_res;
483 size_t
484 SimplePDF::OutlineItem::fillInDictionary( RefCountPtr< SimplePDF::PDF_Dictionary > dstDic, const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_dstDic, SimplePDF::PDF_Version & pdfVersion ) const
486 dstDic->dic[ "Title" ] = SimplePDF::newString( title_.getPtr( ) );
487 dstDic->dic[ "Dest" ] = destination_;
488 const SimplePDF::PDF_Version::Version FANCY_OUTLINE_VERSION = SimplePDF::PDF_Version::PDF_1_3;
489 if( fontBold_ || fontItalic_ )
491 if( pdfVersion.greaterOrEqual( FANCY_OUTLINE_VERSION ) )
493 dstDic->dic[ "F" ] = SimplePDF::newInt( ( fontBold_ ? 2 : 0 ) + ( fontItalic_ ? 1 : 0 ) );
495 else
497 pdfVersion.message( FANCY_OUTLINE_VERSION, "The outline item font flags were ignored." );
500 if( color_.mean( ) > 0 )
502 if( pdfVersion.greaterOrEqual( FANCY_OUTLINE_VERSION ) )
504 dstDic->dic[ "C" ] = color_.componentVector( );
506 else
508 pdfVersion.message( FANCY_OUTLINE_VERSION, "The outline item color was ignored." );
513 size_t openCount = 0;
515 if( ! kids_.empty( ) )
517 typedef typeof kids_ ListType;
518 ListType::const_iterator i = kids_.begin( );
520 RefCountPtr< SimplePDF::PDF_Dictionary > newKid( new SimplePDF::PDF_Dictionary );
521 newKid->dic[ "Parent" ] = i_dstDic;
522 RefCountPtr< SimplePDF::PDF_Indirect_out > i_newKid = SimplePDF::indirect( newKid, & Shapes::Kernel::theIndirectObjectCount );
523 openCount += (*i)->fillInDictionary( newKid, i_newKid, pdfVersion );
525 RefCountPtr< SimplePDF::PDF_Indirect_out > i_first = i_newKid;
527 ++i;
528 for( ; i != kids_.end( ); ++i )
530 RefCountPtr< SimplePDF::PDF_Dictionary > lastKid = newKid;
531 newKid = RefCountPtr< SimplePDF::PDF_Dictionary >( new SimplePDF::PDF_Dictionary );
532 newKid->dic[ "Prev" ] = i_newKid;
533 i_newKid = SimplePDF::indirect( newKid, & Shapes::Kernel::theIndirectObjectCount );
534 lastKid->dic[ "Next" ] = i_newKid;
535 newKid->dic[ "Parent" ] = i_dstDic;
536 openCount += (*i)->fillInDictionary( newKid, i_newKid, pdfVersion );
539 dstDic->dic[ "First" ] = i_first;
540 dstDic->dic[ "Last" ] = i_newKid;
544 if( openCount > 0 )
546 if( isOpen_ )
548 dstDic->dic[ "Count" ] = SimplePDF::newInt( openCount );
550 else
552 dstDic->dic[ "Count" ] = SimplePDF::newInt( -openCount );
556 if( isOpen_ )
558 return openCount + 1;
560 return 1;