diff options
-rw-r--r-- | filter/source/pdf/pdfexport.cxx | 32 | ||||
-rw-r--r-- | include/vcl/pdfwriter.hxx | 18 | ||||
-rw-r--r-- | vcl/inc/pdf/pdfwriter_impl.hxx | 33 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter.cxx | 4 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 266 |
5 files changed, 194 insertions, 159 deletions
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx index 338463f184f8..7e710dabd928 100644 --- a/filter/source/pdf/pdfexport.cxx +++ b/filter/source/pdf/pdfexport.cxx @@ -27,6 +27,7 @@ #include <vcl/canvastools.hxx> #include <vcl/mapmod.hxx> #include <vcl/gdimtf.hxx> +#include <rtl/ustring.hxx> #include <comphelper/propertyvalue.hxx> #include <comphelper/sequence.hxx> #include <comphelper/string.hxx> @@ -312,19 +313,16 @@ void PDFExportStreamDoc::write( const Reference< XOutputStream >& xStream ) if( !xStore.is() ) return; - Sequence< beans::PropertyValue > aArgs( 2 + (m_aPreparedPassword.hasElements() ? 1 : 0) ); - aArgs.getArray()[0].Name = "FilterName"; - aArgs.getArray()[1].Name = "OutputStream"; - aArgs.getArray()[1].Value <<= xStream; - if( m_aPreparedPassword.hasElements() ) - { - aArgs.getArray()[2].Name = "EncryptionData"; - aArgs.getArray()[2].Value <<= m_aPreparedPassword; - } + std::vector<beans::PropertyValue> aArgs { + comphelper::makePropertyValue("FilterName", OUString()), + comphelper::makePropertyValue("OutputStream", xStream), + }; + if (m_aPreparedPassword.hasElements()) + aArgs.push_back(comphelper::makePropertyValue("EncryptionData", m_aPreparedPassword)); try { - xStore->storeToURL( "private:stream", aArgs ); + xStore->storeToURL("private:stream", comphelper::containerToSequence(aArgs)); } catch( const IOException& ) { @@ -927,9 +925,17 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& // export stream // get mimetype OUString aSrcMimetype = getMimetypeForDocument( mxContext, mxSrcDoc ); - aPDFWriter.AddStream( aSrcMimetype, - new PDFExportStreamDoc( mxSrcDoc, aPreparedPermissionPassword ) - ); + OUString aExt; + if (aSrcMimetype == "application/vnd.oasis.opendocument.text") + aExt = ".odt"; + else if (aSrcMimetype == "application/vnd.oasis.opendocument.presentation") + aExt = ".odp"; + else if (aSrcMimetype == "application/vnd.oasis.opendocument.spreadsheet") + aExt = ".ods"; + else if (aSrcMimetype == "application/vnd.oasis.opendocument.graphics") + aExt = ".odg"; + std::unique_ptr<vcl::PDFOutputStream> pStream(new PDFExportStreamDoc(mxSrcDoc, aPreparedPermissionPassword)); + aPDFWriter.AddAttachedFile("Original" + aExt, aSrcMimetype, std::move(pStream)); } if ( pOut ) diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx index f98f2c231261..80b71237138e 100644 --- a/include/vcl/pdfwriter.hxx +++ b/include/vcl/pdfwriter.hxx @@ -1193,24 +1193,24 @@ The following structure describes the permissions used in PDF security */ sal_Int32 CreateControl( const AnyWidget& rControlType ); - /** Inserts an additional stream to the PDF file + /** Attaches an additional file to the PDF file - This function adds an arbitrary stream to the produced PDF file. May be called - any time before Emit(). The stream will be written during - Emit by calling the PDFOutputStream Object's write - method. After the call the PDFOutputStream will be deleted. + This function adds an arbitrary stream that represents an attached file + in the PDF file. - All additional streams and their mimetypes will be entered into an array - in the trailer dictionary. + This also adds an additional stream array entry (with the mimetype) in + the trailer dictionary for backwards compatibility. + + @param rFileName + the filename of the additional file as presented in the stream @param rMimeType the mimetype of the stream @param pStream the interface to the additional stream - */ - void AddStream( const OUString& rMimeType, PDFOutputStream* pStream ); + void AddAttachedFile(OUString const& rFileName, const OUString& rMimeType, std::unique_ptr<PDFOutputStream> pStream); /// Write rString as a PDF hex string into rBuffer. static void AppendUnicodeTextString(const OUString& rString, OStringBuffer& rBuffer); diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx index ea7c3d3462ab..5f6abd7bca82 100644 --- a/vcl/inc/pdf/pdfwriter_impl.hxx +++ b/vcl/inc/pdf/pdfwriter_impl.hxx @@ -440,8 +440,10 @@ struct PDFEmbeddedFile { /// ID of the file. sal_Int32 m_nObject; + OUString m_aSubType; /// Contents of the file. BinaryDataContainer m_aDataContainer; + std::unique_ptr<PDFOutputStream> m_pStream; PDFEmbeddedFile() : m_nObject(0) @@ -673,7 +675,15 @@ struct GraphicsState enum class Mode { DEFAULT, NOWRITE }; -} +struct PDFDocumentAttachedFile +{ + OUString maFilename; + OUString maMimeType; + sal_Int32 mnEmbeddedFileObjectId; + sal_Int32 mnObjectId; +}; + +} // end pdf namespace class PDFWriterImpl final : public VirtualDevice, public PDFObjectContainer { @@ -728,6 +738,9 @@ private: std::vector<PDFScreen> m_aScreens; /// Contains embedded files. std::vector<PDFEmbeddedFile> m_aEmbeddedFiles; + + std::vector<PDFDocumentAttachedFile> m_aDocumentAttachedFiles; + /* makes correctly encoded for export to PDF URLS */ css::uno::Reference< css::util::XURLTransformer > m_xTrans; @@ -810,11 +823,20 @@ private: std::unique_ptr<ZCodec> m_pCodec; std::unique_ptr<SvMemoryStream> m_pMemStream; - std::vector< PDFAddStream > m_aAdditionalStreams; std::set< PDFWriter::ErrorCode > m_aErrors; ::comphelper::Hash m_DocDigest; + sal_uInt64 getCurrentFilePosition() + { + sal_uInt64 nPosition{}; + if (osl::File::E_None != m_aFile.getPos(nPosition)) + { + m_aFile.close(); + m_bOpen = false; + } + return nPosition; + } /* variables for PDF security i12626 @@ -1317,8 +1339,11 @@ public: // controls sal_Int32 createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr = -1 ); - // additional streams - void addStream( const OUString& rMimeType, PDFOutputStream* pStream ); + // attached file + void addDocumentAttachedFile(OUString const& rFileName, OUString const& rMimeType, std::unique_ptr<PDFOutputStream> rStream); + + sal_Int32 addEmbeddedFile(BinaryDataContainer const & rDataContainer); + sal_Int32 addEmbeddedFile(std::unique_ptr<PDFOutputStream> rStream, OUString const& rMimeType); // helper: eventually begin marked content sequence and // emit a comment in debug case diff --git a/vcl/source/gdi/pdfwriter.cxx b/vcl/source/gdi/pdfwriter.cxx index 294d071db154..7d53f45c4699 100644 --- a/vcl/source/gdi/pdfwriter.cxx +++ b/vcl/source/gdi/pdfwriter.cxx @@ -443,9 +443,9 @@ PDFOutputStream::~PDFOutputStream() { } -void PDFWriter::AddStream( const OUString& rMimeType, PDFOutputStream* pStream ) +void PDFWriter::AddAttachedFile(OUString const& rFileName, OUString const& rMimeType, std::unique_ptr<PDFOutputStream> pStream) { - xImplementation->addStream( rMimeType, pStream ); + xImplementation->addDocumentAttachedFile(rFileName, rMimeType, std::move(pStream)); } std::set< PDFWriter::ErrorCode > const & PDFWriter::GetErrors() const diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index e0f868d5d926..2a87f8cb4e23 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -3423,6 +3423,35 @@ bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ) return true; } +void PDFWriterImpl::addDocumentAttachedFile(OUString const& rFileName, OUString const& rMimeType, std::unique_ptr<PDFOutputStream> rStream) +{ + sal_Int32 nObjectID = addEmbeddedFile(std::move(rStream), rMimeType); + auto& rAttachedFile = m_aDocumentAttachedFiles.emplace_back(); + rAttachedFile.maFilename = rFileName; + rAttachedFile.maMimeType = rMimeType; + rAttachedFile.mnEmbeddedFileObjectId = nObjectID; + rAttachedFile.mnObjectId = createObject(); +} + +sal_Int32 PDFWriterImpl::addEmbeddedFile(std::unique_ptr<PDFOutputStream> rStream, OUString const& rMimeType) +{ + sal_Int32 aObjectID = createObject(); + auto& rEmbedded = m_aEmbeddedFiles.emplace_back(); + rEmbedded.m_nObject = aObjectID; + rEmbedded.m_aSubType = rMimeType; + rEmbedded.m_pStream = std::move(rStream); + return aObjectID; +} + +sal_Int32 PDFWriterImpl::addEmbeddedFile(BinaryDataContainer const & rDataContainer) +{ + sal_Int32 aObjectID = createObject(); + m_aEmbeddedFiles.emplace_back(); + m_aEmbeddedFiles.back().m_nObject = aObjectID; + m_aEmbeddedFiles.back().m_aDataContainer = rDataContainer; + return aObjectID; +} + bool PDFWriterImpl::emitScreenAnnotations() { int nAnnots = m_aScreens.size(); @@ -4871,26 +4900,81 @@ bool PDFWriterImpl::emitAnnotations() return true; } +class PDFStreamIf : public cppu::WeakImplHelper< css::io::XOutputStream > +{ + VclPtr<PDFWriterImpl> m_pWriter; + bool m_bWrite; + public: + explicit PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} + + virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override + { + if( m_bWrite && aData.hasElements() ) + { + sal_Int32 nBytes = aData.getLength(); + m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); + } + } + virtual void SAL_CALL flush() override {} + virtual void SAL_CALL closeOutput() override + { + m_bWrite = false; + } +}; + bool PDFWriterImpl::emitEmbeddedFiles() { - for (const auto& rEmbeddedFile : m_aEmbeddedFiles) + for (auto& rEmbeddedFile : m_aEmbeddedFiles) { if (!updateObject(rEmbeddedFile.m_nObject)) continue; + sal_Int32 nSizeObject = createObject(); + OStringBuffer aLine; aLine.append(rEmbeddedFile.m_nObject); aLine.append(" 0 obj\n"); - aLine.append("<< /Type /EmbeddedFile /Length "); - aLine.append(static_cast<sal_Int64>(rEmbeddedFile.m_aDataContainer.getSize())); - aLine.append(" >>\nstream\n"); + aLine.append("<< /Type /EmbeddedFile"); + if (!rEmbeddedFile.m_aSubType.isEmpty()) + { + aLine.append("/Subtype /"); + appendName(rEmbeddedFile.m_aSubType, aLine); + } + aLine.append(" /Length "); + appendObjectReference(nSizeObject, aLine); + aLine.append(">>\nstream\n"); + checkAndEnableStreamEncryption(rEmbeddedFile.m_nObject); CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); + disableStreamEncryption(); aLine.setLength(0); - CHECK_RETURN(writeBuffer(rEmbeddedFile.m_aDataContainer.getData(), rEmbeddedFile.m_aDataContainer.getSize())); - + sal_Int64 nSize{}; + if (!rEmbeddedFile.m_aDataContainer.isEmpty()) + { + nSize = rEmbeddedFile.m_aDataContainer.getSize(); + CHECK_RETURN(writeBuffer(rEmbeddedFile.m_aDataContainer.getData(), rEmbeddedFile.m_aDataContainer.getSize())); + } + else if (rEmbeddedFile.m_pStream) + { + sal_uInt64 nBegin = getCurrentFilePosition(); + css::uno::Reference<css::io::XOutputStream> xStream(new PDFStreamIf(this)); + rEmbeddedFile.m_pStream->write(xStream); + rEmbeddedFile.m_pStream.reset(); + xStream.clear(); + nSize = sal_Int64(getCurrentFilePosition() - nBegin); + } aLine.append("\nendstream\nendobj\n\n"); CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); + aLine.setLength(0); + + if (!updateObject(nSizeObject)) + return false; + aLine.append(nSizeObject); + aLine.append(" 0 obj\n"); + aLine.append(nSize); + aLine.append("\nendobj\n\n"); + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + return false; } return true; } @@ -5001,6 +5085,25 @@ bool PDFWriterImpl::emitCatalog() CHECK_RETURN( emitAnnotations() ); CHECK_RETURN( emitEmbeddedFiles() ); + // emit attached files + for (auto & rAttachedFile : m_aDocumentAttachedFiles) + { + if (!updateObject(rAttachedFile.mnObjectId)) + return false; + aLine.setLength( 0 ); + + appendObjectID(rAttachedFile.mnObjectId, aLine); + aLine.append("<</Type/Filespec /F"); + aLine.append('<'); + PDFWriter::AppendUnicodeTextString(rAttachedFile.maFilename, aLine); + aLine.append('>'); + aLine.append(" /EF <</F "); + appendObjectReference(rAttachedFile.mnEmbeddedFileObjectId, aLine); + aLine.append(">>"); + aLine.append(">>\nendobj\n\n"); + CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); + } + // emit Catalog m_nCatalogObject = createObject(); if( ! updateObject( m_nCatalogObject ) ) @@ -5020,6 +5123,22 @@ bool PDFWriterImpl::emitCatalog() aLine.append( " 0 R\n" ); } + if (!m_aDocumentAttachedFiles.empty()) + { + aLine.append("/Names "); + aLine.append("<</EmbeddedFiles <</Names ["); + for (auto & rAttachedFile : m_aDocumentAttachedFiles) + { + aLine.append('<'); + PDFWriter::AppendUnicodeTextString(rAttachedFile.maFilename, aLine); + aLine.append('>'); + aLine.append(' '); + appendObjectReference(rAttachedFile.mnObjectId, aLine); + } + aLine.append("]>>>>"); + aLine.append("\n" ); + } + if( m_aContext.PageLayout != PDFWriter::DefaultLayout ) switch( m_aContext.PageLayout ) { @@ -5797,19 +5916,21 @@ bool PDFWriterImpl::emitTrailer() aLine.append( aDocChecksum ); aLine.append( "\n" ); } - if( !m_aAdditionalStreams.empty() ) + + if (!m_aDocumentAttachedFiles.empty()) { aLine.append( "/AdditionalStreams [" ); - for(const PDFAddStream & rAdditionalStream : m_aAdditionalStreams) + for (auto const& rAttachedFile : m_aDocumentAttachedFiles) { aLine.append( "/" ); - appendName( rAdditionalStream.m_aMimeType, aLine ); - aLine.append( " " ); - aLine.append( rAdditionalStream.m_nStreamObject ); - aLine.append( " 0 R\n" ); + appendName(rAttachedFile.maMimeType, aLine); + aLine.append(" "); + appendObjectReference(rAttachedFile.mnEmbeddedFileObjectId, aLine); + aLine.append("\n"); } aLine.append( "]\n" ); } + aLine.append( ">>\n" "startxref\n" ); aLine.append( static_cast<sal_Int64>(nXRefOffset) ); @@ -5927,104 +6048,6 @@ void PDFWriterImpl::sortWidgets() // FIXME: implement tab order in structure tree for PDF 1.5 } -class PDFStreamIf : - public cppu::WeakImplHelper< css::io::XOutputStream > -{ - VclPtr<PDFWriterImpl> m_pWriter; - bool m_bWrite; - public: - explicit PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} - - virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override; - virtual void SAL_CALL flush() override; - virtual void SAL_CALL closeOutput() override; -}; - -void SAL_CALL PDFStreamIf::writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) -{ - if( m_bWrite && aData.hasElements() ) - { - sal_Int32 nBytes = aData.getLength(); - m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); - } -} - -void SAL_CALL PDFStreamIf::flush() -{ -} - -void SAL_CALL PDFStreamIf::closeOutput() -{ - m_bWrite = false; -} - -bool PDFWriterImpl::emitAdditionalStreams() -{ - unsigned int nStreams = m_aAdditionalStreams.size(); - for( unsigned int i = 0; i < nStreams; i++ ) - { - PDFAddStream& rStream = m_aAdditionalStreams[i]; - rStream.m_nStreamObject = createObject(); - sal_Int32 nSizeObject = createObject(); - - if( ! updateObject( rStream.m_nStreamObject ) ) - return false; - - OStringBuffer aLine; - aLine.append( rStream.m_nStreamObject ); - aLine.append( " 0 obj\n<</Length " ); - aLine.append( nSizeObject ); - aLine.append( " 0 R" ); - if( rStream.m_bCompress ) - aLine.append( "/Filter/FlateDecode" ); - aLine.append( ">>\nstream\n" ); - if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - return false; - sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0; - if( osl::File::E_None != m_aFile.getPos(nBeginStreamPos) ) - { - m_aFile.close(); - m_bOpen = false; - } - if( rStream.m_bCompress ) - beginCompression(); - - checkAndEnableStreamEncryption( rStream.m_nStreamObject ); - css::uno::Reference< css::io::XOutputStream > xStream( new PDFStreamIf( this ) ); - assert(rStream.m_pStream); - if (!rStream.m_pStream) - return false; - rStream.m_pStream->write( xStream ); - xStream.clear(); - delete rStream.m_pStream; - rStream.m_pStream = nullptr; - disableStreamEncryption(); - - if( rStream.m_bCompress ) - endCompression(); - - if (osl::File::E_None != m_aFile.getPos(nEndStreamPos)) - { - m_aFile.close(); - m_bOpen = false; - return false; - } - if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) - return false ; - // emit stream length object - if( ! updateObject( nSizeObject ) ) - return false; - aLine.setLength( 0 ); - aLine.append( nSizeObject ); - aLine.append( " 0 obj\n" ); - aLine.append( static_cast<sal_Int64>(nEndStreamPos-nBeginStreamPos) ); - aLine.append( "\nendobj\n\n" ); - if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) - return false; - } - return true; -} - bool PDFWriterImpl::emit() { endPage(); @@ -6043,9 +6066,6 @@ bool PDFWriterImpl::emit() } #endif - // emit additional streams - CHECK_RETURN( emitAdditionalStreams() ); - // emit catalog CHECK_RETURN( emitCatalog() ); @@ -9461,10 +9481,8 @@ void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObject if (m_aContext.UseReferenceXObject) { // Store the original PDF data as an embedded file. - m_aEmbeddedFiles.emplace_back(); - m_aEmbeddedFiles.back().m_nObject = createObject(); - m_aEmbeddedFiles.back().m_aDataContainer = rDataContainer; - rEmit.m_nEmbeddedObject = m_aEmbeddedFiles.back().m_nObject; + auto nObjectID = addEmbeddedFile(rDataContainer); + rEmit.m_nEmbeddedObject = nObjectID; } else { @@ -11493,20 +11511,6 @@ sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sa return nNewWidget; } -void PDFWriterImpl::addStream( const OUString& rMimeType, PDFOutputStream* pStream ) -{ - if( pStream ) - { - m_aAdditionalStreams.emplace_back( ); - PDFAddStream& rStream = m_aAdditionalStreams.back(); - rStream.m_aMimeType = !rMimeType.isEmpty() - ? rMimeType - : OUString( "application/octet-stream" ); - rStream.m_pStream = pStream; - rStream.m_bCompress = false; - } -} - void PDFWriterImpl::MARK( const char* pString ) { beginStructureElementMCSeq(); |