offapi/com/sun/star/text/TextGraphicObject.idl | 5 ++ sw/inc/hintids.hxx | 2 - sw/inc/unoprnms.hxx | 1 sw/qa/extras/ooxmlexport/data/tdf162527_hidden_image.docx |binary sw/qa/extras/ooxmlexport/data/tdf169802_hidden_shape.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport13.cxx | 28 ++++++++++++++ sw/source/core/bastyp/init.cxx | 2 - sw/source/core/layout/anchoredobject.cxx | 5 ++ sw/source/core/layout/fly.cxx | 8 ++++ sw/source/core/unocore/unoframe.cxx | 19 ++++++++- sw/source/core/unocore/unomap1.cxx | 1 sw/source/filter/html/css1atr.cxx | 2 - sw/source/filter/html/htmlatr.cxx | 2 - sw/source/filter/ww8/docxattributeoutput.cxx | 2 + sw/source/writerfilter/dmapper/GraphicImport.cxx | 3 + 15 files changed, 74 insertions(+), 6 deletions(-)
New commits: commit 86c6cce3396dce0f46c0e61b1b56ee00fe9c0ebf Author: Aron Budea <[email protected]> AuthorDate: Mon Jan 12 15:04:20 2026 +1030 Commit: Miklos Vajna <[email protected]> CommitDate: Tue Jan 20 15:23:01 2026 +0100 tdf#162527 tdf#169802 hidden image/shape in DOCX should be invisible ...and remain invisible after save. After 0b9e4f6085d147c43a86d107303eea9b86e7f34c shapes did get hidden, but in case of wrap-around shapes text still flowed around their supposed placement. Hidden images were simply shown before and after. Add a new Visible property to SwXTextGraphicObject, and let it set SwFlyDrawObj's Visible member as SdrObject. Import and export wp:docPr's hidden attribute in OOXML based on these properties. To avoid showing their area, let SwAnchoredObject::GetObjRectWithSpaces() return an empty rectangle (during opening this is only called from SwTextFly's IsAnyObj(...) and InitAnchoredObjList(), though). Note that Writer lacks support of changing visibility of images and shapes. This change adds minimal support for hiding them and roundtripping the setting in DOCX, but doesn't add ODF or UI support. Conflicts: sw/inc/hintids.hxx sw/source/core/unocore/unoframe.cxx sw/source/filter/ww8/docxattributeoutput.cxx sw/source/writerfilter/dmapper/GraphicImport.cxx Change-Id: I6e9d062628006a7128e380d1af06508625aa3d06 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197056 Tested-by: Jenkins Reviewed-by: Aron Budea <[email protected]> (cherry picked from commit c1f7ea0db134d63c54b581f11e843ebd5bb83b54) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197308 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Mike Kaganski <[email protected]> (cherry picked from commit 6cf66f291b2d31ec63e9d518dea326f8982bc8b6) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197617 Tested-by: Miklos Vajna <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/offapi/com/sun/star/text/TextGraphicObject.idl b/offapi/com/sun/star/text/TextGraphicObject.idl index 423a41778992..7a425f70ea27 100644 --- a/offapi/com/sun/star/text/TextGraphicObject.idl +++ b/offapi/com/sun/star/text/TextGraphicObject.idl @@ -123,6 +123,11 @@ published service TextGraphicObject */ [optional, property] com::sun::star::graphic::XGraphic Graphic; + /** if this is `FALSE`, the graphic is not visible. + + @since LibreOffice 26.2 + */ + [ optional, property ] boolean Visible; }; diff --git a/sw/inc/hintids.hxx b/sw/inc/hintids.hxx index a03cb568c294..ff8febdd995f 100644 --- a/sw/inc/hintids.hxx +++ b/sw/inc/hintids.hxx @@ -397,7 +397,7 @@ inline constexpr TypedWhichId<SwInvertGrf> RES_GRFATR_INVERT(151); inline constexpr TypedWhichId<SwTransparencyGrf> RES_GRFATR_TRANSPARENCY(152); inline constexpr TypedWhichId<SwDrawModeGrf> RES_GRFATR_DRAWMODE(153); -inline constexpr TypedWhichId<SfxBoolItem> RES_GRFATR_DUMMY4(154); +inline constexpr TypedWhichId<SfxBoolItem> RES_GRFATR_VISIBLE(154); inline constexpr TypedWhichId<SfxBoolItem> RES_GRFATR_DUMMY5(155); inline constexpr sal_uInt16 RES_GRFATR_END(156); diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 20b484566cfc..abe97655d99a 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -587,6 +587,7 @@ inline constexpr OUString UNO_NAME_ADJUST_BLUE = u"AdjustBlue"_ustr; inline constexpr OUString UNO_NAME_GAMMA = u"Gamma"_ustr; inline constexpr OUString UNO_NAME_GRAPHIC_IS_INVERTED = u"GraphicIsInverted"_ustr; inline constexpr OUString UNO_NAME_TRANSPARENCY = u"Transparency"_ustr; +inline constexpr OUString UNO_NAME_VISIBLE = u"Visible"_ustr; inline constexpr OUString UNO_NAME_REDLINE_AUTHOR = u"RedlineAuthor"_ustr; inline constexpr OUString UNO_NAME_REDLINE_DATE_TIME = u"RedlineDateTime"_ustr; inline constexpr OUString UNO_NAME_REDLINE_MOVED_ID = u"RedlineMovedID"_ustr; diff --git a/sw/qa/extras/ooxmlexport/data/tdf162527_hidden_image.docx b/sw/qa/extras/ooxmlexport/data/tdf162527_hidden_image.docx new file mode 100644 index 000000000000..5196bd67d772 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf162527_hidden_image.docx differ diff --git a/sw/qa/extras/ooxmlexport/data/tdf169802_hidden_shape.docx b/sw/qa/extras/ooxmlexport/data/tdf169802_hidden_shape.docx new file mode 100644 index 000000000000..b72fab170a69 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf169802_hidden_shape.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx index f6871744f6ab..0d2494b5042c 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx @@ -703,6 +703,34 @@ DECLARE_OOXMLEXPORT_TEST(testTdf156484, "tdf156484.docx") CPPUNIT_ASSERT_MESSAGE("Third shape should not be printable.", !getProperty<bool>(xShape, u"Printable"_ustr)); } +CPPUNIT_TEST_FIXTURE(Test, testTdf162527_hidden_image) +{ + createSwDoc("tdf162527_hidden_image.docx"); + save(TestFilter::DOCX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); + CPPUNIT_ASSERT(pXmlDoc); + // Without the fix this element would have no 'hidden' attribute + assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:r/w:drawing/wp:anchor/wp:docPr", "hidden", + u"1"); + + auto xShape(getShape(1)); + CPPUNIT_ASSERT_MESSAGE("Shape should not be visible.", + !getProperty<bool>(xShape, u"Visible"_ustr)); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf169802_hidden_shape) +{ + createSwDoc("tdf169802_hidden_shape.docx"); + xmlDocUniquePtr pDump = parseLayoutDump(); + // Just to check that the layout has sane content + int nTextNodes = countXPathNodes(pDump, "//*[contains(@type, 'PortionType::Text')]"); + CPPUNIT_ASSERT_MESSAGE("Layout must contain text nodes", 0 < nTextNodes); + // Layout mustn't contain fly portion, without the fix it would contain several + int nFlyNodes = countXPathNodes(pDump, "//*[contains(@type, 'PortionType::Fly')]"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("No fly portion nodes must exist in the layout", 0, nFlyNodes); +} + DECLARE_OOXMLEXPORT_TEST(testTdf124594, "tdf124594.docx") { xmlDocUniquePtr pDump = parseLayoutDump(); diff --git a/sw/source/core/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx index 4a06f438f6ac..5f82002e23f1 100644 --- a/sw/source/core/bastyp/init.cxx +++ b/sw/source/core/bastyp/init.cxx @@ -450,9 +450,9 @@ std::unique_ptr<ItemInfoPackage> createItemInfoPackageSwAttributes() { RES_GRFATR_INVERT, new SwInvertGrf, 0, SFX_ITEMINFOFLAG_NONE }, { RES_GRFATR_TRANSPARENCY, new SwTransparencyGrf, 0, SFX_ITEMINFOFLAG_NONE }, { RES_GRFATR_DRAWMODE, new SwDrawModeGrf, 0, SFX_ITEMINFOFLAG_NONE }, + { RES_GRFATR_VISIBLE, new SfxBoolItem(RES_GRFATR_VISIBLE, true), 0, SFX_ITEMINFOFLAG_NONE }, // GraphicAttr - Dummies - { RES_GRFATR_DUMMY4, new SfxBoolItem( RES_GRFATR_DUMMY4 ), 0, SFX_ITEMINFOFLAG_NONE }, { RES_GRFATR_DUMMY5, new SfxBoolItem( RES_GRFATR_DUMMY5 ), 0, SFX_ITEMINFOFLAG_NONE }, { RES_BOXATR_FORMAT, new SwTableBoxNumFormat, 0, SFX_ITEMINFOFLAG_NONE }, { RES_BOXATR_FORMULA, new SwTableBoxFormula( OUString() ), 0, SFX_ITEMINFOFLAG_NONE }, diff --git a/sw/source/core/layout/anchoredobject.cxx b/sw/source/core/layout/anchoredobject.cxx index cb503fdb61dc..cb5d1e5b7aff 100644 --- a/sw/source/core/layout/anchoredobject.cxx +++ b/sw/source/core/layout/anchoredobject.cxx @@ -564,6 +564,11 @@ bool SwAnchoredObject::HasClearedEnvironment() const */ const SwRect& SwAnchoredObject::GetObjRectWithSpaces() const { + static const SwRect aEmptyRect; + // invisible objects have no area + if (!GetDrawObj()->IsVisible()) + return aEmptyRect; + if ( mbObjRectWithSpacesValid && maLastObjRect != GetObjRect() ) { diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx index 78ec67dc6169..3647774f0bb8 100644 --- a/sw/source/core/layout/fly.cxx +++ b/sw/source/core/layout/fly.cxx @@ -1493,6 +1493,14 @@ void SwFlyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderA { OSL_ENSURE( pAttrs, "FlyFrame::Format, pAttrs is 0." ); + if (GetDrawObj() && !GetDrawObj()->IsVisible()) + { + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this); + aFrm.setSwRect(SwRect()); + setFrameAreaSizeValid(true); + return; + } + ColLock(); if ( !isFrameAreaSizeValid() ) diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx index 0d17f192d40f..7c2d9c8e7103 100644 --- a/sw/source/core/unocore/unoframe.cxx +++ b/sw/source/core/unocore/unoframe.cxx @@ -1408,7 +1408,14 @@ void SwXFrame::setPropertyValue(const OUString& rPropertyName, const ::uno::Any& throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, getXWeak() ); SwDoc* pDoc = pFormat->GetDoc(); - if ( ((m_eType == FLYCNTTYPE_GRF) && isGRFATR(pEntry->nWID)) || + if (m_eType == FLYCNTTYPE_GRF && RES_GRFATR_VISIBLE == pEntry->nWID) + { + bool bVisible = true; + aValue >>= bVisible; + SdrObject* pObject = GetOrCreateSdrObject(static_cast<SwFlyFrameFormat&>(*pFormat)); + pObject->SetVisible(bVisible); + } + else if ( ((m_eType == FLYCNTTYPE_GRF) && isGRFATR(pEntry->nWID)) || (FN_PARAM_CONTOUR_PP == pEntry->nWID) || (FN_UNO_IS_AUTOMATIC_CONTOUR == pEntry->nWID) || (FN_UNO_IS_PIXEL_CONTOUR == pEntry->nWID) ) @@ -1959,7 +1966,12 @@ uno::Any SwXFrame::getPropertyValue(const OUString& rPropertyName) } else if(pFormat) { - if( ((m_eType == FLYCNTTYPE_GRF) || (m_eType == FLYCNTTYPE_OLE)) && + if (m_eType == FLYCNTTYPE_GRF && RES_GRFATR_VISIBLE == pEntry->nWID) + { + SdrObject* pObject = GetOrCreateSdrObject(static_cast<SwFlyFrameFormat&>(*pFormat)); + aAny <<= pObject->IsVisible(); + } + else if( ((m_eType == FLYCNTTYPE_GRF) || (m_eType == FLYCNTTYPE_OLE)) && (isGRFATR(pEntry->nWID) || pEntry->nWID == FN_PARAM_CONTOUR_PP || pEntry->nWID == FN_UNO_IS_AUTOMATIC_CONTOUR || @@ -3046,6 +3058,9 @@ void SwXFrame::attachToRange(uno::Reference<text::XTextRange> const& xTextRange, { setPropertyValue(UNO_NAME_DESCRIPTION, *pDescription); } + if (const uno::Any* pVisible = m_pProps->GetProperty(RES_GRFATR_VISIBLE, 0)) + setPropertyValue(UNO_NAME_VISIBLE, *pVisible); + // For grabbag if (const uno::Any* pFrameIntropgrabbagItem = m_pProps->GetProperty(RES_FRMATR_GRABBAG, 0)) diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx index d53bdac79dfb..6efe52f6fa33 100644 --- a/sw/source/core/unocore/unomap1.cxx +++ b/sw/source/core/unocore/unomap1.cxx @@ -850,6 +850,7 @@ std::span<const SfxItemPropertyMapEntry> SwUnoPropertyMapProvider::GetGraphicPro { UNO_NAME_GAMMA, RES_GRFATR_GAMMA, cppu::UnoType<double>::get(), 0, 0}, { UNO_NAME_GRAPHIC_IS_INVERTED, RES_GRFATR_INVERT, cppu::UnoType<bool>::get(), 0, 0}, { UNO_NAME_TRANSPARENCY, RES_GRFATR_TRANSPARENCY, cppu::UnoType<sal_Int16>::get(), 0, 0}, + { UNO_NAME_VISIBLE, RES_GRFATR_VISIBLE, cppu::UnoType<bool>::get(), 0, 0 }, { UNO_NAME_GRAPHIC_COLOR_MODE, RES_GRFATR_DRAWMODE, cppu::UnoType<css::drawing::ColorMode>::get(), 0, 0}, // added FillProperties for SW, same as FILL_PROPERTIES in svx diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx index 6010dba6464b..78fbbba7379e 100644 --- a/sw/source/filter/html/css1atr.cxx +++ b/sw/source/filter/html/css1atr.cxx @@ -3560,7 +3560,7 @@ SwAttrFnTab const aCSS1AttrFnTab = { /* RES_GRFATR_TRANSPARENCY */ nullptr, /* RES_GRFATR_DRWAMODE */ nullptr, /* RES_GRFATR_DUMMY3 */ nullptr, -/* RES_GRFATR_DUMMY4 */ nullptr, +/* RES_GRFATR_VISIBLE */ nullptr, /* RES_GRFATR_DUMMY5 */ nullptr, /* RES_BOXATR_FORMAT */ nullptr, diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index cc9b68354002..b9651a165337 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -3437,7 +3437,7 @@ SwAttrFnTab aHTMLAttrFnTab = { /* RES_GRFATR_TRANSPARENCY */ nullptr, /* RES_GRFATR_DRWAMODE */ nullptr, /* RES_GRFATR_DUMMY3 */ nullptr, -/* RES_GRFATR_DUMMY4 */ nullptr, +/* RES_GRFATR_VISIBLE */ nullptr, /* RES_GRFATR_DUMMY5 */ nullptr, /* RES_BOXATR_FORMAT */ nullptr, diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index b00b509693b5..fa35d20ce189 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -5527,6 +5527,8 @@ void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size OUString const title(pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle()); auto const docPrattrList(CreateDocPrAttrList( GetExport(), pFrameFormat->GetName(), title, descr)); + if (pSdrObj && !pSdrObj->IsVisible()) + docPrattrList->add(XML_hidden, "1"); m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrattrList ); OUString sURL, sRelId; diff --git a/sw/source/writerfilter/dmapper/GraphicImport.cxx b/sw/source/writerfilter/dmapper/GraphicImport.cxx index 6fb8fee5420e..41b6aea4d1c6 100644 --- a/sw/source/writerfilter/dmapper/GraphicImport.cxx +++ b/sw/source/writerfilter/dmapper/GraphicImport.cxx @@ -1899,6 +1899,9 @@ rtl::Reference<SwXTextGraphicObject> GraphicImport::createGraphicObject(uno::Ref sal_Int32 nWidth = - m_pImpl->m_nLeftPosition; if (m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR) { + if (m_pImpl->m_bHidden) + xGraphicObject->setPropertyValue(u"Visible"_ustr, uno::Any(false)); + if (m_pImpl->m_nHoriRelation == text::RelOrientation::FRAME && (m_pImpl->m_nHoriOrient == text::HoriOrientation::LEFT || m_pImpl->m_nHoriOrient == text::HoriOrientation::RIGHT
