sw/qa/extras/layout/data/Tdf121509.odt |binary sw/qa/extras/layout/layout2.cxx | 58 +++++++++++++++++++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 68 ++++++++++++++++++++++++++- sw/source/filter/ww8/docxattributeoutput.hxx | 9 +++ 4 files changed, 134 insertions(+), 1 deletion(-)
New commits: commit 35732c84b05e4f6e50349796636beb01f2a09907 Author: Attila Bakos (NISZ) <bakos.attilakar...@nisz.hu> AuthorDate: Mon Jun 14 15:24:06 2021 +0200 Commit: László Németh <nem...@numbertext.org> CommitDate: Thu Jul 1 12:47:35 2021 +0200 tdf#121509 DOCX export: fix corrupt shape anchoring in textbox MSO doesn't support shapes anchored to character in a textbox. Convert these shapes by re-anchoring them to the anchor point of the textbox (also recalculating their positions in simple cases), so Word can now open the exported document. Change-Id: I28b244975981d69c50e5d4a46249918af089bae5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117163 Tested-by: László Németh <nem...@numbertext.org> Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/sw/qa/extras/layout/data/Tdf121509.odt b/sw/qa/extras/layout/data/Tdf121509.odt new file mode 100644 index 000000000000..856f60c88764 Binary files /dev/null and b/sw/qa/extras/layout/data/Tdf121509.odt differ diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx index 72cdc908b6ca..1db5f1e69214 100644 --- a/sw/qa/extras/layout/layout2.cxx +++ b/sw/qa/extras/layout/layout2.cxx @@ -49,6 +49,9 @@ #include <svx/svdpage.hxx> #include <svx/svdotext.hxx> #include <dcontact.hxx> +#include <frameformats.hxx> +#include <fmtcntnt.hxx> +#include <unotextrange.hxx> constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/extras/layout/data/"; @@ -1442,6 +1445,61 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf127118) assertXPath(pXmlDoc, "/root/page[2]/body/tab/row[1]/cell[1]/txt[1]", "WritingMode", "VertBTLR"); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf121509) +{ + auto pDoc = createSwDoc(DATA_DIRECTORY, "Tdf121509.odt"); + CPPUNIT_ASSERT(pDoc); + + // Get all shape/frame formats + auto vFrameFormats = pDoc->GetSpzFrameFormats(); + // Get the textbox + auto xTextFrame = SwTextBoxHelper::getUnoTextFrame(getShape(1)); + // Get The triangle + auto pTriangleShapeFormat = vFrameFormats->GetFormat(2); + CPPUNIT_ASSERT(xTextFrame); + CPPUNIT_ASSERT(pTriangleShapeFormat); + + // Get the position inside the textbox + auto xTextContentStart = xTextFrame->getText()->getStart(); + SwUnoInternalPaM aCursor(*pDoc); + CPPUNIT_ASSERT(sw::XTextRangeToSwPaM(aCursor, xTextContentStart)); + + // Put the triangle into the textbox + SwFormatAnchor aNewAnch(pTriangleShapeFormat->GetAnchor()); + aNewAnch.SetAnchor(aCursor.Start()); + CPPUNIT_ASSERT(pTriangleShapeFormat->SetFormatAttr(aNewAnch)); + + // Reload (docx) + auto aTemp = utl::TempFile(); + save("Office Open XML Text", aTemp); + + // The second part: check if the reloaded doc has flys inside a fly + uno::Reference<lang::XComponent> xComponent + = loadFromDesktop(aTemp.GetURL(), "com.sun.star.text.TextDocument"); + uno::Reference<text::XTextDocument> xTextDoc(xComponent, uno::UNO_QUERY); + auto pTextDoc = dynamic_cast<SwXTextDocument*>(xTextDoc.get()); + CPPUNIT_ASSERT(pTextDoc); + auto pSecondDoc = pTextDoc->GetDocShell()->GetDoc(); + auto pSecondFormats = pSecondDoc->GetSpzFrameFormats(); + + bool bFlyInFlyFound = false; + for (auto secondformat : *pSecondFormats) + { + auto& pNd = secondformat->GetAnchor().GetContentAnchor()->nNode.GetNode(); + if (pNd.FindFlyStartNode()) + { + // So there is a fly inside another -> problem. + bFlyInFlyFound = true; + break; + } + } + // Drop the tempfile + aTemp.CloseStream(); + + // With the fix this cannot be true, if it is, that means Word unable to read the file.. + CPPUNIT_ASSERT_MESSAGE("Corrupt exported docx file!", !bFlyInFlyFound); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf134685) { createSwDoc(DATA_DIRECTORY, "tdf134685.docx"); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 7d185f5ce9c0..19dc42aa119c 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -6082,7 +6082,7 @@ void DocxAttributeOutput::WritePostponedDMLDrawing() m_pPostponedOLEs = std::move(pPostponedOLEs); } -void DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame &rFrame, const Point& /*rNdTopLeft*/ ) +void DocxAttributeOutput::WriteFlyFrame(const ww8::Frame& rFrame) { m_pSerializer->mark(Tag_OutputFlyFrame); @@ -6254,6 +6254,71 @@ void DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame &rFrame, const P m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame); } +void DocxAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/) +{ + /// The old OutputFlyFrame_Impl() moved to WriteFlyFrame(). + /// Now if a frame anchored inside another frame, it will + /// not be exported immediately, because OOXML does not + /// support that feature, instead it postponed and exported + /// later when the original shape closed. + + if (rFrame.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR + || rFrame.IsInline()) + { + m_nEmbedFlyLevel++; + WriteFlyFrame(rFrame); + m_nEmbedFlyLevel--; + return; + } + + if (m_nEmbedFlyLevel == 0) + { + if (m_vPostponedFlys.empty()) + { + m_nEmbedFlyLevel++; + WriteFlyFrame(rFrame); + m_nEmbedFlyLevel--; + } + else + for (auto it = m_vPostponedFlys.begin(); it != m_vPostponedFlys.end();) + { + m_nEmbedFlyLevel++; + WriteFlyFrame(*it); + it = m_vPostponedFlys.erase(it); + m_nEmbedFlyLevel--; + } + } + else + { + bool bFound = false; + for (const auto& i : m_vPostponedFlys) + { + if (i.RefersToSameFrameAs(rFrame)) + { + bFound = true; + break; + } + } + if (!bFound) + { + if (auto pParentFly = rFrame.GetContentNode()->GetFlyFormat()) + { + auto aHori(rFrame.GetFrameFormat().GetHoriOrient()); + aHori.SetPos(aHori.GetPos() + pParentFly->GetHoriOrient().GetPos()); + auto aVori(rFrame.GetFrameFormat().GetVertOrient()); + aVori.SetPos(aVori.GetPos() + pParentFly->GetVertOrient().GetPos()); + + const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aHori); + const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aVori); + const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(pParentFly->GetAnchor()); + + m_vPostponedFlys.push_back(rFrame); + } + + } + } +} + void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj) { const EditTextObject& rEditObj = rParaObj.GetTextObject(); @@ -9993,6 +10058,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr m_sFieldBkm( ), m_nNextBookmarkId( 0 ), m_nNextAnnotationMarkId( 0 ), + m_nEmbedFlyLevel(0), m_pCurrentFrame( nullptr ), m_bParagraphOpened( false ), m_bParagraphFrameOpen( false ), diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index b0688183ab69..7f880cc506c4 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -718,6 +718,7 @@ private: void WritePostponedOLE(); void WritePostponedDMLDrawing(); void WritePostponedCustomShape(); + void WriteFlyFrame(const ww8::Frame& rFrame); void WriteSdtBlock(sal_Int32& nSdtPrToken, rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, @@ -802,6 +803,14 @@ private: OUString m_sRawText; + /// The first frame (anchored to the main text) is 0. + /// The second frame what is anchored to the previous in, is 1 + /// The third anchored inside the second is the 2 etc. + sal_uInt32 m_nEmbedFlyLevel; + + /// Stores the flys what are anchored inside a fly + std::vector<ww8::Frame> m_vPostponedFlys; + /// Bookmarks to output std::vector<OUString> m_rBookmarksStart; std::vector<OUString> m_rBookmarksEnd; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits