drawinglayer/source/primitive2d/structuretagprimitive2d.cxx  |    4 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx   |   27 +--
 include/drawinglayer/primitive2d/structuretagprimitive2d.hxx |    6 
 include/svx/svdobj.hxx                                       |    2 
 svx/source/sdr/contact/viewobjectcontact.cxx                 |    6 
 svx/source/svdraw/svdobj.cxx                                 |    4 
 sw/inc/EnhancedPDFExportHelper.hxx                           |    6 
 sw/inc/dcontact.hxx                                          |    2 
 sw/source/core/text/EnhancedPDFExportHelper.cxx              |   86 +++++------
 sw/source/uibase/docvw/AnnotationWin.cxx                     |   81 +++-------
 sw/source/uibase/docvw/AnnotationWin2.cxx                    |   13 -
 vcl/inc/pdf/pdfwriter_impl.hxx                               |    2 
 vcl/qa/cppunit/pdfexport/data/tdf154982.odt                  |binary
 vcl/qa/cppunit/pdfexport/pdfexport.cxx                       |   83 ++++++++++
 vcl/source/gdi/pdfwriter_impl.cxx                            |    5 
 15 files changed, 190 insertions(+), 137 deletions(-)

New commits:
commit a22f1aa4ff46328327d209e524300d8bfe464511
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Fri Jul 14 11:16:26 2023 +0300
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Fri Jul 14 14:57:01 2023 +0200

    Simplify the logic a bit
    
    At least for me, it's easier to understand it this way.
    
    Change-Id: Ib66d4163c85263971e93390161c37614c0e5d87b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154436
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/sw/source/uibase/docvw/AnnotationWin.cxx 
b/sw/source/uibase/docvw/AnnotationWin.cxx
index 4654e376e464..c8f442dafc26 100644
--- a/sw/source/uibase/docvw/AnnotationWin.cxx
+++ b/sw/source/uibase/docvw/AnnotationWin.cxx
@@ -235,8 +235,9 @@ void SwAnnotationWin::ToggleResolved()
 
 void SwAnnotationWin::ToggleResolvedForThread()
 {
-    GetTopReplyNote()->ToggleResolved();
-    mrMgr.UpdateResolvedStatus(GetTopReplyNote());
+    auto pTop = GetTopReplyNote();
+    pTop->ToggleResolved();
+    mrMgr.UpdateResolvedStatus(pTop);
     mrMgr.LayoutPostIts();
 }
 
@@ -263,17 +264,15 @@ sal_uInt32 SwAnnotationWin::CreateUniqueParaId()
 void SwAnnotationWin::DeleteThread()
 {
     // Go to the top and delete each comment one by one
-    SwAnnotationWin *current, *topNote;
-    current = topNote = GetTopReplyNote();
-    SwAnnotationWin* next = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current);
-
-    while(next && next->GetTopReplyNote() == topNote)
+    SwAnnotationWin* topNote = GetTopReplyNote();
+    for (SwAnnotationWin* current = topNote;;)
     {
+        SwAnnotationWin* next = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current);
         current->mnDeleteEventId = Application::PostUserEvent( LINK( current, 
SwAnnotationWin, DeleteHdl), nullptr, true );
+        if (!next || next->GetTopReplyNote() != topNote)
+            return;
         current = next;
-        next = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current);
     }
-    current->mnDeleteEventId = Application::PostUserEvent( LINK( current, 
SwAnnotationWin, DeleteHdl), nullptr, true );
 }
 
 bool SwAnnotationWin::IsResolved() const
@@ -285,21 +284,15 @@ bool SwAnnotationWin::IsThreadResolved()
 {
     /// First Get the top note
     // then iterate downwards checking resolved status
-    SwAnnotationWin *pTopNote, *TopNote;
-    pTopNote = TopNote = GetTopReplyNote();
-    if (!pTopNote->IsResolved())
-        return false;
-
-    SwAnnotationWin* pSidebarWin = mrMgr.GetNextPostIt(KEY_PAGEDOWN, pTopNote);
-
-    while (pSidebarWin && pSidebarWin->GetTopReplyNote() == TopNote)
+    SwAnnotationWin* topNote = GetTopReplyNote();
+    for (SwAnnotationWin* current = topNote;;)
     {
-        pTopNote = pSidebarWin;
-        if (!pTopNote->IsResolved())
+        if (!current->IsResolved())
             return false;
-        pSidebarWin = mrMgr.GetNextPostIt(KEY_PAGEDOWN, pSidebarWin);
+        current = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current);
+        if (!current || current->GetTopReplyNote() != topNote)
+            return true;
     }
