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);