diff options
-rw-r--r-- | svx/source/xml/xmlgrhlp.cxx | 15 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/pdfexport.cxx | 7 | ||||
-rw-r--r-- | vcl/source/filter/ipdf/pdfread.cxx | 107 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 32 |
4 files changed, 123 insertions, 38 deletions
diff --git a/svx/source/xml/xmlgrhlp.cxx b/svx/source/xml/xmlgrhlp.cxx index 04b92711aa69..3946bb94c3c0 100644 --- a/svx/source/xml/xmlgrhlp.cxx +++ b/svx/source/xml/xmlgrhlp.cxx @@ -555,7 +555,20 @@ bool SvXMLGraphicHelper::ImplWriteGraphic( const OUString& rPictureStorageName, std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aStream.xStream )); if( bUseGfxLink && aGfxLink.GetDataSize() && aGfxLink.GetData() ) - pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize()); + { + const uno::Sequence<sal_Int8>& rPdfData = aGraphic.getPdfData(); + if (rPdfData.hasElements()) + { + // The graphic has PDF data attached to it, use that. + // vcl::ImportPDF() possibly downgraded the PDF data from a + // higher PDF version, while aGfxLink still contains the + // original data provided by the user. + pStream->WriteBytes(rPdfData.getConstArray(), rPdfData.getLength()); + bRet = (pStream->GetError() == 0); + } + else + pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize()); + } else { if( aGraphic.GetType() == GraphicType::Bitmap ) diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 1b9eaf153d2c..76cad0f2a9af 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -383,10 +383,9 @@ void PdfExportTest::testTdf106972Pdf17() vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(pXObjects->GetItems().begin()->first); CPPUNIT_ASSERT(pXObject); - // This failed, the "image" had resources; that typically means we tried to - // preserve the original PDF markup here; which is not OK till our default - // output is PDF 1.4, and this bugdoc has PDF 1.7 data. - CPPUNIT_ASSERT(!pXObject->Lookup("Resources")); + // Assert that we now attempt to preserve the original PDF data, even if + // the original input was PDF >= 1.4. + CPPUNIT_ASSERT(pXObject->Lookup("Resources")); } void PdfExportTest::testTdf107013() diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx index b2f68d02b07e..f1a7e2b52a17 100644 --- a/vcl/source/filter/ipdf/pdfread.cxx +++ b/vcl/source/filter/ipdf/pdfread.cxx @@ -14,6 +14,7 @@ #if HAVE_FEATURE_PDFIUM #include <fpdfview.h> #include <fpdf_edit.h> +#include <fpdf_save.h> #endif #include <vcl/bitmapaccess.hxx> @@ -25,6 +26,29 @@ namespace #if HAVE_FEATURE_PDFIUM +/// Callback class to be used with FPDF_SaveWithVersion(). +struct CompatibleWriter : public FPDF_FILEWRITE +{ +public: + CompatibleWriter(); + static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite, const void* pData, unsigned long nSize); + + SvMemoryStream m_aStream; +}; + +CompatibleWriter::CompatibleWriter() +{ + FPDF_FILEWRITE::version = 1; + FPDF_FILEWRITE::WriteBlock = CompatibleWriter::WriteBlockCallback; +} + +int CompatibleWriter::WriteBlockCallback(FPDF_FILEWRITE* pFileWrite, const void* pData, unsigned long nSize) +{ + auto pImpl = static_cast<CompatibleWriter*>(pFileWrite); + pImpl->m_aStream.WriteBytes(pData, nSize); + return 1; +} + /// Convert to inch, then assume 96 DPI. double pointToPixel(double fPoint) { @@ -88,6 +112,70 @@ bool generatePreview(SvStream& rStream, Graphic& rGraphic) return true; } + +/// Decide if PDF data is old enough to be compatible. +bool isCompatible(SvStream& rInStream) +{ + // %PDF-x.y + sal_uInt8 aFirstBytes[8]; + rInStream.Seek(STREAM_SEEK_TO_BEGIN); + sal_uLong nRead = rInStream.ReadBytes(aFirstBytes, 8); + if (nRead < 8) + return false; + + if ((aFirstBytes[0] != '%' || aFirstBytes[1] != 'P' || aFirstBytes[2] != 'D' || aFirstBytes[3] != 'F' || aFirstBytes[4] != '-')) + return false; + + sal_Int32 nMajor = OString(aFirstBytes[5]).toInt32(); + sal_Int32 nMinor = OString(aFirstBytes[7]).toInt32(); + if (nMajor > 1 || (nMajor == 1 && nMinor > 4)) + return false; + + return true; +} + +/// Takes care of transparently downgrading the version of the PDF stream in +/// case it's too new for our PDF export. +bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream) +{ + bool bCompatible = isCompatible(rInStream); + rInStream.Seek(STREAM_SEEK_TO_BEGIN); + if (bCompatible) + // Not converting. + rOutStream.WriteStream(rInStream); + else + { + // Downconvert to PDF-1.4. + FPDF_LIBRARY_CONFIG aConfig; + aConfig.version = 2; + aConfig.m_pUserFontPaths = nullptr; + aConfig.m_pIsolate = nullptr; + aConfig.m_v8EmbedderSlot = 0; + FPDF_InitLibraryWithConfig(&aConfig); + + // Read input into a buffer. + SvMemoryStream aInBuffer; + aInBuffer.WriteStream(rInStream); + + // Load the buffer using pdfium. + FPDF_DOCUMENT pPdfDocument = FPDF_LoadMemDocument(aInBuffer.GetData(), aInBuffer.GetSize(), /*password=*/nullptr); + if (!pPdfDocument) + return false; + + CompatibleWriter aWriter; + // 14 means PDF-1.4. + if (!FPDF_SaveWithVersion(pPdfDocument, &aWriter, 0, 14)) + return false; + + FPDF_CloseDocument(pPdfDocument); + FPDF_DestroyLibrary(); + + aWriter.m_aStream.Seek(STREAM_SEEK_TO_BEGIN); + rOutStream.WriteStream(aWriter.m_aStream); + } + + return rOutStream.good(); +} #else bool generatePreview(SvStream& rStream, Graphic& rGraphic) { @@ -96,6 +184,13 @@ bool generatePreview(SvStream& rStream, Graphic& rGraphic) return true; } + +bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream) +{ + rInStream.Seek(STREAM_SEEK_TO_BEGIN); + rOutStream.WriteStream(rInStream); + return rOutStream.good(); +} #endif // HAVE_FEATURE_PDFIUM } @@ -110,10 +205,14 @@ bool ImportPDF(SvStream& rStream, Graphic& rGraphic) return false; // Save the original PDF stream for later use. - rStream.Seek(STREAM_SEEK_TO_END); - uno::Sequence<sal_Int8> aPdfData(rStream.Tell()); - rStream.Seek(STREAM_SEEK_TO_BEGIN); - rStream.ReadBytes(aPdfData.getArray(), aPdfData.getLength()); + SvMemoryStream aMemoryStream; + if (!getCompatibleStream(rStream, aMemoryStream)) + return false; + + aMemoryStream.Seek(STREAM_SEEK_TO_END); + uno::Sequence<sal_Int8> aPdfData(aMemoryStream.Tell()); + aMemoryStream.Seek(STREAM_SEEK_TO_BEGIN); + aMemoryStream.ReadBytes(aPdfData.getArray(), aPdfData.getLength()); rGraphic.setPdfData(aPdfData); return true; diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index f05c94e78e32..4c7542de2bfa 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -676,32 +676,6 @@ static void appendDestinationName( const OUString& rString, OStringBuffer& rBuff } } -/// Decide if rGraphic has PDF data that is possible to embed in our output. -static bool hasPdfData(const Graphic& rGraphic, bool bUseReferenceXObject) -{ - const css::uno::Sequence<sal_Int8>& rData = rGraphic.getPdfData(); - - if (rData.getLength() < 8) - return false; - - if (rData[0] != '%' || rData[1] != 'P' || rData[2] != 'D' || rData[3] != 'F' || rData[4] != '-') - // Unexpected header. - return false; - - if (bUseReferenceXObject) - // This is possible for all versions. - return true; - - sal_Int32 nMajor = OString(rData[5]).toInt32(); - sal_Int32 nMinor = OString(rData[7]).toInt32(); - - if (nMajor > 1 || (nMajor == 1 && nMinor > 4)) - // This is PDF-1.5 or newer, can't embed into PDF-1.4. - return false; - - return true; -} - void PDFWriter::AppendUnicodeTextString(const OUString& rString, OStringBuffer& rBuffer) { rBuffer.append( "FEFF" ); @@ -11648,7 +11622,7 @@ void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObject // no pdf data. rEmit.m_nBitmapObject = nBitmapObject; - if (!hasPdfData(rGraphic, m_aContext.UseReferenceXObject)) + if (!rGraphic.getPdfData().hasElements()) return; if (m_aContext.UseReferenceXObject) @@ -11717,7 +11691,7 @@ void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const { m_aJPGs.emplace( m_aJPGs.begin() ); JPGEmit& rEmit = m_aJPGs.front(); - if (!hasPdfData(rGraphic, m_aContext.UseReferenceXObject) || m_aContext.UseReferenceXObject) + if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject) rEmit.m_nObject = createObject(); rEmit.m_aID = aID; rEmit.m_pStream.reset( pStream ); @@ -11829,7 +11803,7 @@ const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx m_aBitmaps.push_front( BitmapEmit() ); m_aBitmaps.front().m_aID = aID; m_aBitmaps.front().m_aBitmap = aBitmap; - if (!hasPdfData(rGraphic, m_aContext.UseReferenceXObject) || m_aContext.UseReferenceXObject) + if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject) m_aBitmaps.front().m_nObject = createObject(); createEmbeddedFile(rGraphic, m_aBitmaps.front().m_aReferenceXObject, m_aBitmaps.front().m_nObject); it = m_aBitmaps.begin(); |