-    return true;
 }
 
 void SwAnnotationWin::UpdateData()
@@ -377,45 +370,33 @@ sal_uInt32 SwAnnotationWin::MoveCaret()
 sal_uInt32 SwAnnotationWin::CalcParent()
 {
     SwTextField* pTextField = mpFormatField->GetTextField();
-    SwPosition aPosition( pTextField->GetTextNode(), pTextField->GetStart() );
-    SwTextAttr * const pTextAttr =
-        pTextField->GetTextNode().GetTextAttrForCharAt(
-            aPosition.GetContentIndex() - 1,
-            RES_TXTATR_ANNOTATION );
-    const SwField* pField = pTextAttr ? pTextAttr->GetFormatField().GetField() 
: nullptr;
-    sal_uInt32 nParentId = 0;
-    if (pField && pField->Which() == SwFieldIds::Postit)
-    {
-        const SwPostItField* pPostItField = static_cast<const 
SwPostItField*>(pField);
-        nParentId = pPostItField->GetPostItId();
-    }
-    return nParentId;
+    if (SwPosition aPosition(pTextField->GetTextNode(), 
pTextField->GetStart());
+        aPosition.GetContentIndex() > 0)
+        if (const SwTextAttr* pTextAttr = 
pTextField->GetTextNode().GetTextAttrForCharAt(
+                aPosition.GetContentIndex() - 1, RES_TXTATR_ANNOTATION))
+            if (const SwField* pField = pTextAttr->GetFormatField().GetField())
+                if (pField->Which() == SwFieldIds::Postit)
+                    return static_cast<const 
SwPostItField*>(pField)->GetPostItId();
+    return 0;
 }
 
 // counts how many SwPostItField we have right after the current one
 sal_uInt32 SwAnnotationWin::CountFollowing()
 {
-    sal_uInt32 aCount = 1;  // we start with 1, so we have to subtract one at 
the end again
     SwTextField* pTextField = mpFormatField->GetTextField();
     SwPosition aPosition( pTextField->GetTextNode(), pTextField->GetStart() );
 
-    SwTextAttr * pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt(
-                                        aPosition.GetContentIndex() + 1,
-                                        RES_TXTATR_ANNOTATION );
-    SwField* pField = pTextAttr
-                    ? 
const_cast<SwField*>(pTextAttr->GetFormatField().GetField())
-                    : nullptr;
-    while ( pField && ( pField->Which()== SwFieldIds::Postit ) )
+    for (sal_Int32 n = 1;; ++n)
     {
-        aCount++;
-        pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt(
-                                        aPosition.GetContentIndex() + aCount,
-                                        RES_TXTATR_ANNOTATION );
-        pField = pTextAttr
-               ? const_cast<SwField*>(pTextAttr->GetFormatField().GetField())
-               : nullptr;
+        SwTextAttr * pTextAttr = 
pTextField->GetTextNode().GetTextAttrForCharAt(
+                                            aPosition.GetContentIndex() + n,
+                                            RES_TXTATR_ANNOTATION );
+        if (!pTextAttr)
+            return n - 1;
+        const SwField* pField = pTextAttr->GetFormatField().GetField();
+        if (!pField || pField->Which() != SwFieldIds::Postit)
+            return n - 1;
     }
-    return aCount - 1;
 }
 
 void SwAnnotationWin::InitAnswer(OutlinerParaObject const & rText)
