tdf#107089 PDF export of PDF images: handle mixed filters of page streams
It's allowed to compress different page streams differently, and we must
have a single object stream for our form XObject, so just incompress all
of them to be consistent.
Change-Id: I7a20dc2084a902a37dcefa3420d59a576f120bcd
Reviewed-on: https://gerrit.libreoffice.org/36409
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
Tested-by: Jenkins <ci@libreoffice.org>
diff --git a/vcl/qa/cppunit/pdfexport/data/tdf107089.odt b/vcl/qa/cppunit/pdfexport/data/tdf107089.odt
new file mode 100644
index 0000000..5aaaab9
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/tdf107089.odt
Binary files differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index aacf36b..1b9eaf1 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -54,6 +54,7 @@ public:
void testTdf106972Pdf17();
void testTdf107013();
void testTdf107018();
void testTdf107089();
#endif
CPPUNIT_TEST_SUITE(PdfExportTest);
@@ -67,6 +68,7 @@ public:
CPPUNIT_TEST(testTdf106972Pdf17);
CPPUNIT_TEST(testTdf107013);
CPPUNIT_TEST(testTdf107018);
CPPUNIT_TEST(testTdf107089);
#endif
CPPUNIT_TEST_SUITE_END();
};
@@ -452,6 +454,42 @@ void PdfExportTest::testTdf107018()
// copying the page stream of a PDF image.
CPPUNIT_ASSERT_EQUAL(OString("Pages"), pName->GetValue());
}
void PdfExportTest::testTdf107089()
{
vcl::filter::PDFDocument aDocument;
load("tdf107089.odt", aDocument);
// Get access to the only image on the only page.
std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
CPPUNIT_ASSERT(pResources);
auto pXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
CPPUNIT_ASSERT(pXObjects);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
CPPUNIT_ASSERT(pXObject);
// Get access to the form object inside the image.
auto pXObjectResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
CPPUNIT_ASSERT(pXObjectResources);
auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObjectResources->LookupElement("XObject"));
CPPUNIT_ASSERT(pXObjectForms);
vcl::filter::PDFObjectElement* pForm = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
CPPUNIT_ASSERT(pForm);
// Make sure 'Hello' is part of the form object's stream.
vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
CPPUNIT_ASSERT(pStream);
SvMemoryStream& rObjectStream = pStream->GetMemory();
OString aHello("Hello");
auto pStart = static_cast<const char*>(rObjectStream.GetData());
const char* pEnd = pStart + rObjectStream.GetSize();
auto it = std::search(pStart, pEnd, aHello.getStr(), aHello.getStr() + aHello.getLength());
// This failed, 'Hello' was part only a mixed compressed/uncompressed stream, i.e. garbage.
CPPUNIT_ASSERT(it != pEnd);
}
#endif
CPPUNIT_TEST_SUITE_REGISTRATION(PdfExportTest);
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 218ee88..f05c94e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11178,7 +11178,8 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
"ColorSpace",
"ExtGState",
"Font",
"XObject"
"XObject",
"Shading"
};
for (const auto& rKey : aKeys)
aLine.append(copyExternalResources(*pPage, rKey, aCopiedResources));
@@ -11189,14 +11190,6 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
aLine.append(aSize.Height());
aLine.append(" ]");
// For now assume that all the content streams have the same filter.
auto pFilter = dynamic_cast<filter::PDFNameElement*>(aContentStreams[0]->Lookup("Filter"));
if (pFilter)
{
aLine.append(" /Filter /");
aLine.append(pFilter->GetValue());
}
aLine.append(" /Length ");
sal_Int32 nLength = 0;
@@ -11212,8 +11205,31 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
SvMemoryStream& rPageStream = pPageStream->GetMemory();
nLength += rPageStream.GetSize();
aStream.append(static_cast<const sal_Char*>(rPageStream.GetData()), rPageStream.GetSize());
auto pFilter = dynamic_cast<filter::PDFNameElement*>(pContent->Lookup("Filter"));
if (pFilter)
{
if (pFilter->GetValue() != "FlateDecode")
continue;
SvMemoryStream aMemoryStream;
ZCodec aZCodec;
rPageStream.Seek(0);
aZCodec.BeginCompression();
aZCodec.Decompress(rPageStream, aMemoryStream);
if (!aZCodec.EndCompression())
{
SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: decompression failed");
continue;
}
nLength += aMemoryStream.GetSize();
aStream.append(static_cast<const sal_Char*>(aMemoryStream.GetData()), aMemoryStream.GetSize());
}
else
{
nLength += rPageStream.GetSize();
aStream.append(static_cast<const sal_Char*>(rPageStream.GetData()), rPageStream.GetSize());
}
}
aLine.append(nLength);