diff options
author | Tamás Zolnai <tamas.zolnai@collabora.com> | 2017-11-08 19:25:30 +0100 |
---|---|---|
committer | Tamás Zolnai <tamas.zolnai@collabora.com> | 2017-11-09 17:22:31 +0100 |
commit | cbaa72d6e963847a4b98526430cd928bc7928fdd (patch) | |
tree | d62372734ce37855ea88bf285e3a77028fd2d5f7 | |
parent | fc59087c9d84b563733ac13c4eda7930e159c1e8 (diff) |
tdf#42346: DOC export of cross-references to objects
Same solution which was used for DOCX export:
98bc7215935f1eb2e0dc6f1db826d8e729430c13
Change-Id: I8af46db003a6192c6adaae1a35dff58744919eee
Reviewed-on: https://gerrit.libreoffice.org/44502
Reviewed-by: Tamás Zolnai <tamas.zolnai@collabora.com>
Tested-by: Tamás Zolnai <tamas.zolnai@collabora.com>
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport10.cxx | 4 | ||||
-rwxr-xr-x | sw/qa/extras/ww8export/data/object_cross_reference.odt | bin | 0 -> 35667 bytes | |||
-rwxr-xr-x | sw/qa/extras/ww8export/data/table_cross_reference.odt | bin | 0 -> 10251 bytes | |||
-rwxr-xr-x | sw/qa/extras/ww8export/data/table_cross_reference_custom_format.odt | bin | 0 -> 12087 bytes | |||
-rw-r--r-- | sw/qa/extras/ww8export/ww8export2.cxx | 387 | ||||
-rw-r--r-- | sw/source/filter/ww8/attributeoutputbase.hxx | 10 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 209 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.hxx | 18 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.cxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfattributeoutput.cxx | 4 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfattributeoutput.hxx | 6 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtw8nds.cxx | 31 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtww8.cxx | 6 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtww8.hxx | 1 | ||||
-rw-r--r-- | sw/source/filter/ww8/ww8atr.cxx | 203 | ||||
-rw-r--r-- | sw/source/filter/ww8/ww8attributeoutput.hxx | 12 |
16 files changed, 664 insertions, 229 deletions
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx index 913b09b6257f..7c8d3be8acda 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx @@ -1295,6 +1295,8 @@ DECLARE_OOXMLEXPORT_TEST( testTableCrossReference, "table_cross_reference.odt" ) } ++nIndex; } + + CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), nIndex); } DECLARE_OOXMLEXPORT_TEST( testTableCrossReferenceCustomFormat, "table_cross_reference_custom_format.odt" ) @@ -1708,6 +1710,8 @@ DECLARE_OOXMLEXPORT_TEST( testObjectCrossReference, "object_cross_reference.odt" } ++nIndex; } + + CPPUNIT_ASSERT_EQUAL(sal_uInt16(21), nIndex); } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/extras/ww8export/data/object_cross_reference.odt b/sw/qa/extras/ww8export/data/object_cross_reference.odt Binary files differnew file mode 100755 index 000000000000..9eaca352b68c --- /dev/null +++ b/sw/qa/extras/ww8export/data/object_cross_reference.odt diff --git a/sw/qa/extras/ww8export/data/table_cross_reference.odt b/sw/qa/extras/ww8export/data/table_cross_reference.odt Binary files differnew file mode 100755 index 000000000000..95f33139c522 --- /dev/null +++ b/sw/qa/extras/ww8export/data/table_cross_reference.odt 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 Binary files differnew file mode 100755 index 000000000000..1c41e364c6e5 --- /dev/null +++ b/sw/qa/extras/ww8export/data/table_cross_reference_custom_format.odt diff --git a/sw/qa/extras/ww8export/ww8export2.cxx b/sw/qa/extras/ww8export/ww8export2.cxx index 1c180173f11b..0a3179d853c3 100644 --- a/sw/qa/extras/ww8export/ww8export2.cxx +++ b/sw/qa/extras/ww8export/ww8export2.cxx @@ -323,6 +323,393 @@ DECLARE_WW8EXPORT_TEST( testActiveXCheckbox, "checkbox_control.odt" ) CPPUNIT_ASSERT_EQUAL(text::TextContentAnchorType_AS_CHARACTER,getProperty<text::TextContentAnchorType>(xPropertySet2,"AnchorType")); } +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 b2a7605a9330..347153d501d2 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 const & pTableTextNodeInfoInner ); ww8::WidthsPtr GetColumnWidths( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner ); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 1ab6cde62852..9ac659eba81f 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -125,8 +125,6 @@ #include <IDocumentSettingAccess.hxx> #include <IDocumentStylePoolAccess.hxx> #include <IDocumentRedlineAccess.hxx> -#include <IDocumentFieldsAccess.hxx> -#include <reffld.hxx> #include <osl/file.hxx> #include <vcl/embeddedfontshelper.hxx> @@ -596,10 +594,9 @@ void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pT if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() ) m_bParagraphOpened = false; - // Clear generated 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, @@ -1091,7 +1088,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. @@ -1110,7 +1107,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 @@ -1414,9 +1411,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); @@ -1426,9 +1423,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 @@ -2536,7 +2533,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 ); @@ -2552,7 +2549,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) @@ -2561,7 +2558,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 ) @@ -2588,6 +2585,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; @@ -7205,176 +7208,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() == SwFieldIds::SetExp && 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( SwFieldIds::GetRef == 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 separate 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 ) @@ -7480,12 +7313,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 76bfc1a98af0..8a4a565b0a9a 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(); @@ -685,6 +684,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; @@ -700,8 +701,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); @@ -733,7 +734,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( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const sal_Char* sAttrValue ); static void AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nArgs, ... ); @@ -791,9 +791,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 fddce24b159d..cc9b518ff1a5 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -1490,7 +1490,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 ad19d27d5d89..f0af3cbcb98b 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -380,7 +380,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); @@ -395,7 +395,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 0f25754927c4..9b73bf49f22d 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.hxx +++ b/sw/source/filter/ww8/rtfattributeoutput.hxx @@ -63,10 +63,10 @@ public: void EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted) override; /// Start of the text run. - void StartRun(const SwRedlineData* pRedlineData, bool bSingleEmptyRun = false) override; + 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. void StartRunProperties() override; @@ -218,7 +218,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 9925dfbe24d8..570057a34b3f 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -990,6 +990,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 ); @@ -2181,8 +2187,7 @@ void MSWordExportBase::OutputTextNode( 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() ); @@ -2217,20 +2222,20 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) // Is this the only run in this paragraph and it's empty? bool bSingleEmptyRun = nAktPos == 0 && nNextAttr == 0; - AttrOutput().StartRun( pRedlineData, bSingleEmptyRun ); + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); + + if( nNextAttr > nEnd ) + nNextAttr = nEnd; if( m_nTextTyp == TXT_FTN || m_nTextTyp == TXT_EDN ) { if( AttrOutput().FootnoteEndnoteRefTag() ) { - AttrOutput().EndRun( &rNode, nAktPos ); - AttrOutput().StartRun( pRedlineData, bSingleEmptyRun ); + AttrOutput().EndRun( &rNode, nAktPos, nNextAttr == nEnd ); + AttrOutput().StartRun( pRedlineData, nAktPos, bSingleEmptyRun ); } } - if( nNextAttr > nEnd ) - nNextAttr = nEnd; - /* 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. @@ -2497,9 +2502,9 @@ void MSWordExportBase::OutputTextNode( 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) @@ -2509,16 +2514,16 @@ void MSWordExportBase::OutputTextNode( 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 ); diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index dfa54a781ce5..97f4154a9544 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -1513,6 +1513,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 31ccbeba4554..7195677f4688 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -1040,6 +1040,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 9bc93b67fcfa..cb7f1e734a84 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -978,6 +978,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() @@ -986,7 +990,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) { @@ -1001,6 +1005,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() @@ -1008,6 +1019,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 ); @@ -1947,6 +1971,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() == SwFieldIds::SetExp && 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( SwFieldIds::GetRef == 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() ) @@ -2800,8 +2997,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 d814fff62805..cf47f534d9e6 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; @@ -432,6 +430,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; @@ -460,6 +460,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() |