oox/qa/unit/data/tdf151008_eaVertAnchor.pptx       |binary
 oox/qa/unit/export.cxx                             |   30 ++++++++
 oox/qa/unit/shape.cxx                              |   58 +++++++++++++++
 oox/source/drawingml/textbodypropertiescontext.cxx |   46 ++++++++++--
 oox/source/export/drawingml.cxx                    |   78 ++++++++++-----------
 5 files changed, 165 insertions(+), 47 deletions(-)

New commits:
commit 439eaa8efdd8179f93f7aaf44d25ceced6ab1665
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Sun Sep 18 14:41:32 2022 +0200
Commit:     Regina Henschel <rb.hensc...@t-online.de>
CommitDate: Mon Sep 19 10:38:30 2022 +0200

    tdf#151008 adapt anchor for eaVert and mongolianVert
    
    MS Office and LibreOffice act different whether anchor positions are
    rotated for vertical writing modes eaVert and mongolianVert. The patch
    converts the position on import and export.
    
    Currently shapes are not able to render mongolianVert. Nevertheless it
    is included so that the text block has already the correct position
    and the original position is restored on export.
    
    LibreOffice has vertical anchor alignments BOTTOM, that would require
    a third horizontal position in MS Office, which does not exist. It is
    mapped to anchorCtr='1' instead. Such does not occur in pptx-LO-pptx
    round-trip.
    
    Change-Id: I1b0e42a39ce3aba12cdb271b2aa8023dacb9c53d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140118
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>

diff --git a/oox/qa/unit/data/tdf151008_eaVertAnchor.pptx 
b/oox/qa/unit/data/tdf151008_eaVertAnchor.pptx
new file mode 100644
index 000000000000..999cd220408c
Binary files /dev/null and b/oox/qa/unit/data/tdf151008_eaVertAnchor.pptx differ
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
index 09cf5cb5ef48..64e1cfaa47e7 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -808,6 +808,36 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf149538upright)
     assertXPath(pXmlDoc, "//p:spTree/p:sp/p:txBody/a:bodyPr", "upright", "1");
     assertXPathNoAttribute(pXmlDoc, "//p:spTree/p:sp/p:txBody/a:bodyPr", 
"rot");
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testTdf151008VertAnchor)
+{
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"tdf151008_eaVertAnchor.pptx";
+    loadAndSave(aURL, "Impress Office Open XML");
+    std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), 
"ppt/slides/slide1.xml");
+    xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get());
+    // The order of the shapes in the file is by name "Right", "Center", 
"Left", "RightMiddle",
+    // "CenterMiddle" and "LeftMiddle". I access the shapes here by index, 
because the XPath is
+    // easier then.
+    // As of Sep 2022 LibreOffice does not write the default anchorCtr="0"
+    // Right
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[1]/p:txBody/a:bodyPr", "anchor", 
"t");
+    assertXPathNoAttribute(pXmlDoc, "//p:spTree/p:sp[1]/p:txBody/a:bodyPr", 
"anchorCtr");
+    // Center
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[2]/p:txBody/a:bodyPr", "anchor", 
"ctr");
+    assertXPathNoAttribute(pXmlDoc, "//p:spTree/p:sp[2]/p:txBody/a:bodyPr", 
"anchorCtr");
+    // Left
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[3]/p:txBody/a:bodyPr", "anchor", 
"b");
+    assertXPathNoAttribute(pXmlDoc, "//p:spTree/p:sp[3]/p:txBody/a:bodyPr", 
"anchorCtr");
+    // RightMiddle
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[4]/p:txBody/a:bodyPr", "anchor", 
"t");
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[4]/p:txBody/a:bodyPr", "anchorCtr", 
"1");
+    // CenterMiddle
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[5]/p:txBody/a:bodyPr", "anchor", 
"ctr");
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[5]/p:txBody/a:bodyPr", "anchorCtr", 
"1");
+    // LeftMiddle
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[6]/p:txBody/a:bodyPr", "anchor", 
"b");
+    assertXPath(pXmlDoc, "//p:spTree/p:sp[6]/p:txBody/a:bodyPr", "anchorCtr", 
"1");
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/oox/qa/unit/shape.cxx b/oox/qa/unit/shape.cxx
index 345ebb215ad6..c741ff84d4da 100644
--- a/oox/qa/unit/shape.cxx
+++ b/oox/qa/unit/shape.cxx
@@ -15,6 +15,7 @@
 #include <unotest/macros_test.hxx>
 
 #include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
 #include <com/sun/star/frame/Desktop.hpp>
@@ -55,6 +56,7 @@ public:
     void tearDown() override;
     uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
     void load(std::u16string_view rURL);
+    uno::Reference<drawing::XShape> getShapeByName(std::u16string_view aName);
 };
 
 void OoxShapeTest::setUp()
@@ -78,6 +80,26 @@ void OoxShapeTest::load(std::u16string_view rFileName)
     mxComponent = loadFromDesktop(aURL);
 }
 
+uno::Reference<drawing::XShape> 
OoxShapeTest::getShapeByName(std::u16string_view aName)
+{
+    uno::Reference<drawing::XShape> xRet;
+
+    uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPagesSupplier(mxComponent, uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPage> 
xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0),
+                                                 uno::UNO_QUERY);
+    for (sal_Int32 i = 0; i < xDrawPage->getCount(); ++i)
+    {
+        uno::Reference<container::XNamed> xShape(xDrawPage->getByIndex(i), 
uno::UNO_QUERY);
+        if (xShape->getName() == aName)
+        {
+            xRet.set(xShape, uno::UNO_QUERY);
+            break;
+        }
+    }
+
+    return xRet;
+}
+
 CPPUNIT_TEST_FIXTURE(OoxShapeTest, testGroupTransform)
 {
     load(u"tdf141463_GroupTransform.pptx");
@@ -175,6 +197,42 @@ CPPUNIT_TEST_FIXTURE(OoxShapeTest, 
testTdf125582_TextOnCircle)
     CPPUNIT_ASSERT_EQUAL_MESSAGE("TextVerticalAdjust", 
drawing::TextVerticalAdjust_BOTTOM, eAdjust);
 }
 