diff --git a/sw/source/uibase/docvw/AnnotationWin2.cxx 
b/sw/source/uibase/docvw/AnnotationWin2.cxx
index 885c19877dc1..ae52f1c21075 100644
--- a/sw/source/uibase/docvw/AnnotationWin2.cxx
+++ b/sw/source/uibase/docvw/AnnotationWin2.cxx
@@ -1350,14 +1350,15 @@ void SwAnnotationWin::SetViewState(ViewState bViewState)
 
 SwAnnotationWin* SwAnnotationWin::GetTopReplyNote()
 {
-    SwAnnotationWin* pTopNote = this;
-    SwAnnotationWin* pSidebarWin = IsFollow() ? 
mrMgr.GetNextPostIt(KEY_PAGEUP, this) : nullptr;
-    while (pSidebarWin)
+    for (SwAnnotationWin* pTopNote = this;;)
     {
-        pTopNote = pSidebarWin;
-        pSidebarWin = pSidebarWin->IsFollow() ? 
mrMgr.GetNextPostIt(KEY_PAGEUP, pSidebarWin) : nullptr;
+        if (!pTopNote->IsFollow())
+            return pTopNote;
+        SwAnnotationWin* pPrev = mrMgr.GetNextPostIt(KEY_PAGEUP, pTopNote);
+        if (!pPrev)
+            return pTopNote;
+        pTopNote = pPrev;
     }
-    return pTopNote;
 }
 
 void SwAnnotationWin::SwitchToFieldPos()
commit d467f1aa3d028f399826c97e2eecedcd79efcf65
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Wed Jul 12 18:34:24 2023 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Fri Jul 14 14:56:52 2023 +0200

    tdf#154982 drawinglayer,svx,sw,vcl: PDF export: hell flys and shapes...
    
    ... should be below their anchor paragraph in the structure tree.
    
    Refactor SwEnhancedPDFExportHelper (etc.) to use the new
    EnsureStructureElement()/InitStructureElement() functions instead of
    SetCurrentStructureElement() for the frames, and allow it for flys that
    don't have their anchor paragraphs created yet because the hell layer is
    exported before the document body.
    
    Change-Id: I1be3b54002e8196772e6f9d81dd0fd0c85b6e34b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154399
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx 
b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
index 62da91ecc00f..47af55ab9b57 100644
--- a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx
@@ -31,13 +31,13 @@ namespace drawinglayer::primitive2d
             bool bBackground,
             bool bIsImage,
             Primitive2DContainer&& aChildren,
-            sal_Int32 const nAnchorStructureElementId,
+            void const*const pAnchorStructureElementKey,
             ::std::vector<sal_Int32> const*const pAnnotIds)
         :   GroupPrimitive2D(std::move(aChildren)),
             maStructureElement(rStructureElement),
             mbBackground(bBackground),
             mbIsImage(bIsImage)
-        ,   m_nAnchorStructureElementId(nAnchorStructureElementId)
+        ,   m_pAnchorStructureElementKey(pAnchorStructureElementKey)
         {
             if (pAnnotIds)
             {
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index b2045a5e18c0..1a7beb8affc6 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -2527,7 +2527,7 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
     // structured tag primitive
     const vcl::PDFWriter::StructElement& 
rTagElement(rStructureTagCandidate.getStructureElement());
     bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement));
