summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2023-01-23 12:33:39 +0900
committerTomaž Vajngerl <quikee@gmail.com>2023-01-24 10:50:49 +0000
commite052f6e1d49a5289411b31561d6e310bf414d896 (patch)
tree8aa7d8683589077346abe32b4a6c8e6d0d777ae8
parent217ef2ed9b8a757b7b02feac799621d20d0f312e (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>
-rw-r--r--filter/source/pdf/pdfexport.cxx32
-rw-r--r--include/vcl/pdfwriter.hxx18
-rw-r--r--vcl/inc/pdf/pdfwriter_impl.hxx33
-rw-r--r--vcl/source/gdi/pdfwriter.cxx4
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx266
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();