From 23ea1663b72d1aee93d89ae9929c6b4d077aaad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= Date: Thu, 16 Apr 2026 16:13:13 +0000 Subject: [PATCH] put calc background images under link control Change-Id: I5adbbaf7e4860732dad2375dcbee541198ef6fab Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/2393 Tested-by: Jenkins CPCI Reviewed-by: Miklos Vajna (cherry picked from commit 6ebc3fb5db211b6c9de96dcf3ba14b6b7f37c203) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/206342 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- include/svx/fillbitmaplink.hxx | 9 +++ .../data/bullet-image-link.fods | 51 ++++++++++++++++ .../data/page-background-link.fods | 36 +++++++++++ .../data/shape-background-link.fods | 45 ++++++++++++++ sc/qa/unit/tiledrendering/tiledrendering2.cxx | 60 +++++++++++++++++++ sc/source/ui/docshell/docsh4.cxx | 6 ++ sc/source/ui/view/tabvwsh4.cxx | 6 +- .../tiledrendering/data/shape-fill-link.fodp | 37 ++++++++++++ sd/qa/unit/tiledrendering/tiledrendering2.cxx | 20 +++++++ sd/source/core/drawdoc.cxx | 9 +-- svx/source/xoutdev/fillbitmaplink.cxx | 41 +++++++++++-- .../tiledrendering/data/shape-fill-link.fodt | 45 ++++++++++++++ .../extras/tiledrendering/tiledrendering2.cxx | 25 ++++++++ 13 files changed, 375 insertions(+), 15 deletions(-) create mode 100644 sc/qa/unit/tiledrendering/data/bullet-image-link.fods create mode 100644 sc/qa/unit/tiledrendering/data/page-background-link.fods create mode 100644 sc/qa/unit/tiledrendering/data/shape-background-link.fods create mode 100644 sd/qa/unit/tiledrendering/data/shape-fill-link.fodp create mode 100644 sw/qa/extras/tiledrendering/data/shape-fill-link.fodt diff --git a/include/svx/fillbitmaplink.hxx b/include/svx/fillbitmaplink.hxx index 39d44bc77a08..4ce586e50e44 100644 --- a/include/svx/fillbitmaplink.hxx +++ b/include/svx/fillbitmaplink.hxx @@ -14,6 +14,7 @@ #include #include +class SdrModel; class SfxItemPool; namespace sfx2 @@ -21,6 +22,10 @@ namespace sfx2 class LinkManager; } +// Returns true if the pool contains any XATTR_FILLBITMAP items with +// unresolved remote URLs (GraphicType::Default with non-empty originURL). +SVXCORE_DLLPUBLIC bool hasDeferredFillBitmapLinks(const SfxItemPool& rPool); + // Scan pool surrogates for XATTR_FILLBITMAP items with unresolved remote // URLs (GraphicType::Default with non-empty originURL) and register an // sfx2::SvBaseLink for each. When the link is updated, the fetched graphic @@ -28,6 +33,10 @@ class LinkManager; SVXCORE_DLLPUBLIC void registerFillBitmapLinks(SfxItemPool& rPool, sfx2::LinkManager& rLinkMgr, std::function fnInvalidate); +// Convenience overload for the drawing layer: registers fill bitmap links +// and flushes all ViewObjectContacts on all pages when the graphic arrives. +SVXCORE_DLLPUBLIC void registerFillBitmapLinks(SdrModel& rModel, sfx2::LinkManager& rLinkMgr); + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sc/qa/unit/tiledrendering/data/bullet-image-link.fods b/sc/qa/unit/tiledrendering/data/bullet-image-link.fods new file mode 100644 index 000000000000..ee53f179a423 --- /dev/null +++ b/sc/qa/unit/tiledrendering/data/bullet-image-link.fods @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + Cell content. + + + + + + + + First bullet with remote image + + + Second bullet with remote image + + + + + + + + + diff --git a/sc/qa/unit/tiledrendering/data/page-background-link.fods b/sc/qa/unit/tiledrendering/data/page-background-link.fods new file mode 100644 index 000000000000..3dbcd131526a --- /dev/null +++ b/sc/qa/unit/tiledrendering/data/page-background-link.fods @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + Sheet with remote page background image. + + + + + + diff --git a/sc/qa/unit/tiledrendering/data/shape-background-link.fods b/sc/qa/unit/tiledrendering/data/shape-background-link.fods new file mode 100644 index 000000000000..02873b127171 --- /dev/null +++ b/sc/qa/unit/tiledrendering/data/shape-background-link.fods @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + Sheet with shape using remote fill image. + + + + + Rectangle with remote fill. + + + + + + diff --git a/sc/qa/unit/tiledrendering/tiledrendering2.cxx b/sc/qa/unit/tiledrendering/tiledrendering2.cxx index bc4ed800cd23..d6ad5a5fd7df 100644 --- a/sc/qa/unit/tiledrendering/tiledrendering2.cxx +++ b/sc/qa/unit/tiledrendering/tiledrendering2.cxx @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -258,6 +259,65 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testBreakPageView) CPPUNIT_ASSERT_EQUAL(Color(255, 255, 255), aColor); } +CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testPageBackgroundRemoteNotFetched) +{ + // Page background image with a remote URL must not fetch + // the URL during paint when link updates are not allowed. + uno::Sequence aParams = { + comphelper::makePropertyValue(u"UpdateDocMode"_ustr, + sal_Int16(css::document::UpdateDocMode::NO_UPDATE)), + }; + loadFromFile(u"page-background-link.fods", aParams); + ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); + CPPUNIT_ASSERT(pModelObj); + pModelObj->initializeForTiledRendering({}); + + ScopedVclPtrInstance pDevice(DeviceFormat::WITHOUT_ALPHA); + pDevice->SetOutputSizePixel(Size(1024, 768)); + pModelObj->paintTile(*pDevice, 1024, 768, 0, 0, 15360, 7680); +} + +CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testShapeBackgroundRemoteNotFetched) +{ + // Shape fill bitmap with a remote URL must not fetch + // the URL during paint when link updates are not allowed. + // The assert in createNewSdrFillGraphicAttribute will fire if + // a remote fetch is attempted. + uno::Sequence aParams = { + comphelper::makePropertyValue(u"UpdateDocMode"_ustr, + sal_Int16(css::document::UpdateDocMode::NO_UPDATE)), + }; + loadFromFile(u"shape-background-link.fods", aParams); + ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); + CPPUNIT_ASSERT(pModelObj); + pModelObj->initializeForTiledRendering({}); + + ScopedVclPtrInstance pDevice(DeviceFormat::WITHOUT_ALPHA); + pDevice->SetOutputSizePixel(Size(1024, 768)); + pModelObj->paintTile(*pDevice, 1024, 768, 0, 0, 15360, 7680); +} + +CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testBulletImageRemoteNotFetched) +{ + // text:list-level-style-image with a remote URL must not fetch + // the URL during paint when link updates are not allowed. + // Currently the editeng rendering path silently skips unresolved + // GraphicExternalLink graphics. If someone adds fetching here, + // this test should catch it. + uno::Sequence aParams = { + comphelper::makePropertyValue(u"UpdateDocMode"_ustr, + sal_Int16(css::document::UpdateDocMode::NO_UPDATE)), + }; + loadFromFile(u"bullet-image-link.fods", aParams); + ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); + CPPUNIT_ASSERT(pModelObj); + pModelObj->initializeForTiledRendering({}); + + ScopedVclPtrInstance pDevice(DeviceFormat::WITHOUT_ALPHA); + pDevice->SetOutputSizePixel(Size(1024, 768)); + pModelObj->paintTile(*pDevice, 1024, 768, 0, 0, 15360, 7680); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/docshell/docsh4.cxx b/sc/source/ui/docshell/docsh4.cxx index 5b72d4188650..1213cf2f21be 100644 --- a/sc/source/ui/docshell/docsh4.cxx +++ b/sc/source/ui/docshell/docsh4.cxx @@ -94,6 +94,8 @@ #include #include #include +#include +#include #include #include #include @@ -214,6 +216,10 @@ void ScDocShell::ReloadAllLinks() // update linked graphics from the draw layer if (sfx2::LinkManager* pLinkMgr = m_pDocument->GetDocLinkManager().getLinkManager(false)) { + if (ScDrawLayer* pDrawLayer = m_pDocument->GetDrawLayer()) + { + registerFillBitmapLinks(*pDrawLayer, *pLinkMgr); + } pLinkMgr->UpdateAllLinks(false, nullptr, u""_ustr); } } diff --git a/sc/source/ui/view/tabvwsh4.cxx b/sc/source/ui/view/tabvwsh4.cxx index c6b553adbd04..a6696a22ddda 100644 --- a/sc/source/ui/view/tabvwsh4.cxx +++ b/sc/source/ui/view/tabvwsh4.cxx @@ -92,6 +92,8 @@ #include #include #include +#include +#include using namespace com::sun::star; using namespace sfx2::sidebar; @@ -1690,7 +1692,9 @@ void ScTabViewShell::Construct( TriState nForceDesignMode ) if (!bLink) { const sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager(); - if (rDoc.HasLinkFormulaNeedingCheck() || rDoc.HasAreaLinks() || rMgr.hasExternalLinks()) + if (rDoc.HasLinkFormulaNeedingCheck() || rDoc.HasAreaLinks() + || rMgr.hasExternalLinks() + || hasDeferredFillBitmapLinks(*rDoc.GetPool())) bLink = true; } if (bLink) diff --git a/sd/qa/unit/tiledrendering/data/shape-fill-link.fodp b/sd/qa/unit/tiledrendering/data/shape-fill-link.fodp new file mode 100644 index 000000000000..fd7be9e036b7 --- /dev/null +++ b/sd/qa/unit/tiledrendering/data/shape-fill-link.fodp @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + Shape with remote fill image. + + + + + diff --git a/sd/qa/unit/tiledrendering/tiledrendering2.cxx b/sd/qa/unit/tiledrendering/tiledrendering2.cxx index 409df1e46638..842f8ca15c07 100644 --- a/sd/qa/unit/tiledrendering/tiledrendering2.cxx +++ b/sd/qa/unit/tiledrendering/tiledrendering2.cxx @@ -219,6 +219,26 @@ CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testBulletImageRemoteNotFetched) pImpressDocument->paintTile(*pDevice, 1024, 768, 0, 0, 15360, 7680); } +CPPUNIT_TEST_FIXTURE(SdTiledRenderingTest, testShapeFillRemoteNotFetched) +{ + // draw:fill-image with a remote URL on a shape must not fetch + // the URL during paint when link updates are not allowed. + // The assert in createNewSdrFillGraphicAttribute will fire if + // a remote fetch is attempted. + uno::Sequence aParams = { + comphelper::makePropertyValue(u"UpdateDocMode"_ustr, + sal_Int16(css::document::UpdateDocMode::NO_UPDATE)), + }; + loadFromFile(u"shape-fill-link.fodp", aParams); + SdXImpressDocument* pImpressDocument = dynamic_cast(mxComponent.get()); + CPPUNIT_ASSERT(pImpressDocument); + pImpressDocument->initializeForTiledRendering({}); + + ScopedVclPtrInstance pDevice(DeviceFormat::WITHOUT_ALPHA); + pDevice->SetOutputSizePixel(Size(1024, 768)); + pImpressDocument->paintTile(*pDevice, 1024, 768, 0, 0, 15360, 7680); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc.cxx b/sd/source/core/drawdoc.cxx index f4bbfa7c6431..5e444329efff 100644 --- a/sd/source/core/drawdoc.cxx +++ b/sd/source/core/drawdoc.cxx @@ -993,14 +993,7 @@ void SdDrawDocument::UpdateAllLinks() // Register links for fill bitmap items (e.g. slide background images) // with unresolved remote URLs, before checking whether the link list is empty. - registerFillBitmapLinks(GetItemPool(), *m_pLinkManager, - [this]() - { - for (sal_uInt16 i = 0; i < GetPageCount(); ++i) - GetPage(i)->ActionChanged(); - for (sal_uInt16 i = 0; i < GetMasterPageCount(); ++i) - GetMasterPage(i)->ActionChanged(); - }); + registerFillBitmapLinks(*this, *m_pLinkManager); if (m_pLinkManager->GetLinks().empty()) return; diff --git a/svx/source/xoutdev/fillbitmaplink.cxx b/svx/source/xoutdev/fillbitmaplink.cxx index 1b11ab4acf1f..ec4a79187b4c 100644 --- a/svx/source/xoutdev/fillbitmaplink.cxx +++ b/svx/source/xoutdev/fillbitmaplink.cxx @@ -12,7 +12,9 @@ #include #include #include -#include +#include +#include +#include #include #include #include @@ -74,16 +76,33 @@ public: }; } +namespace +{ +OUString getDeferredOriginURL(const XFillBitmapItem& rItem) +{ + const Graphic& rGrf = rItem.GetGraphicObject().GetGraphic(); + if (rGrf.GetType() == GraphicType::Default) + return rGrf.getOriginURL(); + return OUString(); +} +} + +bool hasDeferredFillBitmapLinks(const SfxItemPool& rPool) +{ + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLBITMAP)) + { + if (!getDeferredOriginURL(static_cast(*pItem)).isEmpty()) + return true; + } + return false; +} + void registerFillBitmapLinks(SfxItemPool& rPool, sfx2::LinkManager& rLinkMgr, std::function fnInvalidate) { for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLBITMAP)) { - auto* pBmpItem = static_cast(pItem); - const Graphic& rGrf = pBmpItem->GetGraphicObject().GetGraphic(); - if (rGrf.GetType() != GraphicType::Default) - continue; - OUString aURL = rGrf.getOriginURL(); + OUString aURL = getDeferredOriginURL(static_cast(*pItem)); if (aURL.isEmpty()) continue; tools::SvRef xLink(new FillBitmapLink(rPool, rLinkMgr, fnInvalidate)); @@ -91,4 +110,14 @@ void registerFillBitmapLinks(SfxItemPool& rPool, sfx2::LinkManager& rLinkMgr, } } +void registerFillBitmapLinks(SdrModel& rModel, sfx2::LinkManager& rLinkMgr) +{ + registerFillBitmapLinks(rModel.GetItemPool(), rLinkMgr, [&rModel]() { + for (sal_uInt16 nPage = 0; nPage < rModel.GetPageCount(); ++nPage) + rModel.GetPage(nPage)->GetViewContact().flushViewObjectContacts(); + for (sal_uInt16 nPage = 0; nPage < rModel.GetMasterPageCount(); ++nPage) + rModel.GetMasterPage(nPage)->GetViewContact().flushViewObjectContacts(); + }); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/qa/extras/tiledrendering/data/shape-fill-link.fodt b/sw/qa/extras/tiledrendering/data/shape-fill-link.fodt new file mode 100644 index 000000000000..7782a356a8bd --- /dev/null +++ b/sw/qa/extras/tiledrendering/data/shape-fill-link.fodt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + Shape with remote fill image. + + + + + + diff --git a/sw/qa/extras/tiledrendering/tiledrendering2.cxx b/sw/qa/extras/tiledrendering/tiledrendering2.cxx index 030cef587da2..b1dc8a43a656 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering2.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering2.cxx @@ -930,6 +930,31 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPageBackgroundImageRemoteNotFetch *pDevice, tools::Rectangle(Point(0, 0), pWrtShell->GetLayout()->getFrameArea().SSize())); } +CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testShapeFillRemoteNotFetched) +{ + // draw:fill-image with a remote URL on a shape must not fetch + // the URL during paint when link updates are not allowed. + // The assert in createNewSdrFillGraphicAttribute will fire if + // a remote fetch is attempted. + comphelper::LibreOfficeKit::setActive(false); + + uno::Sequence aParams = { + comphelper::makePropertyValue(u"UpdateDocMode"_ustr, + sal_Int16(css::document::UpdateDocMode::NO_UPDATE)), + }; + loadFromFile(u"shape-fill-link.fodt", aParams); + + SwDocShell* pDocShell = getSwDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + + pWrtShell->CalcLayout(); + + ScopedVclPtrInstance pDevice(DeviceFormat::WITHOUT_ALPHA); + pDevice->SetOutputSizePixel(Size(1024, 1024)); + static_cast(pWrtShell)->Paint( + *pDevice, tools::Rectangle(Point(0, 0), pWrtShell->GetLayout()->getFrameArea().SSize())); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */