include/vcl/pdfwriter.hxx                       |    6 ++
 sw/source/core/text/EnhancedPDFExportHelper.cxx |   12 +++++
 vcl/qa/cppunit/pdfexport/pdfexport.cxx          |   22 +++++++++
 vcl/source/gdi/pdfwriter_impl.cxx               |   55 +++++++++++++++++++++++-
 4 files changed, 92 insertions(+), 3 deletions(-)

New commits:
commit 331bda40b39afa6a49a94e9471dfe1d3f2f7387f
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Tue Mar 7 10:40:23 2023 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Thu Mar 9 21:02:34 2023 +0000

    vcl,sw: PDF/UA export: tag headers and footers as required
    
    ISO 14289-1:2014 has one requirement for specific tagging of artifacts:
    
      7.8 Page headers and footers
      Running headers and footers shall be identified as Pagination
      artifacts and shall be classified as either Header or Footer
      subtypes as per ISO 32000-1:2008, 14.8.2.2.2, Table 330.
    
    It was not immediately obvious how to implement this but the functions
    used for tunnelling structure element attributes through MetaFile can
    be used for this purpose as well with a few tweaks.
    
    Change-Id: I19a3192b1b56b82ed11972c4bbe8d20ab13567be
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148387
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    (cherry picked from commit 7a907965cc6246ab644be92811e35d9f73a90e86)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148412
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index d0eae49ce740..3e62aee7d1f4 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -135,6 +135,9 @@ public:
 
     enum StructAttribute
     {
+        // Artifacts
+        Type, Subtype,
+
         Placement, WritingMode, SpaceBefore, SpaceAfter, StartIndent, 
EndIndent,
         TextIndent, TextAlign, Width, Height, BlockAlign, InlineAlign,
         LineHeight, BaselineShift, TextDecorationType, ListNumbering,
@@ -158,6 +161,9 @@ public:
     {
         Invalid,
         NONE,
+        // Artifacts
+        Pagination, Layout, Page, Background,
+        Header, Footer, Watermark,
         // Placement
         Block, Inline, Before, After, Start, End,
         // WritingMode
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx 
b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index 42141b4013f9..3f6a0c172142 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -602,6 +602,18 @@ void SwTaggedPDFHelper::SetAttributes( 
vcl::PDFWriter::StructElement eType )
                 }
                 break;
 
+            case vcl::PDFWriter::NonStructElement:
+                if (pFrame->IsHeaderFrame() || pFrame->IsFooterFrame())
+                {
+                    // ISO 14289-1:2014, Clause: 7.8
+                    
mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Type, 
vcl::PDFWriter::Pagination);
+                    
mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Subtype,
+                        pFrame->IsHeaderFrame()
+                           ? vcl::PDFWriter::Header
+                           : vcl::PDFWriter::Footer);
+                }
+            break;
+
             default :
                 break;
         }
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 4cf31e708220..30d9b5513c97 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -3050,7 +3050,8 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf139736)
 
     // Enable PDF/UA
     uno::Sequence<beans::PropertyValue> aFilterData(
-        comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) 
} }));
+        comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) 
},
+                                           { "SelectPdfVersion", 
uno::Any(sal_Int32(17)) } }));
     aMediaDescriptor["FilterData"] <<= aFilterData;
     saveAsPDF(u"tdf139736-1.odt");
 
@@ -3081,6 +3082,8 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf139736)
     {
         Default,
         Artifact,
+        ArtifactProps1,
+        ArtifactProps2,
         Tagged
     } state
         = Default;
@@ -3107,6 +3110,23 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf139736)
                 state = Artifact;
                 ++nArtifacts;
             }
+            else if (o3tl::starts_with(line, "/Artifact <<"))
+            {
+                CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, 
state);
+                // check header/footer properties
+                CPPUNIT_ASSERT_EQUAL(std::string_view("/Type/Pagination"), 
line.substr(12));
+                state = ArtifactProps1;
+                ++nArtifacts;
+            }
+            else if (state == ArtifactProps1)
+            {
+                CPPUNIT_ASSERT_EQUAL(std::string_view("/Subtype/Header"), 
line);
+                state = ArtifactProps2;
+            }
+            else if (state == ArtifactProps2 && line == ">> BDC")
+            {
+                state = Artifact;
+            }
             else if (line == "/Standard<</MCID 0>>BDC")
             {
                 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, 
state);
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index f7e7074fd954..b0ef56e64511 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -1830,6 +1830,8 @@ const char* PDFWriterImpl::getAttributeTag( 
PDFWriter::StructAttribute eAttr )
         aAttributeStrings[ PDFWriter::RowSpan ]             = "RowSpan";
         aAttributeStrings[ PDFWriter::ColSpan ]             = "ColSpan";
         aAttributeStrings[ PDFWriter::Scope ]               = "Scope";
