1 /***************************************************************************
2  *   Copyright (C) 2007 by Dominik Seichter                                *
3  *   domseichter@web.de                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Library General Public License as       *
7  *   published by the Free Software Foundation; either version 2 of the    *
8  *   License, or (at your option) any later version.                       *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU Library General Public     *
16  *   License along with this program; if not, write to the                 *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  *                                                                         *
20  *   In addition, as a special exception, the copyright holders give       *
21  *   permission to link the code of portions of this program with the      *
22  *   OpenSSL library under certain conditions as described in each         *
23  *   individual source file, and distribute linked combinations            *
24  *   including the two.                                                    *
25  *   You must obey the GNU General Public License in all respects          *
26  *   for all of the code used other than OpenSSL.  If you modify           *
27  *   file(s) with this exception, you may extend this exception to your    *
28  *   version of the file(s), but you are not obligated to do so.  If you   *
29  *   do not wish to do so, delete this exception statement from your       *
30  *   version.  If you delete this exception statement from all source      *
31  *   files in the program, then also delete it here.                       *
32  ***************************************************************************/
33 
34 #include "PdfImmediateWriter.h"
35 
36 #include "PdfFileStream.h"
37 #include "PdfMemStream.h"
38 #include "PdfObject.h"
39 #include "PdfXRef.h"
40 #include "PdfXRefStream.h"
41 #include "PdfDefinesPrivate.h"
42 
43 namespace PoDoFo {
44 
PdfImmediateWriter(PdfOutputDevice * pDevice,PdfVecObjects * pVecObjects,const PdfObject * pTrailer,EPdfVersion eVersion,PdfEncrypt * pEncrypt,EPdfWriteMode eWriteMode)45 PdfImmediateWriter::PdfImmediateWriter( PdfOutputDevice* pDevice, PdfVecObjects* pVecObjects,
46                                         const PdfObject* pTrailer, EPdfVersion eVersion,
47                                         PdfEncrypt* pEncrypt, EPdfWriteMode eWriteMode )
48     : PdfWriter( pVecObjects ), m_pParent( pVecObjects ),
49       m_pDevice( pDevice ), m_pLast( NULL ), m_bOpenStream( false )
50 {
51     if( m_pTrailer )
52         delete m_pTrailer;
53     m_pTrailer = new PdfObject( *pTrailer );
54 
55     // register as observer for PdfVecObjects
56     m_pParent->Attach( this );
57     // register as stream factory for PdfVecObjects
58     m_pParent->SetStreamFactory( this );
59 
60     this->CreateFileIdentifier( m_identifier, m_pTrailer );
61     // setup encryption
62     if( pEncrypt )
63     {
64         this->SetEncrypted( *pEncrypt );
65         m_pEncrypt->GenerateEncryptionKey( m_identifier );
66     }
67 
68     // start with writing the header
69     this->SetPdfVersion( eVersion );
70     this->SetWriteMode( eWriteMode );
71     this->WritePdfHeader( m_pDevice );
72 
73     m_pXRef = m_bXRefStream ? new PdfXRefStream( m_vecObjects, this ) : new PdfXRef();
74 
75 }
76 
~PdfImmediateWriter()77 PdfImmediateWriter::~PdfImmediateWriter()
78 {
79     if( m_pParent )
80         m_pParent->Detach( this );
81 
82     delete m_pXRef;
83 }
84 
WriteObject(const PdfObject * pObject)85 void PdfImmediateWriter::WriteObject( const PdfObject* pObject )
86 {
87     const int endObjLenght = 7;
88 
89     this->FinishLastObject();
90 
91     m_pXRef->AddObject( pObject->Reference(), m_pDevice->Tell(), true );
92     pObject->WriteObject( m_pDevice, this->GetWriteMode(), m_pEncrypt );
93     // Make sure, no one will add keys now to the object
94     const_cast<PdfObject*>(pObject)->SetImmutable(true);
95 
96     // Let's cheat a bit:
97     // pObject has written an "endobj\n" as last data to the file.
98     // we simply overwrite this string with "stream\n" which
99     // has excatly the same length.
100     m_pDevice->Seek( m_pDevice->Tell() - endObjLenght );
101     m_pDevice->Print( "stream\n" );
102     m_pLast = const_cast<PdfObject*>(pObject);
103 }
104 
ParentDestructed()105 void PdfImmediateWriter::ParentDestructed()
106 {
107     m_pParent = NULL;
108 }
109 
Finish()110 void PdfImmediateWriter::Finish()
111 {
112     // write all objects which are still in RAM
113     this->FinishLastObject();
114 
115     // setup encrypt dictionary
116     if( m_pEncrypt )
117     {
118         // Add our own Encryption dictionary
119         m_pEncryptObj = m_vecObjects->CreateObject();
120         m_pEncrypt->CreateEncryptionDictionary( m_pEncryptObj->GetDictionary() );
121     }
122 
123     this->WritePdfObjects( m_pDevice, *m_pParent, m_pXRef );
124 
125     // write the XRef
126     pdf_uint64 lXRefOffset = static_cast<pdf_uint64>( m_pDevice->Tell() );
127     m_pXRef->Write( m_pDevice );
128 
129     // XRef streams contain the trailer in the XRef
130     if( !m_bXRefStream )
131     {
132         PdfObject trailer;
133 
134         // if we have a dummy offset we write also a prev entry to the trailer
135         FillTrailerObject( &trailer, m_pXRef->GetSize(), false );
136 
137         m_pDevice->Print("trailer\n");
138         trailer.WriteObject( m_pDevice, this->GetWriteMode(), NULL );
139     }
140 
141     m_pDevice->Print( "startxref\n%" PDF_FORMAT_UINT64 "\n%%%%EOF\n", lXRefOffset );
142     m_pDevice->Flush();
143 
144     // we are done now
145     m_pParent->Detach( this );
146     m_pParent = NULL;
147 }
148 
CreateStream(PdfObject * pParent)149 PdfStream* PdfImmediateWriter::CreateStream( PdfObject* pParent )
150 {
151     return m_bOpenStream ?
152         static_cast<PdfStream*>(new PdfMemStream( pParent )) :
153         static_cast<PdfStream*>(new PdfFileStream( pParent, m_pDevice ));
154 }
155 
FinishLastObject()156 void PdfImmediateWriter::FinishLastObject()
157 {
158     if( m_pLast )
159     {
160         m_pDevice->Print( "\nendstream\n" );
161         m_pDevice->Print( "endobj\n" );
162 
163         delete m_pParent->RemoveObject( m_pLast->Reference(), false );
164         m_pLast = NULL;
165 
166     }
167 }
168 
BeginAppendStream(const PdfStream * pStream)169 void PdfImmediateWriter::BeginAppendStream( const PdfStream* pStream )
170 {
171     const PdfFileStream* pFileStream = dynamic_cast<const PdfFileStream*>(pStream );
172     if( pFileStream )
173     {
174         // Only one open file stream is allowed at a time
175         PODOFO_ASSERT( !m_bOpenStream );
176         m_bOpenStream = true;
177 
178         if( m_pEncrypt )
179             const_cast<PdfFileStream*>(pFileStream)->SetEncrypted( m_pEncrypt );
180     }
181 }
182 
EndAppendStream(const PdfStream * pStream)183 void PdfImmediateWriter::EndAppendStream( const PdfStream* pStream )
184 {
185     const PdfFileStream* pFileStream = dynamic_cast<const PdfFileStream*>(pStream );
186     if( pFileStream )
187     {
188         // A PdfFileStream has to be opened before
189         PODOFO_ASSERT( m_bOpenStream );
190         m_bOpenStream = false;
191     }
192 }
193 
194 };
195 
196