diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2023-01-23 12:33:39 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2023-01-24 10:50:49 +0000 |
commit | e052f6e1d49a5289411b31561d6e310bf414d896 (patch) | |
tree | 8aa7d8683589077346abe32b4a6c8e6d0d777ae8 /vcl | |
parent | 217ef2ed9b8a757b7b02feac799621d20d0f312e (diff) |
tdf#66580 write ODF document as an attachment in hybrid mode
This changes the hybrid mode so that the ODF document is written
as an PDF compatible file attachment into the PDF document. It also
keeps writing the /AdditionalStreams element into the trailer to
keep backwards compatibility for now.
Change-Id: Ica31159cfbd591c6741e3da62c42d1fefd085696
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146053
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl')
-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 |
3 files changed, 166 insertions, 137 deletions
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(); |