sw/qa/extras/ww8export/data/object_cross_reference.odt |binary sw/qa/extras/ww8export/data/table_cross_reference.odt |binary sw/qa/extras/ww8export/data/table_cross_reference_custom_format.odt |binary sw/qa/extras/ww8export/ww8export.cxx | 387 ++++++++++ sw/source/filter/ww8/attributeoutputbase.hxx | 10 sw/source/filter/ww8/docxattributeoutput.cxx | 209 ----- sw/source/filter/ww8/docxattributeoutput.hxx | 18 sw/source/filter/ww8/docxexport.cxx | 2 sw/source/filter/ww8/rtfattributeoutput.cxx | 4 sw/source/filter/ww8/rtfattributeoutput.hxx | 6 sw/source/filter/ww8/wrtw8nds.cxx | 27 sw/source/filter/ww8/wrtww8.cxx | 6 sw/source/filter/ww8/wrtww8.hxx | 1 sw/source/filter/ww8/ww8atr.cxx | 203 +++++ sw/source/filter/ww8/ww8attributeoutput.hxx | 12 15 files changed, 658 insertions(+), 227 deletions(-)
New commits: commit 9df0dcc797dfa201f7843bb32984c342d274c1af Author: Tamás Zolnai <tamas.zol...@collabora.com> Date: Thu Nov 9 17:55:14 2017 +0100 tdf#42346: DOC export of cross-references to objects Same solution which was used for DOCX export: 98bc7215935f1eb2e0dc6f1db826d8e729430c13 Reviewed-on: https://gerrit.libreoffice.org/44502 Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> Tested-by: Tamás Zolnai <tamas.zol...@collabora.com> (cherry picked from commit cbaa72d6e963847a4b98526430cd928bc7928fdd) Change-Id: I8af46db003a6192c6adaae1a35dff58744919eee Reviewed-on: https://gerrit.libreoffice.org/44560 Reviewed-by: Thorsten Behrens <thorsten.behr...@cib.de> Tested-by: Thorsten Behrens <thorsten.behr...@cib.de> diff --git a/sw/qa/extras/ww8export/data/object_cross_reference.odt b/sw/qa/extras/ww8export/data/object_cross_reference.odt new file mode 100755 index 000000000000..9eaca352b68c Binary files /dev/null and b/sw/qa/extras/ww8export/data/object_cross_reference.odt differ diff --git a/sw/qa/extras/ww8export/data/table_cross_reference.odt b/sw/qa/extras/ww8export/data/table_cross_reference.odt new file mode 100755 index 000000000000..95f33139c522 Binary files /dev/null and b/sw/qa/extras/ww8export/data/table_cross_reference.odt differ diff --git a/sw/qa/extras/ww8export/data/table_cross_reference_custom_format.odt b/sw/qa/extras/ww8export/data/table_cross_reference_custom_format.odt new file mode 100755 index 000000000000..1c41e364c6e5 Binary files /dev/null and b/sw/qa/extras/ww8export/data/table_cross_reference_custom_format.odt differ diff --git a/sw/qa/extras/ww8export/ww8export.cxx b/sw/qa/extras/ww8export/ww8export.cxx index f31bbda02a06..7985f479f0fa 100644 --- a/sw/qa/extras/ww8export/ww8export.cxx +++ b/sw/qa/extras/ww8export/ww8export.cxx @@ -726,6 +726,393 @@ DECLARE_WW8EXPORT_TEST(testTdf99474, "tdf99474.odt") CPPUNIT_ASSERT_EQUAL(COL_AUTO, charColor); } +DECLARE_OOXMLEXPORT_TEST( testTableCrossReference, "table_cross_reference.odt" ) +{ + // tdf#42346: Cross references to tables were not saved + // MSO uses simple bookmarks for referencing table caption, so we do the same by export + if (!mbExported) + return; + + // Check whether we have all the neccessary bookmarks exported and imported back + uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xBookmarksByIdx(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), xBookmarksByIdx->getCount()); + uno::Reference<container::XNameAccess> xBookmarksByName(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_number_only")); + + // Check bookmark text ranges + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table 1: Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table 1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1"), xRange->getString()); + } + + // Check reference fields + uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields()); + uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration()); + CPPUNIT_ASSERT(xFields->hasMoreElements()); + + sal_uInt16 nIndex = 0; + while (xFields->hasMoreElements()) + { + uno::Reference<lang::XServiceInfo> xServiceInfo(xFields->nextElement(), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPropertySet(xServiceInfo, uno::UNO_QUERY); + switch (nIndex) + { + // Full reference to table caption + case 0: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Table 1: Table caption"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_full"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Reference to table number + case 1: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_number_only"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Reference to caption only + case 2: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Table caption"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_caption_only"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Reference to category and number + case 3: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Table 1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_label_and_number"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Reference to page of the table + case 4: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_full"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Page style reference / exported as simple page reference + case 5: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_full"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Above / bellow reference + case 6: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + sal_Int16 nValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("above"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Table0_full"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + default: + break; + } + ++nIndex; + } + CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), nIndex); +} + +DECLARE_OOXMLEXPORT_TEST( testTableCrossReferenceCustomFormat, "table_cross_reference_custom_format.odt" ) +{ + // tdf#42346: Cross references to tables were not saved + // Check also captions with custom formatting + if (!mbExported) + return; + + // Check whether we have all the neccessary bookmarks exported and imported back + uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xBookmarksByIdx(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(16), xBookmarksByIdx->getCount()); + uno::Reference<container::XNameAccess> xBookmarksByName(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table0_number_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table1_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table1_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table1_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table1_number_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table2_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table2_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table2_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table2_number_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table3_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table3_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table3_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Table3_number_only")); + + // Check bookmark text ranges + // First table's caption + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1. Table: Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1. Table"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table0_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1"), xRange->getString()); + } + // Second table's caption + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table1_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("2. TableTable caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table1_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("2. Table"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table1_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table1_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("2"), xRange->getString()); + } + // Third table's caption + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table2_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("3) Table Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table2_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("3) Table"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table2_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table2_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("3"), xRange->getString()); + } + // Fourth table's caption + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table3_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table 4- Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table3_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table 4"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table3_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Table caption"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Table3_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("4"), xRange->getString()); + } +} + +DECLARE_OOXMLEXPORT_TEST( testObjectCrossReference, "object_cross_reference.odt" ) +{ + // tdf#42346: Cross references to objects were not saved + // MSO uses simple bookmarks for referencing table caption, so we do the same by export + if (!mbExported) + return; + + // Check whether we have all the neccessary bookmarks exported and imported back + uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xBookmarksByIdx(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(15), xBookmarksByIdx->getCount()); + uno::Reference<container::XNameAccess> xBookmarksByName(xBookmarksSupplier->getBookmarks(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Drawing0_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Drawing0_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Drawing0_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Drawing0_number_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Drawing1_full")); + + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Illustration0_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Illustration0_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Illustration0_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Illustration0_number_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Illustration1_caption_only")); + + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Text0_full")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Text0_label_and_number")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Text0_caption_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Text0_number_only")); + CPPUNIT_ASSERT(xBookmarksByName->hasByName("Ref_Text1_label_and_number")); + + // Check bookmark text ranges + // Cross references to shapes + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Drawing0_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Drawing 1: A rectangle"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Drawing0_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Drawing 1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Drawing0_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("A rectangle"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Drawing0_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Drawing1_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Drawing 2: a circle"), xRange->getString()); + } + + // Cross references to pictures + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Illustration0_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Illustration 1: A picture"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Illustration0_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Illustration 1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Illustration0_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("A picture"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Illustration0_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Illustration1_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("an other image"), xRange->getString()); + } + + // Cross references to text frames + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Text0_full"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Text 1: A frame"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Text0_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Text 1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Text0_caption_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("A frame"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Text0_number_only"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("1"), xRange->getString()); + } + { + uno::Reference<text::XTextContent> xContent(xBookmarksByName->getByName("Ref_Text1_label_and_number"), uno::UNO_QUERY); + uno::Reference<text::XTextRange> xRange(xContent->getAnchor(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Text 2"), xRange->getString()); + } +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx index dc32dd36d8fe..86b468198ed9 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -169,10 +169,10 @@ public: virtual void EmptyParagraph() = 0; /// Start of the text run. - virtual void StartRun( const SwRedlineData* pRedlineData, bool bSingleEmptyRun = false ) = 0; + virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) = 0; /// End of the text run. - virtual void EndRun( const SwTextNode* pNode, sal_Int32 nPos ) = 0; + virtual void EndRun( const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false ) = 0; /// Called before we start outputting the attributes. virtual void StartRunProperties() = 0; @@ -209,7 +209,8 @@ public: virtual void FieldVanish( const OUString& rText, ww::eField eType ) = 0; - virtual void GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter) = 0; + /// MSO uses bookmarks to reference sequence fields, so we need to generate these additional bookmarks during export + void GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter); void StartTOX( const SwSection& rSect ); @@ -625,6 +626,9 @@ protected: virtual bool AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ); + /// Insert a bookmark inside the currently processed parargaph. + virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) = 0; + ww8::GridColsPtr GetGridCols( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ); ww8::WidthsPtr GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner ); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index e8cdfc86182c..76de5cabb222 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -117,8 +117,6 @@ #include <IDocumentSettingAccess.hxx> #include <IDocumentStylePoolAccess.hxx> #include <IDocumentRedlineAccess.hxx> -#include <IDocumentFieldsAccess.hxx> -#include <reffld.hxx> #include <osl/file.hxx> #include <vcl/embeddedfontshelper.hxx> @@ -603,10 +601,9 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) m_bParagraphOpened = false; - // Clear gererated bookmarks - m_aBookmarksWithPosStart.clear(); - m_aBookmarksWithPosEnd.clear(); - + // Clear bookmarks of the current paragraph + m_aBookmarksOfParagraphStart.clear(); + m_aBookmarksOfParagraphEnd.clear(); } void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, @@ -1103,7 +1100,7 @@ bool DocxAttributeOutput::IsFlyProcessingPostponed() return m_bPostponedProcessingFly; } -void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bSingleEmptyRun*/ ) +void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ ) { // Don't start redline data here, possibly there is a hyperlink later, and // that has to be started first. @@ -1122,7 +1119,7 @@ void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bS m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text" } -void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos) +void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool /*bLastRun*/) { int nFieldsInPrevHyperlink = m_nFieldsInHyperlink; // Reset m_nFieldsInHyperlink if a new hyperlink is about to start @@ -1401,9 +1398,9 @@ void DocxAttributeOutput::DoWriteBookmarkTagEnd(const OUString & bookmarkName) } } -void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nPos) +void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nRunPos) { - auto aRange = m_aBookmarksWithPosStart.equal_range(nPos); + auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos); for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) { DoWriteBookmarkTagStart(aIter->second); @@ -1413,9 +1410,9 @@ void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nPos) } } -void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nPos) +void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nRunPos) { - auto aRange = m_aBookmarksWithPosEnd.equal_range(nPos); + auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos); for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) { // Get the id of the bookmark @@ -2524,7 +2521,7 @@ void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, co m_pSerializer->endElementNS( XML_w, XML_rubyPr ); m_pSerializer->startElementNS( XML_w, XML_rt, FSEND ); - StartRun( nullptr ); + StartRun( nullptr, nPos ); StartRunProperties( ); SwWW8AttrIter aAttrIt( m_rExport, rNode ); aAttrIt.OutAttr( nPos, true ); @@ -2540,7 +2537,7 @@ void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, co m_pSerializer->endElementNS( XML_w, XML_rt ); m_pSerializer->startElementNS( XML_w, XML_rubyBase, FSEND ); - StartRun( nullptr ); + StartRun( nullptr, nPos ); } void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos) @@ -2549,7 +2546,7 @@ void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos) EndRun( &rNode, nPos ); m_pSerializer->endElementNS( XML_w, XML_rubyBase ); m_pSerializer->endElementNS( XML_w, XML_ruby ); - StartRun(nullptr); // open Run again so OutputTextNode loop can close it + StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it } bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) @@ -2576,6 +2573,12 @@ bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTar return bBookMarkOnly; } +void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) +{ + m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName)); + m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName)); +} + bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget ) { OUString sMark; @@ -6988,176 +6991,6 @@ bool DocxAttributeOutput::PlaceholderField( const SwField* pField ) return false; // do not expand } -void DocxAttributeOutput::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter) -{ - if (const SwpHints* pTextAttrs = rNode.GetpSwpHints()) - { - for( size_t i = 0; i < pTextAttrs->Count(); ++i ) - { - const SwTextAttr* pHt = pTextAttrs->Get(i); - if (pHt->GetAttr().Which() == RES_TXTATR_FIELD) - { - const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr()); - const SwField* pField = rField.GetField(); - // Need to have bookmarks only for sequence fields - if (pField && pField->GetTyp()->Which() == RES_SETEXPFLD && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ) - { - const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber(); - const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName(); - const SwFieldTypes* pFieldTypes = m_rExport.m_pDoc->getIDocumentFieldsAccess().GetFieldTypes(); - bool bHaveFullBkm = false; - bool bHaveLabelAndNumberBkm = false; - bool bHaveCaptionOnlyBkm = false; - bool bHaveNumberOnlyBkm = false; - bool bRunSplittedAtSep = false; - for( auto pFieldType : *pFieldTypes ) - { - if( RES_GETREFFLD == pFieldType->Which() ) - { - SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType ); - for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() ) - { - SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField()); - // If we have a reference to the current sequence field - if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName) - { - // Need to create a seperate run for separator character - SwWW8AttrIter aLocalAttrIter( m_rExport, rNode ); - const OUString aText = rNode.GetText(); - const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName()); - const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart()); - bool bCategoryFirst = nCategoryStart < pHt->GetStart(); - sal_Int32 nSeparatorPos = 0; - if (bCategoryFirst) - { - nSeparatorPos = aLocalAttrIter.WhereNext(); - while (nSeparatorPos <= nPosBeforeSeparator) - { - aLocalAttrIter.NextPos(); - nSeparatorPos = aLocalAttrIter.WhereNext(); - } - } - else - { - nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength(); - } - sal_Int32 nRefTextPos = 0; - if(nSeparatorPos < aText.getLength()) - { - nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), *m_rExport.m_pDoc, nSeparatorPos); - if(nRefTextPos != nSeparatorPos) - { - if(!bRunSplittedAtSep) - { - if(!bCategoryFirst) - rAttrIter.SplitRun(nSeparatorPos); - rAttrIter.SplitRun(nRefTextPos); - bRunSplittedAtSep = true; - } - if(!bCategoryFirst) - aLocalAttrIter.SplitRun(nSeparatorPos); - aLocalAttrIter.SplitRun(nRefTextPos); - } - else if (bCategoryFirst) - { - if(!bRunSplittedAtSep) - { - rAttrIter.SplitRun(nSeparatorPos); - bRunSplittedAtSep = true; - } - aLocalAttrIter.SplitRun(nSeparatorPos); - } - } - // Generate bookmarks on the right position - OUString sName("Ref_" + pRefField->GetSetRefName()); - sName += OUString::number(pRefField->GetSeqNo()); - switch (pRefField->GetFormat()) - { - case REF_PAGE: - case REF_PAGE_PGDESC: - case REF_CONTENT: - case REF_UPDOWN: - sName += "_full"; - if(!bHaveFullBkm) - { - sal_Int32 nLastAttrStart = 0; - sal_Int32 nActAttr = aLocalAttrIter.WhereNext(); - while (nActAttr < rNode.GetText().getLength()) - { - nLastAttrStart = nActAttr; - aLocalAttrIter.NextPos(); - nActAttr = aLocalAttrIter.WhereNext(); - } - WriteBookmarks_Impl( sName, std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart ); - bHaveFullBkm = true; - } - break; - case REF_ONLYNUMBER: - { - sName += "_label_and_number"; - if(!bHaveLabelAndNumberBkm) - { - if(bCategoryFirst) - WriteBookmarks_Impl( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) ); - else - { - // Find the last run which contains category text - SwWW8AttrIter aLocalAttrIter2( m_rExport, rNode ); - sal_Int32 nCatLastRun = 0; - sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext(); - while (nNextAttr < nSeparatorPos) - { - nCatLastRun = nNextAttr; - aLocalAttrIter2.NextPos(); - nNextAttr = aLocalAttrIter2.WhereNext(); - } - WriteBookmarks_Impl( sName, pHt->GetStart(), nCatLastRun ); - } - bHaveLabelAndNumberBkm = true; - } - break; - } - case REF_ONLYCAPTION: - { - sName += "_caption_only"; - if(!bHaveCaptionOnlyBkm) - { - // Find last run - sal_Int32 nLastAttrStart = 0; - sal_Int32 nActAttr = aLocalAttrIter.WhereNext(); - while (nActAttr < rNode.GetText().getLength()) - { - nLastAttrStart = nActAttr; - aLocalAttrIter.NextPos(); - nActAttr = aLocalAttrIter.WhereNext(); - } - WriteBookmarks_Impl( sName, nRefTextPos, nLastAttrStart ); - bHaveCaptionOnlyBkm = true; - } - break; - } - case REF_ONLYSEQNO: - { - sName += "_number_only"; - if(!bHaveNumberOnlyBkm) - { - WriteBookmarks_Impl( sName, pHt->GetStart(), pHt->GetStart() ); - bHaveNumberOnlyBkm = true; - } - break; - } - } - } - } - } - } - return; - } - } - } - } -} - void DocxAttributeOutput::WritePendingPlaceholder() { if( pendingPlaceholder == nullptr ) @@ -7264,12 +7097,6 @@ void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, rEnds.clear(); } -void DocxAttributeOutput::WriteBookmarks_Impl( const OUString& rName, sal_Int32 nWithStartPos, sal_Int32 nWithEndPos ) -{ - m_aBookmarksWithPosStart.insert(std::pair<sal_Int32, OUString>(nWithStartPos, rName)); - m_aBookmarksWithPosEnd.insert(std::pair<sal_Int32, OUString>(nWithEndPos, rName)); -} - void DocxAttributeOutput::WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ) { diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index 64fa4238e617..c76b8a671f2d 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -163,10 +163,10 @@ public: virtual void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) override; /// Start of the text run. - virtual void StartRun( const SwRedlineData* pRedlineData, bool bSingleEmptyRun = false ) override; + virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) override; /// End of the text run. - virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos) override; + virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false) override; /// Called before we start outputting the attributes. virtual void StartRunProperties() override; @@ -370,7 +370,6 @@ public: void WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark ); void WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); - void WriteBookmarks_Impl( const OUString& rName, sal_Int32 nWithStartPos, sal_Int32 nWithEndPos ); void WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); void PushRelIdCache(); void PopRelIdCache(); @@ -682,6 +681,8 @@ protected: virtual bool AnalyzeURL( const OUString& rURL, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) override; + virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) override; + /// Reference to the export, where to get the data from DocxExport &m_rExport; @@ -697,8 +698,8 @@ private: void DoWriteBookmarkTagEnd(const OUString & bookmarkName); void DoWriteBookmarksStart(); void DoWriteBookmarksEnd(); - void DoWriteBookmarkStartIfExist(sal_Int32 nPos); - void DoWriteBookmarkEndIfExist(sal_Int32 nPos); + void DoWriteBookmarkStartIfExist(sal_Int32 nRunPos); + void DoWriteBookmarkEndIfExist(sal_Int32 nRunPos); void DoWritePermissionTagStart(const OUString & permission); void DoWritePermissionTagEnd(const OUString & permission); @@ -730,7 +731,6 @@ private: void CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun ); void EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos ); void DoWriteFieldRunProperties( const SwTextNode* pNode, sal_Int32 nPos, bool bWriteCombChars = false ); - virtual void GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter) override; static void AddToAttrList( css::uno::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const sal_Char* sAttrValue ); static void AddToAttrList( css::uno::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nArgs, ... ); @@ -788,9 +788,9 @@ private: std::vector<OUString> m_rBookmarksStart; std::vector<OUString> m_rBookmarksEnd; - /// Bookmarks with position to output - std::multimap<sal_Int32, OUString> m_aBookmarksWithPosStart; - std::multimap<sal_Int32, OUString> m_aBookmarksWithPosEnd; + /// Bookmarks of the current paragraph + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphStart; + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphEnd; /// Permissions to output std::vector<OUString> m_rPermissionsStart; diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 3f62a6c56310..7e32fe727519 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -1442,7 +1442,7 @@ void DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTy sal_Int32 nAktPos = 0; const sal_Int32 nEnd = aStr.getLength(); do { - AttrOutput().StartRun( nullptr ); + AttrOutput().StartRun( nullptr, 0 ); const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd); rtl_TextEncoding eNextChrSet = aAttrIter.GetNextCharSet(); diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx index ee3ff68665e7..880bf36f12f7 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -376,7 +376,7 @@ void RtfAttributeOutput::EndParagraphProperties(const SfxItemSet& /*rParagraphMa m_rExport.Strm().WriteCharPtr(m_aStyles.makeStringAndClear().getStr()); } -void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, bool bSingleEmptyRun) +void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool bSingleEmptyRun) { SAL_INFO("sw.rtf", OSL_THIS_FUNC << ", bSingleEmptyRun: " << bSingleEmptyRun); @@ -391,7 +391,7 @@ void RtfAttributeOutput::StartRun(const SwRedlineData* pRedlineData, bool bSingl OSL_ENSURE(m_aRunText.getLength() == 0, "m_aRunText is not empty"); } -void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/) +void RtfAttributeOutput::EndRun(const SwTextNode* /*pNode*/, sal_Int32 /*nPos*/, bool /*bLastRun*/) { m_aRun->append(SAL_NEWLINE_STRING); m_aRun.appendAndClear(m_aRunText); diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx index 82e2817e1a03..9dfebef27a86 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.hxx +++ b/sw/source/filter/ww8/rtfattributeoutput.hxx @@ -62,10 +62,10 @@ public: virtual void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) override; /// Start of the text run. - virtual void StartRun(const SwRedlineData* pRedlineData, bool bSingleEmptyRun = false) override; + virtual void StartRun(const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false) override; /// End of the text run. - void EndRun(const SwTextNode* pNode, sal_Int32 nPos) override; + void EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false) override; /// Called before we start outputting the attributes. virtual void StartRunProperties() override; @@ -217,7 +217,7 @@ public: void WriteBookmarks_Impl(std::vector< OUString >& rStarts, std::vector< OUString >& rEnds); void WriteAnnotationMarks_Impl(std::vector< OUString >& rStarts, std::vector< OUString >& rEnds); void WriteHeaderFooter_Impl(const SwFrameFormat& rFormat, bool bHeader, const sal_Char* pStr, bool bTitlepg); - void GenerateBookmarksForSequenceField(const SwTextNode& /*rNode*/, SwWW8AttrIter& /*rAttrIter*/) override {}; + void WriteBookmarkInActParagraph( const OUString& /*rName*/, sal_Int32 /*nFirstRunPos*/, sal_Int32 /*nLastRunPos*/ ) override {}; protected: /// Output frames - the implementation. diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 33b67ea5e627..cbc0a777154f 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -975,6 +975,12 @@ bool WW8AttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarg return bBookMarkOnly; } +void WW8AttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) +{ + m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName)); + m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName)); +} + bool WW8AttributeOutput::StartURL( const OUString &rUrl, const OUString &rTarget ) { INetURLObject aURL( rUrl ); @@ -2166,8 +2172,7 @@ void MSWordExportBase::OutputTextNode( const SwTextNode& rNode ) } // Call this before write out fields and runs - if(GetExportFormat() == ExportFormat::DOCX) - AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter); + AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter); const OUString& aStr( rNode.GetText() ); @@ -2192,14 +2197,14 @@ void MSWordExportBase::OutputTextNode( const SwTextNode& rNode ) sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nAktPos ); // Is this the only run in this paragraph and it's empty? bool bSingleEmptyRun = nAktPos == 0 && nNextAttr == 0; - AttrOutput().StartRun( pRedlineData, bSingleEmptyRun ); - - if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN ) - AttrOutput().FootnoteEndnoteRefTag(); + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); if( nNextAttr > nEnd ) nNextAttr = nEnd; + if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN ) + AttrOutput().FootnoteEndnoteRefTag(); + /* 1) If there is a text node and an overlapping anchor, then write them in two different runs and not as part of the same run. @@ -2461,9 +2466,9 @@ void MSWordExportBase::OutputTextNode( const SwTextNode& rNode ) if( bPostponeWritingText && FLY_PROCESSED == nStateOfFlyFrame ) { - AttrOutput().EndRun(&rNode, nAktPos); + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); //write the postponed text run - AttrOutput().StartRun( pRedlineData, bSingleEmptyRun ); + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); AttrOutput().SetAnchorIsLinkedToNode( false ); AttrOutput().ResetFlyProcessingFlag(); if (0 != nEnd) @@ -2473,16 +2478,16 @@ void MSWordExportBase::OutputTextNode( const SwTextNode& rNode ) AttrOutput().EndRunProperties( pRedlineData ); } AttrOutput().RunText( aSavedSnippet, eChrSet ); - AttrOutput().EndRun(&rNode, nAktPos); + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); } else if( bPostponeWritingText && !aSavedSnippet.isEmpty() ) { //write the postponed text run AttrOutput().RunText( aSavedSnippet, eChrSet ); - AttrOutput().EndRun(&rNode, nAktPos); + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); } else - AttrOutput().EndRun(&rNode, nAktPos); + AttrOutput().EndRun(&rNode, nAktPos, nNextAttr == nEnd); nAktPos = nNextAttr; UpdatePosition( &aAttrIter, nAktPos, nEnd ); diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index 85bf4ea3a312..8e8c02496560 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -1514,6 +1514,12 @@ void WW8Export::AppendBookmark( const OUString& rName ) m_pBkmks->Append( nSttCP, rName ); } +void WW8Export::AppendBookmarkEndWithCorrection( const OUString& rName ) +{ + sal_uLong nEndCP = Fc2Cp( Strm().Tell() ); + m_pBkmks->Append( nEndCP - 1, rName ); +} + boost::optional<SvxBrushItem> MSWordExportBase::getBackground() { boost::optional<SvxBrushItem> oRet; diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index ff25dedd1c3e..b5633010789a 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -1036,6 +1036,7 @@ public: virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nAktPos, sal_Int32 nLen ) override; virtual void AppendBookmark( const OUString& rName ) override; + void AppendBookmarkEndWithCorrection( const OUString& rName ); virtual void AppendAnnotationMarks( const SwTextNode& rNd, sal_Int32 nAktPos, sal_Int32 nLen ) override; diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 3250808165a8..33b1bc42ee66 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -981,6 +981,10 @@ void WW8AttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTe m_rWW8Export.pO->clear(); } } + + // Clear bookmarks of the current paragraph + m_aBookmarksOfParagraphStart.clear(); + m_aBookmarksOfParagraphEnd.clear(); } void WW8AttributeOutput::StartRunProperties() @@ -989,7 +993,7 @@ void WW8AttributeOutput::StartRunProperties() m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0; } -void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bSingleEmptyRun*/ ) +void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ ) { if (pRedlineData) { @@ -1004,6 +1008,13 @@ void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bSi } } } + + /// Insert bookmarks started at this run + auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + GetExport().AppendBookmark(BookmarkToWord(aIter->second)); + } } void WW8AttributeOutput::OnTOXEnding() @@ -1011,6 +1022,19 @@ void WW8AttributeOutput::OnTOXEnding() mbOnTOXEnding = true; } +void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, bool bLastRun ) +{ + /// Insert bookmarks ended after this run + auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + if(bLastRun) + GetExport().AppendBookmarkEndWithCorrection(BookmarkToWord(aIter->second)); + else + GetExport().AppendBookmark(BookmarkToWord(aIter->second)); + } +} + void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData ) { Redline( pRedlineData ); @@ -1950,6 +1974,179 @@ static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl) return bRes; } +void AttributeOutputBase::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter) +{ + if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF + return; + + if (const SwpHints* pTextAttrs = rNode.GetpSwpHints()) + { + for( size_t i = 0; i < pTextAttrs->Count(); ++i ) + { + const SwTextAttr* pHt = pTextAttrs->Get(i); + if (pHt->GetAttr().Which() == RES_TXTATR_FIELD) + { + const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr()); + const SwField* pField = rField.GetField(); + // Need to have bookmarks only for sequence fields + if (pField && pField->GetTyp()->Which() == RES_SETEXPFLD && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ) + { + const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber(); + const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName(); + const SwFieldTypes* pFieldTypes = GetExport().m_pDoc->getIDocumentFieldsAccess().GetFieldTypes(); + bool bHaveFullBkm = false; + bool bHaveLabelAndNumberBkm = false; + bool bHaveCaptionOnlyBkm = false; + bool bHaveNumberOnlyBkm = false; + bool bRunSplittedAtSep = false; + for( auto pFieldType : *pFieldTypes ) + { + if( RES_GETREFFLD == pFieldType->Which() ) + { + SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType ); + for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() ) + { + SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField()); + // If we have a reference to the current sequence field + if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName) + { + // Need to create a seperate run for separator character + SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs + const OUString aText = rNode.GetText(); + const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName()); + const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart()); + bool bCategoryFirst = nCategoryStart < pHt->GetStart(); + sal_Int32 nSeparatorPos = 0; + if (bCategoryFirst) + { + nSeparatorPos = aLocalAttrIter.WhereNext(); + while (nSeparatorPos <= nPosBeforeSeparator) + { + aLocalAttrIter.NextPos(); + nSeparatorPos = aLocalAttrIter.WhereNext(); + } + } + else + { + nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength(); + } + sal_Int32 nRefTextPos = 0; + if(nSeparatorPos < aText.getLength()) + { + nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), *GetExport().m_pDoc, nSeparatorPos); + if(nRefTextPos != nSeparatorPos) + { + if(!bRunSplittedAtSep) + { + if(!bCategoryFirst) + rAttrIter.SplitRun(nSeparatorPos); + rAttrIter.SplitRun(nRefTextPos); + bRunSplittedAtSep = true; + } + if(!bCategoryFirst) + aLocalAttrIter.SplitRun(nSeparatorPos); + aLocalAttrIter.SplitRun(nRefTextPos); + } + else if (bCategoryFirst) + { + if(!bRunSplittedAtSep) + { + rAttrIter.SplitRun(nSeparatorPos); + bRunSplittedAtSep = true; + } + aLocalAttrIter.SplitRun(nSeparatorPos); + } + } + // Generate bookmarks on the right position + OUString sName("Ref_" + pRefField->GetSetRefName()); + sName += OUString::number(pRefField->GetSeqNo()); + switch (pRefField->GetFormat()) + { + case REF_PAGE: + case REF_PAGE_PGDESC: + case REF_CONTENT: + case REF_UPDOWN: + sName += "_full"; + if(!bHaveFullBkm) + { + sal_Int32 nLastAttrStart = 0; + sal_Int32 nActAttr = aLocalAttrIter.WhereNext(); + while (nActAttr < rNode.GetText().getLength()) + { + nLastAttrStart = nActAttr; + aLocalAttrIter.NextPos(); + nActAttr = aLocalAttrIter.WhereNext(); + } + WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart ); + bHaveFullBkm = true; + } + break; + case REF_ONLYNUMBER: + { + sName += "_label_and_number"; + if(!bHaveLabelAndNumberBkm) + { + if(bCategoryFirst) + WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) ); + else + { + // Find the last run which contains category text + SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode ); + sal_Int32 nCatLastRun = 0; + sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext(); + while (nNextAttr < nSeparatorPos) + { + nCatLastRun = nNextAttr; + aLocalAttrIter2.NextPos(); + nNextAttr = aLocalAttrIter2.WhereNext(); + } + WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun ); + } + bHaveLabelAndNumberBkm = true; + } + break; + } + case REF_ONLYCAPTION: + { + sName += "_caption_only"; + if(!bHaveCaptionOnlyBkm) + { + // Find last run + sal_Int32 nLastAttrStart = 0; + sal_Int32 nActAttr = aLocalAttrIter.WhereNext(); + while (nActAttr < rNode.GetText().getLength()) + { + nLastAttrStart = nActAttr; + aLocalAttrIter.NextPos(); + nActAttr = aLocalAttrIter.WhereNext(); + } + WriteBookmarkInActParagraph( sName, nRefTextPos, nLastAttrStart ); + bHaveCaptionOnlyBkm = true; + } + break; + } + case REF_ONLYSEQNO: + { + sName += "_number_only"; + if(!bHaveNumberOnlyBkm) + { + WriteBookmarkInActParagraph( sName, pHt->GetStart(), pHt->GetStart() ); + bHaveNumberOnlyBkm = true; + } + break; + } + } + } + } + } + } + return; + } + } + } + } +} + void AttributeOutputBase::StartTOX( const SwSection& rSect ) { if ( const SwTOXBase* pTOX = rSect.GetTOXBase() ) @@ -2805,8 +3002,8 @@ void AttributeOutputBase::TextField( const SwFormatField& rField ) break; case REF_SEQUENCEFLD: { - // Have this only for DOCX format by now - if(!(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::DOCX)) + // Not implemented for RTF + if(!(GetExport().GetExportFormat() != MSWordExportBase::ExportFormat::RTF)) break; switch (pField->GetFormat()) diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx index e2a978fcb4ac..dc21f2a2fb28 100644 --- a/sw/source/filter/ww8/ww8attributeoutput.hxx +++ b/sw/source/filter/ww8/ww8attributeoutput.hxx @@ -49,14 +49,14 @@ public: /// Start of the text run. /// - virtual void StartRun( const SwRedlineData* pRedlineData, bool bSingleEmptyRun = false ) override; + virtual void StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool bSingleEmptyRun = false ) override; virtual void OnTOXEnding() override; /// End of the text run. /// /// No-op for binary filters. - virtual void EndRun(const SwTextNode* , sal_Int32 ) override {} + virtual void EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool bLastRun = false) override; /// Before we start outputting the attributes. virtual void StartRunProperties() override; @@ -84,8 +84,6 @@ public: virtual void FieldVanish( const OUString& rText, ww::eField eType ) override; - virtual void GenerateBookmarksForSequenceField(const SwTextNode& /*rNode*/, SwWW8AttrIter& /*rAttrIter*/) override {}; - /// Output redlining. virtual void Redline( const SwRedlineData* pRedline ) override; @@ -429,6 +427,8 @@ protected: virtual bool AnalyzeURL( const OUString& rURL, const OUString& rTarget, OUString* pLinkURL, OUString* pMark ) override; + virtual void WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos ) override; + /// Reference to the export, where to get the data from WW8Export &m_rWW8Export; @@ -457,6 +457,10 @@ protected: bool mbOnTOXEnding; + /// Bookmarks of the current paragraph + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphStart; + std::multimap<sal_Int32, OUString> m_aBookmarksOfParagraphEnd; + public: explicit WW8AttributeOutput( WW8Export &rWW8Export ) : AttributeOutputBase()
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits