Updating the changelog in the VERSION file, and version_sync.
[shapes.git] / source / multipage.cc
blobaa361d4895ec5eaca3ccff51e63c60b72d3812b2
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 Henrik Tidefelt
19 #include "Shapes_Helpers_decls.h"
21 #include "multipage.h"
22 #include "annotations.h"
23 #include "pdfstructure.h"
24 #include "classtypes.h"
25 #include "pagecontentstates.h"
26 #include "continuations.h"
27 #include "globals.h"
28 #include "shapesexceptions.h"
29 #include "shapescore.h"
31 #include <algorithm>
34 using namespace Shapes;
36 Lang::DocumentDestination::DocumentDestination( bool remote, RefCountPtr< const char > name, int outlineLevel,
37 RefCountPtr< const char > outlineText, bool outlineOpen, bool outlineFontBold, bool outlineFontItalic, const Concrete::RGB & outlineColor )
38 : remote_( remote ), name_( name ), outlineLevel_( outlineLevel ),
39 outlineText_( outlineText ), outlineOpen_( outlineOpen ), outlineFontBold_ ( outlineFontBold ), outlineFontItalic_( outlineFontItalic ), outlineColor_( outlineColor ),
40 target_( NullPtr< const Lang::Drawable2D >( ) )
42 if( ! remote_ )
44 throw Exceptions::InternalError( "The constructor of DocumentDestination requires <remote> to be true." );
46 if( name_ == NullPtr< const char >( ) )
48 throw Exceptions::InternalError( "The constructor remote DocumentDestination requires <name> to be non-null." );
52 Lang::DocumentDestination::DocumentDestination( RefCountPtr< const char > name, int outlineLevel,
53 RefCountPtr< const char > outlineText, bool outlineOpen, bool outlineFontBold, bool outlineFontItalic, const Concrete::RGB & outlineColor,
54 Sides sidesMode, RefCountPtr< const Lang::Drawable2D > target, bool fittobbox, double zoom )
55 : remote_( false ), name_( name ), outlineLevel_( outlineLevel ),
56 outlineText_( outlineText ), outlineOpen_( outlineOpen ), outlineFontBold_ ( outlineFontBold ), outlineFontItalic_( outlineFontItalic ), outlineColor_( outlineColor ),
57 sidesMode_( sidesMode ), target_( target ), fittobbox_( fittobbox ), zoom_( zoom )
58 { }
60 Lang::DocumentDestination::~DocumentDestination( )
61 { }
64 RefCountPtr< const Lang::Class > Lang::DocumentDestination::TypeID( new Lang::SystemFinalClass( strrefdup( "Destination" ) ) );
65 TYPEINFOIMPL( DocumentDestination );
67 RefCountPtr< const Lang::Geometric2D >
68 Lang::DocumentDestination::transformed( const Lang::Transform2D & transform, const RefCountPtr< const Lang::Geometric2D > & self ) const
70 if( remote_ )
72 return self;
75 if( target_ == NullPtr< const Lang::Drawable2D >( ) )
77 return self;
80 return
81 RefCountPtr< const Lang::Geometric2D >
82 ( new Lang::DocumentDestination( name_, outlineLevel_,
83 outlineText_, outlineOpen_, outlineFontBold_, outlineFontItalic_, outlineColor_,
84 sidesMode_, target_->typed_transformed( transform, target_ ), fittobbox_, zoom_ ) );
87 RefCountPtr< const Lang::Geometric3D >
88 Lang::DocumentDestination::to3D( const RefCountPtr< const Lang::Geometric2D > & self ) const
90 throw Exceptions::NotImplemented( "Destinations in 3D." );
94 void
95 Lang::DocumentDestination::gcMark( Kernel::GCMarkedSet & marked )
97 if( target_ != NullPtr< const Lang::Drawable2D >( ) )
99 const_cast< Lang::Drawable2D * >( target_.getPtr( ) )->gcMark( marked );
103 bool
104 Lang::DocumentDestination::definesNamed( ) const
106 return ! remote_ && name_ != NullPtr< const char >( );
109 RefCountPtr< const char >
110 Lang::DocumentDestination::name( ) const
112 return name_;
115 bool
116 Lang::DocumentDestination::isOutlineEntry( ) const
118 return outlineLevel_ >= 0;
121 size_t
122 Lang::DocumentDestination::outlineLevel( ) const
124 return static_cast< size_t >( outlineLevel_ );
127 RefCountPtr< SimplePDF::PDF_Vector >
128 Lang::DocumentDestination::getDirectDestination( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_page ) const
130 RefCountPtr< SimplePDF::PDF_Vector > res( new SimplePDF::PDF_Vector );
131 res->vec.push_back( i_page );
133 Concrete::Coords2D llcorner( 0, 0 );
134 Concrete::Coords2D urcorner( 0, 0 );
135 if( target_ != NullPtr< const Lang::Drawable2D >( ) )
137 RefCountPtr< const Lang::ElementaryPath2D > theBBox = target_->bbox( Lang::Drawable2D::BOUNDING );
138 if( theBBox->empty( ) )
140 throw Exceptions::MiscellaneousRequirement( "The destination target produced an empty bounding box." );
142 theBBox->boundingRectangle( & llcorner, & urcorner );
144 switch( sidesMode_ )
146 case PAGE:
148 if( fittobbox_ )
150 res->vec.push_back( SimplePDF::newName( "FitB" ) );
152 else
154 res->vec.push_back( SimplePDF::newName( "Fit" ) );
157 break;
158 case TOPLEFT:
160 res->vec.push_back( SimplePDF::newName( "XYZ" ) );
161 res->vec.push_back( SimplePDF::newFloat( llcorner.x_.offtype< 1, 0 >( ) ) );
162 res->vec.push_back( SimplePDF::newFloat( urcorner.y_.offtype< 1, 0 >( ) ) );
163 res->vec.push_back( SimplePDF::newFloat( zoom_ ) );
165 break;
166 case TOP:
168 if( fittobbox_ )
170 res->vec.push_back( SimplePDF::newName( "FitH" ) );
172 else
174 res->vec.push_back( SimplePDF::newName( "FitBH" ) );
176 res->vec.push_back( SimplePDF::newFloat( urcorner.y_.offtype< 1, 0 >( ) ) );
178 break;
179 case LEFT:
181 if( fittobbox_ )
183 res->vec.push_back( SimplePDF::newName( "FitV" ) );
185 else
187 res->vec.push_back( SimplePDF::newName( "FitBV" ) );
189 res->vec.push_back( SimplePDF::newFloat( llcorner.x_.offtype< 1, 0 >( ) ) );
191 break;
192 case RECTANGLE:
194 res->vec.push_back( SimplePDF::newName( "FitR" ) );
195 res->vec.push_back( SimplePDF::newFloat( llcorner.x_.offtype< 1, 0 >( ) ) );
196 res->vec.push_back( SimplePDF::newFloat( llcorner.y_.offtype< 1, 0 >( ) ) );
197 res->vec.push_back( SimplePDF::newFloat( urcorner.x_.offtype< 1, 0 >( ) ) );
198 res->vec.push_back( SimplePDF::newFloat( urcorner.y_.offtype< 1, 0 >( ) ) );
200 break;
201 default:
202 throw Exceptions::InternalError( "Destination's sidesMode_ out of range in getDestination." );
205 return res;
208 RefCountPtr< SimplePDF::PDF_Object >
209 Lang::DocumentDestination::getDestination( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_page ) const
211 if( remote_ )
213 return SimplePDF::newString( name_.getPtr( ) );
215 return getDirectDestination( i_page );
218 RefCountPtr< SimplePDF::OutlineItem >
219 Lang::DocumentDestination::getOutlineItem( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_page, RefCountPtr< const char > otherText ) const
221 RefCountPtr< const char > theText = otherText;
222 if( theText == NullPtr< const char >( ) )
224 theText = outlineText_;
227 if( name_ != NullPtr< const char >( ) )
229 return RefCountPtr< SimplePDF::OutlineItem >
230 ( new SimplePDF::OutlineItem( SimplePDF::newString( name_.getPtr( ) ), theText,
231 outlineOpen_, outlineFontBold_, outlineFontItalic_, outlineColor_ ) );
233 return RefCountPtr< SimplePDF::OutlineItem >
234 ( new SimplePDF::OutlineItem( getDestination( i_page ), theText,
235 outlineOpen_, outlineFontBold_, outlineFontItalic_, outlineColor_ ) );
238 Kernel::WarmCatalog::BoundingRectangle::BoundingRectangle( )
239 : xmin_( Concrete::HUGE_LENGTH ), ymin_( Concrete::HUGE_LENGTH ),
240 xmax_( -Concrete::HUGE_LENGTH ), ymax_( -Concrete::HUGE_LENGTH ),
241 modified_( true ), pdfVec_( RefCountPtr< SimplePDF::PDF_Vector >( NullPtr< SimplePDF::PDF_Vector >( ) ) )
244 void
245 Kernel::WarmCatalog::BoundingRectangle::growToContain( const Concrete::Coords2D & ll, const Concrete::Coords2D & ur )
247 xmin_ = std::min( xmin_, ll.x_ );
248 ymin_ = std::min( ymin_, ll.y_ );
249 xmax_ = std::max( xmax_, ur.x_ );
250 ymax_ = std::max( ymax_, ur.y_ );
251 modified_ = true;
254 RefCountPtr< SimplePDF::PDF_Vector >
255 Kernel::WarmCatalog::BoundingRectangle::pdfVector( ) const
257 if( modified_ )
259 modified_ = false;
261 pdfVec_ = RefCountPtr< SimplePDF::PDF_Vector >( new SimplePDF::PDF_Vector( xmin_.offtype< 1, 0 >( ),
262 ymin_.offtype< 1, 0 >( ),
263 xmax_.offtype< 1, 0 >( ),
264 ymax_.offtype< 1, 0 >( ) ) );
265 return pdfVec_;
269 Kernel::WarmCatalog::Page::Page( size_t index, const RefCountPtr< SimplePDF::PDF_Resources > & resources, const RefCountPtr< SimplePDF::PDF_Stream_out > & contents, const RefCountPtr< Kernel::WarmCatalog::BoundingRectangle > & mediabox )
270 : index_( index ), resources_( resources ), contents_( contents ), mediabox_( mediabox )
273 Kernel::WarmCatalog::Page::~Page( )
277 Kernel::WarmCatalog::PageLabelEntry::PageLabelEntry( size_t pageIndex, const RefCountPtr< const char > & prefix, Style style, size_t startNumber )
278 : pageIndex_( pageIndex ), prefix_( prefix ), style_( style ), startNumber_( startNumber )
281 Kernel::WarmCatalog::PageLabelEntry::~PageLabelEntry( )
284 namespace Shapes
286 namespace Kernel
289 template< class T >
290 class Mutator_setbboxgroup : public Lang::CoreFunction
292 public:
293 Mutator_setbboxgroup( const char * title )
294 : CoreFunction( title, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( title ), true ) )
296 formals_->appendEvaluatedCoreFormal( "key", Kernel::THE_VOID_VARIABLE );
298 virtual void
299 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
301 args.applyDefaults( );
303 typedef T StateType;
304 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
305 state->setBBoxGroup( Helpers::down_cast_CoreArgument< const Lang::Symbol >( title_, args, 0, callLoc, true ) );
307 Kernel::ContRef cont = evalState->cont_;
308 cont->takeValue( Lang::THE_VOID,
309 evalState );
313 template< class T >
314 class Mutator_setpagelabel : public Lang::CoreFunction
316 public:
317 Mutator_setpagelabel( const char * title )
318 : CoreFunction( title, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( title ), true ) )
320 formals_->appendEvaluatedCoreFormal( "prefix", Kernel::THE_VOID_VARIABLE );
321 formals_->appendEvaluatedCoreFormal( "style", Kernel::THE_VOID_VARIABLE );
322 formals_->appendEvaluatedCoreFormal( "number", Kernel::THE_VOID_VARIABLE );
324 virtual void
325 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
327 args.applyDefaults( );
329 typedef T StateType;
330 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
332 size_t argsi = 0;
333 typedef const Lang::String PrefixValType;
334 RefCountPtr< PrefixValType > prefixVal = Helpers::down_cast_CoreArgument< PrefixValType >( title_, args, argsi, callLoc, true );
335 RefCountPtr< const char > prefix = state->getNextPagePrefix( );
336 if( prefixVal != NullPtr< PrefixValType >( ) )
338 prefix = prefixVal->val_;
341 ++argsi;
342 typedef const Lang::Symbol StyleType;
343 RefCountPtr< StyleType > style = Helpers::down_cast_CoreArgument< StyleType >( title_, args, argsi, callLoc, true );
345 static Lang::Symbol STYLE_none( "none" );
346 static Lang::Symbol STYLE_decimal( "decimal" );
347 static Lang::Symbol STYLE_ROMAN( "ROMAN" );
348 static Lang::Symbol STYLE_roman( "roman" );
349 static Lang::Symbol STYLE_ALPHABET( "ALPHABET" );
350 static Lang::Symbol STYLE_alphabet( "alphabet" );
352 Kernel::WarmCatalog::PageLabelEntry::Style styleVal;
353 if( style == NullPtr< StyleType >( ) )
355 styleVal = state->getNextPageStyle( );
357 else
359 if( *style == STYLE_none )
361 styleVal = Kernel::WarmCatalog::PageLabelEntry::NONE;
363 else if( *style == STYLE_decimal )
365 styleVal = Kernel::WarmCatalog::PageLabelEntry::DECIMAL;
367 else if( *style == STYLE_ROMAN )
369 styleVal = Kernel::WarmCatalog::PageLabelEntry::ROMAN;
371 else if( *style == STYLE_roman )
373 styleVal = Kernel::WarmCatalog::PageLabelEntry::rOMAN;
375 else if( *style == STYLE_ALPHABET )
377 styleVal = Kernel::WarmCatalog::PageLabelEntry::ALPHABET;
379 else if( *style == STYLE_alphabet )
381 styleVal = Kernel::WarmCatalog::PageLabelEntry::aLPHABET;
383 else
385 std::ostringstream oss;
386 oss << "Valid page label styles are the symbols { "
387 << STYLE_none.name( ).getPtr( ) << ", "
388 << STYLE_decimal.name( ).getPtr( ) << ", "
389 << STYLE_ROMAN.name( ).getPtr( ) << ", "
390 << STYLE_roman.name( ).getPtr( ) << ", "
391 << STYLE_alphabet.name( ).getPtr( ) << ", "
392 << STYLE_ALPHABET.name( ).getPtr( )
393 << " }." ;
394 throw Exceptions::CoreOutOfRange( title_, args, argsi, strrefdup( oss ) );
398 ++argsi;
399 typedef const Lang::Integer NumberType;
400 RefCountPtr< NumberType > numberVal = Helpers::down_cast_CoreArgument< NumberType >( title_, args, argsi, callLoc, true );
401 size_t number;
402 if( numberVal != NullPtr< NumberType >( ) )
404 if( numberVal->val_ < 1 )
406 throw Exceptions::CoreOutOfRange( title_, args, argsi, "PDF only allows strictly positive page numbers." );
408 number = static_cast< size_t >( numberVal->val_ );
410 else
412 number = state->getNextPageNumber( );
415 state->setLabel( prefix, styleVal, number );
417 Kernel::ContRef cont = evalState->cont_;
418 cont->takeHandle( Kernel::THE_VOID_VARIABLE,
419 evalState );
423 template< class T >
424 class Mutator_nextpagelabel : public Lang::CoreFunction
426 public:
427 Mutator_nextpagelabel( const char * title )
428 : CoreFunction( title, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( title ), true ) )
431 virtual void
432 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
434 args.applyDefaults( );
436 typedef T StateType;
437 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
438 Kernel::ContRef cont = evalState->cont_;
439 cont->takeValue( Kernel::ValueRef( new Lang::String( state->getNextPageLabel( ) ) ),
440 evalState );
444 template< class T >
445 class Mutator_nextpagenumber : public Lang::CoreFunction
447 public:
448 Mutator_nextpagenumber( const char * title )
449 : CoreFunction( title, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( title ), true ) )
452 virtual void
453 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
455 args.applyDefaults( );
457 typedef T StateType;
458 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
459 Kernel::ContRef cont = evalState->cont_;
460 cont->takeValue( Kernel::ValueRef( new Lang::Integer( state->getNextPageNumber( ) ) ),
461 evalState );
467 Kernel::WarmCatalog::WarmCatalog( )
468 : pageLabelsActivated_( false ),
469 bboxGroup_( RefCountPtr< const Lang::Symbol >( NullPtr< const Lang::Symbol >( ) ) )
471 labelEntries_.push_back( new Kernel::WarmCatalog::PageLabelEntry( 0, strrefdup( "" ), PageLabelEntry::DECIMAL, 1 ) );
474 Kernel::WarmCatalog::~WarmCatalog( )
477 void
478 WarmCatalog_register_mutators( Lang::SystemFinalClass * dstClass )
480 dstClass->registerMutator( new Kernel::Mutator_setbboxgroup< Kernel::WarmCatalog >( "setbboxgroup" ) );
481 dstClass->registerMutator( new Kernel::Mutator_setpagelabel< Kernel::WarmCatalog >( "setpagelabel" ) );
482 dstClass->registerMutator( new Kernel::Mutator_nextpagelabel< Kernel::WarmCatalog >( "nextpagelabel" ) );
483 dstClass->registerMutator( new Kernel::Mutator_nextpagenumber< Kernel::WarmCatalog >( "nextpagenumber" ) );
486 RefCountPtr< const Lang::Class > Kernel::WarmCatalog::TypeID( new Lang::SystemFinalClass( strrefdup( "#Catalog" ), WarmCatalog_register_mutators ) );
487 TYPEINFOIMPL_STATE( WarmCatalog );
489 void
490 Kernel::WarmCatalog::tackOnImpl( Kernel::EvalState * evalState, const RefCountPtr< const Lang::Value > & piece, const Ast::SourceLocation & callLoc )
492 typedef const Lang::Drawable2D ArgType;
493 RefCountPtr< const ArgType > pageContents( Helpers::down_cast< ArgType >( piece, callLoc ) );
495 tackOnPage( evalState->dyn_, pageContents, callLoc );
497 Kernel::ContRef cont = evalState->cont_;
498 cont->takeHandle( Kernel::THE_VOID_VARIABLE,
499 evalState );
502 void
503 Kernel::WarmCatalog::freezeImpl( Kernel::EvalState * evalState, const Ast::SourceLocation & callLoc )
505 throw Exceptions::MiscellaneousRequirement( strrefdup( "The catalog state cannot be frozen." ) );
508 void
509 Kernel::WarmCatalog::peekImpl( Kernel::EvalState * evalState, const Ast::SourceLocation & callLoc )
511 throw Exceptions::MiscellaneousRequirement( strrefdup( "The catalog state cannot be peeked." ) );
514 void
515 Kernel::WarmCatalog::gcMark( Kernel::GCMarkedSet & marked )
518 void
519 Kernel::WarmCatalog::setLabel( RefCountPtr< const char > prefix, PageLabelEntry::Style style, size_t start )
521 const SimplePDF::PDF_Version::Version PAGELABEL_VERSION = SimplePDF::PDF_Version::PDF_1_3;
522 if( ! Kernel::the_PDF_version.greaterOrEqual( PAGELABEL_VERSION ) )
524 Kernel::the_PDF_version.message( PAGELABEL_VERSION, "The page label setting was ignored." );
525 return;
528 pageLabelsActivated_ = true;
530 if( labelEntries_.back( )->pageIndex_ == pages_.size( ) )
532 delete labelEntries_.back( );
533 labelEntries_.pop_back( );
535 labelEntries_.push_back( new Kernel::WarmCatalog::PageLabelEntry( pages_.size( ), prefix, style, start ) );
538 size_t
539 Kernel::WarmCatalog::getNextPageNumber( ) const
541 const Kernel::WarmCatalog::PageLabelEntry * lastEntry = labelEntries_.back( );
542 return lastEntry->startNumber_ + pages_.size( ) - lastEntry->pageIndex_;
545 Kernel::WarmCatalog::PageLabelEntry::Style
546 Kernel::WarmCatalog::getNextPageStyle( ) const
548 const Kernel::WarmCatalog::PageLabelEntry * lastEntry = labelEntries_.back( );
549 return lastEntry->style_;
552 RefCountPtr< const char >
553 Kernel::WarmCatalog::getNextPagePrefix( ) const
555 const Kernel::WarmCatalog::PageLabelEntry * lastEntry = labelEntries_.back( );
556 return lastEntry->prefix_;
559 RefCountPtr< const char >
560 Kernel::WarmCatalog::getNextPageLabel( ) const
562 return getPageLabel( labelEntries_.back( ), pages_.size( ) );
565 RefCountPtr< const char >
566 Kernel::WarmCatalog::getPageLabel( size_t index ) const
568 typedef typeof labelEntries_ ListType;
569 ListType::const_iterator i = labelEntries_.end( );
570 --i;
571 // Note that labelEntries_.begin( )->pageIndex is 0.
572 while( (*i)->pageIndex_ > index )
574 --i;
576 return getPageLabel( *i, index );
579 RefCountPtr< const char >
580 Kernel::WarmCatalog::getPageLabel( const Kernel::WarmCatalog::PageLabelEntry * entry, size_t index ) const
582 size_t current = entry->startNumber_ + index - entry->pageIndex_;
583 std::ostringstream oss;
584 oss << entry->prefix_.getPtr( ) ;
585 switch( entry->style_ )
587 case PageLabelEntry::NONE:
589 break;
590 case PageLabelEntry::DECIMAL:
592 oss << current ;
594 break;
595 case PageLabelEntry::ROMAN:
597 if( current >= 5000 )
599 throw Exceptions::NotImplemented( "Conversion to roman numerals of numbers greater or equal 5000." );
601 if( current == 0 )
603 throw Exceptions::OutOfRange( "Conversion to roman numeral of the page number 0." );
605 while( current >= 1000 )
607 oss << "M" ;
608 current -= 1000;
610 if( current >= 900 )
612 oss << "CM" ;
613 current -= 900;
615 if( current >= 500 )
617 oss << "D" ;
618 current -= 500;
620 if( current >= 400 )
622 oss << "CD" ;
623 current -= 400;
625 while( current >= 100 )
627 oss << "C" ;
628 current -= 100;
630 if( current >= 90 )
632 oss << "XC" ;
633 current -= 90;
635 if( current >= 50 )
637 oss << "L" ;
638 current -= 50;
640 if( current >= 40 )
642 oss << "XL" ;
643 current -= 40;
645 while( current >= 10 )
647 oss << "X" ;
648 current -= 10;
650 if( current >= 9 )
652 oss << "IX" ;
653 current -= 9;
655 if( current >= 5 )
657 oss << "V" ;
658 current -= 5;
660 if( current >= 4 )
662 oss << "IV" ;
663 current -= 4;
665 while( current >= 1 )
667 oss << "I" ;
668 current -= 1;
671 break;
672 case PageLabelEntry::rOMAN:
674 if( current >= 5000 )
676 throw Exceptions::NotImplemented( "Conversion to roman numerals of numbers greater or equal 5000." );
678 if( current == 0 )
680 throw Exceptions::OutOfRange( "Conversion to roman numeral of the page number 0." );
682 while( current >= 1000 )
684 oss << "m" ;
685 current -= 1000;
687 if( current >= 900 )
689 oss << "cm" ;
690 current -= 900;
692 if( current >= 500 )
694 oss << "d" ;
695 current -= 500;
697 if( current >= 400 )
699 oss << "cd" ;
700 current -= 400;
702 while( current >= 100 )
704 oss << "c" ;
705 current -= 100;
707 if( current >= 90 )
709 oss << "xc" ;
710 current -= 90;
712 if( current >= 50 )
714 oss << "l" ;
715 current -= 50;
717 if( current >= 40 )
719 oss << "xl" ;
720 current -= 40;
722 while( current >= 10 )
724 oss << "x" ;
725 current -= 10;
727 if( current >= 9 )
729 oss << "ix" ;
730 current -= 9;
732 if( current >= 5 )
734 oss << "v" ;
735 current -= 5;
737 if( current >= 4 )
739 oss << "iv" ;
740 current -= 4;
742 while( current >= 1 )
744 oss << "i" ;
745 current -= 1;
748 break;
749 case PageLabelEntry::ALPHABET:
751 size_t base = static_cast< size_t >( 'Z' ) - static_cast< size_t >( 'A' ) + 1;
752 size_t baseFactor = ( current - 1 ) / base ;
753 char rest = 'A' + ( current - baseFactor * base ) - 1;
754 for( size_t i = 0; i < baseFactor + 1; ++i )
756 oss << rest ;
759 break;
760 case PageLabelEntry::aLPHABET:
762 size_t base = static_cast< size_t >( 'Z' ) - static_cast< size_t >( 'A' ) + 1;
763 size_t baseFactor = ( current - 1 ) / base ;
764 char rest = 'a' + ( current - baseFactor * base ) - 1;
765 for( size_t i = 0; i < baseFactor + 1; ++i )
767 oss << rest ;
770 break;
771 default:
772 throw Exceptions::InternalError( "Page label style out of range." );
774 return strrefdup( oss );
777 void
778 Kernel::WarmCatalog::setBBoxGroup( const RefCountPtr< const Lang::Symbol > & group )
780 bboxGroup_ = group;
783 bool
784 Kernel::WarmCatalog::isEmpty( ) const
786 return pages_.empty( );
789 void
790 Kernel::WarmCatalog::tackOnPage( const Kernel::PassedDyn & dyn, const RefCountPtr< const Lang::Drawable2D > & pageContents, const Ast::SourceLocation & callLoc )
792 RefCountPtr< SimplePDF::PDF_Resources > resources( new SimplePDF::PDF_Resources );
793 RefCountPtr< SimplePDF::PDF_Stream_out > contents( new SimplePDF::PDF_Stream_out );
795 resources->requireProcedureSet( SimplePDF::PDF_Resources::PROC_SET_PDF );
797 // Forcing to synch is a bad thing, due to PDF version differences. Instead, refer to the PDF documentation
798 // on the graphics state dictionary (page 180 in the PDF-1.6 reference) to find out the correct default values,
799 // and make sure that these are the initial values of the pdfState.
800 Kernel::PageContentStates pdfState( resources, true );
802 pageContents->shipout( contents->data, & pdfState, Lang::Transform2D( 1, 0, 0, 1, 0, 0 ) );
804 RefCountPtr< const Lang::ElementaryPath2D > theBBox = pageContents->bbox( Lang::Drawable2D::BLEED );
805 Concrete::Coords2D llcorner( 0, 0 );
806 Concrete::Coords2D urcorner( 0, 0 );
807 if( ! theBBox->empty( ) )
809 theBBox->boundingRectangle( & llcorner, & urcorner );
811 RefCountPtr< BoundingRectangle > mediabox = RefCountPtr< BoundingRectangle >( NullPtr< BoundingRectangle >( ) );
812 if( bboxGroup_ == NullPtr< const Lang::Symbol >( ) )
814 if( theBBox->empty( ) )
816 throw Exceptions::InsertingEmptyPage( callLoc );
818 mediabox = RefCountPtr< BoundingRectangle >( );
820 else
822 mediabox = mediaBoxes_[ bboxGroup_->getKey( ) ];
824 if( ! theBBox->empty( ) )
826 mediabox->growToContain( llcorner, urcorner );
828 Page * newPage( new Page( pages_.size( ), resources, contents, mediabox ) );
829 pages_.push_back( newPage );
832 std::vector< Kernel::ValueRef > destinations;
833 pageContents->findTags( & destinations, dyn, Kernel::THE_NAVIGATION_SYMBOL->getKey( ), Lang::THE_2D_IDENTITY );
834 newPage->destinations_.reserve( destinations.size( ) );
835 typedef typeof destinations ListType;
836 for( ListType::const_iterator i = destinations.begin( ); i != destinations.end( ); ++i )
838 typedef const Lang::DocumentDestination ValType;
839 RefCountPtr< ValType > dest = i->down_cast< ValType >( );
840 if( dest == NullPtr< ValType >( ) )
842 throw Exceptions::TypeMismatch( callLoc, "The values tagged for navigations must have a certain type.", (*i)->getTypeName( ), ValType::staticTypeName( ) );
844 newPage->destinations_.push_back( dest );
849 std::vector< Kernel::ValueRef > annotations;
850 pageContents->findTags( & annotations, dyn, Kernel::THE_ANNOTATION_SYMBOL->getKey( ), Lang::THE_2D_IDENTITY );
851 newPage->annotations_.reserve( annotations.size( ) );
852 typedef typeof annotations ListType;
853 for( ListType::const_iterator i = annotations.begin( ); i != annotations.end( ); ++i )
855 typedef const Lang::AnnotationBase ValType;
856 RefCountPtr< ValType > dest = i->down_cast< ValType >( );
857 if( dest == NullPtr< ValType >( ) )
859 throw Exceptions::TypeMismatch( callLoc, "The values tagged for annotation must have a certain type.", (*i)->getTypeName( ), ValType::staticTypeName( ) );
861 newPage->annotations_.push_back( dest );
866 void
867 Kernel::WarmCatalog::shipout( bool split, ShipoutList * docs )
869 RefCountPtr< SimplePDF::PDF_Indirect_out > i_info = Kernel::theDocInfo.getIndirect( & Kernel::theIndirectObjectCount );
871 if( split )
873 for( size_t pageNo = 0; pageNo < pages_.size( ); ++pageNo )
875 docs->push_back( shipoutOne( i_info, pageNo ) );
878 else
880 docs->push_back( shipoutOne( i_info, -1 ) );
885 SimplePDF::PDF_out
886 Kernel::WarmCatalog::shipoutOne( RefCountPtr< SimplePDF::PDF_Indirect_out > i_info, int pageNo )
888 RefCountPtr< SimplePDF::PDF_Dictionary > root = RefCountPtr< SimplePDF::PDF_Dictionary >( new SimplePDF::PDF_Dictionary );
889 RefCountPtr< SimplePDF::PDF_Indirect_out > i_root = SimplePDF::indirect( root, & Kernel::theIndirectObjectCount );
890 root->dic[ "Type" ] = SimplePDF::newName( "Catalog" );
892 std::map< RefCountPtr< const char >, RefCountPtr< SimplePDF::PDF_Vector >, charRefPtrLess > namedDestinations;
893 std::vector< RefCountPtr< SimplePDF::OutlineItem > > outlineStack;
894 outlineStack.reserve( 10 );
895 outlineStack.push_back
896 ( RefCountPtr< SimplePDF::OutlineItem >
897 ( new SimplePDF::OutlineItem( RefCountPtr< SimplePDF::PDF_Object >( NullPtr< SimplePDF::PDF_Object >( ) ), strrefdup( "Top" ),
898 true, false, false, Concrete::RGB( 0, 0, 0 ) ) ) );
900 RefCountPtr< SimplePDF::PDF_Dictionary > pages( new SimplePDF::PDF_Dictionary );
901 RefCountPtr< SimplePDF::PDF_Dictionary > names( new SimplePDF::PDF_Dictionary );
903 RefCountPtr< SimplePDF::PDF_Object > i_pages( SimplePDF::indirect( pages, & Kernel::theIndirectObjectCount ) );
904 root->dic[ "Pages" ] = i_pages;
906 std::list< std::pair< const Page *, std::pair< RefCountPtr< SimplePDF::PDF_Dictionary >, RefCountPtr< SimplePDF::PDF_Indirect_out > > > > annotations;
908 pages->dic[ "Type" ] = SimplePDF::newName( "Pages" );
909 RefCountPtr< SimplePDF::PDF_Vector > pagesKids( new SimplePDF::PDF_Vector );
911 int currentPage = 0;
912 typedef typeof pages_ ListType;
913 for( ListType::const_iterator i = pages_.begin( ); i != pages_.end( ); ++i, ++currentPage )
915 if( pageNo >= 0 && currentPage != pageNo )
917 continue;
919 RefCountPtr< SimplePDF::PDF_Dictionary > newPage( new SimplePDF::PDF_Dictionary );
920 RefCountPtr< SimplePDF::PDF_Indirect_out > i_newPage = SimplePDF::indirect( newPage, & Kernel::theIndirectObjectCount );
921 pagesKids->vec.push_back( i_newPage );
922 newPage->dic[ "Type" ] = SimplePDF::newName( "Page" );
923 newPage->dic[ "Parent" ] = i_pages;
924 newPage->dic[ "MediaBox" ] = (*i)->mediabox_->pdfVector( );
925 newPage->dic[ "Contents" ] = SimplePDF::indirect( (*i)->contents_, & Kernel::theIndirectObjectCount );
926 newPage->dic[ "Resources" ] = SimplePDF::indirect( (*i)->resources_, & Kernel::theIndirectObjectCount );
927 /* The UserUnit entry appears in PDF 1.6, and cannot be relyed on */
928 // newPage->dic[ "UserUnit" ] = RefCountPtr< SimplePDF::PDF_Object >( new SimplePDF::PDF_Float( 72 / 2.52 ) );
930 typedef typeof (*i)->destinations_ DestListType;
931 for( DestListType::const_iterator j = (*i)->destinations_.begin( ); j != (*i)->destinations_.end( ); ++j )
933 RefCountPtr< const Lang::DocumentDestination > dest = *j;
934 if( dest->definesNamed( ) )
936 typedef typeof namedDestinations MapType;
937 RefCountPtr< const char > name = dest->name( );
938 if( namedDestinations.find( name ) != namedDestinations.end( ) )
940 std::ostringstream oss;
941 oss << "The named destination \"" << name.getPtr( ) << "\" appeared a second time (and possibly also the first time) on the page labeled "
942 << getPageLabel( (*i)->index_ ).getPtr( ) << ", with zero-based physical index " << (*i)->index_ << "." ;
943 throw Exceptions::MiscellaneousRequirement( strrefdup( oss ) );
945 namedDestinations.insert( MapType::value_type( name, dest->getDirectDestination( i_newPage ) ) );
948 if( dest->isOutlineEntry( ) )
950 /* This is the index in the stack at which the item belongs.
951 In other words, this shall be the size of outlineStack just before the new item is pushed.
953 const size_t stackLevel = dest->outlineLevel( ) + 1;
955 if( outlineStack.size( ) < stackLevel )
957 RefCountPtr< const char > missingText = strrefdup( " " );
958 while( outlineStack.size( ) < stackLevel )
960 RefCountPtr< SimplePDF::OutlineItem > missingItem = dest->getOutlineItem( i_newPage, missingText );
961 outlineStack.back( )->addKid( missingItem );
962 outlineStack.push_back( missingItem );
965 while( outlineStack.size( ) > stackLevel )
967 outlineStack.pop_back( );
969 RefCountPtr< SimplePDF::OutlineItem > item = dest->getOutlineItem( i_newPage );
970 outlineStack.back( )->addKid( item );
971 outlineStack.push_back( item );
975 if( ! (*i)->annotations_.empty( ) )
977 typedef typeof annotations ListType;
978 annotations.push_back( ListType::value_type( *i, ListType::value_type::second_type( newPage, i_newPage ) ) );
983 pages->dic[ "Kids" ] = pagesKids;
984 pages->dic[ "Count" ] = SimplePDF::newInt( pagesKids->vec.size( ) );
987 typedef std::list< std::pair< const Page *, std::pair< RefCountPtr< SimplePDF::PDF_Dictionary >, RefCountPtr< SimplePDF::PDF_Indirect_out > > > > ListType;
988 for( ListType::const_iterator h = annotations.begin( ); h != annotations.end( ); ++h )
990 const Page * i = h->first;
991 RefCountPtr< SimplePDF::PDF_Dictionary > newPage = h->second.first;
992 RefCountPtr< SimplePDF::PDF_Indirect_out > i_newPage = h->second.second;
993 RefCountPtr< SimplePDF::PDF_Vector > annots( new SimplePDF::PDF_Vector );
994 newPage->dic[ "Annots" ] = SimplePDF::indirect( annots, & Kernel::theIndirectObjectCount );
995 annots->vec.reserve( i->annotations_.size( ) );
997 typedef typeof i->annotations_ AnnotListType;
998 for( AnnotListType::const_iterator j = i->annotations_.begin( ); j != i->annotations_.end( ); ++j )
1000 RefCountPtr< const Lang::AnnotationBase > annot = *j;
1003 annots->vec.push_back( SimplePDF::indirect( annot->getDictionary( i_newPage, namedDestinations ), & Kernel::theIndirectObjectCount ) );
1005 catch( Exceptions::UndefinedCrossRef * ball )
1007 if( pageNo >= 0 )
1009 /* If this is just one page in the document, we expect cross links in the document to be broken, so we just ignore this. */
1010 delete ball;
1012 else
1014 Kernel::thePostCheckErrorsList.push_back( ball );
1022 const SimplePDF::PDF_Version::Version PAGELABELS_VERSION = SimplePDF::PDF_Version::PDF_1_3;
1023 if( pageLabelsActivated_ &&
1024 Kernel::the_PDF_version.greaterOrEqual( PAGELABELS_VERSION ) )
1026 RefCountPtr< SimplePDF::PDF_Dictionary > pageLabels( new SimplePDF::PDF_Dictionary );
1027 root->dic[ "PageLabels" ] = SimplePDF::indirect( pageLabels, & Kernel::theIndirectObjectCount );
1028 pageLabels->dic[ "Type" ] = SimplePDF::newName( "PageLabels" );
1029 RefCountPtr< SimplePDF::PDF_Vector > nums( new SimplePDF::PDF_Vector );
1030 pageLabels->dic[ "Nums" ] = nums;
1031 typedef typeof labelEntries_ ListType;
1032 for( ListType::const_iterator i = labelEntries_.begin( ); i != labelEntries_.end( ); ++i )
1034 if( pageNo >= 0 )
1036 size_t signedPageNo = static_cast< size_t >( pageNo );
1037 ListType::const_iterator next = i;
1038 ++next;
1039 if( next != labelEntries_.end( ) && (*next)->pageIndex_ <= signedPageNo )
1041 continue;
1044 RefCountPtr< SimplePDF::PDF_Dictionary > newEntry( new SimplePDF::PDF_Dictionary );
1045 nums->vec.push_back( SimplePDF::newInt( ( pageNo >= 0 ) ? 0 : (*i)->pageIndex_ ) );
1046 nums->vec.push_back( newEntry );
1048 switch( (*i)->style_ )
1050 case Kernel::WarmCatalog::PageLabelEntry::NONE:
1052 break;
1053 case Kernel::WarmCatalog::PageLabelEntry::DECIMAL:
1055 newEntry->dic[ "S" ] = SimplePDF::newName( "D" );
1057 break;
1058 case Kernel::WarmCatalog::PageLabelEntry::ROMAN:
1060 newEntry->dic[ "S" ] = SimplePDF::newName( "R" );
1062 break;
1063 case Kernel::WarmCatalog::PageLabelEntry::rOMAN:
1065 newEntry->dic[ "S" ] = SimplePDF::newName( "r" );
1067 break;
1068 case Kernel::WarmCatalog::PageLabelEntry::ALPHABET:
1070 newEntry->dic[ "S" ] = SimplePDF::newName( "A" );
1072 break;
1073 case Kernel::WarmCatalog::PageLabelEntry::aLPHABET:
1075 newEntry->dic[ "S" ] = SimplePDF::newName( "a" );
1077 break;
1078 default:
1079 throw Exceptions::InternalError( "Page label style out of range during shipout." );
1082 if( strlen( (*i)->prefix_.getPtr( ) ) > 0 )
1084 newEntry->dic[ "P" ] = SimplePDF::newString( (*i)->prefix_.getPtr( ) );
1087 if( (*i)->startNumber_ != 1 )
1089 newEntry->dic[ "St" ] = SimplePDF::newInt( (*i)->startNumber_ + ( ( pageNo >= 0 ) ? ( pageNo - (*i)->pageIndex_ ) : 0 ) );
1092 if( pageNo >= 0 )
1094 break;
1099 if( outlineStack.front( )->hasKids( ) &&
1100 pageNo == -1 ) /* The outline will just be a mess unless we include all pages in the document. */
1102 root->dic[ "Outlines" ] = outlineStack.front( )->getTopIndirectDictionary( Kernel::the_PDF_version );
1104 if( ! namedDestinations.empty( ) )
1106 // If there are named destinations, the PDF version is already checked to be high enough.
1107 RefCountPtr< SimplePDF::PDF_Dictionary > dests( new SimplePDF::PDF_Dictionary );
1108 names->dic[ "Dests" ] = SimplePDF::indirect( dests, & Kernel::theIndirectObjectCount );
1110 RefCountPtr< SimplePDF::PDF_Vector > names( new SimplePDF::PDF_Vector );
1111 typedef typeof namedDestinations Maptype;
1112 for( Maptype::const_iterator i = namedDestinations.begin( ); i != namedDestinations.end( ); ++i )
1114 RefCountPtr< SimplePDF::PDF_Dictionary > newEntry( new SimplePDF::PDF_Dictionary );
1115 names->vec.push_back( SimplePDF::newString( i->first.getPtr( ) ) );
1116 names->vec.push_back( i->second );
1118 dests->dic[ "Names" ] = names;
1122 if( ! names->dic.empty( ) )
1124 root->dic[ "Names" ] = SimplePDF::indirect( names, & Kernel::theIndirectObjectCount );
1127 return SimplePDF::PDF_out( i_root, i_info, getPageLabel( std::max( 0, pageNo ) ) );