Update procedures
[shapes.git] / source / pdfimport.cc
blob7854508d9e2feaf19cc5f6708f7ef8fc9fe7974e
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 "pdfimport.h"
20 #include "globals.h"
21 #include "shapesexceptions.h"
22 #include "pathtypes.h"
23 #include "drawabletypes.h"
24 #include "texlabelmanager.h"
26 using namespace Shapes;
27 using namespace SimplePDF;
29 Kernel::Import::Import( )
30 { }
32 RefCountPtr< const std::vector< RefCountPtr< const Shapes::Lang::XObject > > >
33 Kernel::Import::addPagesAsXObjects( RefCountPtr< PDF_in > pdfi )
35 using namespace Shapes;
37 importSources_.push_back( pdfi ); // Keep the source alive so that it can be used when finally producing output
38 IndirectRemapType indirectRemap;
39 std::vector< RefCountPtr< const Lang::XObject > > * res = new std::vector< RefCountPtr< const Lang::XObject > >;
40 for( size_t pageNumber( 0 ); pageNumber < pdfi->getPageCount( ); ++pageNumber )
42 RefCountPtr< PDF_Dictionary > pageDic( pdfi->getPage( pageNumber ) );
43 RefCountPtr< PDF_Vector > cropBox = down_cast_follow< PDF_Vector >( pageDic->getInheritable( "CropBox" ) );
44 if( cropBox == NullPtr< PDF_Vector >( ) )
46 cropBox = down_cast_follow< PDF_Vector >( pageDic->getInheritable( "MediaBox" ) );
48 if( cropBox == NullPtr< PDF_Vector >( ) )
50 throw Exceptions::InternalError( strrefdup( "Failed to find crop box of imported page. (Searched to the page tree root.)" ) );
52 RefCountPtr< PDF_Stream_in > original( pdfi->follow< PDF_Stream_in >( (*pageDic)[ "Contents" ] ) );
54 // RefCountPtr< PDF_Dictionary > xobjectDic( pdfi->follow< PDF_Dictionary >( (*pdfi->follow< PDF_Dictionary >( (*pageDic)[ "Resources" ] ))[ "XObject" ] ) );
55 // if( xobjectDic->dic.size( ) != 1 )
56 // {
57 // throw Exceptions::InternalError( strrefdup( "Expected exactly 1 XObject on a TeX label input page" ) );
58 // }
59 // RefCountPtr< PDF_Stream_in > original( pdfi->follow< PDF_Stream_in >( xobjectDic->dic.begin( )->second ) );
60 RefCountPtr< PDF_Stream_in > newObj( new PDF_Stream_in( original->is, original->dataStart ) );
61 (*newObj)[ "Subtype" ] = newName( "Form" );
62 (*newObj)[ "FormType" ] = newInt( 1 );
63 (*newObj)[ "BBox" ] = cropBox; // ->rectangleIntersection( down_cast_follow< PDF_Vector >( (*original)[ "BBox" ] ) );
64 (*newObj)[ "Matrix" ] = RefCountPtr< PDF_Object >( new PDF_Vector( 1, 0, 0, 1, 0, 0 ) );
65 (*newObj)[ "Filter" ] = (*original)[ "Filter" ];
66 (*newObj)[ "Length" ] = RefCountPtr< PDF_Object >( new PDF_Int( pdfi->follow< PDF_Int >( (*original)[ "Length" ] )->value( ) ) );
67 (*newObj)[ "Resources" ] = deepCopy( (*pageDic)[ "Resources" ], & Kernel::theIndirectObjectCount, & indirectRemap );
69 Concrete::Length xmin;
70 Concrete::Length xmax;
71 Concrete::Length ymin;
72 Concrete::Length ymax;
73 try
75 RefCountPtr< PDF_Vector > bboxTyped = pdfi->follow< PDF_Vector >( (*newObj)[ "BBox" ] );
76 if( bboxTyped->vec.size( ) != 4 )
78 throw Exceptions::InternalError( strrefdup( "The bbox of the imported page was not of size 4." ) );
80 xmin = Concrete::Length( pdfi->follow< PDF_Float >( bboxTyped->vec[ 0 ] )->value( ) );
81 xmax = Concrete::Length( pdfi->follow< PDF_Float >( bboxTyped->vec[ 2 ] )->value( ) );
82 ymin = Concrete::Length( pdfi->follow< PDF_Float >( bboxTyped->vec[ 1 ] )->value( ) );
83 ymax = Concrete::Length( pdfi->follow< PDF_Float >( bboxTyped->vec[ 3 ] )->value( ) );
85 catch( Exceptions::Exception & ball )
87 throw;
89 catch( const char * ball )
91 std::ostringstream oss;
92 oss << "An error occurred while evaluating the bbox of the imported page: " << ball ;
93 throw Exceptions::InternalError( strrefdup( oss.str( ).c_str( ) ) );
95 catch( ... )
97 throw Exceptions::InternalError( strrefdup( "An error occurred while evaluating the bbox of the imported page." ) );
99 Lang::ElementaryPath2D * bboxpath = new Lang::ElementaryPath2D;
100 bboxpath->push_back( new Concrete::PathPoint2D( xmin, ymin ) );
101 bboxpath->push_back( new Concrete::PathPoint2D( xmin, ymax ) );
102 bboxpath->push_back( new Concrete::PathPoint2D( xmax, ymax ) );
103 bboxpath->push_back( new Concrete::PathPoint2D( xmax, ymin ) );
104 bboxpath->close( );
106 RefCountPtr< PDF_Object > indirection = SimplePDF::indirect( newObj, & Kernel::theIndirectObjectCount );
107 RefCountPtr< const Lang::ElementaryPath2D > bbox = RefCountPtr< const Lang::ElementaryPath2D >( bboxpath );
108 res->push_back( RefCountPtr< Lang::XObject >( new Lang::XObject( indirection, bbox, bbox, Kernel::THE_DEFAULT_STATE ) ) );
110 return RefCountPtr< const std::vector< RefCountPtr< const Lang::XObject > > >( res );
113 void
114 Kernel::Import::importBtexEtexThings( RefCountPtr< PDF_in > pdfi, Shapes::Kernel::TeXLabelManager::MapType * dstMap, const std::string & setupCodeHash )
116 using namespace Shapes;
118 importSources_.push_back( pdfi ); // Keep the source alive so that it can be used when finally producing output
119 IndirectRemapType indirectRemap;
121 PDF_in::PageIterator theEnd = pdfi->endPages( );
122 for( PDF_in::PageIterator i = pdfi->beginPages( ); i != theEnd; ++i )
124 RefCountPtr< PDF_Dictionary > xobjectDic( pdfi->follow< PDF_Dictionary >( (*pdfi->follow< PDF_Dictionary >( (**i)[ "Resources" ] ))[ "XObject" ] ) );
125 if( xobjectDic->dic.size( ) != 1 )
127 throw Exceptions::InternalError( strrefdup( "Expected exactly 1 XObject on a TeX label input page" ) );
129 RefCountPtr< PDF_Stream_in > original( pdfi->follow< PDF_Stream_in >( xobjectDic->dic.begin( )->second ) );
131 std::string texStr;
133 RefCountPtr< PDF_Stream_in > texStream( pdfi->follow< PDF_Stream_in >( (*original)[ "TeXsrc" ] ) );
134 std::ostringstream tmp;
135 texStream->writeDataDefilteredTo( tmp );
136 texStr = tmp.str( );
138 if( i == pdfi->beginPages( ) )
140 /* This page only contains information about the TeX context */
141 if( texStr != setupCodeHash )
143 throw Exceptions::TeXSetupHasChanged( );
145 continue;
147 Concrete::Length height( pdfNameToDouble( (*original)[ "TeXht" ] ) );
148 Concrete::Length depth( pdfNameToDouble( (*original)[ "TeXdp" ] ) );
149 Concrete::Length width( pdfNameToDouble( (*original)[ "TeXwd" ] ) );
151 const Concrete::Length bboxAddY = 0.08 * ( depth + height );
152 const Concrete::Length xmin = Concrete::ZERO_LENGTH;
153 const Concrete::Length xmax = width;
154 const Concrete::Length ymin = -depth - bboxAddY;
155 const Concrete::Length ymax = height + bboxAddY;
157 RefCountPtr< PDF_Stream_in > newObj( new PDF_Stream_in( original->is, original->dataStart ) );
158 (*newObj)[ "Subtype" ] = newName( "Form" );
159 (*newObj)[ "FormType" ] = newInt( 1 );
160 (*newObj)[ "BBox" ] = RefCountPtr< PDF_Object >( new PDF_Vector( 0.0,
161 - bboxAddY.offtype< 1, 0 >( ),
162 ( xmax - xmin ).offtype< 1, 0 >( ),
163 ( ymax - ymin ).offtype< 1, 0 >( ) ) );
164 // (*newObj)[ "BBox" ] = (*original)[ "BBox" ]; /* This box is too small! */
165 (*newObj)[ "Matrix" ] = RefCountPtr< PDF_Object >( new PDF_Vector( 1, 0, 0, 1, 0, - depth.offtype< 1, 0 >( ) ) );
166 (*newObj)[ "Filter" ] = (*original)[ "Filter" ];
167 (*newObj)[ "Length" ] = RefCountPtr< PDF_Object >( new PDF_Int( pdfi->follow< PDF_Int >( (*original)[ "Length" ] )->value( ) ) );
168 (*newObj)[ "Resources" ] = deepCopy( (*original)[ "Resources" ], & Kernel::theIndirectObjectCount, & indirectRemap );
170 Lang::ElementaryPath2D * bboxpath = new Lang::ElementaryPath2D;
171 bboxpath->push_back( new Concrete::PathPoint2D( xmin, ymin ) );
172 bboxpath->push_back( new Concrete::PathPoint2D( xmin, ymax ) );
173 bboxpath->push_back( new Concrete::PathPoint2D( xmax, ymax ) );
174 bboxpath->push_back( new Concrete::PathPoint2D( xmax, ymin ) );
175 bboxpath->close( );
177 if( dstMap->find( texStr ) != dstMap->end( ) )
179 throw Exceptions::InternalError( strrefdup( "Multiply generated TeX label: " + texStr ) );
182 RefCountPtr< PDF_Object > indirection = SimplePDF::indirect( newObj, & Kernel::theIndirectObjectCount );
183 dstMap->insert( Shapes::Kernel::TeXLabelManager::MapType::value_type( texStr, RefCountPtr< const Lang::XObject >( new Lang::XObject( indirection, RefCountPtr< const Lang::ElementaryPath2D >( bboxpath ) ) ) ) );
187 void
188 Kernel::Import::free( )
190 while( ! importSources_.empty( ) )
192 importSources_.pop_back( );
196 double
197 Kernel::Import::pdfNameToDouble( RefCountPtr< PDF_Object > nameObject )
199 RefCountPtr< PDF_Name > name( nameObject.down_cast< PDF_Name >( ) );
200 if( name == NullPtr< PDF_Name >( ) )
202 throw( "PDF_out::pdfNameToDouble: The object was not a name" );
204 const double pointToBigPointFactor = 72 / 72.27;
205 char * end;
206 double res = pointToBigPointFactor * strtod( name->name( ).c_str( ), & end );
207 if( strcmp( end, "pt" ) != 0 )
209 throw( "PDF_out::pdfNameToDouble: Expected \"pt\" to follow length number" );
211 return res;