include/vcl/filter/pdfdocument.hxx                  |    7 +-
 vcl/qa/cppunit/pdfexport/data/xnview-colorspace.pdf |binary
 vcl/qa/cppunit/pdfexport/pdfexport2.cxx             |   66 ++++++++++++++++++++
 vcl/source/filter/ipdf/pdfdocument.cxx              |   16 ++++
 vcl/source/gdi/pdfobjectcopier.cxx                  |   15 ++++
 5 files changed, 103 insertions(+), 1 deletion(-)

New commits:
commit 2956d02e2b696ad91ae7b6b92ceb2919a6e4185a
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Tue Jul 23 15:39:24 2024 +0100
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Wed Jul 24 12:06:46 2024 +0200

    Resolves: tdf#162161 reexport of specific pdf appears blank
    
    the contents of the referenced colorspace obj disappears so it gets
    rendered blank
    
    Change-Id: Iaa76d355b5903c695e74edadc329043156bad3b6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170904
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170929

diff --git a/include/vcl/filter/pdfdocument.hxx 
b/include/vcl/filter/pdfdocument.hxx
index dce7d976ee37..05e471e65e01 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -44,6 +44,7 @@ class PDFDocument;
 class PDFDictionaryElement;
 class PDFArrayElement;
 class PDFStreamElement;
+class PDFNameElement;
 class PDFNumberElement;
 
 /// A byte range in a PDF file.
@@ -73,6 +74,8 @@ class VCL_DLLPUBLIC PDFObjectElement final : public PDFElement
     double m_fGenerationValue;
     /// If set, the object contains this number element (outside any 
dictionary/array).
     PDFNumberElement* m_pNumberElement;
+    /// If set, the object contains this name element (outside any 
dictionary/array).
+    PDFNameElement* m_pNameElement;
     /// Position after the '<<' token.
     sal_uInt64 m_nDictionaryOffset;
     /// Length of the dictionary buffer till (before) the '>>' token.
@@ -113,7 +116,9 @@ public:
     PDFDictionaryElement* GetDictionary();
     SAL_DLLPRIVATE void SetDictionary(PDFDictionaryElement* 
pDictionaryElement);
     SAL_DLLPRIVATE void SetNumberElement(PDFNumberElement* pNumberElement);
-    SAL_DLLPRIVATE PDFNumberElement* GetNumberElement() const;
+    PDFNumberElement* GetNumberElement() const;
+    SAL_DLLPRIVATE void SetNameElement(PDFNameElement* pNameElement);
+    PDFNameElement* GetNameElement() const;
     /// Get access to the parsed key-value items from the object dictionary.
     const std::map<OString, PDFElement*>& GetDictionaryItems();
     SAL_DLLPRIVATE const std::vector<PDFReferenceElement*>& 
GetDictionaryReferences() const;
diff --git a/vcl/qa/cppunit/pdfexport/data/xnview-colorspace.pdf 
b/vcl/qa/cppunit/pdfexport/data/xnview-colorspace.pdf
new file mode 100644
index 000000000000..0370c2bbd2e6
Binary files /dev/null and 
b/vcl/qa/cppunit/pdfexport/data/xnview-colorspace.pdf differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
index bdcd57ee80b4..1ecdb98608bc 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -5268,6 +5268,72 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf101686)
     CPPUNIT_ASSERT_EQUAL(u"Textbox"_ustr, aText[2].trim());
 }
 
+// tdf#162161 reexport appears to have blank image
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportXnViewColorspace)
+{
+    // We need to enable PDFium import (and make sure to disable after the 
test)
+    bool bResetEnvVar = false;
+    if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+    {
+        bResetEnvVar = true;
+        osl_setEnvironment(u"LO_IMPORT_USE_PDFIUM"_ustr.pData, 
u"1"_ustr.pData);
+    }
+    comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+        if (bResetEnvVar)
+            osl_clearEnvironment(u"LO_IMPORT_USE_PDFIUM"_ustr.pData);
+    });
+
+    // Load the PDF and save as PDF
+    vcl::filter::PDFDocument aDocument;
+    load(u"xnview-colorspace.pdf", aDocument);
+
+    std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+    CPPUNIT_ASSERT_EQUAL(size_t(1), aPages.size());
+
+    // Get access to the only image on the only page.
+    vcl::filter::PDFObjectElement* pResources = 
aPages[0]->LookupObject("Resources"_ostr);
+    CPPUNIT_ASSERT(pResources);
+
+    auto pXObjects
+        = 
dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"_ostr));
+    CPPUNIT_ASSERT(pXObjects);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects->GetItems().size());
+    vcl::filter::PDFObjectElement* pXObject
+        = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+    CPPUNIT_ASSERT(pXObject);
+
+    auto pSubResources
+        = 
dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"_ostr));
+    CPPUNIT_ASSERT(pSubResources);
+    pXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+        pSubResources->LookupElement("XObject"_ostr));
+    CPPUNIT_ASSERT(pXObjects);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects->GetItems().size());
+    pXObject = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+    CPPUNIT_ASSERT(pXObject);
+
+    pSubResources
+        = 
dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"_ostr));
+    CPPUNIT_ASSERT(pSubResources);
+    pXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+        pSubResources->LookupElement("XObject"_ostr));
+    CPPUNIT_ASSERT(pXObjects);
+    CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects->GetItems().size());
+    pXObject = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+    CPPUNIT_ASSERT(pXObject);
+
+    // Dig all the way down to this element which is originally
+    // 8 0 obj
+    // /DeviceRGB
+    // endobj
+    // and appeared blank when we lost the /DeviceRGB line
+    auto pColorspace = pXObject->LookupObject("ColorSpace"_ostr);
+    CPPUNIT_ASSERT(pColorspace);
+    auto pColorSpaceElement = pColorspace->GetNameElement();
+    CPPUNIT_ASSERT(pColorSpaceElement);
+    CPPUNIT_ASSERT_EQUAL("DeviceRGB"_ostr, pColorSpaceElement->GetValue());
+}
+
 } // end anonymous namespace
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx 
b/vcl/source/filter/ipdf/pdfdocument.cxx
index 159db9a38ba2..dac68d5344af 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -1102,6 +1102,14 @@ bool PDFDocument::Tokenize(SvStream& rStream, 
TokenizeMode eMode,
                     pObjectStream = pObject;
                 else
                     pObjectKey = pNameElement;
+
+                if (bInObject && !nDepth && pObject)
+                {
+                    // Name element inside an object, but outside a
+                    // dictionary / array: remember it.
+                    pObject->SetNameElement(pNameElement);
+                }
+
                 break;
             }
             case '(':
@@ -2291,6 +2299,7 @@ PDFObjectElement::PDFObjectElement(PDFDocument& rDoc, 
double fObjectValue, doubl
     , m_fObjectValue(fObjectValue)
     , m_fGenerationValue(fGenerationValue)
     , m_pNumberElement(nullptr)
+    , m_pNameElement(nullptr)
     , m_nDictionaryOffset(0)
     , m_nDictionaryLength(0)
     , m_pDictionaryElement(nullptr)
@@ -2462,6 +2471,13 @@ void 
PDFObjectElement::SetNumberElement(PDFNumberElement* pNumberElement)
 
 PDFNumberElement* PDFObjectElement::GetNumberElement() const { return 
m_pNumberElement; }
 
+void PDFObjectElement::SetNameElement(PDFNameElement* pNameElement)
+{
+    m_pNameElement = pNameElement;
+}
+
+PDFNameElement* PDFObjectElement::GetNameElement() const { return 
m_pNameElement; }
+
 const std::vector<PDFReferenceElement*>& 
PDFObjectElement::GetDictionaryReferences() const
 {
     return m_aDictionaryReferences;
diff --git a/vcl/source/gdi/pdfobjectcopier.cxx 
b/vcl/source/gdi/pdfobjectcopier.cxx
index 0d07c0df7f67..56c3ba6e8138 100644
--- a/vcl/source/gdi/pdfobjectcopier.cxx
+++ b/vcl/source/gdi/pdfobjectcopier.cxx
@@ -139,6 +139,21 @@ sal_Int32 
PDFObjectCopier::copyExternalResource(SvMemoryStream& rDocBuffer,
         pNumber->writeString(aLine);
         aLine.append("
");
     }
+    // If the object has a name element outside a dictionary or array, copy 
that.
+    else if (filter::PDFNameElement* pName = rObject.GetNameElement())
+    {
+        // currently just handle the exact case seen in the real world
+        if (pName->GetValue() == "DeviceRGB")
+        {
+            pName->writeString(aLine);
+            aLine.append("
");
+        }
+        else
+        {
+            SAL_INFO("vcl.pdfwriter",
+                     "PDFObjectCopier::copyExternalResource: skipping: " << 
pName->GetValue());
+        }
+    }
 
     // We have the whole object, now write it to the output.
     if (!m_rContainer.updateObject(nObject))

Reply via email to