Mutators for Catalog states.
[shapes.git] / source / multipage.cc
bloba6885df3c098ba81561d2560d0d08b1cc007669d
1 #include "Shapes_Helpers_decls.h"
3 #include "multipage.h"
4 #include "annotations.h"
5 #include "pdfstructure.h"
6 #include "classtypes.h"
7 #include "pagecontentstates.h"
8 #include "continuations.h"
9 #include "globals.h"
10 #include "shapesexceptions.h"
11 #include "shapescore.h"
13 #include <algorithm>
16 using namespace Shapes;
18 Lang::DocumentDestination::DocumentDestination( bool remote, RefCountPtr< const char > name, int outlineLevel,
19 RefCountPtr< const char > outlineText, bool outlineOpen, bool outlineFontBold, bool outlineFontItalic, const Concrete::RGB & outlineColor )
20 : remote_( remote ), name_( name ), outlineLevel_( outlineLevel ),
21 outlineText_( outlineText ), outlineOpen_( outlineOpen ), outlineFontBold_ ( outlineFontBold ), outlineFontItalic_( outlineFontItalic ), outlineColor_( outlineColor ),
22 target_( NullPtr< const Lang::Drawable2D >( ) )
24 if( ! remote_ )
26 throw Exceptions::InternalError( "The constructor of DocumentDestination requires <remote> to be true." );
28 if( name_ == NullPtr< const char >( ) )
30 throw Exceptions::InternalError( "The constructor remote DocumentDestination requires <name> to be non-null." );
34 Lang::DocumentDestination::DocumentDestination( RefCountPtr< const char > name, int outlineLevel,
35 RefCountPtr< const char > outlineText, bool outlineOpen, bool outlineFontBold, bool outlineFontItalic, const Concrete::RGB & outlineColor,
36 Sides sidesMode, RefCountPtr< const Lang::Drawable2D > target, bool fittobbox, double zoom )
37 : remote_( false ), name_( name ), outlineLevel_( outlineLevel ),
38 outlineText_( outlineText ), outlineOpen_( outlineOpen ), outlineFontBold_ ( outlineFontBold ), outlineFontItalic_( outlineFontItalic ), outlineColor_( outlineColor ),
39 sidesMode_( sidesMode ), target_( target ), fittobbox_( fittobbox ), zoom_( zoom )
40 { }
42 Lang::DocumentDestination::~DocumentDestination( )
43 { }
46 RefCountPtr< const Lang::Class > Lang::DocumentDestination::TypeID( new Lang::SystemFinalClass( strrefdup( "Destination" ) ) );
47 TYPEINFOIMPL( DocumentDestination );
49 RefCountPtr< const Lang::Geometric2D >
50 Lang::DocumentDestination::transformed( const Lang::Transform2D & transform, const RefCountPtr< const Lang::Geometric2D > & self ) const
52 if( remote_ )
54 return self;
57 if( target_ == NullPtr< const Lang::Drawable2D >( ) )
59 return self;
62 return
63 RefCountPtr< const Lang::Geometric2D >
64 ( new Lang::DocumentDestination( name_, outlineLevel_,
65 outlineText_, outlineOpen_, outlineFontBold_, outlineFontItalic_, outlineColor_,
66 sidesMode_, target_->typed_transformed( transform, target_ ), fittobbox_, zoom_ ) );
69 RefCountPtr< const Lang::Geometric3D >
70 Lang::DocumentDestination::to3D( const RefCountPtr< const Lang::Geometric2D > & self ) const
72 throw Exceptions::NotImplemented( "Destinations in 3D." );
76 void
77 Lang::DocumentDestination::gcMark( Kernel::GCMarkedSet & marked )
79 if( target_ != NullPtr< const Lang::Drawable2D >( ) )
81 const_cast< Lang::Drawable2D * >( target_.getPtr( ) )->gcMark( marked );
85 bool
86 Lang::DocumentDestination::definesNamed( ) const
88 return ! remote_ && name_ != NullPtr< const char >( );
91 RefCountPtr< const char >
92 Lang::DocumentDestination::name( ) const
94 return name_;
97 bool
98 Lang::DocumentDestination::isOutlineEntry( ) const
100 return outlineLevel_ >= 0;
103 size_t
104 Lang::DocumentDestination::outlineLevel( ) const
106 return static_cast< size_t >( outlineLevel_ );
109 RefCountPtr< SimplePDF::PDF_Vector >
110 Lang::DocumentDestination::getDirectDestination( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_page ) const
112 RefCountPtr< SimplePDF::PDF_Vector > res( new SimplePDF::PDF_Vector );
113 res->vec.push_back( i_page );
115 Concrete::Coords2D llcorner( 0, 0 );
116 Concrete::Coords2D urcorner( 0, 0 );
117 if( target_ != NullPtr< const Lang::Drawable2D >( ) )
119 RefCountPtr< const Lang::ElementaryPath2D > theBBox = target_->bbox( );
120 if( theBBox->empty( ) )
122 throw Exceptions::MiscellaneousRequirement( "The destination target produced an empty bounding box." );
124 theBBox->boundingRectangle( & llcorner, & urcorner );
126 switch( sidesMode_ )
128 case PAGE:
130 if( fittobbox_ )
132 res->vec.push_back( SimplePDF::newName( "FitB" ) );
134 else
136 res->vec.push_back( SimplePDF::newName( "Fit" ) );
139 break;
140 case TOPLEFT:
142 res->vec.push_back( SimplePDF::newName( "XYZ" ) );
143 res->vec.push_back( SimplePDF::newFloat( llcorner.x_.offtype< 1, 0 >( ) ) );
144 res->vec.push_back( SimplePDF::newFloat( urcorner.y_.offtype< 1, 0 >( ) ) );
145 res->vec.push_back( SimplePDF::newFloat( zoom_ ) );
147 break;
148 case TOP:
150 if( fittobbox_ )
152 res->vec.push_back( SimplePDF::newName( "FitH" ) );
154 else
156 res->vec.push_back( SimplePDF::newName( "FitBH" ) );
158 res->vec.push_back( SimplePDF::newFloat( urcorner.y_.offtype< 1, 0 >( ) ) );
160 break;
161 case LEFT:
163 if( fittobbox_ )
165 res->vec.push_back( SimplePDF::newName( "FitV" ) );
167 else
169 res->vec.push_back( SimplePDF::newName( "FitBV" ) );
171 res->vec.push_back( SimplePDF::newFloat( llcorner.x_.offtype< 1, 0 >( ) ) );
173 break;
174 case RECTANGLE:
176 res->vec.push_back( SimplePDF::newName( "FitR" ) );
177 res->vec.push_back( SimplePDF::newFloat( llcorner.x_.offtype< 1, 0 >( ) ) );
178 res->vec.push_back( SimplePDF::newFloat( llcorner.y_.offtype< 1, 0 >( ) ) );
179 res->vec.push_back( SimplePDF::newFloat( urcorner.x_.offtype< 1, 0 >( ) ) );
180 res->vec.push_back( SimplePDF::newFloat( urcorner.y_.offtype< 1, 0 >( ) ) );
182 break;
183 default:
184 throw Exceptions::InternalError( "Destination's sidesMode_ out of range in getDestination." );
187 return res;
190 RefCountPtr< SimplePDF::PDF_Object >
191 Lang::DocumentDestination::getDestination( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_page ) const
193 if( remote_ )
195 return SimplePDF::newString( name_.getPtr( ) );
197 return getDirectDestination( i_page );
200 RefCountPtr< SimplePDF::OutlineItem >
201 Lang::DocumentDestination::getOutlineItem( const RefCountPtr< SimplePDF::PDF_Indirect_out > & i_page, RefCountPtr< const char > otherText ) const
203 RefCountPtr< const char > theText = otherText;
204 if( theText == NullPtr< const char >( ) )
206 theText = outlineText_;
209 if( name_ != NullPtr< const char >( ) )
211 return RefCountPtr< SimplePDF::OutlineItem >
212 ( new SimplePDF::OutlineItem( SimplePDF::newString( name_.getPtr( ) ), theText,
213 outlineOpen_, outlineFontBold_, outlineFontItalic_, outlineColor_ ) );
215 return RefCountPtr< SimplePDF::OutlineItem >
216 ( new SimplePDF::OutlineItem( getDestination( i_page ), theText,
217 outlineOpen_, outlineFontBold_, outlineFontItalic_, outlineColor_ ) );
220 Kernel::WarmCatalog::BoundingRectangle::BoundingRectangle( )
221 : xmin_( Concrete::HUGE_LENGTH ), ymin_( Concrete::HUGE_LENGTH ),
222 xmax_( -Concrete::HUGE_LENGTH ), ymax_( -Concrete::HUGE_LENGTH ),
223 modified_( true ), pdfVec_( RefCountPtr< SimplePDF::PDF_Vector >( NullPtr< SimplePDF::PDF_Vector >( ) ) )
226 void
227 Kernel::WarmCatalog::BoundingRectangle::growToContain( const Concrete::Coords2D & ll, const Concrete::Coords2D & ur )
229 xmin_ = std::min( xmin_, ll.x_ );
230 ymin_ = std::min( ymin_, ll.y_ );
231 xmax_ = std::max( xmax_, ur.x_ );
232 ymax_ = std::max( ymax_, ur.y_ );
233 modified_ = true;
236 RefCountPtr< SimplePDF::PDF_Vector >
237 Kernel::WarmCatalog::BoundingRectangle::pdfVector( ) const
239 if( modified_ )
241 modified_ = false;
243 pdfVec_ = RefCountPtr< SimplePDF::PDF_Vector >( new SimplePDF::PDF_Vector( xmin_.offtype< 1, 0 >( ),
244 ymin_.offtype< 1, 0 >( ),
245 xmax_.offtype< 1, 0 >( ),
246 ymax_.offtype< 1, 0 >( ) ) );
247 return pdfVec_;
251 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 )
252 : index_( index ), resources_( resources ), contents_( contents ), mediabox_( mediabox )
255 Kernel::WarmCatalog::Page::~Page( )
259 Kernel::WarmCatalog::PageLabelEntry::PageLabelEntry( size_t pageIndex, const RefCountPtr< const char > & prefix, Style style, size_t startNumber )
260 : pageIndex_( pageIndex ), prefix_( prefix ), style_( style ), startNumber_( startNumber )
263 Kernel::WarmCatalog::PageLabelEntry::~PageLabelEntry( )
266 namespace Shapes
268 namespace Kernel
271 template< class T >
272 class Mutator_setbboxgroup : public Lang::CoreFunction
274 public:
275 Mutator_setbboxgroup( const char * title )
276 : CoreFunction( title, new Kernel::EvaluatedFormals( title, true ) )
278 formals_->appendEvaluatedCoreFormal( "key", Kernel::THE_VOID_VARIABLE );
280 virtual void
281 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
283 args.applyDefaults( );
285 typedef T StateType;
286 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
287 state->setBBoxGroup( Helpers::down_cast_CoreArgument< const Lang::Symbol >( title_, args, 0, callLoc, true ) );
289 Kernel::ContRef cont = evalState->cont_;
290 cont->takeValue( Lang::THE_VOID,
291 evalState );
295 template< class T >
296 class Mutator_setpagelabel : public Lang::CoreFunction
298 public:
299 Mutator_setpagelabel( const char * title )
300 : CoreFunction( title, new Kernel::EvaluatedFormals( title, true ) )
302 formals_->appendEvaluatedCoreFormal( "prefix", Kernel::THE_VOID_VARIABLE );
303 formals_->appendEvaluatedCoreFormal( "style", Kernel::THE_VOID_VARIABLE );
304 formals_->appendEvaluatedCoreFormal( "number", Kernel::THE_VOID_VARIABLE );
306 virtual void
307 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
309 args.applyDefaults( );
311 typedef T StateType;
312 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
314 size_t argsi = 0;
315 typedef const Lang::String PrefixValType;
316 RefCountPtr< PrefixValType > prefixVal = Helpers::down_cast_CoreArgument< PrefixValType >( title_, args, argsi, callLoc, true );
317 RefCountPtr< const char > prefix = state->getNextPagePrefix( );
318 if( prefixVal != NullPtr< PrefixValType >( ) )
320 prefix = prefixVal->val_;
323 ++argsi;
324 typedef const Lang::Symbol StyleType;
325 RefCountPtr< StyleType > style = Helpers::down_cast_CoreArgument< StyleType >( title_, args, argsi, callLoc, true );
327 static Lang::Symbol STYLE_none( "none" );
328 static Lang::Symbol STYLE_decimal( "decimal" );
329 static Lang::Symbol STYLE_ROMAN( "ROMAN" );
330 static Lang::Symbol STYLE_roman( "roman" );
331 static Lang::Symbol STYLE_ALPHABET( "ALPHABET" );
332 static Lang::Symbol STYLE_alphabet( "alphabet" );
334 Kernel::WarmCatalog::PageLabelEntry::Style styleVal;
335 if( style == NullPtr< StyleType >( ) )
337 styleVal = state->getNextPageStyle( );
339 else
341 if( *style == STYLE_none )
343 styleVal = Kernel::WarmCatalog::PageLabelEntry::NONE;
345 else if( *style == STYLE_decimal )
347 styleVal = Kernel::WarmCatalog::PageLabelEntry::DECIMAL;
349 else if( *style == STYLE_ROMAN )
351 styleVal = Kernel::WarmCatalog::PageLabelEntry::ROMAN;
353 else if( *style == STYLE_roman )
355 styleVal = Kernel::WarmCatalog::PageLabelEntry::rOMAN;
357 else if( *style == STYLE_ALPHABET )
359 styleVal = Kernel::WarmCatalog::PageLabelEntry::ALPHABET;
361 else if( *style == STYLE_alphabet )
363 styleVal = Kernel::WarmCatalog::PageLabelEntry::aLPHABET;
365 else
367 std::ostringstream oss;
368 oss << "Valid page label styles are the symbols { "
369 << STYLE_none.name( ).getPtr( ) << ", "
370 << STYLE_decimal.name( ).getPtr( ) << ", "
371 << STYLE_ROMAN.name( ).getPtr( ) << ", "
372 << STYLE_roman.name( ).getPtr( ) << ", "
373 << STYLE_alphabet.name( ).getPtr( ) << ", "
374 << STYLE_ALPHABET.name( ).getPtr( )
375 << " }." ;
376 throw Exceptions::CoreOutOfRange( title_, args, argsi, strrefdup( oss ) );
380 ++argsi;
381 typedef const Lang::Integer NumberType;
382 RefCountPtr< NumberType > numberVal = Helpers::down_cast_CoreArgument< NumberType >( title_, args, argsi, callLoc, true );
383 size_t number;
384 if( numberVal != NullPtr< NumberType >( ) )
386 if( numberVal->val_ < 1 )
388 throw Exceptions::CoreOutOfRange( title_, args, argsi, "PDF only allows strictly positive page numbers." );
390 number = static_cast< size_t >( numberVal->val_ );
392 else
394 number = state->getNextPageNumber( );
397 state->setLabel( prefix, styleVal, number );
399 Kernel::ContRef cont = evalState->cont_;
400 cont->takeHandle( Kernel::THE_SLOT_VARIABLE,
401 evalState );
405 template< class T >
406 class Mutator_nextpagelabel : public Lang::CoreFunction
408 public:
409 Mutator_nextpagelabel( const char * title )
410 : CoreFunction( title, new Kernel::EvaluatedFormals( title, true ) )
413 virtual void
414 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
416 args.applyDefaults( );
418 typedef T StateType;
419 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
420 Kernel::ContRef cont = evalState->cont_;
421 cont->takeValue( Kernel::ValueRef( new Lang::String( state->getNextPageLabel( ) ) ),
422 evalState );
426 template< class T >
427 class Mutator_nextpagenumber : public Lang::CoreFunction
429 public:
430 Mutator_nextpagenumber( const char * title )
431 : CoreFunction( title, new Kernel::EvaluatedFormals( title, true ) )
434 virtual void
435 call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
437 args.applyDefaults( );
439 typedef T StateType;
440 StateType * state = Helpers::mutator_cast_self< StateType >( args.getMutatorSelf( ) );
441 Kernel::ContRef cont = evalState->cont_;
442 cont->takeValue( Kernel::ValueRef( new Lang::Integer( state->getNextPageNumber( ) ) ),
443 evalState );
449 Kernel::WarmCatalog::WarmCatalog( )
450 : pageLabelsActivated_( false ),
451 bboxGroup_( RefCountPtr< const Lang::Symbol >( NullPtr< const Lang::Symbol >( ) ) )
453 labelEntries_.push_back( new Kernel::WarmCatalog::PageLabelEntry( 0, strrefdup( "" ), PageLabelEntry::DECIMAL, 1 ) );
456 Kernel::WarmCatalog::~WarmCatalog( )
459 void
460 WarmCatalog_register_mutators( Lang::SystemFinalClass * dstClass )
462 dstClass->registerMutator( new Kernel::Mutator_setbboxgroup< Kernel::WarmCatalog >( "setbboxgroup" ) );
463 dstClass->registerMutator( new Kernel::Mutator_setpagelabel< Kernel::WarmCatalog >( "setpagelabel" ) );
464 dstClass->registerMutator( new Kernel::Mutator_nextpagelabel< Kernel::WarmCatalog >( "nextpagelabel" ) );
465 dstClass->registerMutator( new Kernel::Mutator_nextpagenumber< Kernel::WarmCatalog >( "nextpagenumber" ) );
468 RefCountPtr< const Lang::Class > Kernel::WarmCatalog::TypeID( new Lang::SystemFinalClass( strrefdup( "#Catalog" ), WarmCatalog_register_mutators ) );
469 TYPEINFOIMPL_STATE( WarmCatalog );
471 void
472 Kernel::WarmCatalog::tackOnImpl( Kernel::EvalState * evalState, const RefCountPtr< const Lang::Value > & piece, const Kernel::PassedDyn & dyn, const Ast::SourceLocation & callLoc )
474 typedef const Lang::Drawable2D ArgType;
475 RefCountPtr< const ArgType > pageContents( Helpers::down_cast< ArgType >( piece, callLoc ) );
477 tackOnPage( evalState->dyn_, pageContents, callLoc );
479 Kernel::ContRef cont = evalState->cont_;
480 cont->takeHandle( Kernel::THE_SLOT_VARIABLE,
481 evalState );
484 void
485 Kernel::WarmCatalog::freezeImpl( Kernel::EvalState * evalState, const Ast::SourceLocation & callLoc )
487 throw Exceptions::MiscellaneousRequirement( strrefdup( "The catalog state cannot be frozen." ) );
490 void
491 Kernel::WarmCatalog::peekImpl( Kernel::EvalState * evalState, const Ast::SourceLocation & callLoc )
493 throw Exceptions::MiscellaneousRequirement( strrefdup( "The catalog state cannot be peeked." ) );
496 void
497 Kernel::WarmCatalog::gcMark( Kernel::GCMarkedSet & marked )
500 void
501 Kernel::WarmCatalog::setLabel( RefCountPtr< const char > prefix, PageLabelEntry::Style style, size_t start )
503 const SimplePDF::PDF_Version::Version PAGELABEL_VERSION = SimplePDF::PDF_Version::PDF_1_3;
504 if( ! Kernel::the_PDF_version.greaterOrEqual( PAGELABEL_VERSION ) )
506 Kernel::the_PDF_version.message( PAGELABEL_VERSION, "The page label setting was ignored." );
507 return;
510 pageLabelsActivated_ = true;
512 if( labelEntries_.back( )->pageIndex_ == pages_.size( ) )
514 delete labelEntries_.back( );
515 labelEntries_.pop_back( );
517 labelEntries_.push_back( new Kernel::WarmCatalog::PageLabelEntry( pages_.size( ), prefix, style, start ) );
520 size_t
521 Kernel::WarmCatalog::getNextPageNumber( ) const
523 const Kernel::WarmCatalog::PageLabelEntry * lastEntry = labelEntries_.back( );
524 return lastEntry->startNumber_ + pages_.size( ) - lastEntry->pageIndex_;
527 Kernel::WarmCatalog::PageLabelEntry::Style
528 Kernel::WarmCatalog::getNextPageStyle( ) const
530 const Kernel::WarmCatalog::PageLabelEntry * lastEntry = labelEntries_.back( );
531 return lastEntry->style_;
534 RefCountPtr< const char >
535 Kernel::WarmCatalog::getNextPagePrefix( ) const
537 const Kernel::WarmCatalog::PageLabelEntry * lastEntry = labelEntries_.back( );
538 return lastEntry->prefix_;
541 RefCountPtr< const char >
542 Kernel::WarmCatalog::getNextPageLabel( ) const
544 return getPageLabel( labelEntries_.back( ), pages_.size( ) );
547 RefCountPtr< const char >
548 Kernel::WarmCatalog::getPageLabel( size_t index ) const
550 typedef typeof labelEntries_ ListType;
551 ListType::const_iterator i = labelEntries_.end( );
552 --i;
553 // Note that labelEntries_.begin( )->pageIndex is 0.
554 while( (*i)->pageIndex_ > index )
556 --i;
558 return getPageLabel( *i, index );
561 RefCountPtr< const char >
562 Kernel::WarmCatalog::getPageLabel( const Kernel::WarmCatalog::PageLabelEntry * entry, size_t index ) const
564 size_t current = entry->startNumber_ + index - entry->pageIndex_;
565 std::ostringstream oss;
566 oss << entry->prefix_.getPtr( ) ;
567 switch( entry->style_ )
569 case PageLabelEntry::NONE:
571 break;
572 case PageLabelEntry::DECIMAL:
574 oss << current ;
576 break;
577 case PageLabelEntry::ROMAN:
579 if( current >= 5000 )
581 throw Exceptions::NotImplemented( "Conversion to roman numerals of numbers greater or equal 5000." );
583 if( current == 0 )
585 throw Exceptions::OutOfRange( "Conversion to roman numeral of the page number 0." );
587 while( current >= 1000 )
589 oss << "M" ;
590 current -= 1000;
592 if( current >= 900 )
594 oss << "CM" ;
595 current -= 900;
597 if( current >= 500 )
599 oss << "D" ;
600 current -= 500;
602 if( current >= 400 )
604 oss << "CD" ;
605 current -= 400;
607 while( current >= 100 )
609 oss << "C" ;
610 current -= 100;
612 if( current >= 90 )
614 oss << "XC" ;
615 current -= 90;
617 if( current >= 50 )
619 oss << "L" ;
620 current -= 50;
622 if( current >= 40 )
624 oss << "XL" ;
625 current -= 40;
627 while( current >= 10 )
629 oss << "X" ;
630 current -= 10;
632 if( current >= 9 )
634 oss << "IX" ;
635 current -= 9;
637 if( current >= 5 )
639 oss << "V" ;
640 current -= 5;
642 if( current >= 4 )
644 oss << "IV" ;
645 current -= 4;
647 while( current >= 1 )
649 oss << "I" ;
650 current -= 1;
653 break;
654 case PageLabelEntry::rOMAN:
656 if( current >= 5000 )
658 throw Exceptions::NotImplemented( "Conversion to roman numerals of numbers greater or equal 5000." );
660 if( current == 0 )
662 throw Exceptions::OutOfRange( "Conversion to roman numeral of the page number 0." );
664 while( current >= 1000 )
666 oss << "m" ;
667 current -= 1000;
669 if( current >= 900 )
671 oss << "cm" ;
672 current -= 900;
674 if( current >= 500 )
676 oss << "d" ;
677 current -= 500;
679 if( current >= 400 )
681 oss << "cd" ;
682 current -= 400;
684 while( current >= 100 )
686 oss << "c" ;
687 current -= 100;
689 if( current >= 90 )
691 oss << "xc" ;
692 current -= 90;
694 if( current >= 50 )
696 oss << "l" ;
697 current -= 50;
699 if( current >= 40 )
701 oss << "xl" ;
702 current -= 40;
704 while( current >= 10 )
706 oss << "x" ;
707 current -= 10;
709 if( current >= 9 )
711 oss << "ix" ;
712 current -= 9;
714 if( current >= 5 )
716 oss << "v" ;
717 current -= 5;
719 if( current >= 4 )
721 oss << "iv" ;
722 current -= 4;
724 while( current >= 1 )
726 oss << "i" ;
727 current -= 1;
730 break;
731 case PageLabelEntry::ALPHABET:
733 size_t base = static_cast< size_t >( 'Z' ) - static_cast< size_t >( 'A' ) + 1;
734 size_t baseFactor = ( current - 1 ) / base ;
735 char rest = 'A' + ( current - baseFactor * base ) - 1;
736 for( size_t i = 0; i < baseFactor + 1; ++i )
738 oss << rest ;
741 break;
742 case PageLabelEntry::aLPHABET:
744 size_t base = static_cast< size_t >( 'Z' ) - static_cast< size_t >( 'A' ) + 1;
745 size_t baseFactor = ( current - 1 ) / base ;
746 char rest = 'a' + ( current - baseFactor * base ) - 1;
747 for( size_t i = 0; i < baseFactor + 1; ++i )
749 oss << rest ;
752 break;
753 default:
754 throw Exceptions::InternalError( "Page label style out of range." );
756 return strrefdup( oss );
759 void
760 Kernel::WarmCatalog::setBBoxGroup( const RefCountPtr< const Lang::Symbol > & group )
762 bboxGroup_ = group;
765 bool
766 Kernel::WarmCatalog::isEmpty( ) const
768 return pages_.empty( );
771 void
772 Kernel::WarmCatalog::tackOnPage( const Kernel::PassedDyn & dyn, const RefCountPtr< const Lang::Drawable2D > & pageContents, const Ast::SourceLocation & callLoc )
774 RefCountPtr< SimplePDF::PDF_Resources > resources( new SimplePDF::PDF_Resources );
775 RefCountPtr< SimplePDF::PDF_Stream_out > contents( new SimplePDF::PDF_Stream_out );
777 resources->requireProcedureSet( SimplePDF::PDF_Resources::PROC_SET_PDF );
779 // Forcing to synch is a bad thing, due to PDF version differences. Instead, refer to the PDF documentation
780 // on the graphics state dictionary (page 180 in the PDF-1.6 reference) to find out the correct default values,
781 // and make sure that these are the initial values of the pdfState.
782 Kernel::PageContentStates pdfState( resources, true );
784 pageContents->shipout( contents->data, & pdfState, Lang::Transform2D( 1, 0, 0, 1, 0, 0 ) );
786 RefCountPtr< const Lang::ElementaryPath2D > theBBox = pageContents->bbox( );
787 Concrete::Coords2D llcorner( 0, 0 );
788 Concrete::Coords2D urcorner( 0, 0 );
789 if( ! theBBox->empty( ) )
791 theBBox->boundingRectangle( & llcorner, & urcorner );
793 RefCountPtr< BoundingRectangle > mediabox = RefCountPtr< BoundingRectangle >( NullPtr< BoundingRectangle >( ) );
794 if( bboxGroup_ == NullPtr< const Lang::Symbol >( ) )
796 if( theBBox->empty( ) )
798 throw Exceptions::InsertingEmptyPage( callLoc );
800 mediabox = RefCountPtr< BoundingRectangle >( );
802 else
804 mediabox = mediaBoxes_[ bboxGroup_->getKey( ) ];
806 if( ! theBBox->empty( ) )
808 mediabox->growToContain( llcorner, urcorner );
810 Page * newPage( new Page( pages_.size( ), resources, contents, mediabox ) );
811 pages_.push_back( newPage );
814 std::vector< Kernel::ValueRef > destinations;
815 pageContents->findTags( & destinations, dyn, Kernel::THE_NAVIGATION_SYMBOL->getKey( ), Lang::THE_2D_IDENTITY );
816 newPage->destinations_.reserve( destinations.size( ) );
817 typedef typeof destinations ListType;
818 for( ListType::const_iterator i = destinations.begin( ); i != destinations.end( ); ++i )
820 typedef const Lang::DocumentDestination ValType;
821 RefCountPtr< ValType > dest = i->down_cast< ValType >( );
822 if( dest == NullPtr< ValType >( ) )
824 throw Exceptions::TypeMismatch( callLoc, "The values tagged for navigations must have a certain type.", (*i)->getTypeName( ), ValType::staticTypeName( ) );
826 newPage->destinations_.push_back( dest );
831 std::vector< Kernel::ValueRef > annotations;
832 pageContents->findTags( & annotations, dyn, Kernel::THE_ANNOTATION_SYMBOL->getKey( ), Lang::THE_2D_IDENTITY );
833 newPage->annotations_.reserve( annotations.size( ) );
834 typedef typeof annotations ListType;
835 for( ListType::const_iterator i = annotations.begin( ); i != annotations.end( ); ++i )
837 typedef const Lang::AnnotationBase ValType;
838 RefCountPtr< ValType > dest = i->down_cast< ValType >( );
839 if( dest == NullPtr< ValType >( ) )
841 throw Exceptions::TypeMismatch( callLoc, "The values tagged for annotation must have a certain type.", (*i)->getTypeName( ), ValType::staticTypeName( ) );
843 newPage->annotations_.push_back( dest );
848 void
849 Kernel::WarmCatalog::shipout( bool split, ShipoutList * docs )
851 RefCountPtr< SimplePDF::PDF_Indirect_out > i_info = Kernel::theDocInfo.getIndirect( & Kernel::theIndirectObjectCount );
853 if( split )
855 for( size_t pageNo = 0; pageNo < pages_.size( ); ++pageNo )
857 docs->push_back( shipoutOne( i_info, pageNo ) );
860 else
862 docs->push_back( shipoutOne( i_info, -1 ) );
867 SimplePDF::PDF_out
868 Kernel::WarmCatalog::shipoutOne( RefCountPtr< SimplePDF::PDF_Indirect_out > i_info, int pageNo )
870 RefCountPtr< SimplePDF::PDF_Dictionary > root = RefCountPtr< SimplePDF::PDF_Dictionary >( new SimplePDF::PDF_Dictionary );
871 RefCountPtr< SimplePDF::PDF_Indirect_out > i_root = SimplePDF::indirect( root, & Kernel::theIndirectObjectCount );
872 root->dic[ "Type" ] = SimplePDF::newName( "Catalog" );
874 std::map< RefCountPtr< const char >, RefCountPtr< SimplePDF::PDF_Vector >, charRefPtrLess > namedDestinations;
875 std::vector< RefCountPtr< SimplePDF::OutlineItem > > outlineStack;
876 outlineStack.reserve( 10 );
877 outlineStack.push_back
878 ( RefCountPtr< SimplePDF::OutlineItem >
879 ( new SimplePDF::OutlineItem( RefCountPtr< SimplePDF::PDF_Object >( NullPtr< SimplePDF::PDF_Object >( ) ), strrefdup( "Top" ),
880 true, false, false, Concrete::RGB( 0, 0, 0 ) ) ) );
882 RefCountPtr< SimplePDF::PDF_Dictionary > pages( new SimplePDF::PDF_Dictionary );
883 RefCountPtr< SimplePDF::PDF_Dictionary > names( new SimplePDF::PDF_Dictionary );
885 RefCountPtr< SimplePDF::PDF_Object > i_pages( SimplePDF::indirect( pages, & Kernel::theIndirectObjectCount ) );
886 root->dic[ "Pages" ] = i_pages;
888 std::list< std::pair< const Page *, std::pair< RefCountPtr< SimplePDF::PDF_Dictionary >, RefCountPtr< SimplePDF::PDF_Indirect_out > > > > annotations;
890 pages->dic[ "Type" ] = SimplePDF::newName( "Pages" );
891 RefCountPtr< SimplePDF::PDF_Vector > pagesKids( new SimplePDF::PDF_Vector );
893 int currentPage = 0;
894 typedef typeof pages_ ListType;
895 for( ListType::const_iterator i = pages_.begin( ); i != pages_.end( ); ++i, ++currentPage )
897 if( pageNo >= 0 && currentPage != pageNo )
899 continue;
901 RefCountPtr< SimplePDF::PDF_Dictionary > newPage( new SimplePDF::PDF_Dictionary );
902 RefCountPtr< SimplePDF::PDF_Indirect_out > i_newPage = SimplePDF::indirect( newPage, & Kernel::theIndirectObjectCount );
903 pagesKids->vec.push_back( i_newPage );
904 newPage->dic[ "Type" ] = SimplePDF::newName( "Page" );
905 newPage->dic[ "Parent" ] = i_pages;
906 newPage->dic[ "MediaBox" ] = (*i)->mediabox_->pdfVector( );
907 newPage->dic[ "Contents" ] = SimplePDF::indirect( (*i)->contents_, & Kernel::theIndirectObjectCount );
908 newPage->dic[ "Resources" ] = SimplePDF::indirect( (*i)->resources_, & Kernel::theIndirectObjectCount );
909 /* The UserUnit entry appears in PDF 1.6, and cannot be relyed on */
910 // newPage->dic[ "UserUnit" ] = RefCountPtr< SimplePDF::PDF_Object >( new SimplePDF::PDF_Float( 72 / 2.52 ) );
912 typedef typeof (*i)->destinations_ DestListType;
913 for( DestListType::const_iterator j = (*i)->destinations_.begin( ); j != (*i)->destinations_.end( ); ++j )
915 RefCountPtr< const Lang::DocumentDestination > dest = *j;
916 if( dest->definesNamed( ) )
918 typedef typeof namedDestinations MapType;
919 RefCountPtr< const char > name = dest->name( );
920 if( namedDestinations.find( name ) != namedDestinations.end( ) )
922 std::ostringstream oss;
923 oss << "The named destination \"" << name.getPtr( ) << "\" appeared a second time (and possibly also the first time) on the page labeled "
924 << getPageLabel( (*i)->index_ ).getPtr( ) << ", with zero-based physical index " << (*i)->index_ << "." ;
925 throw Exceptions::MiscellaneousRequirement( strrefdup( oss ) );
927 namedDestinations.insert( MapType::value_type( name, dest->getDirectDestination( i_newPage ) ) );
930 if( dest->isOutlineEntry( ) )
932 /* This is the index in the stack at which the item belongs.
933 In other words, this shall be the size of outlineStack just before the new item is pushed.
935 const size_t stackLevel = dest->outlineLevel( ) + 1;
937 if( outlineStack.size( ) < stackLevel )
939 RefCountPtr< const char > missingText = strrefdup( " " );
940 while( outlineStack.size( ) < stackLevel )
942 RefCountPtr< SimplePDF::OutlineItem > missingItem = dest->getOutlineItem( i_newPage, missingText );
943 outlineStack.back( )->addKid( missingItem );
944 outlineStack.push_back( missingItem );
947 while( outlineStack.size( ) > stackLevel )
949 outlineStack.pop_back( );
951 RefCountPtr< SimplePDF::OutlineItem > item = dest->getOutlineItem( i_newPage );
952 outlineStack.back( )->addKid( item );
953 outlineStack.push_back( item );
957 if( ! (*i)->annotations_.empty( ) )
959 typedef typeof annotations ListType;
960 annotations.push_back( ListType::value_type( *i, ListType::value_type::second_type( newPage, i_newPage ) ) );
965 pages->dic[ "Kids" ] = pagesKids;
966 pages->dic[ "Count" ] = SimplePDF::newInt( pagesKids->vec.size( ) );
969 typedef std::list< std::pair< const Page *, std::pair< RefCountPtr< SimplePDF::PDF_Dictionary >, RefCountPtr< SimplePDF::PDF_Indirect_out > > > > ListType;
970 for( ListType::const_iterator h = annotations.begin( ); h != annotations.end( ); ++h )
972 const Page * i = h->first;
973 RefCountPtr< SimplePDF::PDF_Dictionary > newPage = h->second.first;
974 RefCountPtr< SimplePDF::PDF_Indirect_out > i_newPage = h->second.second;
975 RefCountPtr< SimplePDF::PDF_Vector > annots( new SimplePDF::PDF_Vector );
976 newPage->dic[ "Annots" ] = SimplePDF::indirect( annots, & Kernel::theIndirectObjectCount );
977 annots->vec.reserve( i->annotations_.size( ) );
979 typedef typeof i->annotations_ AnnotListType;
980 for( AnnotListType::const_iterator j = i->annotations_.begin( ); j != i->annotations_.end( ); ++j )
982 RefCountPtr< const Lang::AnnotationBase > annot = *j;
985 annots->vec.push_back( SimplePDF::indirect( annot->getDictionary( i_newPage, namedDestinations ), & Kernel::theIndirectObjectCount ) );
987 catch( Exceptions::UndefinedCrossRef * ball )
989 if( pageNo >= 0 )
991 /* If this is just one page in the document, we expect cross links in the document to be broken, so we just ignore this. */
992 delete ball;
994 else
996 Kernel::thePostCheckErrorsList.push_back( ball );
1004 const SimplePDF::PDF_Version::Version PAGELABELS_VERSION = SimplePDF::PDF_Version::PDF_1_3;
1005 if( pageLabelsActivated_ &&
1006 Kernel::the_PDF_version.greaterOrEqual( PAGELABELS_VERSION ) )
1008 RefCountPtr< SimplePDF::PDF_Dictionary > pageLabels( new SimplePDF::PDF_Dictionary );
1009 root->dic[ "PageLabels" ] = SimplePDF::indirect( pageLabels, & Kernel::theIndirectObjectCount );
1010 pageLabels->dic[ "Type" ] = SimplePDF::newName( "PageLabels" );
1011 RefCountPtr< SimplePDF::PDF_Vector > nums( new SimplePDF::PDF_Vector );
1012 pageLabels->dic[ "Nums" ] = nums;
1013 typedef typeof labelEntries_ ListType;
1014 for( ListType::const_iterator i = labelEntries_.begin( ); i != labelEntries_.end( ); ++i )
1016 if( pageNo >= 0 )
1018 size_t signedPageNo = static_cast< size_t >( pageNo );
1019 ListType::const_iterator next = i;
1020 ++next;
1021 if( next != labelEntries_.end( ) && (*next)->pageIndex_ <= signedPageNo )
1023 continue;
1026 RefCountPtr< SimplePDF::PDF_Dictionary > newEntry( new SimplePDF::PDF_Dictionary );
1027 nums->vec.push_back( SimplePDF::newInt( ( pageNo >= 0 ) ? 0 : (*i)->pageIndex_ ) );
1028 nums->vec.push_back( newEntry );
1030 switch( (*i)->style_ )
1032 case Kernel::WarmCatalog::PageLabelEntry::NONE:
1034 break;
1035 case Kernel::WarmCatalog::PageLabelEntry::DECIMAL:
1037 newEntry->dic[ "S" ] = SimplePDF::newName( "D" );
1039 break;
1040 case Kernel::WarmCatalog::PageLabelEntry::ROMAN:
1042 newEntry->dic[ "S" ] = SimplePDF::newName( "R" );
1044 break;
1045 case Kernel::WarmCatalog::PageLabelEntry::rOMAN:
1047 newEntry->dic[ "S" ] = SimplePDF::newName( "r" );
1049 break;
1050 case Kernel::WarmCatalog::PageLabelEntry::ALPHABET:
1052 newEntry->dic[ "S" ] = SimplePDF::newName( "A" );
1054 break;
1055 case Kernel::WarmCatalog::PageLabelEntry::aLPHABET:
1057 newEntry->dic[ "S" ] = SimplePDF::newName( "a" );
1059 break;
1060 default:
1061 throw Exceptions::InternalError( "Page label style out of range during shipout." );
1064 if( strlen( (*i)->prefix_.getPtr( ) ) > 0 )
1066 newEntry->dic[ "P" ] = SimplePDF::newString( (*i)->prefix_.getPtr( ) );
1069 if( (*i)->startNumber_ != 1 )
1071 newEntry->dic[ "St" ] = SimplePDF::newInt( (*i)->startNumber_ + ( ( pageNo >= 0 ) ? ( pageNo - (*i)->pageIndex_ ) : 0 ) );
1074 if( pageNo >= 0 )
1076 break;
1081 if( outlineStack.front( )->hasKids( ) &&
1082 pageNo == -1 ) /* The outline will just be a mess unless we include all pages in the document. */
1084 root->dic[ "Outlines" ] = outlineStack.front( )->getTopIndirectDictionary( Kernel::the_PDF_version );
1086 if( ! namedDestinations.empty( ) )
1088 // If there are named destinations, the PDF version is already checked to be high enough.
1089 RefCountPtr< SimplePDF::PDF_Dictionary > dests( new SimplePDF::PDF_Dictionary );
1090 names->dic[ "Dests" ] = SimplePDF::indirect( dests, & Kernel::theIndirectObjectCount );
1092 RefCountPtr< SimplePDF::PDF_Vector > names( new SimplePDF::PDF_Vector );
1093 typedef typeof namedDestinations Maptype;
1094 for( Maptype::const_iterator i = namedDestinations.begin( ); i != namedDestinations.end( ); ++i )
1096 RefCountPtr< SimplePDF::PDF_Dictionary > newEntry( new SimplePDF::PDF_Dictionary );
1097 names->vec.push_back( SimplePDF::newString( i->first.getPtr( ) ) );
1098 names->vec.push_back( i->second );
1100 dests->dic[ "Names" ] = names;
1104 if( ! names->dic.empty( ) )
1106 root->dic[ "Names" ] = SimplePDF::indirect( names, & Kernel::theIndirectObjectCount );
1109 return SimplePDF::PDF_out( i_root, i_info, getPageLabel( std::max( 0, pageNo ) ) );