-    sal_Int32 nPreviousElement(-1);
+    bool bNeedEndAnchor(false);
 
     if (!rStructureTagCandidate.isTaggedSdrObject())
     {
@@ -2539,19 +2539,12 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
         // foreground object: tag as regular structure element
         if (!rStructureTagCandidate.isBackground())
         {
-            if (rStructureTagCandidate.GetAnchorStructureElementId() != -1)
+            if (rStructureTagCandidate.GetAnchorStructureElementKey() != 
nullptr)
             {
-                auto const nTemp = 
mpPDFExtOutDevData->GetCurrentStructureElement();
-                bool const bSuccess = 
mpPDFExtOutDevData->SetCurrentStructureElement(
-                    rStructureTagCandidate.GetAnchorStructureElementId());
-                if (bSuccess)
-                {
-                    nPreviousElement = nTemp;
-                }
-                else
-                {
-                    SAL_WARN("drawinglayer", "anchor structure element not 
found?");
-                }
+                sal_Int32 const id = 
mpPDFExtOutDevData->EnsureStructureElement(
+                    rStructureTagCandidate.GetAnchorStructureElementKey());
+                mpPDFExtOutDevData->BeginStructureElement(id);
+                bNeedEndAnchor = true;
             }
             mpPDFExtOutDevData->WrapBeginStructureElement(rTagElement);
             switch (rTagElement)
@@ -2627,13 +2620,9 @@ void 
VclMetafileProcessor2D::processStructureTagPrimitive2D(
     {
         // write end tag
         mpPDFExtOutDevData->EndStructureElement();
-        if (nPreviousElement != -1)
+        if (bNeedEndAnchor)
         {
-#ifndef NDEBUG
-            bool const bSuccess =
-#endif
-                
mpPDFExtOutDevData->SetCurrentStructureElement(nPreviousElement);
-            assert(bSuccess);
+            mpPDFExtOutDevData->EndStructureElement();
         }
     }
 }
diff --git a/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx 
b/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
index 8ee2b267b57f..0d7e6ba57735 100644
--- a/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/structuretagprimitive2d.hxx
@@ -50,7 +50,7 @@ namespace drawinglayer::primitive2d
             /// flag for image (OBJ_GRAF)
             bool                                    mbIsImage;
             /// anchor structure element (Writer)
-            sal_Int32 m_nAnchorStructureElementId;
+            void const* m_pAnchorStructureElementKey;
             /// for Annot structure element, the ids of the annotations
             ::std::vector<sal_Int32> m_AnnotIds;
 
@@ -61,7 +61,7 @@ namespace drawinglayer::primitive2d
                 bool bBackground,
                 bool bIsImage,
                 Primitive2DContainer&& aChildren,
-                sal_Int32 nAnchorStructureElementId = -1,
+                void const* pAnchorStructureElementKey = nullptr,
                 ::std::vector<sal_Int32> const* pAnnotIds = nullptr);
 
             /// data read access
@@ -69,7 +69,7 @@ namespace drawinglayer::primitive2d
             bool isBackground() const { return mbBackground; }
             bool isImage() const { return mbIsImage; }
             bool isTaggedSdrObject() const;
-            sal_Int32 GetAnchorStructureElementId() const { return 
m_nAnchorStructureElementId; }
+            void const* GetAnchorStructureElementKey() const { return 
m_pAnchorStructureElementKey; }
             ::std::vector<sal_Int32> GetAnnotIds() const { return m_AnnotIds; }
 
             /// compare operator
diff --git a/include/svx/svdobj.hxx b/include/svx/svdobj.hxx
index 8dadf982b490..ed62c1db983f 100644
--- a/include/svx/svdobj.hxx
+++ b/include/svx/svdobj.hxx
@@ -129,7 +129,7 @@ class SVXCORE_DLLPUBLIC SdrObjUserCall
 public:
     virtual ~SdrObjUserCall();
     virtual void Changed(const SdrObject& rObj, SdrUserCallType eType, const 
tools::Rectangle& rOldBoundRect);
-    virtual sal_Int32 GetPDFAnchorStructureElementId(SdrObject const& rObj);
+    virtual void const* GetPDFAnchorStructureElementKey(SdrObject const& rObj);
 };
 
 class SVXCORE_DLLPUBLIC SdrObjMacroHitRec
diff --git a/svx/source/sdr/contact/viewobjectcontact.cxx 
b/svx/source/sdr/contact/viewobjectcontact.cxx
index fabf99a13d75..caac4a301042 100644
--- a/svx/source/sdr/contact/viewobjectcontact.cxx
+++ b/svx/source/sdr/contact/viewobjectcontact.cxx
@@ -436,10 +436,10 @@ drawinglayer::primitive2d::Primitive2DContainer const & 
ViewObjectContact::getPr
                     const bool bBackground(pSdrPage->IsMasterPage());
                     const bool bImage(SdrObjKind::Graphic == 
pSdrObj->GetObjIdentifier());
                     // note: there must be output device here, in PDF export
-                    sal_Int32 nAnchorId(-1);
+                    void const* pAnchorKey(nullptr);
                     if (auto const pUserCall = pSdrObj->GetUserCall())
                     {
-                        nAnchorId = 
pUserCall->GetPDFAnchorStructureElementId(*pSdrObj);
+                        pAnchorKey = 
pUserCall->GetPDFAnchorStructureElementKey(*pSdrObj);
                     }
 
                     ::std::vector<sal_Int32> annotIds;
@@ -457,7 +457,7 @@ drawinglayer::primitive2d::Primitive2DContainer const & 
ViewObjectContact::getPr
                             bBackground,
                             bImage,
                             std::move(xNewPrimitiveSequence),
-                            nAnchorId,
+                            pAnchorKey,
                             &annotIds));
                     xNewPrimitiveSequence = 
