editeng/source/items/frmitems.cxx                           |    9 +
 include/editeng/editrids.hrc                                |    1 
 include/editeng/frmdir.hxx                                  |    5 
 include/editeng/frmdiritem.hxx                              |    2 
 include/svx/svddef.hxx                                      |    7 
 include/svx/unoshprp.hxx                                    |    3 
 include/xmloff/xmltoken.hxx                                 |    1 
 offapi/com/sun/star/text/WritingMode2.idl                   |   10 +
 oox/inc/drawingml/customshapeproperties.hxx                 |    4 
 oox/inc/drawingml/textbodyproperties.hxx                    |    2 
 oox/source/drawingml/customshapeproperties.cxx              |    8 -
 oox/source/drawingml/shape.cxx                              |    9 -
 oox/source/drawingml/textbodyproperties.cxx                 |    4 
 oox/source/drawingml/textbodypropertiescontext.cxx          |   22 ++
 oox/source/export/drawingml.cxx                             |   89 ++++++++----
 oox/source/export/vmlexport.cxx                             |   12 -
 oox/source/shape/WpsContext.cxx                             |   45 +++---
 sc/qa/unit/subsequent_filters_test2.cxx                     |   22 +-
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng |    1 
 sd/qa/unit/data/xml/n902652_0.xml                           |    4 
 sd/qa/unit/export-tests.cxx                                 |   63 ++++++--
 sd/qa/unit/import-tests2.cxx                                |   26 +--
 svx/source/sdr/properties/customshapeproperties.cxx         |    1 
 svx/source/svdraw/svdattr.cxx                               |    5 
 svx/source/svdraw/svdoashp.cxx                              |   31 +++-
 svx/source/unodraw/unoshape.cxx                             |   10 +
 sw/qa/extras/ooxmlexport/ooxmlexport13.cxx                  |   12 -
 sw/qa/extras/ooxmlexport/ooxmlexport8.cxx                   |    4 
 sw/source/core/doc/textboxhelper.cxx                        |   11 +
 sw/source/core/layout/wsfrm.cxx                             |    6 
 sw/source/filter/ww8/docxattributeoutput.cxx                |   12 +
 sw/source/filter/ww8/docxsdrexport.cxx                      |    5 
 sw/source/filter/xml/xmlexpit.cxx                           |   13 +
 sw/source/filter/xml/xmlimpit.cxx                           |    7 
 writerfilter/source/dmapper/DomainMapper.cxx                |   37 +++-
 xmloff/inc/xmlsdtypes.hxx                                   |    2 
 xmloff/source/core/xmltoken.cxx                             |    1 
 xmloff/source/draw/sdpropls.cxx                             |   37 ++++
 xmloff/source/draw/shapeexport.cxx                          |   30 +++-
 xmloff/source/style/prhdlfac.cxx                            |    3 
 xmloff/source/style/xmlexppr.cxx                            |    3 
 xmloff/source/token/tokens.txt                              |    1 
 42 files changed, 436 insertions(+), 144 deletions(-)

New commits:
commit c70ee4a6b9071468255e5d4fdb893e9c9bdf4fad
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Wed Aug 17 02:31:44 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Sep 14 08:32:08 2022 +0200

    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 <vmik...@collabora.com>

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

Reply via email to