summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2017-04-11 16:50:53 +0200
committerMiklos Vajna <vmiklos@collabora.co.uk>2017-04-12 10:46:01 +0200
commit198c3a5929e183ed710c8838844148bd0c1a533a (patch)
tree0294783e43423a26c00a05eb9f646d95e9f6e9d0 /vcl
parent4ea13cdfebb24cdb59af37000f216e8201f87b22 (diff)
Related: tdf#106972 vcl PDF import: downgrade PDF >= 1.5
There are two problems with these newer PDF versions: - the current PDF export code doesn't know how to roundtrip such PDF images (needs work on both the import and export side) - upgrading the default PDF export version would upset readers who can't parse PDF >= 1.5 So instead of raising the default PDF export version, for now just be conservative and depend on pdfium to downgrade the PDF image version to 1.4 if it would be higher. Given that this modifies the input of the graphic filter this also needs changes in the ODF export, so that the filter result will contain that downgraded data, not the original one. Reviewed-on: https://gerrit.libreoffice.org/36413 Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk> Tested-by: Jenkins <ci@libreoffice.org> (cherry picked from commit 37bdf1659ddb11d8706289511623cc7c8b0d264b) Conflicts: vcl/source/filter/ipdf/pdfread.cxx Change-Id: I1efa97af8110e9a6ee3e8a7339bcc7d70457cfb0
Diffstat (limited to 'vcl')
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport.cxx7
-rw-r--r--vcl/source/filter/ipdf/pdfread.cxx107
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx32
3 files changed, 109 insertions, 37 deletions
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 f11f55a0b0f7..f8754405d0b7 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -17,6 +17,7 @@
#endif
#include <fpdfview.h>
#include <fpdf_edit.h>
+#include <fpdf_save.h>
#ifdef WNT
#include <postwin.h>
#endif
@@ -31,6 +32,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)
{
@@ -94,6 +118,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)
{
@@ -102,6 +190,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
}
@@ -116,10 +211,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 2a68cbf683a3..2c94de56c183 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -665,32 +665,6 @@ static void appendDestinationName( const OUString& rString, OStringBuffer& rBuff
}
//<--- i56629
-/// 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" );
@@ -12487,7 +12461,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)
@@ -12556,7 +12530,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 );
@@ -12668,7 +12642,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();