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
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
21 #include "simplepdfo.h"
22 #include "simplepdfi.h"
23 #include "pdfversion.h"
24 #include "shapesexceptions.h"
25 #include "texlabelmanager.h"
26 #include "shapestypes.h"
32 #include <openssl/sha.h>
38 using namespace SimplePDF
;
40 PDF_Resources::PDF_Resources( )
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
)
50 PDF_Resources::~PDF_Resources( )
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
;
94 (*dic
)->dic
[ reference
.str( ) ] = obj
;
95 reverseMap
->insert( ReverseMap::value_type( obj
.getPtr( ), PDF_Name( reference
.str( ) ) ) );
96 i
= reverseMap
->find( obj
.getPtr( ) );
102 SimplePDF::PDF_Resources::nameofXObject( const RefCountPtr
< PDF_Object
> & obj
)
104 return nameof( obj
, & reverse_xobject
, & xobject
, "x", & counter
);
108 SimplePDF::PDF_Resources::nameofGraphicsState( const RefCountPtr
< PDF_Object
> & obj
)
110 return nameof( obj
, & reverse_graphicsStates
, & graphicsStates
, "g", & counter
);
114 SimplePDF::PDF_Resources::nameofColorSpace( const RefCountPtr
< PDF_Object
> & obj
)
116 return nameof( obj
, & reverse_colorSpaces
, & colorSpaces
, "c", & counter
);
120 SimplePDF::PDF_Resources::nameofFont( const RefCountPtr
< PDF_Object
> & obj
)
122 return nameof( obj
, & reverse_fonts
, & fonts
, "f", & counter
);
126 SimplePDF::PDF_Resources::nameofShading( const RefCountPtr
< PDF_Object
> & obj
)
128 return nameof( obj
, & reverse_shadings
, & shadings
, "s", & counter
);
133 SimplePDF::PDF_Resources::requireProcedureSet( ProcSet procSet
)
135 if( procSets
.find( procSet
) == procSets
.end( ) )
140 procSetsVector
->vec
.push_back( SimplePDF::newName( "PDF" ) );
143 procSetsVector
->vec
.push_back( SimplePDF::newName( "Text" ) );
145 case PROC_SET_IMAGE_GRAY
:
146 procSetsVector
->vec
.push_back( SimplePDF::newName( "ImageB" ) );
148 case PROC_SET_IMAGE_COLOR
:
149 procSetsVector
->vec
.push_back( SimplePDF::newName( "ImageC" ) );
151 case PROC_SET_IMAGE_INDEXED
:
152 procSetsVector
->vec
.push_back( SimplePDF::newName( "ImageI" ) );
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" );
170 SimplePDF::DocumentInfo::addExtensionAuthorString( const std::string
& str
)
174 throw "Internal error: DocumentInfo::addExtensionAuthorString: Already finalized.";
176 extensionAuthorStrings
.push_back( str
);
180 SimplePDF::DocumentInfo::addInfo( const char * key
, const RefCountPtr
< PDF_Object
> & datum
)
184 throw "Internal error: DocumentInfo::addInfo: Already finalized.";
187 if( info_
->hasKey( key
) )
191 (*info_
)[ key
] = datum
;
196 RefCountPtr
< SimplePDF::PDF_Indirect_out
>
197 SimplePDF::DocumentInfo::getIndirect( size_t * indirectObjectCounter
)
201 if( ! extensionAuthorStrings
.empty( ) )
203 std::ostringstream oss
;
204 typedef typeof extensionAuthorStrings ListType
;
205 ListType::const_iterator i
= extensionAuthorStrings
.begin( );
208 for( ; i
!= extensionAuthorStrings
.end( ); ++i
)
212 (*info_
)[ "ExtensionAuthors" ] = newString( oss
.str( ).c_str( ) );
215 i_info_
= SimplePDF::indirect( info_
, indirectObjectCounter
);
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
)
225 firstPageLabel_( firstPageLabel
)
228 SimplePDF::PDF_out::~PDF_out( )
231 RefCountPtr
< const char >
232 SimplePDF::PDF_out::getFirstPageLabel( ) const
234 return firstPageLabel_
;
238 /* Helper function that takes a value in the range 0..15 and returns the char
239 * representing its value in hex form.
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
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').
261 SHA256_Init( & ctx
); /* Ignore return value. */
264 /* Add approximate file size. */
265 std::ostringstream oss
;
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. */
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 );
299 RefCountPtr
< PDF_HexString
> res( new PDF_HexString( mdHex
) );
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
;
313 os
<< "%" << pdfVersion
.maxRequestVersionString( ) << endl
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_
;
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( );
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
;
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. */
355 trailer
->writeTo( os
, & my_xref
, trailer
);
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
;
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
;
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
)
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( )
427 SimplePDF::OutlineItem::addKid( const RefCountPtr
< OutlineItem
> & kid
)
429 kids_
.push_back( kid
);
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
;
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
;
476 res
->dic
[ "Count" ] = SimplePDF::newInt( openCount
);
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 ) );
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( );
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
;
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
;
548 dstDic
->dic
[ "Count" ] = SimplePDF::newInt( openCount
);
552 dstDic
->dic
[ "Count" ] = SimplePDF::newInt( -openCount
);
558 return openCount
+ 1;