1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sdext.hxx"
30 
31 #include "pdfiadaptor.hxx"
32 #include "filterdet.hxx"
33 #include "saxemitter.hxx"
34 #include "odfemitter.hxx"
35 #include "inc/wrapper.hxx"
36 #include "inc/contentsink.hxx"
37 #include "tree/pdfiprocessor.hxx"
38 
39 #include <osl/file.h>
40 #include <osl/thread.h>
41 #include <osl/diagnose.h>
42 #include <cppuhelper/factory.hxx>
43 #include <cppuhelper/implementationentry.hxx>
44 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
45 #include <com/sun/star/uno/RuntimeException.hpp>
46 #include <com/sun/star/io/XInputStream.hpp>
47 #include <com/sun/star/frame/XLoadable.hpp>
48 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
49 #include <com/sun/star/io/XSeekable.hpp>
50 
51 
52 #include <boost/shared_ptr.hpp>
53 
54 using namespace com::sun::star;
55 
56 
57 namespace pdfi
58 {
59 
60 PDFIHybridAdaptor::PDFIHybridAdaptor( const uno::Reference< uno::XComponentContext >& xContext ) :
61 	PDFIHybridAdaptorBase( m_aMutex ),
62 	m_xContext( xContext ),
63 	m_xModel()
64 {
65 }
66 
67 // XFilter
68 sal_Bool SAL_CALL PDFIHybridAdaptor::filter( const uno::Sequence< beans::PropertyValue >& rFilterData ) throw( uno::RuntimeException )
69 {
70     sal_Bool bRet = sal_False;
71     if( m_xModel.is() )
72     {
73         uno::Reference< io::XStream > xSubStream;
74         rtl::OUString aPwd;
75         const beans::PropertyValue* pAttribs = rFilterData.getConstArray();
76         sal_Int32 nAttribs = rFilterData.getLength();
77         sal_Int32 nPwPos = -1;
78         for( sal_Int32 i = 0; i < nAttribs; i++ )
79         {
80             #if OSL_DEBUG_LEVEL > 1
81             rtl::OUString aVal( RTL_CONSTASCII_USTRINGPARAM( "<no string>" ) );
82             pAttribs[i].Value >>= aVal;
83             OSL_TRACE( "filter: Attrib: %s = %s\n",
84                        rtl::OUStringToOString( pAttribs[i].Name, RTL_TEXTENCODING_UTF8 ).getStr(),
85                        rtl::OUStringToOString( aVal, RTL_TEXTENCODING_UTF8 ).getStr() );
86             #endif
87             if( pAttribs[i].Name.equalsAscii( "EmbeddedSubstream" ) )
88                 pAttribs[i].Value >>= xSubStream;
89             else if( pAttribs[i].Name.equalsAscii( "Password" ) )
90             {
91                 nPwPos = i;
92                 pAttribs[i].Value >>= aPwd;
93             }
94         }
95         bool bAddPwdProp = false;
96         if( ! xSubStream.is() )
97         {
98             uno::Reference< io::XInputStream > xInput;
99             for( sal_Int32 i = 0; i < nAttribs; i++ )
100             {
101                 if( pAttribs[i].Name.equalsAscii( "InputStream" ) )
102                 {
103                     pAttribs[i].Value >>= xInput;
104                     break;
105                 }
106             }
107             if( xInput.is() )
108             {
109 				// TODO(P2): extracting hybrid substream twice - once during detection, second time here
110                 uno::Reference< io::XSeekable > xSeek( xInput, uno::UNO_QUERY );
111                 if( xSeek.is() )
112                     xSeek->seek( 0 );
113                 oslFileHandle aFile = NULL;
114                 sal_uInt64 nWritten = 0;
115                 rtl::OUString aURL;
116                 if( osl_createTempFile( NULL, &aFile, &aURL.pData ) == osl_File_E_None )
117                 {
118                     OSL_TRACE( "created temp file %s\n", rtl::OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 ).getStr() );
119                     const sal_Int32 nBufSize = 4096;
120                     uno::Sequence<sal_Int8> aBuf(nBufSize);
121                     // copy the bytes
122                     sal_Int32 nBytes;
123                     do
124                     {
125                         nBytes = xInput->readBytes( aBuf, nBufSize );
126                         if( nBytes > 0 )
127                         {
128                             osl_writeFile( aFile, aBuf.getConstArray(), nBytes, &nWritten );
129                             if( static_cast<sal_Int32>(nWritten) != nBytes )
130                             {
131                                 xInput.clear();
132                                 break;
133                             }
134                         }
135                     } while( nBytes == nBufSize );
136                     osl_closeFile( aFile );
137                     if( xInput.is() )
138                     {
139                         rtl::OUString aEmbedMimetype;
140                         rtl::OUString aOrgPwd( aPwd );
141                         xSubStream = getAdditionalStream( aURL, aEmbedMimetype, aPwd, m_xContext, rFilterData, true );
142                         if( aOrgPwd != aPwd )
143                             bAddPwdProp = true;
144                     }
145                     osl_removeFile( aURL.pData );
146                 }
147                 else
148                     xSubStream.clear();
149             }
150         }
151         if( xSubStream.is() )
152         {
153             uno::Sequence< uno::Any > aArgs( 2 );
154             aArgs[0] <<= m_xModel;
155             aArgs[1] <<= xSubStream;
156 
157             OSL_TRACE( "try to instantiate subfilter\n" );
158             uno::Reference< document::XFilter > xSubFilter;
159             try {
160                 xSubFilter = uno::Reference<document::XFilter>(
161                     m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
162                         rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.document.OwnSubFilter" ) ),
163                         aArgs,
164                         m_xContext ),
165                     uno::UNO_QUERY );
166             }
167             catch(uno::Exception& e)
168             {
169                 (void)e;
170                 OSL_TRACE( "subfilter exception: %s\n",
171                            OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
172             }
173 
174             OSL_TRACE( "subfilter: %p\n", xSubFilter.get() );
175             if( xSubFilter.is() )
176             {
177                 if( bAddPwdProp )
178                 {
179                     uno::Sequence<beans::PropertyValue> aFilterData( rFilterData );
180                     if( nPwPos == -1 )
181                     {
182                         nPwPos = aFilterData.getLength();
183                         aFilterData.realloc( nPwPos+1 );
184                         aFilterData[nPwPos].Name = rtl::OUString(
185                             RTL_CONSTASCII_USTRINGPARAM( "Password" ) );
186                     }
187                     aFilterData[nPwPos].Value <<= aPwd;
188                     bRet = xSubFilter->filter( aFilterData );
189                 }
190                 else
191                     bRet = xSubFilter->filter( rFilterData );
192             }
193         }
194         #if OSL_DEBUG_LEVEL > 1
195         else
196             OSL_TRACE( "PDFIAdaptor::filter: no embedded substream set\n" );
197         #endif
198     }
199     #if OSL_DEBUG_LEVEL > 1
200     else
201         OSL_TRACE( "PDFIAdaptor::filter: no model set\n" );
202     #endif
203 
204     return bRet;
205 }
206 
207 void SAL_CALL PDFIHybridAdaptor::cancel() throw()
208 {
209 }
210 
211 //XImporter
212 void SAL_CALL PDFIHybridAdaptor::setTargetDocument( const uno::Reference< lang::XComponent >& xDocument ) throw( lang::IllegalArgumentException )
213 {
214     OSL_TRACE( "PDFIAdaptor::setTargetDocument\n" );
215     m_xModel = uno::Reference< frame::XModel >( xDocument, uno::UNO_QUERY );
216     if( xDocument.is() && ! m_xModel.is() )
217         throw lang::IllegalArgumentException();
218 }
219 
220 //---------------------------------------------------------------------------------------
221 
222 PDFIRawAdaptor::PDFIRawAdaptor( const uno::Reference< uno::XComponentContext >& xContext ) :
223     PDFIAdaptorBase( m_aMutex ),
224     m_xContext( xContext ),
225     m_xModel(),
226     m_pVisitorFactory(),
227     m_bEnableToplevelText(false)
228 {
229 }
230 
231 void PDFIRawAdaptor::setTreeVisitorFactory(const TreeVisitorFactorySharedPtr& rVisitorFactory)
232 {
233     m_pVisitorFactory = rVisitorFactory;
234 }
235 
236 bool PDFIRawAdaptor::parse( const uno::Reference<io::XInputStream>&       xInput,
237                             const uno::Reference<task::XInteractionHandler>& xIHdl,
238                             const rtl::OUString&                          rPwd,
239                             const uno::Reference<task::XStatusIndicator>& xStatus,
240                             const XmlEmitterSharedPtr&                    rEmitter,
241                             const rtl::OUString&                          rURL )
242 {
243     // container for metaformat
244     boost::shared_ptr<PDFIProcessor> pSink(
245         new PDFIProcessor(xStatus, m_xContext));
246 
247     // TEMP! TEMP!
248     if( m_bEnableToplevelText )
249         pSink->enableToplevelText();
250 
251     bool bSuccess=false;
252 
253     if( xInput.is() && (!rURL.getLength() || rURL.compareToAscii( "file:", 5 ) != 0) )
254         bSuccess = xpdf_ImportFromStream( xInput, pSink, xIHdl, rPwd, m_xContext );
255     else
256         bSuccess = xpdf_ImportFromFile( rURL, pSink, xIHdl, rPwd, m_xContext );
257 
258     if( bSuccess )
259         pSink->emit(*rEmitter,*m_pVisitorFactory);
260 
261     return bSuccess;
262 }
263 
264 bool PDFIRawAdaptor::odfConvert( const rtl::OUString&                          rURL,
265                                  const uno::Reference<io::XOutputStream>&      xOutput,
266                                  const uno::Reference<task::XStatusIndicator>& xStatus )
267 {
268     XmlEmitterSharedPtr pEmitter = createOdfEmitter(xOutput);
269     const bool bSuccess = parse(uno::Reference<io::XInputStream>(),
270                                 uno::Reference<task::XInteractionHandler>(),
271                                 rtl::OUString(),
272                                 xStatus,pEmitter,rURL);
273 
274     // tell input stream that it is no longer needed
275 	xOutput->closeOutput();
276 
277     return bSuccess;
278 }
279 
280 // XImportFilter
281 sal_Bool SAL_CALL PDFIRawAdaptor::importer( const uno::Sequence< beans::PropertyValue >&        rSourceData,
282                                             const uno::Reference< xml::sax::XDocumentHandler >& rHdl,
283                                             const uno::Sequence< rtl::OUString >&               /*rUserData*/ ) throw( uno::RuntimeException )
284 {
285     // get the InputStream carrying the PDF content
286     uno::Reference< io::XInputStream > xInput;
287     uno::Reference< task::XStatusIndicator > xStatus;
288     uno::Reference< task::XInteractionHandler > xInteractionHandler;
289     rtl::OUString aURL;
290     rtl::OUString aPwd;
291     const beans::PropertyValue* pAttribs = rSourceData.getConstArray();
292     sal_Int32 nAttribs = rSourceData.getLength();
293     for( sal_Int32 i = 0; i < nAttribs; i++, pAttribs++ )
294     {
295         OSL_TRACE("importer Attrib: %s\n", OUStringToOString( pAttribs->Name, RTL_TEXTENCODING_UTF8 ).getStr() );
296         if( pAttribs->Name.equalsAscii( "InputStream" ) )
297             pAttribs->Value >>= xInput;
298         else if( pAttribs->Name.equalsAscii( "URL" ) )
299             pAttribs->Value >>= aURL;
300         else if( pAttribs->Name.equalsAscii( "StatusIndicator" ) )
301             pAttribs->Value >>= xStatus;
302         else if( pAttribs->Name.equalsAscii( "InteractionHandler" ) )
303             pAttribs->Value >>= xInteractionHandler;
304         else if( pAttribs->Name.equalsAscii( "Password" ) )
305             pAttribs->Value >>= aPwd;
306     }
307     if( !xInput.is() )
308         return sal_False;
309 
310     XmlEmitterSharedPtr pEmitter = createSaxEmitter(rHdl);
311     const bool bSuccess = parse(xInput,xInteractionHandler, aPwd, xStatus,pEmitter,aURL);
312 
313     // tell input stream that it is no longer needed
314     xInput->closeInput();
315     xInput.clear();
316 
317     return bSuccess;
318 }
319 
320 //XImporter
321 void SAL_CALL PDFIRawAdaptor::setTargetDocument( const uno::Reference< lang::XComponent >& xDocument ) throw( lang::IllegalArgumentException )
322 {
323     OSL_TRACE( "PDFIAdaptor::setTargetDocument\n" );
324     m_xModel = uno::Reference< frame::XModel >( xDocument, uno::UNO_QUERY );
325     if( xDocument.is() && ! m_xModel.is() )
326         throw lang::IllegalArgumentException();
327 }
328 
329 }
330