filter/source/pdf/pdfexport.cxx        |   16 +++++
 vcl/qa/cppunit/pdfexport/pdfexport.cxx |   90 +++++++++++++++++++++++++++++++++
 vcl/source/gdi/pdfwriter_impl.cxx      |    4 -
 3 files changed, 108 insertions(+), 2 deletions(-)

New commits:
commit f3e4a5450cdf602c3ed176c4f66c8845482fff97
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Tue Jul 4 11:32:07 2023 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Tue Jul 4 15:20:32 2023 +0200

    tdf#152235 filter,vcl: PDF/UA export: tag watermark as Artifact
    
    Change-Id: I64e57a832678be935b69a5cea328cc252a4bf29d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153951
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    (cherry picked from commit dd4ce9b2fc7feaf288055b24b5bc4842c0d2b4de)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153927

diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx
index a29ff1afb777..eac4128da177 100644
--- a/filter/source/pdf/pdfexport.cxx
+++ b/filter/source/pdf/pdfexport.cxx
@@ -1262,6 +1262,13 @@ void PDFExport::ImplWriteWatermark( vcl::PDFWriter& 
rWriter, const Size& rPageSi
     pDev->Pop();
 
     rWriter.Push();
+    // tdf#152235 tag around the reference to the XObject on the page
+    rWriter.BeginStructureElement(vcl::PDFWriter::NonStructElement, 
::std::u16string_view());
+    rWriter.SetStructureAttribute(vcl::PDFWriter::Type, 
vcl::PDFWriter::Pagination);
+    rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, 
vcl::PDFWriter::Watermark);
+    // HACK: this should produce *nothing* itself but is necessary to output
+    // the Artifact tag here, not inside the XObject
+    rWriter.DrawPolyLine({});
     rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
     rWriter.SetFont( aFont );
     rWriter.SetTextColor(maWatermarkColor);
@@ -1304,6 +1311,7 @@ void PDFExport::ImplWriteWatermark( vcl::PDFWriter& 
rWriter, const Size& rPageSi
     rWriter.BeginTransparencyGroup();
     rWriter.DrawText( aTextPoint, msWatermark );
     rWriter.EndTransparencyGroup( aTextRect, 50 );
+    rWriter.EndStructureElement();
     rWriter.Pop();
 }
 
@@ -1350,6 +1358,13 @@ void PDFExport::ImplWriteTiledWatermark( vcl::PDFWriter& 
rWriter, const Size& rP
     pDev->Pop();
 
     rWriter.Push();
+    // tdf#152235 tag around the reference to the XObject on the page
+    rWriter.BeginStructureElement(vcl::PDFWriter::NonStructElement, 
::std::u16string_view());
+    rWriter.SetStructureAttribute(vcl::PDFWriter::Type, 
vcl::PDFWriter::Pagination);
+    rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, 
vcl::PDFWriter::Watermark);
+    // HACK: this should produce *nothing* itself but is necessary to output
+    // the Artifact tag here, not inside the XObject
+    rWriter.DrawPolyLine({});
     rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
     rWriter.SetFont(aFont);
     rWriter.SetTextColor( Color(19,20,22) );
@@ -1385,6 +1400,7 @@ void PDFExport::ImplWriteTiledWatermark( vcl::PDFWriter& 
rWriter, const Size& rP
         aTextPoint.Move( nTextWidth*1.5, 0 );
     }
 
+    rWriter.EndStructureElement();
     rWriter.Pop();
 }
 
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 34492890fe7f..a78ea33e38ba 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -3314,6 +3314,96 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf152231)
     CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nPara)>(12), nPara);
 }
 
+CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf152235)
+{
+    aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+    // Enable PDF/UA
+    uno::Sequence<beans::PropertyValue> 
aFilterData(comphelper::InitPropertySequence(
+        { { "PDFUACompliance", uno::Any(true) },
+          { "Watermark", uno::Any(OUString("kendy")) },
+          // need to set a font to avoid assertions about missing "Helvetica"
+          { "WatermarkFontName", uno::Any(OUString("Liberation Sans")) },
+          { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
+    aMediaDescriptor["FilterData"] <<= aFilterData;
+    mxComponent = loadFromDesktop("private:factory/swriter");
+    uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+    xStorable->storeToURL(maTempFile.GetURL(), 
aMediaDescriptor.getAsConstPropertyValueList());
+
+    vcl::filter::PDFDocument aDocument;
+    SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+    CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+    std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+    vcl::filter::PDFObjectElement* pContents = 
aPages[0]->LookupObject("Contents");
+    CPPUNIT_ASSERT(pContents);
+    vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+    CPPUNIT_ASSERT(pStream);
+    SvMemoryStream& rObjectStream = pStream->GetMemory();
+    // Uncompress it.
+    SvMemoryStream aUncompressed;
+    ZCodec aZCodec;
+    aZCodec.BeginCompression();
+    rObjectStream.Seek(0);
+    aZCodec.Decompress(rObjectStream, aUncompressed);
+    CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+    auto pStart = static_cast<const char*>(aUncompressed.GetData());
+    const char* const pEnd = pStart + aUncompressed.GetSize();
+
+    enum
+    {
+        Default,
+        Artifact,
+        Tagged
+    } state
+        = Default;
+
+    auto nLine(0);
+    auto nTagged(0);
+    auto nArtifacts(0);
+    while (true)
+    {
+        ++nLine;
+        auto const pLine = ::std::find(pStart, pEnd, '\n');
+        if (pLine == pEnd)
+        {
+            break;
+        }
+        std::string_view const line(pStart, pLine - pStart);
+        pStart = pLine + 1;
+        if (!line.empty() && line[0] != '%')
+        {
+            ::std::cerr << nLine << ": " << line << "\n";
+            if (o3tl::starts_with(line, "/Artifact "))
+            {
+                CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, 
state);
+                state = Artifact;
+                ++nArtifacts;
+            }
+            else if (o3tl::starts_with(line, "/Standard<</MCID "))
+            {
+                CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, 
state);
+                state = Tagged;
+                ++nTagged;
+            }
+            else if (line == "EMC")
+            {
+                CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
+                state = Default;
+            }
+            else if (nLine > 1) // first line is expected "0.1 w"
+            {
+                CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state 
!= Default);
+            }
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
+    CPPUNIT_ASSERT(nTagged >= 0); // text in body
+    CPPUNIT_ASSERT(nArtifacts >= 2); // 1 watermark + 1 other thing
+}
+
 CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf149140)
 {
     aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index c65e004ca605..e0d09d7e9b2c 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11076,8 +11076,8 @@ bool PDFWriterImpl::setStructureAttribute( enum 
PDFWriter::StructAttribute eAttr
         && (m_bEmitStructure
             // allow it for topmost non-structured element
             || (m_aContext.Tagged
-                && 0 < m_aStructure[m_nCurrentStructElement].m_nParentElement
-                && 
m_aStructure[m_aStructure[m_nCurrentStructElement].m_nParentElement].m_eType != 
PDFWriter::NonStructElement)))
+                && (0 == m_aStructure[m_nCurrentStructElement].m_nParentElement
+                    || 
m_aStructure[m_aStructure[m_nCurrentStructElement].m_nParentElement].m_eType != 
PDFWriter::NonStructElement))))
     {
         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement 
].m_eType;
         switch( eAttr )

Reply via email to