include/oox/vml/vmlshape.hxx | 2 oox/source/vml/vmlshape.cxx | 4 - oox/source/vml/vmlshapecontext.cxx | 120 +++++++++++++++++++++++-------- sc/source/filter/oox/drawingfragment.cxx | 51 +++++++++++++ 4 files changed, 148 insertions(+), 29 deletions(-)
New commits: commit 52a2a51cdea4eb4b24d51c745351049a9536925c Author: Johann LORBER <[email protected]> AuthorDate: Fri Aug 22 11:30:04 2025 +0200 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Tue Dec 9 13:35:13 2025 +0100 tdf#166724 - Checkbox anchor wrongly imported Form Controls are not being "fully" imported as the MoveWithCell and SizeWithCell properties are not set internally in Calc. Contrary to their own documentation, Microsoft Excel seems to declare (in the VML file) those properties with no value when they are false, and does not declare them when they are true. Test xlsx documents generated with Excel 2007, 2010, 2013 and 365 have led me to this conclusion. Change-Id: I7ba64983ae8036df8d672d6c94b2ca54d740a064 Signed-off-by: Johann LORBER <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190166 Tested-by: Jenkins Reviewed-by: Justin Luth <[email protected]> Code-Style: Justin Luth <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194707 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> (cherry picked from commit 8f47e219f34c3e81de6b155e3be1bfa405f9b37a) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195274 Tested-by: Miklos Vajna <[email protected]> Reviewed-by: Tomaž Vajngerl <[email protected]> diff --git a/include/oox/vml/vmlshape.hxx b/include/oox/vml/vmlshape.hxx index 64259c554caf..2d5ba7bcd124 100644 --- a/include/oox/vml/vmlshape.hxx +++ b/include/oox/vml/vmlshape.hxx @@ -194,6 +194,8 @@ struct ClientData bool mbMultiLine; ///< True = textbox allows line breaks. bool mbVScroll; ///< True = textbox has a vertical scrollbar. bool mbSecretEdit; ///< True = textbox is a password edit field. + bool mbMoveWithCells; ///< True = shape moves with the cell (anchored to cell). + bool mbSizeWithCells; ///< True = shape resizes with the cell. explicit ClientData(); }; diff --git a/oox/source/vml/vmlshape.cxx b/oox/source/vml/vmlshape.cxx index 1f5a8f27c59e..3ddcda38825f 100644 --- a/oox/source/vml/vmlshape.cxx +++ b/oox/source/vml/vmlshape.cxx @@ -259,7 +259,9 @@ ClientData::ClientData() : mbNo3D2( false ), mbMultiLine( false ), mbVScroll( false ), - mbSecretEdit( false ) + mbSecretEdit( false ), + mbMoveWithCells( true ), + mbSizeWithCells( true ) { } diff --git a/oox/source/vml/vmlshapecontext.cxx b/oox/source/vml/vmlshapecontext.cxx index daedc527f465..8e769409094c 100644 --- a/oox/source/vml/vmlshapecontext.cxx +++ b/oox/source/vml/vmlshapecontext.cxx @@ -196,34 +196,98 @@ void ClientDataContext::onEndElement() { switch( getCurrentElement() ) { - case VMLX_TOKEN( Anchor ): mrClientData.maAnchor = maElementText; break; - case VMLX_TOKEN( FmlaMacro ): mrClientData.maFmlaMacro = maElementText; break; - case VMLX_TOKEN( FmlaPict ): mrClientData.maFmlaPict = maElementText; break; - case VMLX_TOKEN( FmlaLink ): mrClientData.maFmlaLink = maElementText; break; - case VMLX_TOKEN( FmlaRange ): mrClientData.maFmlaRange = maElementText; break; - case VMLX_TOKEN( FmlaGroup ): mrClientData.maFmlaGroup = maElementText; break; - case VMLX_TOKEN( TextHAlign ): mrClientData.mnTextHAlign = AttributeConversion::decodeToken( maElementText ); break; - case VMLX_TOKEN( TextVAlign ): mrClientData.mnTextVAlign = AttributeConversion::decodeToken( maElementText ); break; - case VMLX_TOKEN( Column ): mrClientData.mnCol = maElementText.toInt32(); break; - case VMLX_TOKEN( Row ): mrClientData.mnRow = maElementText.toInt32(); break; - case VMLX_TOKEN( Checked ): mrClientData.mnChecked = maElementText.toInt32(); break; - case VMLX_TOKEN( DropStyle ): mrClientData.mnDropStyle = AttributeConversion::decodeToken( maElementText ); break; - case VMLX_TOKEN( DropLines ): mrClientData.mnDropLines = maElementText.toInt32(); break; - case VMLX_TOKEN( Val ): mrClientData.mnVal = maElementText.toInt32(); break; - case VMLX_TOKEN( Min ): mrClientData.mnMin = maElementText.toInt32(); break; - case VMLX_TOKEN( Max ): mrClientData.mnMax = maElementText.toInt32(); break; - case VMLX_TOKEN( Inc ): mrClientData.mnInc = maElementText.toInt32(); break; - case VMLX_TOKEN( Page ): mrClientData.mnPage = maElementText.toInt32(); break; - case VMLX_TOKEN( SelType ): mrClientData.mnSelType = AttributeConversion::decodeToken( maElementText ); break; - case VMLX_TOKEN( VTEdit ): mrClientData.mnVTEdit = maElementText.toInt32(); break; - case VMLX_TOKEN( PrintObject ): mrClientData.mbPrintObject = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( Visible ): mrClientData.mbVisible = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( DDE ): mrClientData.mbDde = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( NoThreeD ): mrClientData.mbNo3D = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( NoThreeD2 ): mrClientData.mbNo3D2 = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( MultiLine ): mrClientData.mbMultiLine = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( VScroll ): mrClientData.mbVScroll = lclDecodeVmlxBool( maElementText, true ); break; - case VMLX_TOKEN( SecretEdit ): mrClientData.mbSecretEdit = lclDecodeVmlxBool( maElementText, true ); break; + case VMLX_TOKEN(Anchor): + mrClientData.maAnchor = maElementText; + break; + case VMLX_TOKEN(FmlaMacro): + mrClientData.maFmlaMacro = maElementText; + break; + case VMLX_TOKEN(FmlaPict): + mrClientData.maFmlaPict = maElementText; + break; + case VMLX_TOKEN(FmlaLink): + mrClientData.maFmlaLink = maElementText; + break; + case VMLX_TOKEN(FmlaRange): + mrClientData.maFmlaRange = maElementText; + break; + case VMLX_TOKEN(FmlaGroup): + mrClientData.maFmlaGroup = maElementText; + break; + case VMLX_TOKEN(TextHAlign): + mrClientData.mnTextHAlign = AttributeConversion::decodeToken(maElementText); + break; + case VMLX_TOKEN(TextVAlign): + mrClientData.mnTextVAlign = AttributeConversion::decodeToken(maElementText); + break; + case VMLX_TOKEN(Column): + mrClientData.mnCol = maElementText.toInt32(); + break; + case VMLX_TOKEN(Row): + mrClientData.mnRow = maElementText.toInt32(); + break; + case VMLX_TOKEN(Checked): + mrClientData.mnChecked = maElementText.toInt32(); + break; + case VMLX_TOKEN(DropStyle): + mrClientData.mnDropStyle = AttributeConversion::decodeToken(maElementText); + break; + case VMLX_TOKEN(DropLines): + mrClientData.mnDropLines = maElementText.toInt32(); + break; + case VMLX_TOKEN(Val): + mrClientData.mnVal = maElementText.toInt32(); + break; + case VMLX_TOKEN(Min): + mrClientData.mnMin = maElementText.toInt32(); + break; + case VMLX_TOKEN(Max): + mrClientData.mnMax = maElementText.toInt32(); + break; + case VMLX_TOKEN(Inc): + mrClientData.mnInc = maElementText.toInt32(); + break; + case VMLX_TOKEN(Page): + mrClientData.mnPage = maElementText.toInt32(); + break; + case VMLX_TOKEN(SelType): + mrClientData.mnSelType = AttributeConversion::decodeToken(maElementText); + break; + case VMLX_TOKEN(VTEdit): + mrClientData.mnVTEdit = maElementText.toInt32(); + break; + case VMLX_TOKEN(PrintObject): + mrClientData.mbPrintObject = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(Visible): + mrClientData.mbVisible = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(DDE): + mrClientData.mbDde = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(NoThreeD): + mrClientData.mbNo3D = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(NoThreeD2): + mrClientData.mbNo3D2 = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(MultiLine): + mrClientData.mbMultiLine = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(VScroll): + mrClientData.mbVScroll = lclDecodeVmlxBool(maElementText, true); + break; + case VMLX_TOKEN(SecretEdit): + mrClientData.mbSecretEdit = lclDecodeVmlxBool(maElementText, true); + break; + // Oddly, Excel will declare MoveWithCells and SizeWithCells with no value if these are false (therefore, bDefaultForEmpty == false) + // When MoveWithCells and SizeWithCells are NOT declared they should be assumed true. + case VMLX_TOKEN(MoveWithCells): + mrClientData.mbMoveWithCells = lclDecodeVmlxBool(maElementText, false); + break; + case VMLX_TOKEN(SizeWithCells): + mrClientData.mbSizeWithCells = lclDecodeVmlxBool(maElementText, false); + break; } } diff --git a/sc/source/filter/oox/drawingfragment.cxx b/sc/source/filter/oox/drawingfragment.cxx index 6b58204ddc1d..eb9c20451560 100644 --- a/sc/source/filter/oox/drawingfragment.cxx +++ b/sc/source/filter/oox/drawingfragment.cxx @@ -23,6 +23,7 @@ #include <comphelper/propertyvalue.hxx> #include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> #include <com/sun/star/container/XIndexContainer.hpp> #include <com/sun/star/container/XNameReplace.hpp> #include <com/sun/star/document/XEventsSupplier.hpp> @@ -31,6 +32,8 @@ #include <com/sun/star/drawing/XShapes.hpp> #include <com/sun/star/script/ScriptEventDescriptor.hpp> #include <com/sun/star/script/XEventAttacherManager.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> #include <rtl/strbuf.hxx> #include <svx/svdobj.hxx> #include <drwlayer.hxx> @@ -752,6 +755,54 @@ void VmlDrawing::notifyXShapeInserted( const Reference< XShape >& rxShape, catch( Exception& ) { } + + // Anchor to cell holding top left corner of the shape if true + if (pClientData->mbMoveWithCells) + { + Reference< css::sheet::XSpreadsheet > xSheet; + Reference< css::sheet::XSpreadsheetDocument > xSSDoc( this->getFilter().getModel(), UNO_QUERY ); + if ( xSSDoc.is() ) + { + Reference< XIndexAccess > xSheets( xSSDoc->getSheets(), UNO_QUERY ); + if ( xSheets.is() ) + { + Any aSheetAny = xSheets->getByIndex( getSheetIndex() ); + aSheetAny >>= xSheet; + } + } + if ( xSheet.is() ) + { + sal_Int32 nCol = -1; + sal_Int32 nRow = -1; + + if ( pClientData->mnCol >= 0 && pClientData->mnRow >= 0 ) + { + nCol = pClientData->mnCol; + nRow = pClientData->mnRow; + } + else if ( !pClientData->maAnchor.isEmpty() ) + { + // maAnchor is "LeftColumn, LeftOffset, TopRow, TopOffset, RightColumn, RightOffset, BottomRow, BottomOffset". Extract tokens 0 and 2. + sal_Int32 nIndex = 0; + nCol = o3tl::toInt32(o3tl::getToken(pClientData->maAnchor, 0, ',', nIndex)); + // skip LeftOffset + o3tl::getToken(pClientData->maAnchor, 0, ',', nIndex); + nRow = o3tl::toInt32(o3tl::getToken(pClientData->maAnchor, 0, ',', nIndex)); + } + + if ( nCol >= 0 && nRow >= 0 ) + { + Reference< css::table::XCell > xCell = xSheet->getCellByPosition( nCol, nRow ); + if ( xCell.is() ) + { + Reference< XPropertySet > aPropertySet( rxShape, UNO_QUERY_THROW ); + aPropertySet->setPropertyValue( "Anchor", Any( xCell ) ); + if (pClientData->mbSizeWithCells) + aPropertySet->setPropertyValue( "ResizeWithCell", Any( true ) ); + } + } + } + } } // private --------------------------------------------------------------------
