diff options
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 |