drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx |   20 -
 editeng/source/editeng/impedit3.cxx                        |    2 
 filter/source/pdf/pdfexport.cxx                            |    8 
 include/vcl/pdfextoutdevdata.hxx                           |   11 
 include/vcl/pdfwriter.hxx                                  |   10 
 sw/source/core/text/EnhancedPDFExportHelper.cxx            |    2 
 sw/source/core/text/itrform2.cxx                           |    2 
 sw/source/uibase/docvw/AnnotationWin2.cxx                  |    2 
 vcl/inc/pdf/pdfwriter_impl.hxx                             |    7 
 vcl/source/gdi/pdfextoutdevdata.cxx                        |   98 ++++--
 vcl/source/gdi/pdfwriter.cxx                               |   15 
 vcl/source/gdi/pdfwriter_impl.cxx                          |  200 +++++++++----
 12 files changed, 277 insertions(+), 100 deletions(-)

New commits:
commit 07d790ca473cd6e71f0343419b819fa6b485dc01
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Wed Jul 12 15:26:25 2023 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Fri Jul 14 14:56:24 2023 +0200

    tdf#154982 vcl: PDF Export: split BeginStructureElement
    
    ... into 3 parts:
    EnsureStructureElement/InitStructureElement/BeginStructureElement
    
    So EnsureStructureElement and BeginStructureElement/EndStructureElement
    can be called multiple times for the same object, passing in a unique
    key and PDFExtOutDevData will only create the element once.
    
    InitStructureElement will be used exactly once for each object when its
    actual content is exported.
    
    In PDFExtOutDevData rely on the indexes being the same here and in
    PDFWriterImpl, because then only PDFExtOutDevData needs to maintain the
    map from key to index.
    
    Change-Id: Idea6e34627fe559038cf13cf01dafe84b759e3c8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154357
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 85b9ca35abf1..b2045a5e18c0 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -1127,7 +1127,7 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
                                                    
mpOutputDevice->GetMapMode());
             pPDFControl->TextFont.SetFontSize(aFontSize);
 
-            mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
+            
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
             vcl::PDFWriter::StructAttributeValue role;
             switch (pPDFControl->Type)
             {
@@ -1174,7 +1174,7 @@ void VclMetafileProcessor2D::processControlPrimitive2D(
 
     if (mpPDFExtOutDevData)
     { // no corresponding PDF Form, use Figure instead
-        mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Figure);
+        mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Figure);
         mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement, 
vcl::PDFWriter::Block);
         auto const 
range(rControlPrimitive.getB2DRange(getViewInformation2D()));
         tools::Rectangle const aLogicRect(
@@ -1315,7 +1315,7 @@ void 
VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D(
     if (mbInListItem)
     {
         maListElements.push(vcl::PDFWriter::LILabel);
-        mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel);
+        mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::LILabel);
     }
 
     // process recursively and add MetaFile comment
@@ -1354,7 +1354,7 @@ void 
VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
     {
         // No Tagged PDF -> Dump as Paragraph
         // Emulate data handling from old ImpEditEngine::Paint
-        mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+        
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
 
         // Process recursively and add MetaFile comment
         process(rParagraphPrimitive);
@@ -1380,7 +1380,7 @@ void 
VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
             for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; 
++a)
             {
                 maListElements.push(vcl::PDFWriter::List);
-                
mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::List);
+                
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::List);
             }
         }
         else // if(nNewOutlineLevel < mnCurrentOutlineLevel)
@@ -1411,13 +1411,13 @@ void 
VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D(
     {
         // Dump as ListItem
         maListElements.push(vcl::PDFWriter::ListItem);
-        mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::ListItem);
+        
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::ListItem);
         mbInListItem = true;
     }
     else
     {
         // Dump as Paragraph
-        mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph);
+        
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
     }
 
     // Process recursively and add MetaFile comment
@@ -1461,7 +1461,7 @@ void 
VclMetafileProcessor2D::processTextSimplePortionPrimitive2D(
     if (mbInListItem && mbBulletPresent)
     {
         maListElements.push(vcl::PDFWriter::LIBody);
-        mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody);
+        mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::LIBody);
     }
 
     // directdraw of text simple portion; use default processing
@@ -2553,7 +2553,7 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
                     SAL_WARN("drawinglayer", "anchor structure element not 
found?");
                 }
             }
