summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2017-04-05 15:35:13 +0200
committerMiklos Vajna <vmiklos@collabora.co.uk>2017-04-05 19:07:32 +0200
commit242a9b634213acf03cabc373928555dc81afc672 (patch)
tree020f6139fb8bd0164d913749f39adcbae75a1d7f /vcl
parent53d53650c29f1fa1faddbf7a95ccdf2e84f1994a (diff)
tdf#106972 vcl PDF export, PDF images: handle indirect font references
There were a number of problems here: - the /Resources key of a page object may be an indirect object - the /Font key of a resource object may be an indirect object - the /Length key of an object may be an indirect object So in all these cases handle not only a direct dictionary / number but also when we have a reference-to-dictionary/number. Change-Id: Ie74371f0ba43a133a1299843ef20cbfc75fe26d7
Diffstat (limited to 'vcl')
-rw-r--r--vcl/qa/cppunit/pdfexport/data/tdf106972.odtbin0 -> 21376 bytes
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport.cxx48
-rw-r--r--vcl/source/filter/ipdf/pdfdocument.cxx24
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx36
4 files changed, 99 insertions, 9 deletions
diff --git a/vcl/qa/cppunit/pdfexport/data/tdf106972.odt b/vcl/qa/cppunit/pdfexport/data/tdf106972.odt
new file mode 100644
index 000000000000..3fa76c49fab9
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/tdf106972.odt
Binary files differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index ede50077b62c..2bbb9f3c9f01 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -49,6 +49,7 @@ public:
void testTdf106206();
/// Tests export of PDF images without reference XObjects.
void testTdf106693();
+ void testTdf106972();
#endif
CPPUNIT_TEST_SUITE(PdfExportTest);
@@ -58,6 +59,7 @@ public:
CPPUNIT_TEST(testTdf105093);
CPPUNIT_TEST(testTdf106206);
CPPUNIT_TEST(testTdf106693);
+ CPPUNIT_TEST(testTdf106972);
#endif
CPPUNIT_TEST_SUITE_END();
};
@@ -317,6 +319,52 @@ void PdfExportTest::testTdf106206()
// This failed, object #0 was referenced.
CPPUNIT_ASSERT(bool(it == pEnd));
}
+
+void PdfExportTest::testTdf106972()
+{
+ // Import the bugdoc and export as PDF.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf106972.odt";
+ mxComponent = loadFromDesktop(aURL);
+ CPPUNIT_ASSERT(mxComponent.is());
+
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(aTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // Get access to the only form object 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 only image inside the form object.
+ auto pFormResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
+ CPPUNIT_ASSERT(pFormResources);
+ auto pImages = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pFormResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pImages);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pImages->GetItems().size());
+ vcl::filter::PDFObjectElement* pImage = pImages->LookupObject(pImages->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pImage);
+
+ // Assert resources of the image.
+ auto pImageResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pImage->Lookup("Resources"));
+ CPPUNIT_ASSERT(pImageResources);
+ // This failed: the PDF image had no Font resource.
+ CPPUNIT_ASSERT(pImageResources->LookupElement("Font"));
+}
#endif
CPPUNIT_TEST_SUITE_REGISTRATION(PdfExportTest);
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index 86aeade27137..444ec9239d20 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -880,6 +880,8 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
int nArrayDepth = 0;
// Last seen array token that's outside any dictionaries.
PDFArrayElement* pArray = nullptr;
+ // If we're inside an obj/endobj pair.
+ bool bInObject = false;
while (true)
{
char ch;
@@ -1030,6 +1032,10 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
if (it != m_aOffsetObjects.end())
m_pXRefStream = it->second;
}
+ else if (bInObject && !nDictionaryDepth && !nArrayDepth && pObject)
+ // Number element inside an object, but outside a
+ // dictionary / array: remember it.
+ pObject->SetNumberElement(pNumberElement);
}
else if (rtl::isAsciiAlpha(static_cast<unsigned char>(ch)))
{
@@ -1061,6 +1067,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
rElements.push_back(std::unique_ptr<PDFElement>(pObject));
m_aOffsetObjects[pObjectNumber->GetLocation()] = pObject;
m_aIDObjects[pObjectNumber->GetValue()] = pObject;
+ bInObject = true;
}
else
{
@@ -1150,6 +1157,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
pObjectStream = nullptr;
pObjectKey = nullptr;
}
+ bInObject = false;
}
else if (aKeyword == "true" || aKeyword == "false")
rElements.push_back(std::unique_ptr<PDFElement>(new PDFBooleanElement(aKeyword.toBoolean())));
@@ -2080,6 +2088,7 @@ PDFObjectElement::PDFObjectElement(PDFDocument& rDoc, double fObjectValue, doubl
: m_rDoc(rDoc),
m_fObjectValue(fObjectValue),
m_fGenerationValue(fGenerationValue),
+ m_pNumberElement(nullptr),
m_nDictionaryOffset(0),
m_nDictionaryLength(0),
m_pDictionaryElement(nullptr),
@@ -2477,6 +2486,16 @@ void PDFObjectElement::SetDictionary(PDFDictionaryElement* pDictionaryElement)
m_pDictionaryElement = pDictionaryElement;
}
+void PDFObjectElement::SetNumberElement(PDFNumberElement* pNumberElement)
+{
+ m_pNumberElement = pNumberElement;
+}
+
+PDFNumberElement* PDFObjectElement::GetNumberElement() const
+{
+ return m_pNumberElement;
+}
+
std::vector< std::pair<OString, PDFElement*> > PDFObjectElement::GetDictionaryItemsByOffset()
{
std::vector< std::pair<OString, PDFElement*> > aRet;
@@ -2496,8 +2515,11 @@ std::vector< std::pair<OString, PDFElement*> > PDFObjectElement::GetDictionaryIt
return aRet;
}
-const std::map<OString, PDFElement*>& PDFObjectElement::GetDictionaryItems() const
+const std::map<OString, PDFElement*>& PDFObjectElement::GetDictionaryItems()
{
+ if (m_aDictionary.empty())
+ PDFDictionaryElement::Parse(m_rDoc.GetElements(), this, m_aDictionary);
+
return m_aDictionary;
}
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index f11ef8591f59..698fdab61d6b 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -10990,6 +10990,14 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
aLine.append("]\n");
}
+ // If the object has a number element outside a dictionary or array, copy that.
+ if (filter::PDFNumberElement* pNumber = rObject.GetNumberElement())
+ {
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + pNumber->GetLocation(), pNumber->GetLength());
+ aLine.append("\n");
+ }
+
+
aLine.append("endobj\n\n");
// We have the whole object, now write it to the output.
@@ -11008,18 +11016,30 @@ OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, co
std::map<OString, sal_Int32> aRet;
// Get the rKind subset of the resource dictionary.
- auto pResources = dynamic_cast<filter::PDFDictionaryElement*>(rPage.Lookup("Resources"));
- if (!pResources)
- return OString();
-
- auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pResources->LookupElement(rKind));
- if (!pDictionary)
+ std::map<OString, filter::PDFElement*> aItems;
+ if (auto pResources = dynamic_cast<filter::PDFDictionaryElement*>(rPage.Lookup("Resources")))
+ {
+ // Resources is a direct dictionary.
+ if (auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pResources->LookupElement(rKind)))
+ aItems = pDictionary->GetItems();
+ }
+ else if (filter::PDFObjectElement* pPageResources = rPage.LookupObject("Resources"))
+ {
+ // Resources is an indirect object.
+ filter::PDFElement* pValue = pPageResources->Lookup(rKind);
+ if (auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pValue))
+ // Kind is a direct dictionary.
+ aItems = pDictionary->GetItems();
+ else if (filter::PDFObjectElement* pObject = pPageResources->LookupObject(rKind))
+ // Kind is an indirect object.
+ aItems = pObject->GetDictionaryItems();
+ }
+ if (aItems.empty())
return OString();
SvMemoryStream& rDocBuffer = rPage.GetDocument().GetEditBuffer();
- const std::map<OString, filter::PDFElement*>& rItems = pDictionary->GetItems();
- for (const auto& rItem : rItems)
+ for (const auto& rItem : aItems)
{
// For each item copy it over to our output then insert it into aRet.
auto pReference = dynamic_cast<filter::PDFReferenceElement*>(rItem.second);