drawinglayer::primitive2d::Primitive2DContainer { xReference };
                 }
diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx
index 6ea7dfe83839..77a78035cdda 100644
--- a/svx/source/svdraw/svdobj.cxx
+++ b/svx/source/svdraw/svdobj.cxx
@@ -136,9 +136,9 @@ void SdrObjUserCall::Changed(const SdrObject& /*rObj*/, 
SdrUserCallType /*eType*
 {
 }
 
-sal_Int32 SdrObjUserCall::GetPDFAnchorStructureElementId(SdrObject const&)
+void const* SdrObjUserCall::GetPDFAnchorStructureElementKey(SdrObject const&)
 {
-    return -1;
+    return nullptr;
 }
 
 SdrObjMacroHitRec::SdrObjMacroHitRec() :
diff --git a/sw/inc/EnhancedPDFExportHelper.hxx 
b/sw/inc/EnhancedPDFExportHelper.hxx
index a52a03f786ae..a6fad2594ae8 100644
--- a/sw/inc/EnhancedPDFExportHelper.hxx
+++ b/sw/inc/EnhancedPDFExportHelper.hxx
@@ -190,7 +190,7 @@ typedef std::vector< IdMapEntry > LinkIdMap;
 typedef std::map< const SwTable*, TableColumnsMapEntry > TableColumnsMap;
 typedef std::map< const SwNumberTreeNode*, sal_Int32 > NumListIdMap;
 typedef std::map< const SwNumberTreeNode*, sal_Int32 > NumListBodyIdMap;
-typedef std::map< const void*, sal_Int32 > FrameTagIdMap;
+typedef std::set<const void*> FrameTagSet;
 
 class SwEnhancedPDFExportHelper
 {
@@ -218,7 +218,7 @@ class SwEnhancedPDFExportHelper
     static LinkIdMap s_aLinkIdMap;
     static NumListIdMap s_aNumListIdMap;
     static NumListBodyIdMap s_aNumListBodyIdMap;
-    static FrameTagIdMap s_aFrameTagIdMap;
+    static FrameTagSet s_FrameTagSet;
 
     static LanguageType s_eLanguageDefault;
 
@@ -250,7 +250,7 @@ class SwEnhancedPDFExportHelper
     static LinkIdMap& GetLinkIdMap() { return s_aLinkIdMap; }
     static NumListIdMap& GetNumListIdMap() {return s_aNumListIdMap; }
     static NumListBodyIdMap& GetNumListBodyIdMap() {return 
s_aNumListBodyIdMap; }
-    static FrameTagIdMap& GetFrameTagIdMap() { return s_aFrameTagIdMap; }
+    static FrameTagSet & GetFrameTagSet() { return s_FrameTagSet; }
 
     static LanguageType GetDefaultLanguage() {return s_eLanguageDefault; }
 
diff --git a/sw/inc/dcontact.hxx b/sw/inc/dcontact.hxx
index 20d99f4e7523..b4d501f44ccf 100644
--- a/sw/inc/dcontact.hxx
+++ b/sw/inc/dcontact.hxx
@@ -390,7 +390,7 @@ class SAL_DLLPUBLIC_RTTI SwDrawContact final : public 
SwContact
 
         /// Virtual methods of SdrObjUserCall.
         virtual void Changed(const SdrObject& rObj, SdrUserCallType eType, 
const tools::Rectangle& rOldBoundRect) override;
-        virtual sal_Int32 GetPDFAnchorStructureElementId(SdrObject const& 
rObj) override;
+        virtual void const* GetPDFAnchorStructureElementKey(SdrObject const& 
rObj) override;
 
         /** Used by Changed() and by UndoDraw.
          Notifies paragraphs that have to get out of the way. */
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx 
b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index e48774f2b2c2..69fdfcacb096 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -96,7 +96,7 @@ TableColumnsMap SwEnhancedPDFExportHelper::s_aTableColumnsMap;
 LinkIdMap SwEnhancedPDFExportHelper::s_aLinkIdMap;
 NumListIdMap SwEnhancedPDFExportHelper::s_aNumListIdMap;
 NumListBodyIdMap SwEnhancedPDFExportHelper::s_aNumListBodyIdMap;
-FrameTagIdMap SwEnhancedPDFExportHelper::s_aFrameTagIdMap;
+FrameTagSet SwEnhancedPDFExportHelper::s_FrameTagSet;
 
 LanguageType SwEnhancedPDFExportHelper::s_eLanguageDefault = LANGUAGE_SYSTEM;
 
@@ -361,32 +361,16 @@ SwTaggedPDFHelper::~SwTaggedPDFHelper()
 #endif
 }
 
