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

Reply via email to