sw/qa/extras/ooxmlexport/data/tdf162551_notLayoutInCell_charLeft_fromTop.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport21.cxx | 21 ++++++++++ sw/qa/extras/ww8export/ww8export4.cxx | 21 +++++----- sw/source/filter/ww8/wrtw8esh.cxx | 4 + sw/source/filter/ww8/ww8graf.cxx | 21 ++++++++-- sw/source/writerfilter/dmapper/GraphicImport.cxx | 15 +++++++ 6 files changed, 70 insertions(+), 12 deletions(-)
New commits: commit 0640a0f673635f861a28d14d21e40599e7a54553 Author: Justin Luth <jl...@mail.com> AuthorDate: Thu Aug 22 16:46:18 2024 -0400 Commit: Justin Luth <jl...@mail.com> CommitDate: Sat Aug 24 14:34:59 2024 +0200 tdf#162551 mso formats: line(vert) or char(hori) forces layoutInCell Already DOCX compat15 always forces layoutInCell, but even earlier than 2013, if the shape is oriented vertically to the line, or horizontally to the character then everything is treated as if it was layoutInCell. So let's make it explicit while we import. That seems to make the most sense. make CppunitTest_sw_ooxmlexport21 CPPUNIT_TEST_NAME=testTdf162551 make CppunitTest_sw_ww8export4 CPPUNIT_TEST_NAME=testTdf162542 No existing unit tests found. Change-Id: Ibdb4933e248c86ab595df68f81fad1f23345c2a2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172296 Reviewed-by: Justin Luth <jl...@mail.com> Tested-by: Jenkins diff --git a/sw/qa/extras/ooxmlexport/data/tdf162551_notLayoutInCell_charLeft_fromTop.docx b/sw/qa/extras/ooxmlexport/data/tdf162551_notLayoutInCell_charLeft_fromTop.docx new file mode 100644 index 000000000000..20609bf733b6 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf162551_notLayoutInCell_charLeft_fromTop.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx index a3a60c2973cf..a7c46b536f95 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx @@ -676,6 +676,27 @@ DECLARE_OOXMLEXPORT_TEST(testTdf153909_followTextFlow, "tdf153909_followTextFlow CPPUNIT_ASSERT(nTableLeft > nRectLeft); } +DECLARE_OOXMLEXPORT_TEST(testTdf162551, "tdf162551_notLayoutInCell_charLeft_fromTop.docx") +{ + // given cell B2 with a para-fromTop, char-left image that is NOT layoutInCell + // (but Microsoft sees the CHAR orientation and triggers a layoutInCell anyway) + xmlDocUniquePtr pDump = parseLayoutDump(); + sal_Int32 nShapeTop + = getXPath(pDump, "//tab/row[2]/cell[2]/txt/anchored/fly/SwAnchoredObject/bounds"_ostr, + "top"_ostr) + .toInt32(); + // sal_Int32 nPara1Top + // = getXPath(pDump, "//tab/row[2]/cell[2]/txt/infos/bounds"_ostr, "top"_ostr).toInt32(); + sal_Int32 nCellTop + = getXPath(pDump, "//tab/row[2]/cell[2]/infos/bounds"_ostr, "top"_ostr).toInt32(); + // The image is limited by the cell boundaries (should be limited to cell margin actually) + CPPUNIT_ASSERT(nCellTop <= nShapeTop); + // CPPUNIT_ASSERT_EQUAL(nPara1Top, nShapeTop); // tdf#162539 + + // since in fact layoutInCell is supposed to be applied, we mark (and export) as layoutInCell + CPPUNIT_ASSERT(getProperty<bool>(getShape(1), u"IsFollowingTextFlow"_ustr)); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf159207_footerFramePrBorder) { loadFromFile(u"tdf159207_footerFramePrBorder.docx"); // re-imports as editeng Frame/Shape diff --git a/sw/qa/extras/ww8export/ww8export4.cxx b/sw/qa/extras/ww8export/ww8export4.cxx index 1266a2a06977..3e881a5114c2 100644 --- a/sw/qa/extras/ww8export/ww8export4.cxx +++ b/sw/qa/extras/ww8export/ww8export4.cxx @@ -203,15 +203,18 @@ DECLARE_WW8EXPORT_TEST(testTdf162542, "tdf162542_notLayoutInCell_charLeft_wrapTh = getXPath(pDump, "//tab/row[2]/cell[2]/txt[6]/infos/bounds"_ostr, "left"_ostr).toInt32(); CPPUNIT_ASSERT(nShapeLeft > nPara6Left); // nShapeLeft starts after the word "anchor" - // sal_Int32 nShapeTop - // = getXPath(pDump, "//tab/row[2]/cell[2]/txt[6]/anchored/fly/SwAnchoredObject/bounds"_ostr, - // "top"_ostr) - // .toInt32(); - // sal_Int32 nPara1Top - // = getXPath(pDump, "//tab/row[2]/cell[2]/txt[1]/infos/bounds"_ostr, "top"_ostr).toInt32(); - // CPPUNIT_ASSERT_EQUAL(nPara1Top, nShapeTop); // nShapeTop starts at the cell margin" - - CPPUNIT_ASSERT(!getProperty<bool>(getShape(1), u"IsFollowingTextFlow"_ustr)); + // tdf#162551: The top is oriented to the top of the page - but MSO treats it as layoutInCell + sal_Int32 nShapeTop + = getXPath(pDump, "//tab/row[2]/cell[2]/txt[6]/anchored/fly/SwAnchoredObject/bounds"_ostr, + "top"_ostr) + .toInt32(); + sal_Int32 nPara1Top + = getXPath(pDump, "//tab/row[2]/cell[2]/txt[1]/infos/bounds"_ostr, "top"_ostr).toInt32(); + // layoutInCell uses the cell margin as the top-most point, not the cell edge + CPPUNIT_ASSERT_EQUAL(nPara1Top, nShapeTop); // nShapeTop starts at the cell margin" + + // since in fact layoutInCell is supposed to be applied, we mark (and export) as layoutInCell + CPPUNIT_ASSERT(getProperty<bool>(getShape(1), u"IsFollowingTextFlow"_ustr)); // tdf#162551 } CPPUNIT_TEST_FIXTURE(Test, testEndnotesAtSectEndDOC) diff --git a/sw/source/filter/ww8/wrtw8esh.cxx b/sw/source/filter/ww8/wrtw8esh.cxx index 9513c42d4d01..c1066d355569 100644 --- a/sw/source/filter/ww8/wrtw8esh.cxx +++ b/sw/source/filter/ww8/wrtw8esh.cxx @@ -2707,6 +2707,10 @@ void WinwordAnchoring::SetAnchoring(const SwFrameFormat& rFormat) // so do nothing unless "Follow text flow" is disabled (which is the default in native LO). bool bLayoutInCell = rFormat.GetFollowTextFlow().GetValue(); + // Microsoft will treat any orientation to CHAR or TEXT_LINE as if it had to layoutInCell + if (!bLayoutInCell) + bLayoutInCell = mnYRelTo == 3 || mnXRelTo == 3; + // If this is already MSO format, then we need to round-trip a false FollowingTextFlow value const bool bIsMSOLayout = rFormat.getIDocumentSettingAccess().get( DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION); diff --git a/sw/source/filter/ww8/ww8graf.cxx b/sw/source/filter/ww8/ww8graf.cxx index bdce9d5e340c..222685102019 100644 --- a/sw/source/filter/ww8/ww8graf.cxx +++ b/sw/source/filter/ww8/ww8graf.cxx @@ -2312,6 +2312,8 @@ RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FS SfxItemSet &rFlySet) { bool bCurSectionVertical = m_aSectionManager.CurrentSectionIsVertical(); + bool bIsObjectLayoutInTableCell + = m_nInTable && IsObjectLayoutInTableCell(rRecord.nGroupShapeBooleanProperties); if (!rRecord.nXRelTo) { @@ -2417,6 +2419,20 @@ RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FS text::RelOrientation::TEXT_LINE // 3 is relative to line }; + if (!bIsObjectLayoutInTableCell && m_nInTable && eAnchor == RndStdIds::FLY_AT_CHAR) + { + // Microsoft apparently forces layoutInCell behaviour + // if either horizontal orientation is based on character + // or vertical orientation is based on line + // so make it explicit instead of trying to hack in tons of adjustments. + if (aVertRelOriTab[nYRelTo] == text::RelOrientation::TEXT_LINE + || aHoriRelOriTab[nXRelTo] == text::RelOrientation::CHAR) + { + bIsObjectLayoutInTableCell = true; + rFlySet.Put(SwFormatFollowTextFlow(true)); + } + } + // If the image is inline, then the relative orientation means nothing, // so set it up so that if the user changes it into an anchor, it positions usefully. sal_Int16 eHoriOri @@ -2468,9 +2484,6 @@ RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FS // if the object is anchored inside a table cell, is horizontal aligned // at frame and has wrap through, but its attribute // 'layout in table cell' isn't set, convert its horizontal alignment to page text area. - // #i84783# - use new method <IsObjectLayoutInTableCell()> - const bool bIsObjectLayoutInTableCell - = m_nInTable && IsObjectLayoutInTableCell(rRecord.nGroupShapeBooleanProperties); if (!bIsObjectLayoutInTableCell && m_nInTable && (eHoriRel == text::RelOrientation::FRAME) && rFSPA.nwr == 3) @@ -2536,6 +2549,8 @@ RndStdIds SwWW8ImplReader::ProcessEscherAlign(SvxMSDffImportRec& rRecord, WW8_FS } // #i84783# +// Return whether the fly specifies layoutInCell. +// (NOTE: there are circumstances where layoutInCell is implemented even when set to false) bool SwWW8ImplReader::IsObjectLayoutInTableCell(const sal_uInt32 nGroupShapeBooleanProperties) const { bool bIsObjectLayoutInTableCell = false; diff --git a/sw/source/writerfilter/dmapper/GraphicImport.cxx b/sw/source/writerfilter/dmapper/GraphicImport.cxx index ff08d87b0428..e7332d1245a8 100644 --- a/sw/source/writerfilter/dmapper/GraphicImport.cxx +++ b/sw/source/writerfilter/dmapper/GraphicImport.cxx @@ -830,6 +830,21 @@ void GraphicImport::lcl_attribute(Id nName, const Value& rValue) rValue.getAny( ) >>= xShape; if ( xShape.is( ) ) { + if (!m_pImpl->m_bLayoutInCell && m_pImpl->m_rDomainMapper.IsInTable() + && m_pImpl->m_rGraphicImportType == IMPORT_AS_DETECTED_ANCHOR) + { + // Microsoft apparently forces layoutInCell behaviour + // if either horizontal orientation is based on character + // or vertical orientation is based on line + // so make it explicit instead of trying to hack in tons of adjustments. + if (m_pImpl->m_nVertRelation == text::RelOrientation::TEXT_LINE + || m_pImpl->m_nHoriRelation == text::RelOrientation::CHAR) + { + m_pImpl->m_bLayoutInCell = true; + m_pImpl->m_bCompatForcedLayoutInCell = true; + } + } + if (m_pImpl->m_bLayoutInCell && m_pImpl->m_rDomainMapper.IsInTable()) { // Microsoft is buggy and inconsistent in how they handle layoutInCell.