-static auto GetReopenTagFromFrame(SwFrame const& rFrame) -> sal_Int32
-{
-    void const*const pKey = lcl_GetKeyFromFrame(rFrame);
-
-    if (pKey)
-    {
-        FrameTagIdMap const& 
rFrameTagIdMap(SwEnhancedPDFExportHelper::GetFrameTagIdMap());
-        auto const it(rFrameTagIdMap.find(pKey));
-        if (it != rFrameTagIdMap.end())
-        {
-            return (*it).second;
-        }
-    }
-    return -1;
-}
-
-sal_Int32 SwDrawContact::GetPDFAnchorStructureElementId(SdrObject const& rObj)
+void const* SwDrawContact::GetPDFAnchorStructureElementKey(SdrObject const& 
rObj)
 {
     SwFrame const*const pAnchorFrame(GetAnchoredObj(&rObj)->GetAnchorFrame());
-    return pAnchorFrame ? GetReopenTagFromFrame(*pAnchorFrame) : -1;
+    return pAnchorFrame ? lcl_GetKeyFromFrame(*pAnchorFrame) : nullptr;
 }
 
 bool SwTaggedPDFHelper::CheckReopenTag()
 {
     bool bRet = false;
-    sal_Int32 nReopenTag = -1;
+    void const* pReopenKey(nullptr);
     bool bContinue = false; // in some cases we just have to reopen a tag 
without early returning
 
     if ( mpFrameInfo )
@@ -423,21 +407,27 @@ bool SwTaggedPDFHelper::CheckReopenTag()
 
         if ( pKeyFrame )
         {
-            nReopenTag = GetReopenTagFromFrame(*pKeyFrame);
+            void const*const pKey = lcl_GetKeyFromFrame(*pKeyFrame);
+            FrameTagSet& 
rFrameTagSet(SwEnhancedPDFExportHelper::GetFrameTagSet());
+            if (rFrameTagSet.find(pKey) != rFrameTagSet.end()
+                || rFrame.IsFlyFrame()) // for hell layer flys
+            {
+                pReopenKey = pKey;
+            }
         }
     }
 
-    if ( -1 != nReopenTag )
+    if (pReopenKey)
     {
-        m_nRestoreCurrentTag = 
mpPDFExtOutDevData->GetCurrentStructureElement();
-        const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( 
nReopenTag );
-        OSL_ENSURE( bSuccess, "Failed to reopen tag" );
+        sal_Int32 const id = 
mpPDFExtOutDevData->EnsureStructureElement(pReopenKey);
+        mpPDFExtOutDevData->BeginStructureElement(id);
+        ++m_nEndStructureElement;
 
 #if OSL_DEBUG_LEVEL > 1
         aStructStack.push_back( 99 );
 #endif
 
-        bRet = bSuccess;
+        bRet = true;
     }
 
     return bRet && !bContinue;
@@ -458,8 +448,33 @@ void SwTaggedPDFHelper::CheckRestoreTag() const
 
 void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const 