-            mpPDFExtOutDevData->BeginStructureElement(rTagElement);
+            mpPDFExtOutDevData->WrapBeginStructureElement(rTagElement);
             switch (rTagElement)
             {
                 case vcl::PDFWriter::H1:
@@ -2613,7 +2613,7 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
         {
             // background image: tag as artifact
             if (rStructureTagCandidate.isImage())
-                
mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement);
+                
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
             // any other background object: do not tag
             else
                 assert(false);
diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index 510ab1624319..ba7c2ed04ae3 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -3378,7 +3378,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
             return;
 
         if ( pPDFExtOutDevData )
-            pPDFExtOutDevData->BeginStructureElement( 
vcl::PDFWriter::Paragraph );
+            
pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Paragraph);
 
         const tools::Long nParaHeight = pPortion->GetHeight();
         if ( pPortion->IsVisible() && (
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx
index f9c5fb44af6b..8199132b66b4 100644
--- a/filter/source/pdf/pdfexport.cxx
+++ b/filter/source/pdf/pdfexport.cxx
@@ -1263,7 +1263,9 @@ void PDFExport::ImplWriteWatermark( vcl::PDFWriter& 
rWriter, const Size& rPageSi
 
     rWriter.Push();
     // tdf#152235 tag around the reference to the XObject on the page
-    rWriter.BeginStructureElement(vcl::PDFWriter::NonStructElement, 
::std::u16string_view());
+    sal_Int32 const id = rWriter.EnsureStructureElement();
+    rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, 
::std::u16string_view());
+    rWriter.BeginStructureElement(id);
     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
@@ -1359,7 +1361,9 @@ void PDFExport::ImplWriteTiledWatermark( vcl::PDFWriter& 
rWriter, const Size& rP
 
     rWriter.Push();
     // tdf#152235 tag around the reference to the XObject on the page
-    rWriter.BeginStructureElement(vcl::PDFWriter::NonStructElement, 
::std::u16string_view());
+    sal_Int32 const id = rWriter.EnsureStructureElement();
+    rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, 
::std::u16string_view());
+    rWriter.BeginStructureElement(id);
     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
diff --git a/include/vcl/pdfextoutdevdata.hxx b/include/vcl/pdfextoutdevdata.hxx
index 58088d235eff..987e2a1020b2 100644
--- a/include/vcl/pdfextoutdevdata.hxx
+++ b/include/vcl/pdfextoutdevdata.hxx
@@ -345,6 +345,11 @@ public:
     (e.g. a section can contain a heading and a paragraph). The structure 
hierarchy
     is build automatically from the Begin/EndStructureElement calls.
 
+    The easy way is to call WrapBeginStructureElement, but it's also possible
+    to call EnsureStructureElement/InitStructureElement/BeginStructureElement
+    (its 3 parts) manually for more control; this way a placeholder SE can be
+    inserted and initialised later.
+
     A structural element need not be contained on one page; e.g. paragraphs 
often
     run from one page to the next. In this case the corresponding 
EndStructureElement
     must be called while drawing the next page.
@@ -372,7 +377,11 @@ public:
     @returns
     the id of the newly created structural element
      */
-     sal_Int32 BeginStructureElement( PDFWriter::StructElement eType, const 
OUString& rAlias = OUString() );
+    sal_Int32 WrapBeginStructureElement(PDFWriter::StructElement eType, const 
OUString& rAlias = OUString());
+    sal_Int32 EnsureStructureElement(void const* key);
+    void InitStructureElement(sal_Int32 id, PDFWriter::StructElement eType, 
const OUString& rAlias);
+    void BeginStructureElement(sal_Int32 id);
+
     /** end a logical structure element
 
     @see BeginStructureElement
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 0f4eccb42728..6b9c32707839 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -1056,6 +1056,11 @@ The following structure describes the permissions used 
in PDF security
     (e.g. a section can contain a heading and a paragraph). The structure 
hierarchy
     is build automatically from the Begin/EndStructureElement calls.
 
+    The easy way is to call WrapBeginStructureElement, but it's also possible
+    to call EnsureStructureElement/InitStructureElement/BeginStructureElement
+    (its 3 parts) manually for more control; this way a placeholder SE can be
+    inserted and initialised later.
+
     A structural element need not be contained on one page; e.g. paragraphs 
often
     run from one page to the next. In this case the corresponding 
EndStructureElement
     must be called while drawing the next page.
@@ -1090,7 +1095,10 @@ The following structure describes the permissions used 
in PDF security
     @returns
     the new structure element's id for use in SetCurrentStructureElement
      */
-     sal_Int32 BeginStructureElement( enum StructElement eType, 
std::u16string_view rAlias );
+    void BeginStructureElement(sal_Int32 id);
+    sal_Int32 EnsureStructureElement();
+    void InitStructureElement(sal_Int32 id, PDFWriter::StructElement eType, 
std::u16string_view rAlias);
+
     /** end the current logical structure element
 
     Close the current structure element. The current element's
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx 
b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index 7b5732e925da..e48774f2b2c2 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -459,7 +459,7 @@ void SwTaggedPDFHelper::CheckRestoreTag() const
 void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const 
OUString& rString )
 {
     // write new tag
-    const sal_Int32 nId = mpPDFExtOutDevData->BeginStructureElement( eType, 
rString );
+    const sal_Int32 nId = mpPDFExtOutDevData->WrapBeginStructureElement( 
eType, rString );
     ++m_nEndStructureElement;
 
 #if OSL_DEBUG_LEVEL > 1
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
index 5895825bf668..2f5abb9a5c6b 100644
--- a/sw/source/core/text/itrform2.cxx
+++ b/sw/source/core/text/itrform2.cxx
@@ -1094,7 +1094,7 @@ bool SwContentControlPortion::DescribePDFControl(const 
SwTextPaintInfo& rInf) co
     aLocation.Union(aEndRect);
     pDescriptor->Location = aLocation.SVRect();
 
-    pPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
+    pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::Form);
     pPDFExtOutDevData->CreateControl(*pDescriptor);
     pPDFExtOutDevData->EndStructureElement();
 
diff --git a/sw/source/uibase/docvw/AnnotationWin2.cxx 
b/sw/source/uibase/docvw/AnnotationWin2.cxx
index 047afe5e63d5..885c19877dc1 100644
--- a/sw/source/uibase/docvw/AnnotationWin2.cxx
+++ b/sw/source/uibase/docvw/AnnotationWin2.cxx
@@ -166,7 +166,7 @@ void SwAnnotationWin::DrawForPage(OutputDevice* pDev, const 
Point& rPt)
         dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData()));
     if (pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportTaggedPDF())
     {
-        
pPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement, 
OUString());
+        
pPDFExtOutDevData->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement, 
OUString());
     }
 
     pDev->Push();
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index a9438be8fc13..8c232589e9c7 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -582,7 +582,7 @@ struct PDFStructureElementKid // for Kids entries
 struct PDFStructureElement
 {
     sal_Int32                                           m_nObject;
-    PDFWriter::StructElement                            m_eType;
+    ::std::optional<PDFWriter::StructElement>           m_oType;
     OString                                        m_aAlias;
     sal_Int32                                           m_nOwnElement; // 
index into structure vector
     sal_Int32                                           m_nParentElement; // 
index into structure vector
@@ -603,7 +603,6 @@ struct PDFStructureElement
 
     PDFStructureElement()
             : m_nObject( 0 ),
-              m_eType( PDFWriter::NonStructElement ),
               m_nOwnElement( -1 ),
               m_nParentElement( -1 ),
               m_nFirstPageObject( 0 ),
@@ -1330,7 +1329,9 @@ public:
     // notes
     void createNote( const tools::Rectangle& rRect, const PDFNote& rNote, 
sal_Int32 nPageNr );
     // structure elements
-    sal_Int32 beginStructureElement( PDFWriter::StructElement eType, 
std::u16string_view rAlias );
+    sal_Int32 ensureStructureElement();
+    void initStructureElement(sal_Int32 id, PDFWriter::StructElement eType, 
std::u16string_view rAlias);
+    void beginStructureElement(sal_Int32 id);
     void endStructureElement();
     bool setCurrentStructureElement( sal_Int32 nElement );
     bool setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum 
PDFWriter::StructAttributeValue eVal );
diff --git a/vcl/source/gdi/pdfextoutdevdata.cxx 
b/vcl/source/gdi/pdfextoutdevdata.cxx
index ff99fdbbcd90..057cb4e07549 100644
--- a/vcl/source/gdi/pdfextoutdevdata.cxx
+++ b/vcl/source/gdi/pdfextoutdevdata.cxx
@@ -53,6 +53,8 @@ struct PDFExtOutDevDataSync
                     CreateNote,
                     SetPageTransition,
 
+                    EnsureStructureElement,
+                    InitStructureElement,
                     BeginStructureElement,
                     EndStructureElement,
                     SetCurrentStructureElement,
@@ -95,7 +97,6 @@ struct GlobalSyncData
     ::std::map< sal_Int32, PDFLinkDestination > mFutureDestinations;
 
     sal_Int32 GetMappedId();
-    sal_Int32 GetMappedStructId( sal_Int32 );
 
     /** the way this appears to work: (only) everything that increments mCurId
         at recording time must put an item into mParaIds at playback time,
@@ -104,6 +105,7 @@ struct GlobalSyncData
     sal_Int32                   mCurId;
     std::vector< sal_Int32 >    mParaIds;
     std::vector< sal_Int32 >    mStructIdMap;
+    std::map<void const*, sal_Int32> mSEMap;
 
     sal_Int32                   mCurrentStructElement;
     std::vector< sal_Int32 >    mStructParents;
@@ -111,7 +113,7 @@ struct GlobalSyncData
             mCurId ( 0 ),
             mCurrentStructElement( 0 )
     {
-        mStructParents.push_back( 0 );
+        mStructParents.push_back(0); // because PDFWriterImpl has a dummy root
         mStructIdMap.push_back( 0 );
     }
     void PlayGlobalActions( PDFWriter& rWriter );
@@ -138,18 +140,6 @@ sal_Int32 GlobalSyncData::GetMappedId()
     return nLinkId;
 }
 
-sal_Int32 GlobalSyncData::GetMappedStructId( sal_Int32 nStructId )
-{
-    if ( o3tl::make_unsigned(nStructId) < mStructIdMap.size() )
-        nStructId = mStructIdMap[ nStructId ];
-    else
-        nStructId = -1;
-
-    SAL_WARN_IF( nStructId < 0, "vcl", "unmapped structure id in 
GlobalSyncData" );
-
-    return nStructId;
-}
-
 void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter )
 {
     for (auto const& action : mActions)
@@ -282,6 +272,8 @@ void GlobalSyncData::PlayGlobalActions( PDFWriter& rWriter )
                 mParaInts.pop_front();
             }
             break;
+            case PDFExtOutDevDataSync::EnsureStructureElement:
+            case PDFExtOutDevDataSync::InitStructureElement:
             case PDFExtOutDevDataSync::BeginStructureElement:
             case PDFExtOutDevDataSync::EndStructureElement:
             case PDFExtOutDevDataSync::SetCurrentStructureElement:
@@ -348,12 +340,28 @@ bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, 
sal_uInt32& rCurGDIMtfAc
         mActions.pop_front();
         switch( aDataSync.eAct )
         {
-            case PDFExtOutDevDataSync::BeginStructureElement :
+            case PDFExtOutDevDataSync::EnsureStructureElement:
+            {
+#ifndef NDEBUG
+                sal_Int32 const id =
+#endif
+                    rWriter.EnsureStructureElement();
+                assert(id == -1 || id == mParaInts.front()); // identity 
mapping
+                mParaInts.pop_front();
+            }
+            break;
+            case PDFExtOutDevDataSync::InitStructureElement:
             {
-                sal_Int32 nNewEl = rWriter.BeginStructureElement( 
mParaStructElements.front(), mParaOUStrings.front() ) ;
+                rWriter.InitStructureElement(mParaInts.front(), 
mParaStructElements.front(), mParaOUStrings.front());
+                mParaInts.pop_front();
                 mParaStructElements.pop_front();
                 mParaOUStrings.pop_front();
-                mpGlobalData->mStructIdMap.push_back( nNewEl );
+            }
+            break;
+            case PDFExtOutDevDataSync::BeginStructureElement :
+            {
+                rWriter.BeginStructureElement(mParaInts.front());
+                mParaInts.pop_front();
             }
             break;
             case PDFExtOutDevDataSync::EndStructureElement :
@@ -363,7 +371,7 @@ bool PageSyncData::PlaySyncPageAct( PDFWriter& rWriter, 
sal_uInt32& rCurGDIMtfAc
             break;
             case PDFExtOutDevDataSync::SetCurrentStructureElement:
             {
-                rWriter.SetCurrentStructureElement( 
mpGlobalData->GetMappedStructId( mParaInts.front() ) );
+                rWriter.SetCurrentStructureElement(mParaInts.front());
                 mParaInts.pop_front();
             }
             break;
@@ -789,17 +797,57 @@ void PDFExtOutDevData::SetPageTransition( 
PDFWriter::PageTransition eType, sal_u
 
 /* local (page), actions have to be played synchronously to the actions of
    of the recorded metafile (created by each xRenderable->render()) */
-   sal_Int32 PDFExtOutDevData::BeginStructureElement( PDFWriter::StructElement 
eType, const OUString& rAlias )
+
+sal_Int32 PDFExtOutDevData::EnsureStructureElement(void const*const key)
 {
-    mpPageSyncData->PushAction( mrOutDev, 
PDFExtOutDevDataSync::BeginStructureElement );
+    sal_Int32 id(-1);
+    if (key != nullptr)
+    {
+        auto const it(mpGlobalSyncData->mSEMap.find(key));
+        if (it != mpGlobalSyncData->mSEMap.end())
+        {
+            id = it->second;
+        }
+    }
+    if (id == -1)
+    {
+        mpPageSyncData->PushAction(mrOutDev, 
PDFExtOutDevDataSync::EnsureStructureElement);
+        id = mpGlobalSyncData->mStructParents.size();
+        mpPageSyncData->mParaInts.push_back(id);
+        
mpGlobalSyncData->mStructParents.push_back(mpGlobalSyncData->mCurrentStructElement);
+        if (key != nullptr)
+        {
+            mpGlobalSyncData->mSEMap.emplace(key, id);
+        }
+    }
+    return id;
+}
+
+void PDFExtOutDevData::InitStructureElement(sal_Int32 const id,
+        PDFWriter::StructElement const eType, const OUString& rAlias)
+{
+    mpPageSyncData->PushAction(mrOutDev, 
PDFExtOutDevDataSync::InitStructureElement);
+    mpPageSyncData->mParaInts.push_back(id);
     mpPageSyncData->mParaStructElements.push_back( eType );
     mpPageSyncData->mParaOUStrings.push_back( rAlias );
-    // need a global id
-    sal_Int32 nNewId = mpGlobalSyncData->mStructParents.size();
-    mpGlobalSyncData->mStructParents.push_back( 
mpGlobalSyncData->mCurrentStructElement );
-    mpGlobalSyncData->mCurrentStructElement = nNewId;
-    return nNewId;
 }
+
+void PDFExtOutDevData::BeginStructureElement(sal_Int32 const id)
+{
+    mpPageSyncData->PushAction( mrOutDev, 
PDFExtOutDevDataSync::BeginStructureElement );
+    mpPageSyncData->mParaInts.push_back(id);
+    mpGlobalSyncData->mCurrentStructElement = id;
+}
+
+sal_Int32 PDFExtOutDevData::WrapBeginStructureElement(
+        PDFWriter::StructElement const eType, const OUString& rAlias)
+{
+    sal_Int32 const id = EnsureStructureElement(nullptr);
+    InitStructureElement(id, eType, rAlias);
+    BeginStructureElement(id);
+    return id;
+}
+
 void PDFExtOutDevData::EndStructureElement()
 {
     mpPageSyncData->PushAction( mrOutDev, 
PDFExtOutDevDataSync::EndStructureElement );
diff --git a/vcl/source/gdi/pdfwriter.cxx b/vcl/source/gdi/pdfwriter.cxx
index dda44d2f4adb..ac50b0821c73 100644
--- a/vcl/source/gdi/pdfwriter.cxx
+++ b/vcl/source/gdi/pdfwriter.cxx
@@ -389,9 +389,20 @@ void PDFWriter::CreateNote( const tools::Rectangle& rRect, 
const PDFNote& rNote,
     xImplementation->createNote( rRect, rNote, nPageNr );
 }
 
-sal_Int32 PDFWriter::BeginStructureElement( PDFWriter::StructElement eType, 
std::u16string_view rAlias )
+sal_Int32 PDFWriter::EnsureStructureElement()
 {
-    return xImplementation->beginStructureElement( eType, rAlias );
+    return xImplementation->ensureStructureElement();
+}
+
+void PDFWriter::InitStructureElement(sal_Int32 const id,
+        PDFWriter::StructElement const eType, std::u16string_view const rAlias)
+{
+    return xImplementation->initStructureElement(id, eType, rAlias);
+}
+
+void PDFWriter::BeginStructureElement(sal_Int32 const id)
+{
+    return xImplementation->beginStructureElement(id);
 }
 
 void PDFWriter::EndStructureElement()
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index a86e385f013c..c79d82e134a4 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -300,6 +300,8 @@ GEOMETRY lcl_convert( const MapMode& _rSource, const 
MapMode& _rDest, OutputDevi
     return aPoint;
 }
 
+void removePlaceholderSE(std::vector<PDFStructureElement> & rStructure, 
PDFStructureElement& rEle);
+
 } // end anonymous namespace
 
 void PDFWriter::AppendUnicodeTextString(const OUString& rString, 
OStringBuffer& rBuffer)
@@ -2083,19 +2085,20 @@ OString PDFWriterImpl::emitStructureAttributes( 
PDFStructureElement& i_rEle )
 
 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
 {
-    if(
+    assert(rEle.m_nOwnElement == 0 || rEle.m_oType);
+    if (rEle.m_nOwnElement != rEle.m_nParentElement // emit the struct tree 
root
        // do not emit NonStruct and its children
-       rEle.m_eType == PDFWriter::NonStructElement &&
-       rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the 
struct tree root
-       )
+        && *rEle.m_oType == PDFWriter::NonStructElement)
+    {
         return 0;
+    }
 
     for (auto const& child : rEle.m_aChildren)
     {
         if( child > 0 && o3tl::make_unsigned(child) < m_aStructure.size() )
         {
             PDFStructureElement& rChild = m_aStructure[ child ];
-            if( rChild.m_eType != PDFWriter::NonStructElement )
+            if (*rChild.m_oType != PDFWriter::NonStructElement)
             {
                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
                     emitStructure( rChild );
@@ -2152,7 +2155,7 @@ sal_Int32 PDFWriterImpl::emitStructure( 
PDFStructureElement& rEle )
         if( !rEle.m_aAlias.isEmpty() )
             aLine.append( rEle.m_aAlias );
         else
-            aLine.append( getStructureTag( rEle.m_eType ) );
+            aLine.append( getStructureTag(*rEle.m_oType) );
         if (m_StructElemObjsWithID.find(rEle.m_nObject) != 
m_StructElemObjsWithID.end())
         {
             aLine.append("\n/ID ");
@@ -2218,7 +2221,7 @@ sal_Int32 PDFWriterImpl::emitStructure( 
PDFStructureElement& rEle )
             auto const it(m_aLinkPropertyMap.find(id));
             assert(it != m_aLinkPropertyMap.end());
 
-            if (rEle.m_eType == PDFWriter::Form)
+            if (*rEle.m_oType == PDFWriter::Form)
             {
                 assert(0 <= it->second && o3tl::make_unsigned(it->second) < 
m_aWidgets.size());
                 AppendAnnotKid(rEle, m_aWidgets[it->second]);
@@ -5182,6 +5185,7 @@ bool PDFWriterImpl::emitCatalog()
     sal_Int32 nStructureDict = 0;
     if(m_aStructure.size() > 1)
     {
+        removePlaceholderSE(m_aStructure, m_aStructure[0]);
         // check if dummy structure containers are needed
         addInternalStructureContainer(m_aStructure[0]);
         nStructureDict = m_aStructure[0].m_nObject = createObject();
@@ -10650,11 +10654,13 @@ void PDFWriterImpl::addRoleMap(OString aAlias, 
PDFWriter::StructElement eType)
 
 void PDFWriterImpl::beginStructureElementMCSeq()
 {
+    assert(m_nCurrentStructElement == 0 || 
m_aStructure[m_nCurrentStructElement].m_oType);
     if( m_bEmitStructure &&
         m_nCurrentStructElement > 0 && // StructTreeRoot
         // Document = SwPageFrame => this is not *inside* the page content
         // stream so do not emit MCID!
-        m_aStructure[m_nCurrentStructElement].m_eType != PDFWriter::Document &&
+        m_aStructure[m_nCurrentStructElement].m_oType &&
+        *m_aStructure[m_nCurrentStructElement].m_oType != PDFWriter::Document 
&&
         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already 
opened sequence
         )
     {
@@ -10665,7 +10671,7 @@ void PDFWriterImpl::beginStructureElementMCSeq()
         if( !rEle.m_aAlias.isEmpty() )
             aLine.append( rEle.m_aAlias );
         else
-            aLine.append( getStructureTag( rEle.m_eType ) );
+            aLine.append( getStructureTag(*rEle.m_oType) );
         aLine.append( "<</MCID " );
         aLine.append( nMCID );
         aLine.append( ">>BDC\n" );
@@ -10684,7 +10690,8 @@ void PDFWriterImpl::beginStructureElementMCSeq()
     // handle artifacts
     else if( ! m_bEmitStructure && m_aContext.Tagged &&
                m_nCurrentStructElement > 0 &&
-               m_aStructure[ m_nCurrentStructElement ].m_eType == 
PDFWriter::NonStructElement &&
+               m_aStructure[m_nCurrentStructElement].m_oType &&
+               *m_aStructure[m_nCurrentStructElement].m_oType == 
PDFWriter::NonStructElement &&
              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already 
opened sequence
              )
     {
@@ -10714,9 +10721,10 @@ void PDFWriterImpl::beginStructureElementMCSeq()
 void PDFWriterImpl::endStructureElementMCSeq(EndMode const endMode)
 {
     if (m_nCurrentStructElement > 0 // not StructTreeRoot
+        && m_aStructure[m_nCurrentStructElement].m_oType
         && (m_bEmitStructure
             || (endMode != EndMode::OnlyStruct
-                && m_aStructure[m_nCurrentStructElement].m_eType == 
PDFWriter::NonStructElement))
+                && m_aStructure[m_nCurrentStructElement].m_oType == 
PDFWriter::NonStructElement))
         && m_aStructure[m_nCurrentStructElement].m_bOpenMCSeq)
     {
         writeBuffer( "EMC\n" );
@@ -10733,7 +10741,8 @@ bool PDFWriterImpl::checkEmitStructure()
         sal_Int32 nEle = m_nCurrentStructElement;
         while( nEle > 0 && o3tl::make_unsigned(nEle) < m_aStructure.size() )
         {
-            if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
+            if (m_aStructure[nEle].m_oType
+                && *m_aStructure[nEle].m_oType == PDFWriter::NonStructElement)
             {
                 bEmit = false;
                 break;
@@ -10744,16 +10753,28 @@ bool PDFWriterImpl::checkEmitStructure()
     return bEmit;
 }
 
-sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement 
eType, std::u16string_view rAlias )
+sal_Int32 PDFWriterImpl::ensureStructureElement()
 {
-    if( m_nCurrentPage < 0 )
-        return -1;
-
     if( ! m_aContext.Tagged )
         return -1;
 
-    // close eventual current MC sequence
-    endStructureElementMCSeq(EndMode::OnlyStruct);
+    sal_Int32 nNewId = sal_Int32(m_aStructure.size());
+    m_aStructure.emplace_back();
+    PDFStructureElement& rEle = m_aStructure.back();
+    // leave rEle.m_oType uninitialised
+    rEle.m_nOwnElement      = nNewId;
+    // temporary parent
+    rEle.m_nParentElement   = m_nCurrentStructElement;
+    rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
+    m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
+    return nNewId;
+}
+
+void PDFWriterImpl::initStructureElement(sal_Int32 const id,
+        PDFWriter::StructElement const eType, std::u16string_view const rAlias)
+{
+    if( ! m_aContext.Tagged )
+        return;
 
     if( m_nCurrentStructElement == 0 &&
         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
@@ -10765,7 +10786,9 @@ sal_Int32 PDFWriterImpl::beginStructureElement( 
PDFWriter::StructElement eType,
         {
             const std::vector< sal_Int32 >& rRootChildren = 
m_aStructure[0].m_aChildren;
             auto it = std::find_if(rRootChildren.begin(), rRootChildren.end(),
-                [&](sal_Int32 nElement) { return m_aStructure[ nElement 
].m_eType == PDFWriter::Document; });
+                [&](sal_Int32 nElement) {
+                    return m_aStructure[nElement].m_oType
+                        && *m_aStructure[nElement].m_oType == 
PDFWriter::Document; });
             if( it != rRootChildren.end() )
             {
                 m_nCurrentStructElement = *it;
@@ -10780,15 +10803,17 @@ sal_Int32 PDFWriterImpl::beginStructureElement( 
PDFWriter::StructElement eType,
         }
     }
 
-    sal_Int32 nNewId = sal_Int32(m_aStructure.size());
-    m_aStructure.emplace_back( );
-    PDFStructureElement& rEle = m_aStructure.back();
-    rEle.m_eType            = eType;
-    rEle.m_nOwnElement      = nNewId;
+    PDFStructureElement& rEle = m_aStructure[id];
+    assert(!rEle.m_oType);
+    rEle.m_oType.emplace(eType);
+    // remove it from its possibly placeholder parent; append to real parent
+    auto const 
it(std::find(m_aStructure[rEle.m_nParentElement].m_aChildren.begin(),
+        m_aStructure[rEle.m_nParentElement].m_aChildren.end(), id));
+    assert(it != m_aStructure[rEle.m_nParentElement].m_aChildren.end());
+    m_aStructure[rEle.m_nParentElement].m_aChildren.erase(it);
     rEle.m_nParentElement   = m_nCurrentStructElement;
     rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
-    m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
-    m_nCurrentStructElement = nNewId;
+    m_aStructure[m_nCurrentStructElement].m_aChildren.push_back(id);
 
     // handle alias names
     if( !rAlias.empty() && eType != PDFWriter::NonStructElement )
@@ -10800,12 +10825,41 @@ sal_Int32 PDFWriterImpl::beginStructureElement( 
PDFWriter::StructElement eType,
         addRoleMap(aAliasName, eType);
     }
 
+    if (m_bEmitStructure && eType != PDFWriter::NonStructElement) // don't 
create nonexistent objects
+    {
+        rEle.m_nObject      = createObject();
+        // update parent's kids list
+        m_aStructure[ rEle.m_nParentElement 
].m_aKids.emplace_back(rEle.m_nObject);
+        // ISO 14289-1:2014, Clause: 7.9
+        if (*rEle.m_oType == PDFWriter::Note)
+        {
+            m_StructElemObjsWithID.insert(rEle.m_nObject);
+        }
+    }
+}
+
+void PDFWriterImpl::beginStructureElement(sal_Int32 const id)
+{
+    if( m_nCurrentPage < 0 )
+        return;
+
+    if( ! m_aContext.Tagged )
+        return;
+
+    // close eventual current MC sequence
+    endStructureElementMCSeq(EndMode::OnlyStruct);
+
+    PDFStructureElement& rEle = m_aStructure[id];
+    m_nCurrentStructElement = id;
+
     if (g_bDebugDisableCompression)
     {
         OStringBuffer aLine( "beginStructureElement " );
         aLine.append( m_nCurrentStructElement );
         aLine.append( ": " );
-        aLine.append( getStructureTag( eType ) );
+        aLine.append( rEle.m_oType
+            ? getStructureTag(*rEle.m_oType)
+            : "<placeholder>" );
         if( !rEle.m_aAlias.isEmpty() )
         {
             aLine.append( " aliased as \"" );
@@ -10817,19 +10871,6 @@ sal_Int32 PDFWriterImpl::beginStructureElement( 
PDFWriter::StructElement eType,
 
     // check whether to emit structure henceforth
     m_bEmitStructure = checkEmitStructure();
-
-    if( m_bEmitStructure ) // don't create nonexistent objects
-    {
-        rEle.m_nObject      = createObject();
-        // update parent's kids list
-        m_aStructure[ rEle.m_nParentElement 
].m_aKids.emplace_back(rEle.m_nObject);
-        // ISO 14289-1:2014, Clause: 7.9
-        if (rEle.m_eType == PDFWriter::Note)
-        {
-            m_StructElemObjsWithID.insert(rEle.m_nObject);
-        }
-    }
-    return nNewId;
 }
 
 void PDFWriterImpl::endStructureElement()
@@ -10856,7 +10897,9 @@ void PDFWriterImpl::endStructureElement()
         aLine.append( "endStructureElement " );
         aLine.append( m_nCurrentStructElement );
         aLine.append( ": " );
-        aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement 
].m_eType ) );
+        aLine.append( m_aStructure[m_nCurrentStructElement].m_oType
+            ? getStructureTag(*m_aStructure[m_nCurrentStructElement].m_oType)
+            : "<placeholder>" );
         if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
         {
             aLine.append( " aliased as \"" );
@@ -10877,6 +10920,49 @@ void PDFWriterImpl::endStructureElement()
     }
 }
 
+namespace {
+
+void removePlaceholderSEImpl(std::vector<PDFStructureElement> & rStructure,
+        std::vector<sal_Int32>::iterator & rParentIt)
+{
+    PDFStructureElement& rEle(rStructure[*rParentIt]);
+    removePlaceholderSE(rStructure, rEle);
+
+    if (!rEle.m_oType)
+    {
+        // Placeholder was not initialised - should not happen when printing
+        // a full page, but might if a selection is printed, which can be only
+        // a shape without its anchor.
+        // Handle this by moving the children to the parent SE.
+        PDFStructureElement & rParent(rStructure[rEle.m_nParentElement]);
+        rParentIt = rParent.m_aChildren.erase(rParentIt);
+        std::vector<sal_Int32> children;
+        for (auto const child : rEle.m_aChildren)
+        {
+            PDFStructureElement& rChild = rStructure[child];
+            rChild.m_nParentElement = rEle.m_nParentElement;
+            children.push_back(rChild.m_nOwnElement);
+        }
+        rParentIt = rParent.m_aChildren.insert(rParentIt, children.begin(), 
children.end())
+            + children.size();
+    }
+    else
+    {
+        ++rParentIt;
+    }
+
+}
+
+void removePlaceholderSE(std::vector<PDFStructureElement> & rStructure, 
PDFStructureElement& rEle)
+{
+    for (auto it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); )
+    {
+        removePlaceholderSEImpl(rStructure, it);
+    }
+}
+
+} // end anonymous namespace
+
 /*
  * This function adds an internal structure list container to overcome the 
8191 elements array limitation
  * in kids element emission.
@@ -10885,18 +10971,22 @@ void PDFWriterImpl::endStructureElement()
  */
 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
 {
-    if( rEle.m_eType == PDFWriter::NonStructElement &&
-        rEle.m_nOwnElement != rEle.m_nParentElement )
+    if (rEle.m_nOwnElement != rEle.m_nParentElement
+        && *rEle.m_oType == PDFWriter::NonStructElement)
+    {
         return;
+    }
 
     for (auto const& child : rEle.m_aChildren)
     {
+        assert(child > 0 && o3tl::make_unsigned(child) < m_aStructure.size());
         if( child > 0 && o3tl::make_unsigned(child) < m_aStructure.size() )
         {
             PDFStructureElement& rChild = m_aStructure[ child ];
-            if( rChild.m_eType != PDFWriter::NonStructElement )
+            if (*rChild.m_oType != PDFWriter::NonStructElement)
             {
                 //triggered when a child of the rEle element is found
+                assert(rChild.m_nParentElement == rEle.m_nOwnElement);
                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
                     addInternalStructureContainer( rChild );//examine the child
                 else
@@ -10937,7 +11027,7 @@ void PDFWriterImpl::addInternalStructureContainer( 
PDFStructureElement& rEle )
         m_aStructure.emplace_back( );
         PDFStructureElement& rEleNew = m_aStructure.back();
         rEleNew.m_aAlias            = aAliasName;
-        rEleNew.m_eType             = PDFWriter::Division; // a new Div type 
container
+        rEleNew.m_oType.emplace(PDFWriter::Division); // a new Div type 
container
         rEleNew.m_nOwnElement       = nNewId;
         rEleNew.m_nParentElement    = nCurrentStructElement;
         //inherit the same page as the first child to be reparented
@@ -10988,7 +11078,9 @@ bool PDFWriterImpl::setCurrentStructureElement( 
sal_Int32 nEle )
             OStringBuffer aLine( "setCurrentStructureElement " );
             aLine.append( m_nCurrentStructElement );
             aLine.append( ": " );
-            aLine.append( getStructureTag( m_aStructure[ 
m_nCurrentStructElement ].m_eType ) );
+            aLine.append( m_aStructure[m_nCurrentStructElement].m_oType
+                ? 
getStructureTag(*m_aStructure[m_nCurrentStructElement].m_oType)
+                : "<placeholder>" );
             if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
             {
                 aLine.append( " aliased as \"" );
@@ -11010,15 +11102,17 @@ bool PDFWriterImpl::setStructureAttribute( enum 
PDFWriter::StructAttribute eAttr
     if( !m_aContext.Tagged )
         return false;
 
+    assert(m_aStructure[m_nCurrentStructElement].m_oType);
     bool bInsert = false;
     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))))
+                    || 
!m_aStructure[m_aStructure[m_nCurrentStructElement].m_nParentElement].m_oType
+                    || 
*m_aStructure[m_aStructure[m_nCurrentStructElement].m_nParentElement].m_oType 
!= PDFWriter::NonStructElement))))
     {
-        PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement 
].m_eType;
+        PDFWriter::StructElement const eType = 
*m_aStructure[m_nCurrentStructElement].m_oType;
         switch( eAttr )
         {
             case PDFWriter::Placement:
@@ -11237,7 +11331,7 @@ bool PDFWriterImpl::setStructureAttribute( enum 
PDFWriter::StructAttribute eAttr
         SAL_INFO("vcl.pdfwriter",
                  "rejecting setStructureAttribute( " << getAttributeTag( eAttr 
)
                  << ", " << getAttributeValueTag( eVal )
-                 << " ) on " << getStructureTag( m_aStructure[ 
m_nCurrentStructElement ].m_eType )
+                 << " ) on " << 
getStructureTag(*m_aStructure[m_nCurrentStructElement].m_oType)
                  << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias
                  << ") element");
 
@@ -11249,6 +11343,7 @@ bool PDFWriterImpl::setStructureAttributeNumerical( 
enum PDFWriter::StructAttrib
     if( ! m_aContext.Tagged )
         return false;
 
+    assert(m_aStructure[m_nCurrentStructElement].m_oType);
     bool bInsert = false;
     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
     {
@@ -11258,7 +11353,7 @@ bool PDFWriterImpl::setStructureAttributeNumerical( 
enum PDFWriter::StructAttrib
             return true;
         }
 
-        PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement 
].m_eType;
+        PDFWriter::StructElement const eType = 
*m_aStructure[m_nCurrentStructElement].m_oType;
         switch( eAttr )
         {
             case PDFWriter::SpaceBefore:
@@ -11369,7 +11464,7 @@ bool PDFWriterImpl::setStructureAttributeNumerical( 
enum PDFWriter::StructAttrib
         SAL_INFO("vcl.pdfwriter",
                  "rejecting setStructureAttributeNumerical( " << 
getAttributeTag( eAttr )
                  << ", " << static_cast<int>(nValue)
-                 << " ) on " << getStructureTag( m_aStructure[ 
m_nCurrentStructElement ].m_eType )
+                 << " ) on " << 
getStructureTag(*m_aStructure[m_nCurrentStructElement].m_oType)
                  << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias
                  << ") element");
 
@@ -11385,7 +11480,8 @@ void PDFWriterImpl::setStructureBoundingBox( const 
tools::Rectangle& rRect )
     if( !(m_nCurrentStructElement > 0 && m_bEmitStructure) )
         return;
 
-    PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement 
].m_eType;
+    assert(m_aStructure[m_nCurrentStructElement].m_oType);
+    PDFWriter::StructElement const eType = 
*m_aStructure[m_nCurrentStructElement].m_oType;
     if( eType == PDFWriter::Figure      ||
         eType == PDFWriter::Formula     ||
         eType == PDFWriter::Form        ||

Reply via email to