From 56e1c88a7b2fc31682a239f59ec319f93ba61a25 Mon Sep 17 00:00:00 2001 From: Michael Stahl Date: Tue, 4 Aug 2020 18:56:08 +0200 Subject: tdf#135464 sw: ODT->DOCX: fix positioning of at-page shapes and frames Exporting at-page anchored flys to DOCX can result in wrong positions, because DocxSdrExport::startDMLAnchorInline() converts text::RelOrientation::FRAME to relativeFrom="column", i.e. the margin, but sw displays it as relative to the page. In fact at-page and FRAME is an invalid combination according to the table in ODF 1.3, 20.298 style:horizontal-pos, the paragraph and character relations are not valid for page-anchored flys. Since there are lots of ODT files with this invalid combination, try to fix it on import, in SwXFrame and SwXShape. Funnily, SwXShape is attached before the properties are set, while SwXFrame is attached after the properties are set. The anchor frame for at-page is always a SwPageFrame. Unfortunately there is a case where PRINT_AREA and PAGE_PRINT_AREA differ, namely the CalcClipRect() only handles PRINT_AREA so it will crop to the right margin with that but not with PAGE_PRINT_AREA, so don't map this value. Change-Id: I4d5f7f87d045ac4539b9170e55c34d4afe801f4d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100130 Tested-by: Jenkins Reviewed-by: Michael Stahl (cherry picked from commit 12645900dece0a9aa0661fee796c27f672217977) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/100237 Reviewed-by: Thorsten Behrens --- sw/inc/fmtornt.hxx | 6 ++ sw/qa/extras/ooxmlexport/data/rotated_shape.fodt | 81 ++++++++++++++++++++++++ sw/qa/extras/ooxmlexport/ooxmlexport15.cxx | 29 +++++++++ sw/source/core/layout/atrfrm.cxx | 33 ++++++++++ sw/source/core/unocore/unodraw.cxx | 15 +++++ sw/source/core/unocore/unoframe.cxx | 13 ++++ 6 files changed, 177 insertions(+) create mode 100644 sw/qa/extras/ooxmlexport/data/rotated_shape.fodt diff --git a/sw/inc/fmtornt.hxx b/sw/inc/fmtornt.hxx index 0a8ab6f1a619..59afdb46f26a 100644 --- a/sw/inc/fmtornt.hxx +++ b/sw/inc/fmtornt.hxx @@ -108,6 +108,12 @@ inline const SwFormatVertOrient &SwFormat::GetVertOrient(bool bInP) const inline const SwFormatHoriOrient &SwFormat::GetHoriOrient(bool bInP) const { return m_aSet.GetHoriOrient(bInP); } +namespace sw { + + bool GetAtPageRelOrientation(sal_Int16 & rOrientation, bool const isIgnorePrintArea); + +} + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/extras/ooxmlexport/data/rotated_shape.fodt b/sw/qa/extras/ooxmlexport/data/rotated_shape.fodt new file mode 100644 index 000000000000..fce84f93e945 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/rotated_shape.fodt @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Foo + + + iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAAXNSR0IArs4c6QAAAAZiS0dE + AP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJBhI0A6HXrrIAAABl + SURBVDjLY/z//z8DtQATMYokl7D8p4phMIOIMZCJFBcRMpCJVK/hM5CJnDDCJc9EbmBjU8dE + jkG41DPhM+h5zB9GfHx0fTgjAJtGfOIMDAwMLMQoItZAJgYqglHDhpNhjNQsaQHF4y3hS/bS + HgAAAABJRU5ErkJggg== + + + + + + + diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx index a1f8971772c1..08136531b9df 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx @@ -52,6 +52,35 @@ DECLARE_OOXMLEXPORT_TEST(testTdf134063, "tdf134063.docx") CPPUNIT_ASSERT_EQUAL(sal_Int32(720), getXPath(pDump, "//page[1]/body/txt[1]/Text[3]", "nWidth").toInt32()); } +DECLARE_OOXMLEXPORT_TEST(testAtPageShapeRelOrientation, "rotated_shape.fodt") +{ + // invalid combination of at-page anchor and horizontal-rel="paragraph" + // caused relativeFrom="column" instead of relativeFrom="page" + + xmlDocUniquePtr pXmlDocument = parseExport("word/document.xml"); + if (!pXmlDocument) + return; + + assertXPathContent(pXmlDocument, "/w:document/w:body/w:p/w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor" + "/wp:positionH/wp:posOffset", "-480060"); + assertXPath(pXmlDocument, "/w:document/w:body/w:p/w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor" + "/wp:positionH", "relativeFrom", "page"); + assertXPathContent(pXmlDocument, "/w:document/w:body/w:p/w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor" + "/wp:positionV/wp:posOffset", "8147685"); + assertXPath(pXmlDocument, "/w:document/w:body/w:p/w:r/mc:AlternateContent[1]/mc:Choice/w:drawing/wp:anchor" + "/wp:positionV", "relativeFrom", "page"); + + // same for sw + assertXPathContent(pXmlDocument, "/w:document/w:body/w:p/w:r/w:drawing/wp:anchor" + "/wp:positionH/wp:posOffset", "720090"); + assertXPath(pXmlDocument, "/w:document/w:body/w:p/w:r/w:drawing/wp:anchor" + "/wp:positionH", "relativeFrom", "page"); + assertXPathContent(pXmlDocument, "/w:document/w:body/w:p/w:r/w:drawing/wp:anchor" + "/wp:positionV/wp:posOffset", "1080135"); + assertXPath(pXmlDocument, "/w:document/w:body/w:p/w:r/w:drawing/wp:anchor" + "/wp:positionV", "relativeFrom", "page"); +} + DECLARE_OOXMLEXPORT_TEST(testRelativeAnchorHeightFromBottomMarginHasFooter, "tdf133070_testRelativeAnchorHeightFromBottomMarginHasFooter.docx") { diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx index ae9311d2231d..d9f37a669787 100644 --- a/sw/source/core/layout/atrfrm.cxx +++ b/sw/source/core/layout/atrfrm.cxx @@ -96,6 +96,39 @@ using namespace ::com::sun::star; +namespace sw { + +bool GetAtPageRelOrientation(sal_Int16 & rOrientation, bool const isIgnorePrintArea) +{ + switch (rOrientation) + { + case text::RelOrientation::CHAR: + case text::RelOrientation::FRAME: + rOrientation = text::RelOrientation::PAGE_FRAME; + return true; + case text::RelOrientation::PRINT_AREA: + if (isIgnorePrintArea) + { + return false; + } + else + { + rOrientation = text::RelOrientation::PAGE_PRINT_AREA; + return true; + } + case text::RelOrientation::FRAME_LEFT: + rOrientation = text::RelOrientation::PAGE_LEFT; + return true; + case text::RelOrientation::FRAME_RIGHT: + rOrientation = text::RelOrientation::PAGE_RIGHT; + return true; + default: + return false; + } +} + +} // namespace sw + SfxPoolItem* SwFormatLineNumber::CreateDefault() { return new SwFormatLineNumber; } static sal_Int16 lcl_IntToRelation(const uno::Any& rVal) diff --git a/sw/source/core/unocore/unodraw.cxx b/sw/source/core/unocore/unodraw.cxx index d21756c65720..ddbdf85769db 100644 --- a/sw/source/core/unocore/unodraw.cxx +++ b/sw/source/core/unocore/unodraw.cxx @@ -1226,6 +1226,21 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a } } } + else if (pEntry->nWID == RES_HORI_ORIENT + && pEntry->nMemberId == MID_HORIORIENT_RELATION + && aSet.Get(RES_ANCHOR).GetAnchorId() == RndStdIds::FLY_AT_PAGE) + { + uno::Any value(aValue); + sal_Int16 nRelOrient(text::RelOrientation::PAGE_FRAME); + aValue >>= nRelOrient; + if (sw::GetAtPageRelOrientation(nRelOrient, true)) + { + SAL_WARN("sw.core", "SwXShape: fixing invalid horizontal RelOrientation for at-page anchor"); + value <<= nRelOrient; + } + m_pPropSet->setPropertyValue( *pEntry, value, aSet ); + pFormat->SetFormatAttr(aSet); + } else { m_pPropSet->setPropertyValue( *pEntry, aValue, aSet ); diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx index f827226d0be4..a04ac6908b92 100644 --- a/sw/source/core/unocore/unoframe.cxx +++ b/sw/source/core/unocore/unoframe.cxx @@ -2744,6 +2744,19 @@ void SwXFrame::attachToRange(uno::Reference const& xTextRange, aAnchor.SetAnchor( aPam.GetPoint() ); aFrameSet.Put(aAnchor); } + + if (eAnchorId == RndStdIds::FLY_AT_PAGE) + { + sal_Int16 nRelOrient(aFrameSet.Get(RES_HORI_ORIENT).GetRelationOrient()); + if (sw::GetAtPageRelOrientation(nRelOrient, true)) + { + SAL_WARN("sw.core", "SwXFrame: fixing invalid horizontal RelOrientation for at-page anchor"); + + SwFormatHoriOrient item(aFrameSet.Get(RES_HORI_ORIENT)); + item.SetRelationOrient(nRelOrient); + aFrameSet.Put(item); + } + } } const ::uno::Any* pStyle; -- cgit