summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorAttila Bakos (NISZ) <bakos.attilakaroly@nisz.hu>2021-06-14 15:24:06 +0200
committerLászló Németh <nemeth@numbertext.org>2021-07-01 16:06:34 +0200
commit68688b4ca2288ac815d1dff137177e632bbb5f9f (patch)
tree71a59066f7548d4c5442e72471cd9d7a91668edf /sw
parent89e19634a775d53ea855db8767113f3ab08a3479 (diff)
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 <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org> (cherry picked from commit 35732c84b05e4f6e50349796636beb01f2a09907) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/118179 Tested-by: Jenkins
Diffstat (limited to 'sw')
-rw-r--r--sw/qa/extras/layout/data/Tdf121509.odtbin0 -> 9984 bytes
-rw-r--r--sw/qa/extras/layout/layout2.cxx58
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx68
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.hxx9
4 files changed, 134 insertions, 1 deletions
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
--- /dev/null
+++ b/sw/qa/extras/layout/data/Tdf121509.odt
Binary files 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 7cbf2a8a0df0..02da5d32096f 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;