diff options
author | Regina Henschel <rb.henschel@t-online.de> | 2022-08-17 02:31:44 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-09-14 08:32:08 +0200 |
commit | c70ee4a6b9071468255e5d4fdb893e9c9bdf4fad (patch) | |
tree | 1df672814e562a42f0da889acbc9789322a67685 | |
parent | c97c60f70e9e6de594f7e0e0b85f17944c640dcf (diff) |
tdf#149551 use 'WritingMode' instead of TextPreRotateAngle
Commit 7e23cbdbb6ec0247a29ed8a8f744c01e10963ea0 changed the code so,
that TextPreRotateAngle is used to track ooxml vert attribute. This
patch changes it so, that the style attribute WritingMode is used.
Now text direction can be written in 'writing-mode' attribute in the
graphic properties in ODF, same for shapes as for frames.
The needed conversion from WritingMode BT-LR and TB_LR90 to
TextPreRotateAngle for rendering of text in custom shapes is now in
one place in class SdrObjectCustomshape. The shape edit engine
cannot yet render it itself.
Some unit tests are adapted to use WritingMode property instead of
TextPreRotateAngle.
The value text::WritingMode2::TB_RL90 is introduced, corresponding to
vert='vert' and textDirection='tbRl' or ='rl' in OOXML. It is used
for frames too, so that the original text direction is preserved and
vert='eaVert' can be distinguished from vert='vert'.
TextPreRotateAngle is currently still used in SmartArt import for
'upr' and 'grav' and in emulating 'upright' but no longer to
emulate text direction.
Change-Id: Idc4339bbfc3592fe90b154d75e2c404a1fa30856
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138813
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
42 files changed, 436 insertions, 144 deletions
diff --git a/editeng/source/items/frmitems.cxx b/editeng/source/items/frmitems.cxx index 35e1be7b094c..890d74a75510 100644 --- a/editeng/source/items/frmitems.cxx +++ b/editeng/source/items/frmitems.cxx @@ -3411,7 +3411,8 @@ TranslateId getFrmDirResId(size_t nIndex) RID_SVXITEMS_FRMDIR_VERT_TOP_RIGHT, RID_SVXITEMS_FRMDIR_VERT_TOP_LEFT, RID_SVXITEMS_FRMDIR_ENVIRONMENT, - RID_SVXITEMS_FRMDIR_VERT_BOT_LEFT + RID_SVXITEMS_FRMDIR_VERT_BOT_LEFT, + RID_SVXITEMS_FRMDIR_VERT_TOP_RIGHT90 }; return RID_SVXITEMS_FRMDIR[nIndex]; } @@ -3451,6 +3452,9 @@ bool SvxFrameDirectionItem::PutValue( const css::uno::Any& rVal, case text::WritingMode2::BT_LR: SetValue( SvxFrameDirection::Vertical_LR_BT ); break; + case text::WritingMode2::TB_RL90: + SetValue(SvxFrameDirection::Vertical_RL_TB90); + break; case text::WritingMode2::PAGE: SetValue( SvxFrameDirection::Environment ); break; @@ -3487,6 +3491,9 @@ bool SvxFrameDirectionItem::QueryValue( css::uno::Any& rVal, case SvxFrameDirection::Vertical_LR_BT: nVal = text::WritingMode2::BT_LR; break; + case SvxFrameDirection::Vertical_RL_TB90: + nVal = text::WritingMode2::TB_RL90; + break; case SvxFrameDirection::Environment: nVal = text::WritingMode2::PAGE; break; diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc index 31ac07a1d22b..850641d5ce9f 100644 --- a/include/editeng/editrids.hrc +++ b/include/editeng/editrids.hrc @@ -276,6 +276,7 @@ #define RID_SVXITEMS_FRMDIR_VERT_TOP_LEFT NC_("RID_SVXITEMS_FRMDIR_VERT_TOP_LEFT", "Text direction left-to-right (vertical)") #define RID_SVXITEMS_FRMDIR_ENVIRONMENT NC_("RID_SVXITEMS_FRMDIR_ENVIRONMENT", "Use superordinate object text direction setting") #define RID_SVXITEMS_FRMDIR_VERT_BOT_LEFT NC_("RID_SVXITEMS_FRMDIR_VERT_BOT_LEFT", "Text direction left-to-right (vertical from bottom)") +#define RID_SVXITEMS_FRMDIR_VERT_TOP_RIGHT90 NC_("RID_SVXITEMS_FRMDIR_Vert_TOP_RIGHT90", "Text direction right-to-left (vertical all characters rotated)") #define RID_SVXITEMS_PARASNAPTOGRID_ON NC_("RID_SVXITEMS_PARASNAPTOGRID_ON", "Paragraph snaps to text grid (if active)") #define RID_SVXITEMS_PARASNAPTOGRID_OFF NC_("RID_SVXITEMS_PARASNAPTOGRID_OFF", "Paragraph does not snap to text grid") #define RID_SVXITEMS_CHARHIDDEN_FALSE NC_("RID_SVXITEMS_CHARHIDDEN_FALSE", "Not hidden") diff --git a/include/editeng/frmdir.hxx b/include/editeng/frmdir.hxx index ef5275d26a7d..270ab62c626d 100644 --- a/include/editeng/frmdir.hxx +++ b/include/editeng/frmdir.hxx @@ -51,8 +51,11 @@ enum class SvxFrameDirection /** Use the value from the environment, can only be used in frames. */ Environment = css::text::WritingMode2::CONTEXT, - /** Vertical, from bottom to top, from left to right. */ + /** Vertical, from bottom to top, from left to right (vert="vert270"). */ Vertical_LR_BT = css::text::WritingMode2::BT_LR, + + /** Vertical, from top to bottom, from right to left (vert="vert"). */ + Vertical_RL_TB90 = css::text::WritingMode2::TB_RL90, }; TranslateId getFrmDirResId(size_t nIndex); diff --git a/include/editeng/frmdiritem.hxx b/include/editeng/frmdiritem.hxx index 2a439aa50ca7..7bb6dc09950d 100644 --- a/include/editeng/frmdiritem.hxx +++ b/include/editeng/frmdiritem.hxx @@ -52,7 +52,7 @@ public: virtual sal_uInt16 GetValueCount() const override { - return sal_uInt16(SvxFrameDirection::Vertical_LR_BT) + 1; + return sal_uInt16(SvxFrameDirection::Vertical_RL_TB90) + 1; } // SfxPoolItem copy function dichotomy diff --git a/include/svx/svddef.hxx b/include/svx/svddef.hxx index 9332dc65a59f..059c461721b4 100644 --- a/include/svx/svddef.hxx +++ b/include/svx/svddef.hxx @@ -175,6 +175,7 @@ class SdrRotateAllItem; class Svx3DTextureKindItem; class Svx3DTextureModeItem; class SvXMLAttrContainerItem; +class SvxFrameDirectionItem; constexpr sal_uInt16 SDRATTR_START (XATTR_START); /* 1000 */ /* Pool V4*/ /* Pool V3*/ /* Pool V2*/ @@ -432,7 +433,11 @@ constexpr TypedWhichId<SfxInt16Item> SDRATTR_TEXTCOLUMNS_NUMBER(SDRATTR_TEXTCOLU constexpr TypedWhichId<SdrMetricItem> SDRATTR_TEXTCOLUMNS_SPACING(SDRATTR_TEXTCOLUMNS_FIRST + 1); constexpr sal_uInt16 SDRATTR_TEXTCOLUMNS_LAST(SDRATTR_TEXTCOLUMNS_SPACING); -constexpr sal_uInt16 SDRATTR_END (SDRATTR_TEXTCOLUMNS_LAST); /* 1357 */ /* 1333 V4+++*/ /* 1243 V4+++*/ /*1213*/ /*1085*/ /*1040*/ /*Pool V2: 1123,V1: 1065 */ +constexpr sal_uInt16 SDRATTR_WRITINGMODE2_FIRST(SDRATTR_TEXTCOLUMNS_LAST + 1); +constexpr TypedWhichId<SvxFrameDirectionItem> SDRATTR_WRITINGMODE2(SDRATTR_WRITINGMODE2_FIRST + 0); +constexpr sal_uInt16 SDRATTR_WRITINGMODE2_LAST(SDRATTR_WRITINGMODE2); + +constexpr sal_uInt16 SDRATTR_END (SDRATTR_WRITINGMODE2_LAST); /* 1357 */ /* 1333 V4+++*/ /* 1243 V4+++*/ /*1213*/ /*1085*/ /*1040*/ /*Pool V2: 1123,V1: 1065 */ #endif // INCLUDED_SVX_SVDDEF_HXX diff --git a/include/svx/unoshprp.hxx b/include/svx/unoshprp.hxx index b32b8672c77c..010cbbcd4b36 100644 --- a/include/svx/unoshprp.hxx +++ b/include/svx/unoshprp.hxx @@ -365,7 +365,8 @@ /* #i68101# */ \ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , ::cppu::UnoType<OUString>::get(), 0, 0}, \ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , ::cppu::UnoType<OUString>::get(), 0, 0}, \ - { UNO_NAME_HYPERLINK, OWN_ATTR_HYPERLINK, ::cppu::UnoType<OUString>::get(), 0, 0}, + { UNO_NAME_HYPERLINK, OWN_ATTR_HYPERLINK, ::cppu::UnoType<OUString>::get(), 0, 0}, \ + { u"WritingMode", SDRATTR_WRITINGMODE2, ::cppu::UnoType<sal_Int16>::get(), 0, 0}, #define LINKTARGET_PROPERTIES \ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0}, \ diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index da0e0b83c879..93f00d81db2a 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -2284,6 +2284,7 @@ namespace xmloff::token { XML_LR, XML_RL, XML_TB, + XML_TB_RL90, XML_LAYOUT_GRID_COLOR, XML_LAYOUT_GRID_LINES, diff --git a/offapi/com/sun/star/text/WritingMode2.idl b/offapi/com/sun/star/text/WritingMode2.idl index 3d5d1badc6f2..f75108337a69 100644 --- a/offapi/com/sun/star/text/WritingMode2.idl +++ b/offapi/com/sun/star/text/WritingMode2.idl @@ -81,6 +81,16 @@ published constants WritingMode2 @since LibreOffice 6.3 */ const short BT_LR = 5; + + /** text within a line is written top-to-bottom so as if a horizontal + left-to-right line is clockwise rotated by 90deg. Lines and blocks + are placed right-to-left. This corresponds to OOXML attribute + vert="vert" for shapes and ECMA w:val="tbRl" attribute in + <w:textDirection> element. + + @since LibreOffice 7.5 + */ + const short TB_RL90 = 6; }; diff --git a/oox/inc/drawingml/customshapeproperties.hxx b/oox/inc/drawingml/customshapeproperties.hxx index 2a6baad662ad..c009a0fb7db0 100644 --- a/oox/inc/drawingml/customshapeproperties.hxx +++ b/oox/inc/drawingml/customshapeproperties.hxx @@ -115,7 +115,7 @@ public: std::vector< css::drawing::EnhancedCustomShapeSegment >& getSegments(){ return maSegments; }; void setMirroredX( bool bMirroredX ) { mbMirroredX = bMirroredX; }; void setMirroredY( bool bMirroredY ) { mbMirroredY = bMirroredY; }; - void setTextRotateAngle( sal_Int32 nAngle ) { mnTextRotateAngle = nAngle; }; + void setTextPreRotateAngle( sal_Int32 nAngle ) { mnTextPreRotateAngle = nAngle; }; void setTextCameraZRotateAngle( sal_Int32 nAngle ) { mnTextCameraZRotateAngle = nAngle; }; void setTextAreaRotateAngle(sal_Int32 nAngle) { moTextAreaRotateAngle = nAngle; }; @@ -145,7 +145,7 @@ private: maSegments; bool mbMirroredX; bool mbMirroredY; - sal_Int32 mnTextRotateAngle; // TextPreRotateAngle + sal_Int32 mnTextPreRotateAngle; // TextPreRotateAngle sal_Int32 mnTextCameraZRotateAngle; std::optional< sal_Int32 > moTextAreaRotateAngle; // TextRotateAngle diff --git a/oox/inc/drawingml/textbodyproperties.hxx b/oox/inc/drawingml/textbodyproperties.hxx index bc1d9508daea..1daa5d592a30 100644 --- a/oox/inc/drawingml/textbodyproperties.hxx +++ b/oox/inc/drawingml/textbodyproperties.hxx @@ -34,7 +34,7 @@ namespace oox::drawingml { struct TextBodyProperties { PropertyMap maPropertyMap; - // TextPreRotateAngle. Used for simulating writing modes. + // TextPreRotateAngle. Used in diagram (SmartArt) import. std::optional< sal_Int32 > moTextPreRotation; // TextRotateAngle. ODF draw:text-rotate-angle, OOXML 'rot' attribute in <bodyPr> element std::optional< sal_Int32 > moTextAreaRotation; diff --git a/oox/source/drawingml/customshapeproperties.cxx b/oox/source/drawingml/customshapeproperties.cxx index 2c3204405c2b..ef164194e7f0 100644 --- a/oox/source/drawingml/customshapeproperties.cxx +++ b/oox/source/drawingml/customshapeproperties.cxx @@ -48,7 +48,7 @@ CustomShapeProperties::CustomShapeProperties() , mbShapeTypeOverride(false) , mbMirroredX ( false ) , mbMirroredY ( false ) -, mnTextRotateAngle ( 0 ) +, mnTextPreRotateAngle ( 0 ) , mnTextCameraZRotateAngle ( 0 ) , mnArcNum ( 0 ) { @@ -125,7 +125,7 @@ void CustomShapeProperties::pushToPropSet( aPropertyMap.setProperty( PROP_MirroredX, mbMirroredX ); aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY ); - aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextRotateAngle ); + aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextPreRotateAngle ); aPropertyMap.setProperty( PROP_TextCameraZRotateAngle, mnTextCameraZRotateAngle ); if (moTextAreaRotateAngle.has_value()) aPropertyMap.setProperty(PROP_TextRotateAngle, moTextAreaRotateAngle.value()); @@ -189,8 +189,8 @@ void CustomShapeProperties::pushToPropSet( aPropertyMap.setProperty( PROP_Type, OUString( "ooxml-non-primitive" )); aPropertyMap.setProperty( PROP_MirroredX, mbMirroredX ); aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY ); - if( mnTextRotateAngle ) - aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextRotateAngle ); + if( mnTextPreRotateAngle ) + aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextPreRotateAngle ); if (moTextAreaRotateAngle.has_value()) aPropertyMap.setProperty(PROP_TextRotateAngle, moTextAreaRotateAngle.value()); // Note 1: If Equations are defined - they are processed using internal div by 360 coordinates diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx index be9e8df456b2..cfe1ea29dc95 100644 --- a/oox/source/drawingml/shape.cxx +++ b/oox/source/drawingml/shape.cxx @@ -1447,7 +1447,8 @@ Reference< XShape > const & Shape::createAndInsert( } else if (mbTextBox) { - // ToDo: TextBox has no rotated text, so introduce it only if really needed. tdf#82627 + // This introduces a TextBox in a shape in Writer. ToDo: Can we restrict it to cases + // where the TextBox edit engine is really needed? tdf#82627 aShapeProps.setProperty(PROP_TextBox, true); } @@ -1714,10 +1715,10 @@ Reference< XShape > const & Shape::createAndInsert( sal_Int32 nTextCameraZRotation = getTextBody()->get3DProperties().maCameraRotation.mnRevolution.value_or(0); mpCustomShapePropertiesPtr->setTextCameraZRotateAngle( nTextCameraZRotation / 60000 ); - // TextPreRotateAngle. Text rotates inside the text area. + // TextPreRotateAngle. Text rotates inside the text area. Might be used for diagram layout 'upr' and 'grav'. sal_Int32 nTextPreRotateAngle = static_cast< sal_Int32 >( getTextBody()->getTextProperties().moTextPreRotation.value_or( 0 ) ); - nTextPreRotateAngle -= mnDiagramRotation; + nTextPreRotateAngle -= mnDiagramRotation; // Use of mnDiagramRotation is unclear. It seems to be always 0 here. // TextRotateAngle. The text area rotates. sal_Int32 nTextAreaRotateAngle = getTextBody()->getTextProperties().moTextAreaRotation.value_or(0); @@ -1740,7 +1741,7 @@ Reference< XShape > const & Shape::createAndInsert( } /* OOX measures text rotation clockwise in 1/60000th degrees, relative to the containing shape. set*Angle wants degrees counter-clockwise. */ - mpCustomShapePropertiesPtr->setTextRotateAngle(-nTextPreRotateAngle / 60000); + mpCustomShapePropertiesPtr->setTextPreRotateAngle(-nTextPreRotateAngle / 60000); if (nTextAreaRotateAngle != 0) mpCustomShapePropertiesPtr->setTextAreaRotateAngle(-nTextAreaRotateAngle / 60000); diff --git a/oox/source/drawingml/textbodyproperties.cxx b/oox/source/drawingml/textbodyproperties.cxx index 5cea05256462..ff501e40c413 100644 --- a/oox/source/drawingml/textbodyproperties.cxx +++ b/oox/source/drawingml/textbodyproperties.cxx @@ -87,8 +87,10 @@ void TextBodyProperties::pushTextDistances(Size const& rTextAreaSize) default: break; } - if (moVert && moVert.value() == XML_eaVert) + if (moVert && (moVert.value() == XML_eaVert || moVert.value() == XML_vert)) nOff = (nOff + 3) % aProps.size(); + else if (moVert && moVert.value() == XML_vert270) + nOff = (nOff + 1) % aProps.size(); for (size_t i = 0; i < aProps.size(); i++) { diff --git a/oox/source/drawingml/textbodypropertiescontext.cxx b/oox/source/drawingml/textbodypropertiescontext.cxx index 8b1c6db6a791..9d221a18ffbe 100644 --- a/oox/source/drawingml/textbodypropertiescontext.cxx +++ b/oox/source/drawingml/textbodypropertiescontext.cxx @@ -20,9 +20,11 @@ #include <drawingml/textbodypropertiescontext.hxx> #include <com/sun/star/text/WritingMode.hpp> +#include <com/sun/star/text/WritingMode2.hpp> #include <com/sun/star/drawing/TextFitToSizeType.hpp> #include <com/sun/star/drawing/TextHorizontalAdjust.hpp> #include <com/sun/star/text/XTextColumns.hpp> + #include <drawingml/textbodyproperties.hxx> #include <drawingml/textbody.hxx> #include <drawingml/customshapegeometry.hxx> @@ -119,15 +121,29 @@ TextBodyPropertiesContext::TextBodyPropertiesContext( ContextHandler2Helper cons mrTextBodyProp.moVert = rAttribs.getToken( XML_vert ); sal_Int32 tVert = mrTextBodyProp.moVert.value_or( XML_horz ); if (tVert == XML_eaVert) + { mrTextBodyProp.maPropertyMap.setProperty(PROP_TextWritingMode, WritingMode_TB_RL); - else if (tVert == XML_vert || tVert == XML_mongolianVert) - mrTextBodyProp.moTextPreRotation = 5400000; + mrTextBodyProp.maPropertyMap.setProperty(PROP_WritingMode, text::WritingMode2::TB_RL); + } + else if (tVert == XML_vert) + { + mrTextBodyProp.maPropertyMap.setProperty(PROP_WritingMode, text::WritingMode2::TB_RL90); + } + else if (tVert == XML_mongolianVert) + { + // rendering not yet implemented for shape text, only for frames + mrTextBodyProp.maPropertyMap.setProperty(PROP_WritingMode, text::WritingMode2::TB_LR); + } else if (tVert == XML_vert270) - mrTextBodyProp.moTextPreRotation = 5400000 * 3; + { + mrTextBodyProp.maPropertyMap.setProperty(PROP_WritingMode, text::WritingMode2::BT_LR); + } else { bool bRtl = rAttribs.getBool( XML_rtl, false ); mrTextBodyProp.maPropertyMap.setProperty( PROP_TextWritingMode, ( bRtl ? WritingMode_RL_TB : WritingMode_LR_TB )); + mrTextBodyProp.maPropertyMap.setProperty(PROP_WritingMode, + ( bRtl ? text::WritingMode2::RL_TB : text::WritingMode2::LR_TB)); } } diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 51ac5f660237..42aa3eb04700 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -3335,6 +3335,33 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo bVertical = true; } } + if (GetProperty(rXPropSet, "WritingMode")) + { + sal_Int16 nWritingMode; + if (mAny >>= nWritingMode) + { + if (nWritingMode == text::WritingMode2::TB_RL) + { + sWritingMode = "eaVert"; + bVertical = true; + } + else if (nWritingMode == text::WritingMode2::BT_LR) + { + sWritingMode = "vert270"; + bVertical = true; + } + else if (nWritingMode == text::WritingMode2::TB_RL90) + { + sWritingMode = "vert"; + bVertical = true; + } + else if (nWritingMode == text::WritingMode2::TB_LR) + { + sWritingMode = "mongolianVert"; + bVertical = true; + } + } + } // read values from CustomShapeGeometry Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq; @@ -3395,13 +3422,21 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo switch (nWritingMode) { case WritingMode2::TB_RL: - sWritingMode = "vert"; + sWritingMode = "eaVert"; bVertical = true; break; case WritingMode2::BT_LR: sWritingMode = "vert270"; bVertical = true; break; + case WritingMode2::TB_RL90: + sWritingMode = "vert"; + bVertical = true; + break; + case WritingMode2::TB_LR: + sWritingMode = "mongolianVert"; + bVertical = true; + break; default: break; } @@ -3489,30 +3524,19 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo } } - // Evaluate "TextPreRotateAngle". It is used to simulate not yet implemented writing modes. + // ToDo: Unsure about this. Need to investigate shapes from diagram import, especially digrams + // with vertical text directions. if (nTextPreRotateAngle != 0 && !sWritingMode) { if (nTextPreRotateAngle == -90 || nTextPreRotateAngle == 270) { sWritingMode = "vert"; bVertical = true; - // Our TextPreRotation includes padding, MSO vert does not include padding. Therefore set - // padding so, that is looks the same in MSO as in LO. - sal_Int32 nHelp = nLeft; - nLeft = nBottom; - nBottom = nRight; - nRight = nTop; - nTop = nHelp; } else if (nTextPreRotateAngle == -270 || nTextPreRotateAngle == 90) { sWritingMode = "vert270"; bVertical = true; - sal_Int32 nHelp = nLeft; - nLeft = nTop; - nTop = nRight; - nRight = nBottom; - nBottom = nHelp; } else if (nTextPreRotateAngle == -180 || nTextPreRotateAngle == 180) { @@ -3525,19 +3549,11 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 #pragma GCC diagnostic pop #endif - // ToDo: Examine insets. They might need rotation too. + // ToDo: Examine insets. They might need rotation too. Check diagrams (SmartArt). } else SAL_WARN("oox", "unsuitable value for TextPreRotateAngle:" << nTextPreRotateAngle); } - else if (nTextPreRotateAngle == 0 && sWritingMode && sWritingMode.value() == "eaVert") - { - sal_Int32 nHelp = nLeft; - nLeft = nBottom; - nBottom = nRight; - nRight = nTop; - nTop = nHelp; - } else if (nTextPreRotateAngle != 0 && sWritingMode && sWritingMode.value() == "eaVert") { // ToDo: eaVert plus 270deg clockwise rotation has to be written with vert="horz" @@ -3545,6 +3561,33 @@ void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bo } // else nothing to do + // Our WritingMode introduces text pre rotation which includes padding, MSO vert does not include + // padding. Therefore set padding so, that is looks the same in MSO as in LO. + if (sWritingMode) + { + if (sWritingMode.value() == "vert" || sWritingMode.value() == "eaVert") + { + sal_Int32 nHelp = nLeft; + nLeft = nBottom; + nBottom = nRight; + nRight = nTop; + nTop = nHelp; + } + else if (sWritingMode.value() == "vert270") + { + sal_Int32 nHelp = nLeft; + nLeft = nTop; + nTop = nRight; + nRight = nBottom; + nBottom = nHelp; + } + else if (sWritingMode.value() == "mongolianVert") + { + // ToDo: Examine padding + } + } + + std::optional<OString> sTextRotateAngleMSUnit; if (nTextRotateAngleDeg100.has_value()) #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12 diff --git a/oox/source/export/vmlexport.cxx b/oox/source/export/vmlexport.cxx index 5d6f244bb42f..bda301201ad2 100644 --- a/oox/source/export/vmlexport.cxx +++ b/oox/source/export/vmlexport.cxx @@ -1440,14 +1440,10 @@ void VMLExport::EndShape( sal_Int32 nShapeElement ) if (xPropertySetInfo->hasPropertyByName("CustomShapeGeometry")) { // In this case a DrawingML DOCX was imported. - comphelper::SequenceAsHashMap aCustomShapeProperties( - xPropertySet->getPropertyValue("CustomShapeGeometry")); - if (aCustomShapeProperties.find("TextPreRotateAngle") != aCustomShapeProperties.end()) - { - sal_Int32 nTextRotateAngle = aCustomShapeProperties["TextPreRotateAngle"].get<sal_Int32>(); - if (nTextRotateAngle == -270) - bBottomToTop = true; - } + auto aAny = xPropertySet->getPropertyValue("WritingMode"); + sal_Int16 nWritingMode; + if ((aAny >>= nWritingMode) && nWritingMode == text::WritingMode2::BT_LR) + bBottomToTop = true; } else { diff --git a/oox/source/shape/WpsContext.cxx b/oox/source/shape/WpsContext.cxx index 79fcf1c9dc81..5b0e75617215 100644 --- a/oox/source/shape/WpsContext.cxx +++ b/oox/source/shape/WpsContext.cxx @@ -21,6 +21,7 @@ #include <com/sun/star/text/XText.hpp> #include <com/sun/star/text/XTextCursor.hpp> #include <com/sun/star/text/WritingMode.hpp> +#include <com/sun/star/text/WritingMode2.hpp> #include <svx/svdtrans.hxx> #include <oox/helper/attributelist.hxx> #include <oox/token/namespaces.hxx> @@ -69,20 +70,27 @@ oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY); uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY); sal_Int32 nVert = rAttribs.getToken(XML_vert, XML_horz); - if (nVert == XML_eaVert) + // Values 'wordArtVert' and 'wordArtVertRtl' are not implemented. + // Map them to other vert values. + if (nVert == XML_eaVert || nVert == XML_wordArtVertRtl) { xPropertySet->setPropertyValue("TextWritingMode", uno::Any(text::WritingMode_TB_RL)); + xPropertySet->setPropertyValue("WritingMode", + uno::Any(text::WritingMode2::TB_RL)); } - else if (nVert != XML_horz) + else if (nVert == XML_mongolianVert || nVert == XML_wordArtVert) { - // The UI of Word has only 'vert' and 'vert270'. Further values would be - // 'mongolianVert', 'wordArtVert' and 'wordArtVertRtl'. - const sal_Int32 nRotation = nVert == XML_vert270 ? -270 : -90; + xPropertySet->setPropertyValue("WritingMode", + uno::Any(text::WritingMode2::TB_LR)); + } + else if (nVert != XML_horz) // cases XML_vert and XML_vert270 + { + // Hack to get same rendering as after the fix for tdf#87924. If shape rotation + // plus text direction results in upright text, use horizontal text direction. + // Remove hack when frame is able to rotate. - // Workaround for tdf#87924, produces bug tdf#149809 as of 2022-07 - // If the text is not rotated the way the shape wants it already, set the angle. - // Get the existing rotation of the shape. + // Need transformation matrix since RotateAngle does not contain flip. drawing::HomogenMatrix3 aMatrix; xPropertySet->getPropertyValue("Transformation") >>= aMatrix; basegfx::B2DHomMatrix aTransformation; @@ -100,17 +108,20 @@ oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken double fRotate = 0; double fShearX = 0; aTransformation.decompose(aScale, aTranslate, fRotate, fShearX); - - if (static_cast<sal_Int32>(basegfx::rad2deg(fRotate)) - != NormAngle36000(Degree100(nRotation * 100)).get() / 100) + auto nRotate(static_cast<sal_uInt16>(NormAngle360(basegfx::rad2deg(fRotate)))); + if ((nVert == XML_vert && nRotate == 270) + || (nVert == XML_vert270 && nRotate == 90)) { - comphelper::SequenceAsHashMap aCustomShapeGeometry( - xPropertySet->getPropertyValue("CustomShapeGeometry")); - aCustomShapeGeometry["TextPreRotateAngle"] <<= nRotation; - xPropertySet->setPropertyValue( - "CustomShapeGeometry", - uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList())); + xPropertySet->setPropertyValue("WritingMode", + uno::Any(text::WritingMode2::LR_TB)); + // ToDo: Rembember original vert value and remove hack on export. } + else if (nVert == XML_vert) + xPropertySet->setPropertyValue("WritingMode", + uno::Any(text::WritingMode2::TB_RL90)); + else // nVert == XML_vert270 + xPropertySet->setPropertyValue("WritingMode", + uno::Any(text::WritingMode2::BT_LR)); } if (bool bUpright = rAttribs.getBool(XML_upright, false)) diff --git a/sc/qa/unit/subsequent_filters_test2.cxx b/sc/qa/unit/subsequent_filters_test2.cxx index 1dd93e55ad95..37f177d48297 100644 --- a/sc/qa/unit/subsequent_filters_test2.cxx +++ b/sc/qa/unit/subsequent_filters_test2.cxx @@ -54,6 +54,7 @@ #include <com/sun/star/drawing/XControlShape.hpp> #include <com/sun/star/sheet/XSpreadsheetDocument.hpp> #include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/text/WritingMode2.hpp> #include <comphelper/scopeguard.hxx> #include <tools/UnitConversion.hxx> @@ -2582,7 +2583,8 @@ void ScFiltersTest2::testTextBoxBodyUpright() } CPPUNIT_ASSERT_EQUAL(true, isUpright); - // Check the new textRotateAngle. + // Check the TextPreRotateAngle has the compensation for the additional 90deg area rotation, + // which is added in Shape::createAndInsert to get the same rendering as in MS Office. sal_Int32 nAngle; uno::Any aGeom = xShapeProperties->getPropertyValue("CustomShapeGeometry"); auto aGeomSeq = aGeom.get<Sequence<beans::PropertyValue>>(); @@ -2607,19 +2609,11 @@ void ScFiltersTest2::testTextBoxBodyRotateAngle() uno::Reference<drawing::XShape> xShape(xPage->getByIndex(0), uno::UNO_QUERY_THROW); uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY_THROW); - // Check the new textRotateAngle. - sal_Int32 nAngle; - uno::Any aGeom = xShapeProperties->getPropertyValue("CustomShapeGeometry"); - auto aGeomSeq = aGeom.get<Sequence<beans::PropertyValue>>(); - for (const auto& aProp : std::as_const(aGeomSeq)) - { - if (aProp.Name == "TextPreRotateAngle") - { - aProp.Value >>= nAngle; - break; - } - } - CPPUNIT_ASSERT_EQUAL(sal_Int32(-270), nAngle); + // Check the text direction. + sal_Int16 eWritingMode = text::WritingMode2::LR_TB; + if (xShapeProperties->getPropertySetInfo()->hasPropertyByName("WritingMode")) + xShapeProperties->getPropertyValue("WritingMode") >>= eWritingMode; + CPPUNIT_ASSERT_EQUAL(sal_Int16(text::WritingMode2::BT_LR), eWritingMode); } void ScFiltersTest2::testTextLengthDataValidityXLSX() diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 5109a13687d6..cd0c8fb04244 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2719,6 +2719,7 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. <rng:attribute name="loext:writing-mode"> <rng:choice> <rng:value>bt-lr</rng:value> + <rng:value>tb-rl90</rng:value> </rng:choice> </rng:attribute> </rng:optional> diff --git a/sd/qa/unit/data/xml/n902652_0.xml b/sd/qa/unit/data/xml/n902652_0.xml index b5fd7740ef47..34a18af57fba 100644 --- a/sd/qa/unit/data/xml/n902652_0.xml +++ b/sd/qa/unit/data/xml/n902652_0.xml @@ -197,7 +197,7 @@ </Path> </PropertyValue> <PropertyValue name="TextCameraZRotateAngle" value="0" handle="0" propertyState="DIRECT_VALUE"/> - <PropertyValue name="TextPreRotateAngle" value="-90" handle="0" propertyState="DIRECT_VALUE"/> + <PropertyValue name="TextPreRotateAngle" value="0" handle="0" propertyState="DIRECT_VALUE"/> <PropertyValue name="Type" value="ooxml-roundRect" handle="0" propertyState="DIRECT_VALUE"/> <PropertyValue name="ViewBox"> <ViewBox x="0" y="0" width="0" height="0"/> @@ -299,7 +299,7 @@ </Path> </PropertyValue> <PropertyValue name="TextCameraZRotateAngle" value="0" handle="0" propertyState="DIRECT_VALUE"/> - <PropertyValue name="TextPreRotateAngle" value="-270" handle="0" propertyState="DIRECT_VALUE"/> + <PropertyValue name="TextPreRotateAngle" value="0" handle="0" propertyState="DIRECT_VALUE"/> <PropertyValue name="Type" value="ooxml-roundRect" handle="0" propertyState="DIRECT_VALUE"/> <PropertyValue name="ViewBox"> <ViewBox x="0" y="0" width="0" height="0"/> diff --git a/sd/qa/unit/export-tests.cxx b/sd/qa/unit/export-tests.cxx index 2e1a3ad5ec30..97167d219ff3 100644 --- a/sd/qa/unit/export-tests.cxx +++ b/sd/qa/unit/export-tests.cxx @@ -1096,24 +1096,63 @@ void SdExportTest::testPageWithTransparentBackground() void SdExportTest::testTextRotation() { - ::sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/pptx/shape-text-rotate.pptx"), PPTX); - utl::TempFile tempFile; - xDocShRef = saveAndReload(xDocShRef.get(), ODP, &tempFile); + // Save behavior depends on whether ODF strict or extended is used. + Resetter _([]() { + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Save::ODF::DefaultVersion::set(3, pBatch); + return pBatch->commit(); + }); + + // The contained shape has a text rotation vert="vert" which corresponds to + // loext:writing-mode="tb-rl90" in the graphic-properties of the style of the shape in ODF 1.3 + // extended. + // Save to ODF 1.3 extended. Adapt 3 (=ODFVER_LATEST) to a to be ODFVER_013_EXTENDED when + // attribute value "tb-rl90" is included in ODF strict. + { + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Save::ODF::DefaultVersion::set(3, pBatch); + pBatch->commit(); - uno::Reference<drawing::XDrawPage> xPage(getPage(0, xDocShRef)); - uno::Reference<beans::XPropertySet> xPropSet(getShape(0, xPage)); + ::sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/pptx/shape-text-rotate.pptx"), PPTX); + utl::TempFile tempFile; + xDocShRef = saveAndReload(xDocShRef.get(), ODP, &tempFile); + + uno::Reference<drawing::XDrawPage> xPage(getPage(0, xDocShRef)); + uno::Reference<beans::XPropertySet> xPropSet(getShape(0, xPage)); + CPPUNIT_ASSERT(xPropSet.is()); + + auto aWritingMode = xPropSet->getPropertyValue("WritingMode").get<sal_Int16>(); + CPPUNIT_ASSERT_EQUAL(sal_Int16(text::WritingMode2::TB_RL90), aWritingMode); + + xDocShRef->DoClose(); + } + // In ODF 1.3 strict the workaround to use the TextRotateAngle is used instead. + { + std::shared_ptr<comphelper::ConfigurationChanges> pBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Save::ODF::DefaultVersion::set(10, pBatch); + pBatch->commit(); + + ::sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/pptx/shape-text-rotate.pptx"), PPTX); + utl::TempFile tempFile; + xDocShRef = saveAndReload(xDocShRef.get(), ODP, &tempFile); - CPPUNIT_ASSERT(xPropSet.is()); + uno::Reference<drawing::XDrawPage> xPage(getPage(0, xDocShRef)); + uno::Reference<beans::XPropertySet> xPropSet(getShape(0, xPage)); - auto aGeomPropSeq = xPropSet->getPropertyValue("CustomShapeGeometry").get<uno::Sequence<beans::PropertyValue>>(); - comphelper::SequenceAsHashMap aCustomShapeGeometry(aGeomPropSeq); + CPPUNIT_ASSERT(xPropSet.is()); + auto aGeomPropSeq = xPropSet->getPropertyValue("CustomShapeGeometry").get<uno::Sequence<beans::PropertyValue>>(); + comphelper::SequenceAsHashMap aCustomShapeGeometry(aGeomPropSeq); - auto it = aCustomShapeGeometry.find("TextRotateAngle"); - CPPUNIT_ASSERT(it != aCustomShapeGeometry.end()); + auto it = aCustomShapeGeometry.find("TextRotateAngle"); + CPPUNIT_ASSERT(it != aCustomShapeGeometry.end()); - CPPUNIT_ASSERT_EQUAL(double(-90), aCustomShapeGeometry["TextRotateAngle"].get<double>()); + CPPUNIT_ASSERT_EQUAL(double(-90), aCustomShapeGeometry["TextRotateAngle"].get<double>()); - xDocShRef->DoClose(); + xDocShRef->DoClose(); + } } void SdExportTest::testTdf115394PPT() diff --git a/sd/qa/unit/import-tests2.cxx b/sd/qa/unit/import-tests2.cxx index 23e92530b479..17c793d08ab8 100644 --- a/sd/qa/unit/import-tests2.cxx +++ b/sd/qa/unit/import-tests2.cxx @@ -50,6 +50,7 @@ #include <com/sun/star/style/LineSpacingMode.hpp> #include <com/sun/star/frame/Desktop.hpp> #include <com/sun/star/text/GraphicCrop.hpp> +#include <com/sun/star/text/WritingMode2.hpp> #include <com/sun/star/text/XTextColumns.hpp> #include <com/sun/star/xml/dom/XDocument.hpp> @@ -1843,21 +1844,18 @@ void SdImportTest2::testTdf128684() CPPUNIT_ASSERT(xDoc.is()); uno::Reference<drawing::XDrawPage> xPage(xDoc->getDrawPages()->getByIndex(0), uno::UNO_QUERY); CPPUNIT_ASSERT(xPage.is()); - uno::Reference<beans::XPropertySet> xShape(getShape(0, xPage)); - CPPUNIT_ASSERT(xShape.is()); - uno::Any aAny = xShape->getPropertyValue("CustomShapeGeometry"); - CPPUNIT_ASSERT(aAny.hasValue()); - uno::Sequence<beans::PropertyValue> aProps; - CPPUNIT_ASSERT(aAny >>= aProps); + uno::Reference<beans::XPropertySet> xShapeProperties(getShape(0, xPage)); + CPPUNIT_ASSERT(xShapeProperties.is()); + // Check text direction. + sal_Int16 eWritingMode(text::WritingMode2::LR_TB); + if (xShapeProperties->getPropertySetInfo()->hasPropertyByName("WritingMode")) + xShapeProperties->getPropertyValue("WritingMode") >>= eWritingMode; + CPPUNIT_ASSERT_EQUAL(sal_Int16(text::WritingMode2::TB_RL90), eWritingMode); + // Check shape rotation sal_Int32 nRotateAngle = 0; - for (const auto& rProp : std::as_const(aProps)) - { - if (rProp.Name == "TextPreRotateAngle") - { - rProp.Value >>= nRotateAngle; - } - } - CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-90), nRotateAngle); + if (xShapeProperties->getPropertySetInfo()->hasPropertyByName("RotateAngle")) + xShapeProperties->getPropertyValue("RotateAngle") >>= nRotateAngle; + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(9000), nRotateAngle); } void SdImportTest2::testTdf113198() diff --git a/svx/source/sdr/properties/customshapeproperties.cxx b/svx/source/sdr/properties/customshapeproperties.cxx index 59135cde1d55..cf79f77830c3 100644 --- a/svx/source/sdr/properties/customshapeproperties.cxx +++ b/svx/source/sdr/properties/customshapeproperties.cxx @@ -73,6 +73,7 @@ namespace sdr::properties SDRATTR_GRAF_FIRST, SDRATTR_CUSTOMSHAPE_LAST, SDRATTR_GLOW_FIRST, SDRATTR_SOFTEDGE_LAST, SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + SDRATTR_WRITINGMODE2, SDRATTR_WRITINGMODE2, // Range from SdrTextObj: EE_ITEMS_START, EE_ITEMS_END>); } diff --git a/svx/source/svdraw/svdattr.cxx b/svx/source/svdraw/svdattr.cxx index c0057b6aee70..86794dc92bfe 100644 --- a/svx/source/svdraw/svdattr.cxx +++ b/svx/source/svdraw/svdattr.cxx @@ -104,6 +104,7 @@ #include <sxsiitm.hxx> #include <sxsoitm.hxx> #include <sxtraitm.hxx> +#include <editeng/frmdiritem.hxx> #include <libxml/xmlwriter.h> using namespace ::com::sun::star; @@ -335,6 +336,8 @@ SdrItemPool::SdrItemPool( rPoolDefaults[SDRATTR_TEXTCOLUMNS_NUMBER - SDRATTR_START] = new SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, 1); rPoolDefaults[SDRATTR_TEXTCOLUMNS_SPACING - SDRATTR_START] = new SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, 0); + rPoolDefaults[SDRATTR_WRITINGMODE2 - SDRATTR_START] = new SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, SDRATTR_WRITINGMODE2); + // set own ItemInfos mpLocalItemInfos[SDRATTR_SHADOW-SDRATTR_START]._nSID=SID_ATTR_FILL_SHADOW; mpLocalItemInfos[SDRATTR_SHADOWCOLOR-SDRATTR_START]._nSID=SID_ATTR_SHADOW_COLOR; @@ -359,6 +362,8 @@ SdrItemPool::SdrItemPool( mpLocalItemInfos[SDRATTR_TEXTCOLUMNS_NUMBER - SDRATTR_START]._nSID = 0 /*TODO*/; mpLocalItemInfos[SDRATTR_TEXTCOLUMNS_SPACING - SDRATTR_START]._nSID = 0 /*TODO*/; + mpLocalItemInfos[SDRATTR_WRITINGMODE2 - SDRATTR_START]._nSID = 0 /*TODO*/; + // it's my own creation level, set Defaults and ItemInfos SetDefaults(mpLocalPoolDefaults); SetItemInfos(mpLocalItemInfos.get()); diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx index 5993781186a7..30d959b865e9 100644 --- a/svx/source/svdraw/svdoashp.cxx +++ b/svx/source/svdraw/svdoashp.cxx @@ -86,6 +86,7 @@ #include <sal/log.hxx> #include <o3tl/string_view.hxx> #include "presetooxhandleadjustmentrelations.hxx" +#include <editeng/frmdiritem.hxx> using namespace ::com::sun::star; @@ -503,12 +504,32 @@ void SdrObjCustomShape::SetMirroredY( const bool bMirrorY ) double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const { - const uno::Any* pAny; - const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ); - pAny = rGeometryItem.GetPropertyValueByName( bPreRotation ? OUString( "TextPreRotateAngle" ) : OUString( "TextRotateAngle" ) ); double fExtraTextRotateAngle = 0.0; - if ( pAny ) - *pAny >>= fExtraTextRotateAngle; + if (bPreRotation) + { + // textPreRotateAngle might be set by macro or diagram (SmartArt) import + const uno::Any* pAny; + const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ); + pAny = rGeometryItem.GetPropertyValueByName(u"TextPreRotateAngle"); + if ( pAny ) + *pAny >>= fExtraTextRotateAngle; + + // As long as the edit engine is not able to rendere these text directions we + // emulate them by setting a suitable text pre-rotation. + const SvxFrameDirectionItem& rDirectionItem = GetMergedItem(SDRATTR_WRITINGMODE2); + if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_RL_TB90) + fExtraTextRotateAngle -= 90; + else if (rDirectionItem.GetValue() == SvxFrameDirection::Vertical_LR_BT) + fExtraTextRotateAngle -=270; + } + else + { + const uno::Any* pAny; + const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ); + pAny = rGeometryItem.GetPropertyValueByName(u"TextRotateAngle"); + if ( pAny ) + *pAny >>= fExtraTextRotateAngle; + } return fExtraTextRotateAngle; } diff --git a/svx/source/unodraw/unoshape.cxx b/svx/source/unodraw/unoshape.cxx index 9ab5ef495f69..a37e6ef4c65c 100644 --- a/svx/source/unodraw/unoshape.cxx +++ b/svx/source/unodraw/unoshape.cxx @@ -90,6 +90,7 @@ #include <svx/svdopath.hxx> #include <svx/SvxXTextColumns.hxx> #include <svx/xflclit.hxx> +#include <editeng/frmdiritem.hxx> #include <memory> #include <optional> @@ -2464,6 +2465,15 @@ bool SvxShape::setPropertyValueImpl( const OUString&, const SfxItemPropertyMapEn break; } + case SDRATTR_WRITINGMODE2: + { + SvxFrameDirectionItem aItem(SvxFrameDirection::Environment, SDRATTR_WRITINGMODE2); + aItem.PutValue(rValue, 0); + GetSdrObject()->SetMergedItem(aItem); + return true; + break; + } + default: { return false; diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx index 751ebdcf8167..0b7953bcb54e 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx @@ -312,11 +312,11 @@ DECLARE_OOXMLEXPORT_TEST(testendingSectionProps, "endingSectionProps.docx") DECLARE_OOXMLEXPORT_TEST(testTbrlTextbox, "tbrl-textbox.docx") { uno::Reference<beans::XPropertySet> xPropertySet(getShape(1), uno::UNO_QUERY); - comphelper::SequenceAsHashMap aGeometry(xPropertySet->getPropertyValue("CustomShapeGeometry")); // Without the accompanying fix in place, this test would have failed with 'Expected: -90; // Actual: 0', i.e. tbRl writing direction was imported as lrTb. - CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-90), - aGeometry["TextPreRotateAngle"].get<sal_Int32>()); + // Note: Implementation was changed to use WritingMode property instead of TextPreRotateAngle. + CPPUNIT_ASSERT_EQUAL(text::WritingMode2::TB_RL90, + getProperty<sal_Int16>(xPropertySet, "WritingMode")); } DECLARE_OOXMLEXPORT_TEST(testBtlrShape, "btlr-textbox.docx") @@ -949,11 +949,11 @@ CPPUNIT_TEST_FIXTURE(Test, testBtlrFrame) CPPUNIT_ASSERT_EQUAL(1, getShapes()); CPPUNIT_ASSERT_EQUAL(1, getPages()); uno::Reference<beans::XPropertySet> xPropertySet(getShape(1), uno::UNO_QUERY); - comphelper::SequenceAsHashMap aGeometry(xPropertySet->getPropertyValue("CustomShapeGeometry")); // Without the accompanying fix in place, this test would have failed with 'Expected: // -270; Actual: 0', i.e. the writing direction of the frame was lost. - CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-270), - aGeometry["TextPreRotateAngle"].get<sal_Int32>()); + // Note: Implementation was changed to use WritingMode property instead of TextPreRotateAngle. + CPPUNIT_ASSERT_EQUAL(text::WritingMode2::BT_LR, + getProperty<sal_Int16>(xPropertySet, "WritingMode")); } CPPUNIT_TEST_FIXTURE(Test, testTdf125518) diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx index 62c754af3d0f..50c98590e765 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport8.cxx @@ -1207,9 +1207,9 @@ DECLARE_OOXMLEXPORT_TEST(testVmlTextVerticalAdjust, "vml-text-vertical-adjust.do DECLARE_OOXMLEXPORT_TEST(testFdo69636, "fdo69636.docx") { // The problem was that the mso-layout-flow-alt:bottom-to-top VML shape property wasn't handled for sw text frames. + // Note: VML is no longer used on import. OOXML import uses WritingMode2::BT_LR now. uno::Reference<beans::XPropertySet> xPropertySet(getShape(1), uno::UNO_QUERY); - comphelper::SequenceAsHashMap aCustomShapeGeometry(xPropertySet->getPropertyValue("CustomShapeGeometry")); - CPPUNIT_ASSERT_EQUAL(sal_Int32(-270), aCustomShapeGeometry["TextPreRotateAngle"].get<sal_Int32>()); + CPPUNIT_ASSERT_EQUAL(sal_Int16(text::WritingMode2::BT_LR), getProperty<sal_Int16>(xPropertySet, "WritingMode")); } DECLARE_OOXMLEXPORT_TEST(testChartProp, "chart-prop.docx") diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx index 909d818c54cd..5658d1075862 100644 --- a/sw/source/core/doc/textboxhelper.cxx +++ b/sw/source/core/doc/textboxhelper.cxx @@ -605,6 +605,9 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rP if (!pFormat) return; + // Older documents or documents in ODF strict do not have WritingMode, but have used the + // TextRotateAngle values -90 and -270 to emulate these text directions of frames. + // ToDo: Is TextPreRotateAngle needed for diagrams or can it be removed? comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue); auto it = aCustomShapeGeometry.find("TextPreRotateAngle"); if (it == aCustomShapeGeometry.end()) @@ -624,7 +627,7 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rP switch (nAngle) { case -90: - nDirection = text::WritingMode2::TB_RL; + nDirection = text::WritingMode2::TB_RL90; break; case -270: nDirection = text::WritingMode2::BT_LR; @@ -663,6 +666,12 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rP else if (rValue >>= eMode2) syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj); } + else if (rPropertyName == u"WritingMode") + { + sal_Int16 eMode2; + if (rValue >>= eMode2) + syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj); + } else SAL_INFO("sw.core", "SwTextBoxHelper::syncProperty: unhandled property: " << static_cast<OUString>(rPropertyName)); diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index d6982d40bdee..2d59a1c7ea73 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -375,6 +375,12 @@ void SwFrame::CheckDir( SvxFrameDirection nDir, bool bVert, bool bOnlyBiDi, bool mbVertLR = true; mbVertLRBT = true; } + else if (nDir == SvxFrameDirection::Vertical_RL_TB90) + { + // not yet implemented, render as RL_TB + mbVertLR = false; + mbVertLRBT = false; + } } } else diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index de5bd79bcc4b..49ac7c64a9cf 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -10182,10 +10182,18 @@ void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDi sTextFlow = OString( "lrTb" ); bBiDi = true; break; - case SvxFrameDirection::Vertical_LR_TB: // many things but not this one - case SvxFrameDirection::Vertical_RL_TB: + case SvxFrameDirection::Vertical_LR_TB: // ~ vert="mongolianVert" + sTextFlow = OString("tbLrV"); + break; + case SvxFrameDirection::Vertical_RL_TB: // ~ vert="eaVert" sTextFlow = OString( "tbRl" ); break; + case SvxFrameDirection::Vertical_LR_BT: // ~ vert="vert270" + sTextFlow = OString("btLr"); + break; + case SvxFrameDirection::Vertical_RL_TB90: // ~ vert="vert" + sTextFlow = OString("tbRlV"); + break; } if ( m_rExport.m_bOutPageDescs ) diff --git a/sw/source/filter/ww8/docxsdrexport.cxx b/sw/source/filter/ww8/docxsdrexport.cxx index 3a0ff11b3962..c73418df7254 100644 --- a/sw/source/filter/ww8/docxsdrexport.cxx +++ b/sw/source/filter/ww8/docxsdrexport.cxx @@ -1965,7 +1965,10 @@ void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAncho m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert"); else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT) m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270"); - + else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB) + m_pImpl->getBodyPrAttrList()->add(XML_vert, "mongolianVert"); + else if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB90) + m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert"); { ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true); comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX); diff --git a/sw/source/filter/xml/xmlexpit.cxx b/sw/source/filter/xml/xmlexpit.cxx index c2d0dd76c659..3be82886d11a 100644 --- a/sw/source/filter/xml/xmlexpit.cxx +++ b/sw/source/filter/xml/xmlexpit.cxx @@ -194,7 +194,7 @@ void SvXMLExportItemMapper::exportXML(const SvXMLExport&, { case RES_FRAMEDIR: { - // Write bt-lr to the extension namespace, handle other values + // Write bt-lr and tb-rl90 to the extension namespace, handle other values // below. auto pDirection = static_cast<const SvxFrameDirectionItem*>(&rItem); if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT @@ -207,6 +207,17 @@ void SvXMLExportItemMapper::exportXML(const SvXMLExport&, if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT || pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT) bDone = true; + + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + && pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90) + { + const OUString sName(rNamespaceMap.GetQNameByKey( + XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE))); + rAttrList.AddAttribute(sName, GetXMLToken(XML_TB_RL90)); + } + if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT + || pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90) + bDone = true; break; } } diff --git a/sw/source/filter/xml/xmlimpit.cxx b/sw/source/filter/xml/xmlimpit.cxx index 067ebc41fb90..b092fe85f8af 100644 --- a/sw/source/filter/xml/xmlimpit.cxx +++ b/sw/source/filter/xml/xmlimpit.cxx @@ -997,6 +997,13 @@ bool SvXMLImportItemMapper::PutXMLValue( aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_LR_BT); bOk = rItem.PutValue(aAny, 0); } + else if (IsXMLToken(rValue, XML_TB_RL90)) + { + // Read tb-rl90 from the extension namespace. + Any aAny; + aAny <<= static_cast<sal_uInt16>(SvxFrameDirection::Vertical_RL_TB90); + bOk = rItem.PutValue(aAny, 0); + } else { std::unique_ptr<XMLPropertyHandler> pWritingModeHandler = diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index befaa122c705..9fa547675c8a 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -1618,28 +1618,35 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) { switch (nIntValue) { - case NS_ooxml::LN_Value_ST_TextDirection_tbRl: + case NS_ooxml::LN_Value_ST_TextDirection_tbRl: // ~ vert="eaVert" { m_pImpl->SetFrameDirection(text::WritingMode2::TB_RL); break; } - case NS_ooxml::LN_Value_ST_TextDirection_btLr: + case NS_ooxml::LN_Value_ST_TextDirection_btLr: // ~ vert="vert270" { m_pImpl->SetFrameDirection(text::WritingMode2::BT_LR); break; } case NS_ooxml::LN_Value_ST_TextDirection_lrTbV: { + // East Asian character rotation is not implemented in LO, use ordinary LR_TB instead. m_pImpl->SetFrameDirection(text::WritingMode2::LR_TB); break; } - case NS_ooxml::LN_Value_ST_TextDirection_tbRlV: + case NS_ooxml::LN_Value_ST_TextDirection_tbRlV: // ~ vert="vert" { - m_pImpl->SetFrameDirection(text::WritingMode2::TB_RL); + m_pImpl->SetFrameDirection(text::WritingMode2::TB_RL90); break; } case NS_ooxml::LN_Value_ST_TextDirection_lrTb: - case NS_ooxml::LN_Value_ST_TextDirection_tbLrV: + // default in LO. Do not overwrite RL_TB set by bidi. + break; + case NS_ooxml::LN_Value_ST_TextDirection_tbLrV: // ~ vert="mongolianVert" + { + m_pImpl->SetFrameDirection(text::WritingMode2::TB_LR); + break; + } default: SAL_WARN("writerfilter", "DomainMapper::sprmWithProps: unhandled textDirection"); } @@ -2038,19 +2045,27 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) break; case NS_ooxml::LN_EG_SectPrContents_textDirection: { - /* 0 HoriLR 1 Vert TR 2 Vert TR 3 Vert TT 4 HoriLT - only 0 and 1 can be imported correctly - */ - text::WritingMode nDirection = text::WritingMode_LR_TB; + sal_Int16 nDirection = text::WritingMode2::LR_TB; switch( nIntValue ) { + // East Asian 270deg rotation in lrTbV is not implemented in LO case NS_ooxml::LN_Value_ST_TextDirection_lrTb: case NS_ooxml::LN_Value_ST_TextDirection_lrTbV: - nDirection = text::WritingMode_LR_TB; + nDirection = text::WritingMode2::LR_TB; // =0 break; case NS_ooxml::LN_Value_ST_TextDirection_tbRl: + nDirection = text::WritingMode2::TB_RL; // =2 + break; + // Word does not write btLr in sections, but LO would be able to use it. case NS_ooxml::LN_Value_ST_TextDirection_btLr: - nDirection = text::WritingMode_TB_RL; + nDirection = text::WritingMode2::BT_LR; // =5 + break; + // Word maps mongolian direction to tbRlV in sections in file save, as of Aug 2022. + // From point of OOXML standard it would be tbLrV. Since tbRlV is currently not + // implemented in LO for text direction in page styles, we follow Word here. + case NS_ooxml::LN_Value_ST_TextDirection_tbRlV: + case NS_ooxml::LN_Value_ST_TextDirection_tbLrV: + nDirection = text::WritingMode2::TB_LR; // =3 break; default:; } diff --git a/xmloff/inc/xmlsdtypes.hxx b/xmloff/inc/xmlsdtypes.hxx index 898c475bd7ef..c6e0ad315e22 100644 --- a/xmloff/inc/xmlsdtypes.hxx +++ b/xmloff/inc/xmlsdtypes.hxx @@ -118,6 +118,7 @@ ////////////////////////////////////////////////////////////////////////////// #define XML_SD_TYPE_CELL_ROTATION_ANGLE (XML_SD_TYPES_START + 79 ) +#define XML_SD_TYPE_WRITINGMODE2 (XML_SD_TYPES_START + 80 ) #define CTF_NUMBERINGRULES 1000 #define CTF_CONTROLWRITINGMODE 1001 @@ -200,5 +201,6 @@ #define CTF_SD_OLE_VIS_AREA_EXPORT_HEIGHT 1063 ////////////////////////////////////////////////////////////////////////////// +#define CTF_WRITINGMODE2 1064 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 30158b20660e..ca5c7ec21b08 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -2299,6 +2299,7 @@ namespace xmloff::token { TOKEN( "lr", XML_LR ), TOKEN( "rl", XML_RL ), TOKEN( "tb", XML_TB ), + TOKEN( "tb-rl90", XML_TB_RL90 ), TOKEN( "layout-grid-color", XML_LAYOUT_GRID_COLOR ), TOKEN( "layout-grid-lines", XML_LAYOUT_GRID_LINES ), diff --git a/xmloff/source/draw/sdpropls.cxx b/xmloff/source/draw/sdpropls.cxx index a72a57261b58..3490625e1e8b 100644 --- a/xmloff/source/draw/sdpropls.cxx +++ b/xmloff/source/draw/sdpropls.cxx @@ -317,6 +317,9 @@ const XMLPropertyMapEntry aXMLSDProperties[] = // misc object properties GMAP( "MoveProtect", XML_NAMESPACE_STYLE, XML_PROTECT, XML_SD_TYPE_MOVE_PROTECT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, CTF_SD_MOVE_PROTECT ), GMAP( "SizeProtect", XML_NAMESPACE_STYLE, XML_PROTECT, XML_SD_TYPE_SIZE_PROTECT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, CTF_SD_SIZE_PROTECT ), + GMAP( "WritingMode", XML_NAMESPACE_STYLE, XML_WRITING_MODE, XML_SD_TYPE_WRITINGMODE2, CTF_WRITINGMODE2 ), + {"WritingMode", XML_NAMESPACE_LO_EXT, XML_WRITING_MODE, XML_SD_TYPE_WRITINGMODE2|XML_TYPE_PROP_GRAPHIC, 0, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, true}, + MAP_END() }; @@ -589,6 +592,18 @@ SvXMLEnumMapEntry<text::WritingMode> const aXML_WritingMode_EnumMap[] = { XML_TOKEN_INVALID, text::WritingMode(0) } }; +SvXMLEnumMapEntry<sal_Int16> const aXML_WritingMode2_EnumMap[] = +{ + { XML_LR_TB, text::WritingMode2::LR_TB }, + { XML_RL_TB, text::WritingMode2::RL_TB }, + { XML_TB_RL, text::WritingMode2::TB_RL }, + { XML_TB_LR, text::WritingMode2::TB_LR }, + { XML_PAGE, text::WritingMode2::CONTEXT }, + { XML_BT_LR, text::WritingMode2::BT_LR }, + { XML_TB_RL90, text::WritingMode2::TB_RL90 }, + { XML_TOKEN_INVALID, text::WritingMode2::LR_TB } +}; + SvXMLEnumMapEntry<drawing::TextAnimationKind> const pXML_TextAnimation_Enum[] = { { XML_NONE, drawing::TextAnimationKind_NONE }, @@ -1061,6 +1076,11 @@ const XMLPropertyHandler* XMLSdPropHdlFactory::GetPropertyHandler( sal_Int32 nTy pHdl = new XMLEnumPropertyHdl( aXML_WritingMode_EnumMap ); break; } + case XML_SD_TYPE_WRITINGMODE2 : + { + pHdl = new XMLConstantsPropertyHandler ( aXML_WritingMode2_EnumMap, XML_LR_TB ); + break; + } case XML_SD_TYPE_PRESPAGE_VISIBILITY : { pHdl = new XMLNamedBoolPropertyHdl( GetXMLToken(XML_VISIBLE), GetXMLToken(XML_HIDDEN) ); @@ -1360,6 +1380,7 @@ void XMLShapeExportPropertyMapper::ContextFilter( XMLPropertyState* pClip11State = nullptr; XMLPropertyState* pClipState = nullptr; + XMLPropertyState* pGraphicWritingMode2 = nullptr; XMLPropertyState* pShapeWritingMode = nullptr; XMLPropertyState* pTextWritingMode = nullptr; XMLPropertyState* pControlWritingMode = nullptr; @@ -1391,6 +1412,9 @@ void XMLShapeExportPropertyMapper::ContextFilter( property->mnIndex = -1; } break; + case CTF_WRITINGMODE2: + pGraphicWritingMode2 = property; + break; case CTF_WRITINGMODE: pShapeWritingMode = property; break; @@ -1484,6 +1508,19 @@ void XMLShapeExportPropertyMapper::ContextFilter( } } + if (pGraphicWritingMode2) + { + // A style:writing-mode attribute G in graphic-properties is only evaluated if there is no + // style:writing-mode attribute P in the paragraph-properties of the same graphic style. + // Otherwise the value of P is used. For values lr-tb, rl-tb and tb-rl the values G and P + // should be the same. But other values in G cannot be expressed in P and would produce default + // 0 value in P, preventing evaluation of G. + sal_Int16 eGraphicWritingMode; + if ((pGraphicWritingMode2->maValue >>= eGraphicWritingMode) + && eGraphicWritingMode >= text::WritingMode2::TB_LR && pShapeWritingMode) + pShapeWritingMode->mnIndex = -1; + } + // check for duplicate writing mode if( pShapeWritingMode && (pTextWritingMode || pControlWritingMode) ) { diff --git a/xmloff/source/draw/shapeexport.cxx b/xmloff/source/draw/shapeexport.cxx index c44349184770..a9f0291a53b8 100644 --- a/xmloff/source/draw/shapeexport.cxx +++ b/xmloff/source/draw/shapeexport.cxx @@ -80,6 +80,7 @@ #include <com/sun/star/presentation/ClickAction.hpp> #include <com/sun/star/style/XStyle.hpp> #include <com/sun/star/table/XColumnRowRange.hpp> +#include <com/sun/star/text/WritingMode2.hpp> #include <com/sun/star/text/XText.hpp> #include <comphelper/classids.hxx> @@ -4313,7 +4314,8 @@ static void ImpExportEnhancedGeometry( SvXMLExport& rExport, const uno::Referenc OUString aStr; OUStringBuffer aStrBuffer; - double fTextRotateAngle(0.0); // sum TextRotateAngle and TextPreRotateAngle + double fTextRotateAngle(0.0); + double fTextPreRotateAngle(0.0); // will be consolidated with fTextRotateAngle at the end SvXMLUnitConverter& rUnitConverter = rExport.GetMM100UnitConverter(); uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); @@ -4366,11 +4368,13 @@ static void ImpExportEnhancedGeometry( SvXMLExport& rExport, const uno::Referenc } break; case EAS_TextPreRotateAngle : + { + rGeoProp.Value >>= fTextPreRotateAngle; + } + break; case EAS_TextRotateAngle : { - double fAngle = 0.0; - rGeoProp.Value >>= fAngle; - fTextRotateAngle += fAngle; + rGeoProp.Value >>= fTextRotateAngle; } break; case EAS_Extrusion : @@ -4947,12 +4951,30 @@ static void ImpExportEnhancedGeometry( SvXMLExport& rExport, const uno::Referenc break; } } // for + + // ToDo: Where is TextPreRotateAngle still used? We cannot save it in ODF. + fTextRotateAngle += fTextPreRotateAngle; + // Workaround for writing-mode bt-lr and tb-rl90 in ODF strict, + // otherwise loext:writing-mode is used in style export. + if (!(rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)) + { + if (xPropSetInfo->hasPropertyByName(u"WritingMode")) + { + sal_Int16 nDirection; + xPropSet->getPropertyValue(u"WritingMode") >>= nDirection; + if (nDirection == text::WritingMode2::TB_RL90) + fTextRotateAngle -= 90; + else if (nDirection == text::WritingMode2::BT_LR) + fTextRotateAngle -= 270; + } + } if (fTextRotateAngle != 0) { ::sax::Converter::convertDouble( aStrBuffer, fTextRotateAngle ); aStr = aStrBuffer.makeStringAndClear(); rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TEXT_ROTATE_ANGLE, aStr ); } + rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_TYPE, aCustomShapeType ); // adjustments diff --git a/xmloff/source/style/prhdlfac.cxx b/xmloff/source/style/prhdlfac.cxx index ca96e0421552..3b8cbe371000 100644 --- a/xmloff/source/style/prhdlfac.cxx +++ b/xmloff/source/style/prhdlfac.cxx @@ -99,6 +99,9 @@ SvXMLEnumMapEntry<sal_uInt16> const aXML_WritingDirection_Enum[] = { XML_RL, text::WritingMode2::RL_TB }, { XML_TB, text::WritingMode2::TB_RL }, + // vertical as clockwise 90deg rotation, for OOXML vert="vert" + { XML_TB_RL90, text::WritingMode2::TB_RL90 }, + { XML_TOKEN_INVALID, 0 } }; diff --git a/xmloff/source/style/xmlexppr.cxx b/xmloff/source/style/xmlexppr.cxx index 412b578c42a9..d2088cfea601 100644 --- a/xmloff/source/style/xmlexppr.cxx +++ b/xmloff/source/style/xmlexppr.cxx @@ -938,7 +938,8 @@ namespace sal_Int8 CheckExtendedNamespace(std::u16string_view sXMLAttributeName, std::u16string_view sValue, const SvtSaveOptions::ODFSaneDefaultVersion nODFVersion) { - if (IsXMLToken(sXMLAttributeName, XML_WRITING_MODE) && IsXMLToken(sValue, XML_BT_LR)) + if (IsXMLToken(sXMLAttributeName, XML_WRITING_MODE) + && (IsXMLToken(sValue, XML_BT_LR) || IsXMLToken(sValue, XML_TB_RL90))) return nODFVersion & SvtSaveOptions::ODFSVER_EXTENDED ? 1 : -1; else if (IsXMLToken(sXMLAttributeName, XML_VERTICAL_REL) && (IsXMLToken(sValue, XML_PAGE_CONTENT_BOTTOM) diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 918ab44981c4..506b9a4cb0e4 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -2183,6 +2183,7 @@ bt-lr lr rl tb +tb-rl90 layout-grid-color layout-grid-lines layout-grid-base-height |