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();