+CPPUNIT_TEST_FIXTURE(OoxShapeTest, testTdf151008VertAnchor)
+{
+    // The document contains shapes with all six kind of anchor positions in 
pptx. The text in the
+    // shapes is larger than the shape and has no word wrap. That way anchor 
position is visible
+    // in case you inspect the file manually.
+    load(u"tdf151008_eaVertAnchor.pptx");
+
+    struct anchorDesc
+    {
+        OUString sShapeName;
+        drawing::TextHorizontalAdjust eAnchorHori;
+        drawing::TextVerticalAdjust eAnchorVert;
+    };
+    anchorDesc aExpected[6] = {
+        { u"Right", drawing::TextHorizontalAdjust_RIGHT, 
drawing::TextVerticalAdjust_TOP },
+        { u"Center", drawing::TextHorizontalAdjust_CENTER, 
drawing::TextVerticalAdjust_TOP },
+        { u"Left", drawing::TextHorizontalAdjust_LEFT, 
drawing::TextVerticalAdjust_TOP },
+        { u"RightMiddle", drawing::TextHorizontalAdjust_RIGHT, 
drawing::TextVerticalAdjust_CENTER },
+        { u"CenterMiddle", drawing::TextHorizontalAdjust_CENTER,
+          drawing::TextVerticalAdjust_CENTER },
+        { u"LeftMiddle", drawing::TextHorizontalAdjust_LEFT, 
drawing::TextVerticalAdjust_CENTER }
+    };
+    // without the fix horizontal and vertical anchor positions were exchanged
+    for (size_t i = 0; i < 6; ++i)
+    {
+        uno::Reference<beans::XPropertySet> 
xShape(getShapeByName(aExpected[i].sShapeName),
+                                                   uno::UNO_QUERY);
+        drawing::TextHorizontalAdjust eHori;
+        CPPUNIT_ASSERT(xShape->getPropertyValue("TextHorizontalAdjust") >>= 
eHori);
+        drawing::TextVerticalAdjust eVert;
+        CPPUNIT_ASSERT(xShape->getPropertyValue("TextVerticalAdjust") >>= 
eVert);
+        CPPUNIT_ASSERT_EQUAL(aExpected[i].eAnchorHori, eHori);
+        CPPUNIT_ASSERT_EQUAL(aExpected[i].eAnchorVert, eVert);
+    }
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/drawingml/textbodypropertiescontext.cxx 
b/oox/source/drawingml/textbodypropertiescontext.cxx
index 9d221a18ffbe..47ef04797c93 100644
--- a/oox/source/drawingml/textbodypropertiescontext.cxx
+++ b/oox/source/drawingml/textbodypropertiescontext.cxx
@@ -75,10 +75,6 @@ TextBodyPropertiesContext::TextBodyPropertiesContext( 
ContextHandler2Helper cons
             mrTextBodyProp.moInsets[i] = GetCoordinate( sValue );
     }
 
-    mrTextBodyProp.mbAnchorCtr = rAttribs.getBool( XML_anchorCtr, false );
-    if( mrTextBodyProp.mbAnchorCtr )
-        mrTextBodyProp.maPropertyMap.setProperty( PROP_TextHorizontalAdjust, 
TextHorizontalAdjust_CENTER );
-
 //   bool bCompatLineSpacing = rAttribs.getBool( XML_compatLnSpc, false );
 //   bool bForceAA = rAttribs.getBool( XML_forceAA, false );
     bool bFromWordArt = rAttribs.getBool(XML_fromWordArt, false);
@@ -148,10 +144,46 @@ TextBodyPropertiesContext::TextBodyPropertiesContext( 
ContextHandler2Helper cons
     }
 
     // ST_TextAnchoringType
-    if( rAttribs.hasAttribute( XML_anchor ) )
+    mrTextBodyProp.mbAnchorCtr = rAttribs.getBool(XML_anchorCtr, false );
+    if (rAttribs.hasAttribute(XML_anchor))
+        mrTextBodyProp.meVA = GetTextVerticalAdjust( 
rAttribs.getToken(XML_anchor, XML_t));
+    // else meVA is initialized to TextVerticalAdjust_TOP
+
+    sal_Int32 tVert = mrTextBodyProp.moVert.value_or(XML_horz);
+    if (tVert == XML_eaVert || tVert == XML_mongolianVert)
+    {
+        if (mrTextBodyProp.mbAnchorCtr)
+            mrTextBodyProp.maPropertyMap.setProperty(PROP_TextVerticalAdjust,
+                                                     
TextVerticalAdjust_CENTER);
+        else
+            mrTextBodyProp.maPropertyMap.setProperty(PROP_TextVerticalAdjust,
+                                                     TextVerticalAdjust_TOP);
+
+        if (mrTextBodyProp.meVA == TextVerticalAdjust_CENTER)
+            mrTextBodyProp.maPropertyMap.setProperty(PROP_TextHorizontalAdjust,
+                                                     
TextHorizontalAdjust_CENTER);
+        else if (mrTextBodyProp.meVA == TextVerticalAdjust_TOP)
+        {
+            mrTextBodyProp.maPropertyMap.setProperty(
+                PROP_TextHorizontalAdjust,
+                tVert == XML_eaVert ? TextHorizontalAdjust_RIGHT : 
TextHorizontalAdjust_LEFT);
+        }
+        else // meVA == TextVerticalAdjust_BOTTOM
+        {
+            mrTextBodyProp.maPropertyMap.setProperty(
+                PROP_TextHorizontalAdjust,
+                tVert == XML_eaVert ? TextHorizontalAdjust_LEFT : 
TextHorizontalAdjust_RIGHT);
+        }
+    }
+    else
     {
-        mrTextBodyProp.meVA = GetTextVerticalAdjust( rAttribs.getToken( 
XML_anchor, XML_t ) );
-        mrTextBodyProp.maPropertyMap.setProperty( PROP_TextVerticalAdjust, 
mrTextBodyProp.meVA);
+        if (mrTextBodyProp.mbAnchorCtr)
+            mrTextBodyProp.maPropertyMap.setProperty(PROP_TextHorizontalAdjust,
+                                                     
TextHorizontalAdjust_CENTER);
+        else // BLOCK is nearer to rendering in MS Office than LEFT, see 
tdf#137023
+            mrTextBodyProp.maPropertyMap.setProperty(PROP_TextHorizontalAdjust,
+                                                     
TextHorizontalAdjust_BLOCK);
+        mrTextBodyProp.maPropertyMap.setProperty(PROP_TextVerticalAdjust, 
mrTextBodyProp.meVA);
     }
 
     // Push defaults
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 47c8903093b1..85e73eb35c85 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -3318,22 +3318,12 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
         }
     }
 
-    TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
-    const char* sVerticalAlignment = nullptr;
-    if (GetProperty(rXPropSet, "TextVerticalAdjust"))
-        mAny >>= eVerticalAlignment;
-    sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
-
     std::optional<OString> sWritingMode;
-    bool bVertical = false;
     if (GetProperty(rXPropSet, "TextWritingMode"))
     {
         WritingMode eMode;
         if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL )
-        {
             sWritingMode = "eaVert";
-            bVertical = true;
-        }
     }
     if (GetProperty(rXPropSet, "WritingMode"))
     {
@@ -3341,25 +3331,13 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
         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;
-            }
         }
     }
 
@@ -3423,19 +3401,15 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
                         {
                         case WritingMode2::TB_RL:
                             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;
@@ -3529,15 +3503,9 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
     if (nTextPreRotateAngle != 0 && !sWritingMode)
     {
         if (nTextPreRotateAngle == -90 || nTextPreRotateAngle == 270)
-        {
             sWritingMode = "vert";
-            bVertical = true;
-        }
         else if (nTextPreRotateAngle == -270 || nTextPreRotateAngle == 90)
-        {
             sWritingMode = "vert270";
-            bVertical = true;
-        }
         else if (nTextPreRotateAngle == -180 || nTextPreRotateAngle == 180)
         {
 #if defined __GNUC__ && !defined __clang__ && __GNUC__ == 12
@@ -3600,14 +3568,44 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
 #pragma GCC diagnostic pop
 #endif
 
-    TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
-    bool bHorizontalCenter = false;
+    // Prepare attributes 'anchor' and 'anchorCtr'
+    // LibreOffice has 12 value sets, MS Office only 6. We map them so, that 
it reverses the
+    // 6 mappings from import, and we assign the others approximately.
+    TextVerticalAdjust eVerticalAlignment(TextVerticalAdjust_TOP);
+    if (GetProperty(rXPropSet, "TextVerticalAdjust"))
+        mAny >>= eVerticalAlignment;
+    TextHorizontalAdjust eHorizontalAlignment(TextHorizontalAdjust_CENTER);
     if (GetProperty(rXPropSet, "TextHorizontalAdjust"))
         mAny >>= eHorizontalAlignment;
-    if( eHorizontalAlignment == TextHorizontalAdjust_CENTER )
-        bHorizontalCenter = true;
-    else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT )
-        sVerticalAlignment = "b";
+
+    const char* sAnchor = nullptr;
+    bool bAnchorCtr = false;
+    if (sWritingMode.has_value()
+        && (sWritingMode.value() == "eaVert" || sWritingMode.value() == 
"mongolianVert"))
+    {
+        bAnchorCtr = eVerticalAlignment == TextVerticalAdjust_CENTER
+                     || eVerticalAlignment == TextVerticalAdjust_BOTTOM
+                     || eVerticalAlignment == TextVerticalAdjust_BLOCK;
+        switch (eHorizontalAlignment)
+        {
+            case TextHorizontalAdjust_CENTER:
+                sAnchor = "ctr";
+                break;
+            case TextHorizontalAdjust_LEFT:
+                sAnchor = sWritingMode.value() == "eaVert" ? "b" : "t";
+                break;
+            case TextHorizontalAdjust_RIGHT:
+            default: // TextHorizontalAdjust_BLOCK, should not happen
+                sAnchor = sWritingMode.value() == "eaVert" ? "t" : "b";
+                break;
+        }
+    }
+    else
+    {
+        bAnchorCtr = eHorizontalAlignment == TextHorizontalAdjust_CENTER
+                     || eHorizontalAlignment == TextHorizontalAdjust_RIGHT;
+        sAnchor = GetTextVerticalAdjust(eVerticalAlignment);
+    }
 
     bool bHasWrap = false;
     bool bWrap = false;
@@ -3658,8 +3656,8 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
                                XML_rIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), 
nRight != constDefaultLeftRightInset),
                                XML_tIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), 
nTop != constDefaultTopBottomInset),
                                XML_bIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)),
 nBottom != constDefaultTopBottomInset),
-                               XML_anchor, sVerticalAlignment,
-                               XML_anchorCtr, sax_fastparser::UseIf("1", 
bHorizontalCenter),
+                               XML_anchor, sAnchor,
+                               XML_anchorCtr, sax_fastparser::UseIf("1", 
bAnchorCtr),
                                XML_vert, sWritingMode,
                                XML_upright, isUpright,
                                XML_rot, sTextRotateAngleMSUnit);

Reply via email to