+        aAttributeStrings[ PDFWriter::Type ]                = "Type";
+        aAttributeStrings[ PDFWriter::Subtype ]             = "Subtype";
         aAttributeStrings[ PDFWriter::LinkAnnotation ]      = "LinkAnnotation";
     }
 
@@ -1869,6 +1871,13 @@ const char* PDFWriterImpl::getAttributeValueTag( 
PDFWriter::StructAttributeValue
         aValueStrings[ PDFWriter::Row ]                     = "Row";
         aValueStrings[ PDFWriter::Column ]                  = "Column";
         aValueStrings[ PDFWriter::Both ]                    = "Both";
+        aValueStrings[ PDFWriter::Pagination ]              = "Pagination";
+        aValueStrings[ PDFWriter::Layout ]                  = "Layout";
+        aValueStrings[ PDFWriter::Page ]                    = "Page";
+        aValueStrings[ PDFWriter::Background ]              = "Background";
+        aValueStrings[ PDFWriter::Header ]                  = "Header";
+        aValueStrings[ PDFWriter::Footer ]                  = "Footer";
+        aValueStrings[ PDFWriter::Watermark ]               = "Watermark";
         aValueStrings[ PDFWriter::Disc ]                    = "Disc";
         aValueStrings[ PDFWriter::Circle ]                  = "Circle";
         aValueStrings[ PDFWriter::Square ]                  = "Square";
@@ -10510,8 +10519,24 @@ void PDFWriterImpl::beginStructureElementMCSeq()
              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already 
opened sequence
              )
     {
-        OString aLine = "/Artifact BMC\n";
+        OString aLine = "/Artifact ";
         writeBuffer( aLine.getStr(), aLine.getLength() );
+        // emit property list if requested
+        OStringBuffer buf;
+        for (auto const& rAttr : 
m_aStructure[m_nCurrentStructElement].m_aAttributes)
+        {
+            appendStructureAttributeLine(rAttr.first, rAttr.second, buf, 
false);
+        }
+        if (buf.isEmpty())
+        {
+            writeBuffer("BMC\n", 4);
+        }
+        else
+        {
+            writeBuffer("<<", 2);
+            writeBuffer(buf.getStr(), buf.getLength());
+            writeBuffer(">> BDC\n", 7);
+        }
         // mark element MC sequence as open
         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
     }
@@ -10810,7 +10835,12 @@ bool PDFWriterImpl::setStructureAttribute( enum 
PDFWriter::StructAttribute eAttr
         return false;
 
     bool bInsert = false;
-    if( m_nCurrentStructElement > 0 && m_bEmitStructure )
+    if (m_nCurrentStructElement > 0
+        && (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)))
     {
         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement 
].m_eType;
         switch( eAttr )
@@ -10977,6 +11007,27 @@ bool PDFWriterImpl::setStructureAttribute( enum 
PDFWriter::StructAttribute eAttr
                     }
                 }
                 break;
+            case PDFWriter::Type:
+                if (eVal == PDFWriter::Pagination || eVal == PDFWriter::Layout 
|| eVal == PDFWriter::Page)
+                    // + Background for PDF >= 1.7
+                {
+                    if (eType == PDFWriter::NonStructElement)
+                    {
+                        bInsert = true;
+                    }
+                }
+                break;
+            case PDFWriter::Subtype:
+                if (eVal == PDFWriter::Header || eVal == PDFWriter::Footer || 
eVal == PDFWriter::Watermark)
+                {
+                    if (eType == PDFWriter::NonStructElement
+                        && m_aContext.Version != PDFWriter::PDFVersion::PDF_A_1
+                        && PDFWriter::PDFVersion::PDF_1_7 <= 
m_aContext.Version)
+                    {
+                        bInsert = true;
+                    }
+                }
+                break;
             case PDFWriter::ListNumbering:
                 if( eVal == PDFWriter::NONE         ||
                     eVal == PDFWriter::Disc         ||

Reply via email to