OUString& rString )
 {
+    void const* pKey(nullptr);
+
+    if ( mpFrameInfo )
+    {
+        const SwFrame& rFrame = mpFrameInfo->mrFrame;
+
+        if ( ( rFrame.IsPageFrame() && !static_cast<const 
SwPageFrame&>(rFrame).GetPrev() ) ||
+             ( rFrame.IsFlowFrame() && 
!SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() && 
SwFlowFrame::CastFlowFrame(&rFrame)->HasFollow() ) ||
+             ( rFrame.IsTextFrame() && rFrame.GetDrawObjs() ) ||
+             ( rFrame.IsRowFrame() && rFrame.IsInSplitTableRow() ) ||
+             ( rFrame.IsCellFrame() && 
const_cast<SwFrame&>(rFrame).GetNextCellLeaf() ) )
+        {
+            pKey = lcl_GetKeyFromFrame(rFrame);
+
+            if (pKey)
+            {
+                FrameTagSet& 
rFrameTagSet(SwEnhancedPDFExportHelper::GetFrameTagSet());
+                assert(rFrameTagSet.find(pKey) == rFrameTagSet.end());
+                rFrameTagSet.emplace(pKey);
+            }
+        }
+    }
+
     // write new tag
-    const sal_Int32 nId = mpPDFExtOutDevData->WrapBeginStructureElement( 
eType, rString );
+    const sal_Int32 nId = mpPDFExtOutDevData->EnsureStructureElement(pKey);
+    mpPDFExtOutDevData->InitStructureElement(nId, eType, rString);
+    mpPDFExtOutDevData->BeginStructureElement(nId);
     ++m_nEndStructureElement;
 
 #if OSL_DEBUG_LEVEL > 1
@@ -503,21 +518,6 @@ void SwTaggedPDFHelper::BeginTag( 
vcl::PDFWriter::StructElement eType, const OUS
             NumListBodyIdMap& rNumListBodyIdMap = 
SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
             rNumListBodyIdMap[ pNodeNum ] = nId;
         }
-
-        if ( ( rFrame.IsPageFrame() && !static_cast<const 
SwPageFrame&>(rFrame).GetPrev() ) ||
-             ( rFrame.IsFlowFrame() && 
!SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() && 
SwFlowFrame::CastFlowFrame(&rFrame)->HasFollow() ) ||
-             ( rFrame.IsTextFrame() && rFrame.GetDrawObjs() ) ||
-             ( rFrame.IsRowFrame() && rFrame.IsInSplitTableRow() ) ||
-             ( rFrame.IsCellFrame() && 
const_cast<SwFrame&>(rFrame).GetNextCellLeaf() ) )
-        {
-            const void* pKey = lcl_GetKeyFromFrame( rFrame );
-
-            if ( pKey )
-            {
-                FrameTagIdMap& rFrameTagIdMap = 
SwEnhancedPDFExportHelper::GetFrameTagIdMap();
-                rFrameTagIdMap[ pKey ] = nId;
-            }
-        }
     }
 
     SetAttributes( eType );
@@ -1710,7 +1710,7 @@ SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( 
SwEditShell& rSh,
     s_aLinkIdMap.clear();
     s_aNumListIdMap.clear();
     s_aNumListBodyIdMap.clear();
-    s_aFrameTagIdMap.clear();
+    s_FrameTagSet.clear();
 
 #if OSL_DEBUG_LEVEL > 1
     aStructStack.clear();
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index 8c232589e9c7..edf88459b4f4 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -28,6 +28,7 @@
 #include <memory>
 #include <string_view>
 #include <vector>
+#include <stack>
 
 #include <pdf/ResourceDict.hxx>
 #include <pdf/BitmapID.hxx>
@@ -762,6 +763,7 @@ private:
     /* current object in the structure hierarchy
      */
     sal_Int32                           m_nCurrentStructElement;
+    std::stack<sal_Int32> m_StructElementStack;
     /* structure parent tree */
     std::vector< OString >         m_aStructParentTree;
     /* emit structure marks currently (aka. NonStructElement or not)
diff --git a/vcl/qa/cppunit/pdfexport/data/tdf154982.odt 
b/vcl/qa/cppunit/pdfexport/data/tdf154982.odt
new file mode 100644
index 000000000000..a35ffb861abe
Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/tdf154982.odt differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx 
b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 5e26c693197d..c26db6f23504 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -3596,20 +3596,20 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf57423)
             {
                 switch (nFigure)
                 {
-                    case 0:
+                    case 2:
                         CPPUNIT_ASSERT_EQUAL(OUString(u"QR Code - Tells how to 
get to Mosegaard"),
                                              
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
                                                  
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
                                                      pObject->Lookup("Alt"))));
                         break;
-                    case 1:
+                    case 0:
                         CPPUNIT_ASSERT_EQUAL(OUString(u"Title: Arrows - 
Description:  Explains the "
                                                       u"different arrow 
appearances"),
                                              
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
                                                  
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
                                                      pObject->Lookup("Alt"))));
                         break;
-                    case 2:
+                    case 1:
                         CPPUNIT_ASSERT_EQUAL(
                             OUString(u"My blue triangle - Does not need 
further description"),
                             ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
@@ -3664,6 +3664,83 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf57423)
     CPPUNIT_ASSERT_EQUAL(int(4), nDiv);
 }
 
+CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf154982)
+{
+    aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+    // Enable PDF/UA
+    uno::Sequence<beans::PropertyValue> aFilterData(
+        comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) 
} }));
+    aMediaDescriptor["FilterData"] <<= aFilterData;
+    saveAsPDF(u"tdf154982.odt");
+
+    vcl::filter::PDFDocument aDocument;
+    SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+    CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+    // The document has one page.
+    std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+    int nFigure(0);
+    for (const auto& rDocElement : aDocument.GetElements())
+    {
+        auto pObject = 
dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+        if (!pObject)
+            continue;
+        auto pType = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+        if (pType && pType->GetValue() == "StructElem")
+        {
+            auto pS = 
dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+            if (pS && pS->GetValue() == "Figure")
+            {
+                switch (nFigure)
+                {
+                    case 0:
+                        CPPUNIT_ASSERT_EQUAL(
+                            OUString(u"Here comes the signature - Please sign 
here"),
+                            ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+                                
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
+                                    pObject->Lookup("Alt"))));
+                        break;
+                    case 1:
+                        CPPUNIT_ASSERT_EQUAL(OUString(u"Home"),
+                                             
::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+                                                 
*dynamic_cast<vcl::filter::PDFHexStringElement*>(
+                                                     pObject->Lookup("Alt"))));
+                        break;
+                }
+
+                // the problem was that the figures in the hell layer were not
+                // below their anchor paragraphs in the structure tree
+                auto pParentRef
+                    = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
+                CPPUNIT_ASSERT(pParentRef);
+                auto pParent(pParentRef->LookupObject());
+                CPPUNIT_ASSERT(pParent);
+                auto pParentType
+                    = 
dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
+                CPPUNIT_ASSERT_EQUAL(OString("StructElem"), 
pParentType->GetValue());
+                auto pParentS = 
dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
+                CPPUNIT_ASSERT_EQUAL(OString("Standard"), 
pParentS->GetValue());
+
+                auto pPParentRef
+                    = 
dynamic_cast<vcl::filter::PDFReferenceElement*>(pParent->Lookup("P"));
+                CPPUNIT_ASSERT(pPParentRef);
+                auto pPParent(pPParentRef->LookupObject());
+                CPPUNIT_ASSERT(pPParent);
+                auto pPParentType
+                    = 
dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("Type"));
+                CPPUNIT_ASSERT_EQUAL(OString("StructElem"), 
pPParentType->GetValue());
+                auto pPParentS = 
dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("S"));
+                CPPUNIT_ASSERT_EQUAL(OString("Document"), 
pPParentS->GetValue());
+                ++nFigure;
+            }
+        }
+    }
+    CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
+}
+
 CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135192)
 {
     aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index c79d82e134a4..5765426375ba 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -1236,6 +1236,7 @@ PDFWriterImpl::PDFWriterImpl( const 
PDFWriter::PDFWriterContext& rContext,
     m_aStructure.emplace_back( );
     m_aStructure[0].m_nOwnElement       = 0;
     m_aStructure[0].m_nParentElement    = 0;
+    //m_StructElementStack.push(0);
 
     Font aFont;
     aFont.SetFamilyName( "Times" );
@@ -10850,6 +10851,7 @@ void PDFWriterImpl::beginStructureElement(sal_Int32 
const id)
     endStructureElementMCSeq(EndMode::OnlyStruct);
 
     PDFStructureElement& rEle = m_aStructure[id];
+    m_StructElementStack.push(m_nCurrentStructElement);
     m_nCurrentStructElement = id;
 
     if (g_bDebugDisableCompression)
@@ -10909,7 +10911,8 @@ void PDFWriterImpl::endStructureElement()
     }
 
     // "end" the structure element, the parent becomes current element
-    m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement 
].m_nParentElement;
+    m_nCurrentStructElement = m_StructElementStack.top();
+    m_StructElementStack.pop();
 
     // check whether to emit structure henceforth
     m_bEmitStructure = checkEmitStructure();

Reply via email to