diff options
author | Tamás Zolnai <tamas.zolnai@collabora.com> | 2017-11-04 16:26:23 +0100 |
---|---|---|
committer | Tamás Zolnai <tamas.zolnai@collabora.com> | 2017-11-04 19:11:00 +0100 |
commit | 98bc7215935f1eb2e0dc6f1db826d8e729430c13 (patch) | |
tree | 677dc15a3800537b25738b35f0dac82a54e5bc9e | |
parent | ffb5ad4681f7f68b3b50dc4d94ea7ea8127da5e0 (diff) |
tdf#42346: DOCX export of cross-references to objects
* Objects means tables, images, text frames and shapes
* Implementation
** MSO uses simple bookmark references as cross-references to objects
** So generate bookmarks in export time if a caption is referenced
** In some cases we also need to split some of the runs
** Implemented all types of cross-references, except the chapter reference
Change-Id: I3b17753123d94a04e4f28783ad5663831e7c6c84
Reviewed-on: https://gerrit.libreoffice.org/44294
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tamás Zolnai <tamas.zolnai@collabora.com>
-rw-r--r-- | sw/CppunitTest_sw_ooxmlexport10.mk | 97 | ||||
-rwxr-xr-x | sw/qa/extras/ooxmlexport/data/object_cross_reference.odt | bin | 0 -> 35685 bytes | |||
-rwxr-xr-x | sw/qa/extras/ooxmlexport/data/table_cross_reference.odt | bin | 0 -> 10243 bytes | |||
-rwxr-xr-x | sw/qa/extras/ooxmlexport/data/table_cross_reference_custom_format.odt | bin | 0 -> 12089 bytes | |||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport10.cxx | 578 | ||||
-rw-r--r-- | sw/source/filter/ww8/attributeoutputbase.hxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 216 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.hxx | 8 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.hxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfattributeoutput.hxx | 1 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfexport.hxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtw8nds.cxx | 23 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtww8.hxx | 7 | ||||
-rw-r--r-- | sw/source/filter/ww8/ww8atr.cxx | 64 | ||||
-rw-r--r-- | sw/source/filter/ww8/ww8attributeoutput.hxx | 2 |
15 files changed, 897 insertions, 105 deletions
diff --git a/sw/CppunitTest_sw_ooxmlexport10.mk b/sw/CppunitTest_sw_ooxmlexport10.mk index 0dd1596a615e..4976da4ac605 100644 --- a/sw/CppunitTest_sw_ooxmlexport10.mk +++ b/sw/CppunitTest_sw_ooxmlexport10.mk @@ -9,101 +9,6 @@ # #************************************************************************* -$(eval $(call gb_CppunitTest_CppunitTest,sw_ooxmlexport10)) - -$(eval $(call gb_CppunitTest_add_exception_objects,sw_ooxmlexport10, \ - sw/qa/extras/ooxmlexport/ooxmlexport10 \ -)) - -$(eval $(call gb_CppunitTest_use_libraries,sw_ooxmlexport10, \ - basegfx \ - comphelper \ - cppu \ - cppuhelper \ - sal \ - test \ - unotest \ - utl \ - sfx \ - sw \ - tl \ - vcl \ - svxcore \ -)) - -$(eval $(call gb_CppunitTest_use_externals,sw_ooxmlexport10,\ - boost_headers \ - libxml2 \ -)) - -$(eval $(call gb_CppunitTest_set_include,sw_ooxmlexport10,\ - -I$(SRCDIR)/sw/inc \ - -I$(SRCDIR)/sw/source/core/inc \ - -I$(SRCDIR)/sw/qa/extras/inc \ - $$(INCLUDE) \ -)) - -ifeq ($(OS),MACOSX) - -$(eval $(call gb_CppunitTest_add_cxxflags,sw_ooxmlexport10,\ - $(gb_OBJCXXFLAGS) \ -)) - -$(eval $(call gb_CppunitTest_use_system_darwin_frameworks,sw_ooxmlexport10,\ - AppKit \ -)) - -endif - -$(eval $(call gb_CppunitTest_use_sdk_api,sw_ooxmlexport10)) - -$(eval $(call gb_CppunitTest_use_ure,sw_ooxmlexport10)) -$(eval $(call gb_CppunitTest_use_vcl,sw_ooxmlexport10)) - -$(eval $(call gb_CppunitTest_use_components,sw_ooxmlexport10,\ - basic/util/sb \ - chart2/source/controller/chartcontroller \ - chart2/source/chartcore \ - canvas/source/factory/canvasfactory \ - comphelper/util/comphelp \ - configmgr/source/configmgr \ - drawinglayer/drawinglayer \ - embeddedobj/util/embobj \ - emfio/emfio \ - filter/source/config/cache/filterconfig1 \ - forms/util/frm \ - framework/util/fwk \ - i18npool/util/i18npool \ - linguistic/source/lng \ - oox/util/oox \ - package/source/xstor/xstor \ - package/util/package2 \ - sax/source/expatwrap/expwrap \ - sw/util/sw \ - sw/util/swd \ - sw/util/msword \ - sfx2/util/sfx \ - starmath/util/sm \ - svl/source/fsstor/fsstorage \ - svl/util/svl \ - svtools/util/svt \ - svx/util/svx \ - svx/util/svxcore \ - toolkit/util/tk \ - ucb/source/core/ucb1 \ - ucb/source/ucp/file/ucpfile1 \ - unotools/util/utl \ - unoxml/source/service/unoxml \ - unoxml/source/rdf/unordf \ - uui/util/uui \ - writerfilter/util/writerfilter \ - xmloff/util/xo \ -)) - -$(eval $(call gb_CppunitTest_use_configuration,sw_ooxmlexport10)) - -$(eval $(call gb_CppunitTest_use_uiconfigs,sw_ooxmlexport10,\ - modules/swriter \ -)) +$(eval $(call sw_ooxmlexport_test,10)) # vim: set noet sw=4 ts=4: diff --git a/sw/qa/extras/ooxmlexport/data/object_cross_reference.odt b/sw/qa/extras/ooxmlexport/data/object_cross_reference.odt Binary files differnew file mode 100755 index 000000000000..18b02a38c2a9 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/object_cross_reference.odt diff --git a/sw/qa/extras/ooxmlexport/data/table_cross_reference.odt b/sw/qa/extras/ooxmlexport/data/table_cross_reference.odt Binary files differnew file mode 100755 index 000000000000..bd9c84016157 --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/table_cross_reference.odt diff --git a/sw/qa/extras/ooxmlexport/data/table_cross_reference_custom_format.odt b/sw/qa/extras/ooxmlexport/data/table_cross_reference_custom_format.odt Binary files differnew file mode 100755 index 000000000000..cbf03d34ed9f --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/table_cross_reference_custom_format.odt diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx index 2c9812c121cf..a4265728e0ff 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx @@ -10,13 +10,6 @@ #include <memory> #include <config_test.h> -#ifdef MACOSX -#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 -#include <premac.h> -#include <AppKit/AppKit.h> -#include <postmac.h> -#endif - #include <swmodeltestbase.hxx> #include <com/sun/star/awt/XBitmap.hpp> @@ -41,6 +34,7 @@ #include <com/sun/star/text/XPageCursor.hpp> #include <com/sun/star/text/XTextColumns.hpp> #include <com/sun/star/text/XTextFrame.hpp> +#include <com/sun/star/text/XTextField.hpp> #include <com/sun/star/text/XTextFramesSupplier.hpp> #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp> #include <com/sun/star/text/XTextViewCursorSupplier.hpp> @@ -1146,6 +1140,576 @@ DECLARE_OOXMLEXPORT_TEST( testTdf107359, "tdf107359-char-pitch.docx" ) CPPUNIT_ASSERT_EQUAL( sal_Int32(convertTwipToMm100(24 * 20)), nBaseWidth ); } +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; + } + // Page style reference / exported as simple page reference + 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_full"), sValue); + xPropertySet->getPropertyValue("SequenceNumber") >>= nValue; + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nValue); + break; + } + // Reference to table number + 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("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 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 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 4: + { + 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 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; + } +} + +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()); + } + + // 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) + { + // Reference to image number + case 0: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Illustration0_number_only"), sValue); + break; + } + // Full reference to the circle shape + case 1: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Drawing 2: a circle"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Drawing1_full"), sValue); + break; + } + // Caption only reference to the second picture + case 2: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("an other image"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Illustration1_caption_only"), sValue); + break; + } + // Category and number reference to second text frame + case 3: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Text 2"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Text1_label_and_number"), sValue); + break; + } + // Full reference to rectangle shape + case 4: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Drawing 1: A rectangle"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Drawing0_full"), sValue); + break; + } + // Caption only reference to rectangle shape + case 5: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("A rectangle"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Drawing0_caption_only"), sValue); + break; + } + // Category and number reference to rectangle shape + case 6: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Drawing 1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Drawing0_label_and_number"), sValue); + break; + } + // Reference to rectangle shape's number + case 7: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Drawing0_number_only"), sValue); + break; + } + // Full reference to first text frame + case 8: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Text 1: A frame"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Text0_full"), sValue); + break; + } + // Caption only reference to first text frame + case 9: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("A frame"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Text0_caption_only"), sValue); + break; + } + // Category and number reference to first text frame + case 10: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Text 1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Text0_label_and_number"), sValue); + break; + } + // Number only reference to first text frame + case 11: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Text0_number_only"), sValue); + break; + } + // Full reference to first picture + case 12: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Illustration 1: A picture"), sValue.trim()); // failes on MAC without trim + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Illustration0_full"), sValue); + break; + } + // Reference to first picture' caption + case 13: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("A picture"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Illustration0_caption_only"), sValue); + break; + } + // Category and number reference to first picture + case 14: + { + CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.text.TextField.GetReference")); + OUString sValue; + xPropertySet->getPropertyValue("CurrentPresentation") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Illustration 1"), sValue); + xPropertySet->getPropertyValue("SourceName") >>= sValue; + CPPUNIT_ASSERT_EQUAL(OUString("Ref_Illustration0_label_and_number"), sValue); + break; + } + default: + break; + } + ++nIndex; + } +} + 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 5327be3a819a..b2a7605a9330 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -209,6 +209,8 @@ public: virtual void FieldVanish( const OUString& rText, ww::eField eType ) = 0; + virtual void GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter) = 0; + void StartTOX( const SwSection& rSect ); void EndTOX( const SwSection& rSect,bool bCareEnd=true ); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 57e182dc34ac..4bf1b1c3dc27 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -125,6 +125,8 @@ #include <IDocumentSettingAccess.hxx> #include <IDocumentStylePoolAccess.hxx> #include <IDocumentRedlineAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <reffld.hxx> #include <osl/file.hxx> #include <vcl/embeddedfontshelper.hxx> @@ -594,6 +596,10 @@ 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(); + } void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken, @@ -1278,6 +1284,8 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos) m_endPageRef = true; } + DoWriteBookmarkStartIfExist(nPos); + m_pSerializer->startElementNS( XML_w, XML_r, FSEND ); if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is()) { @@ -1377,6 +1385,8 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos) } m_nFieldsInHyperlink = 0; } + + DoWriteBookmarkEndIfExist(nPos); } void DocxAttributeOutput::DoWriteBookmarkTagStart(const OUString & bookmarkName) @@ -1404,6 +1414,34 @@ void DocxAttributeOutput::DoWriteBookmarkTagEnd(const OUString & bookmarkName) } } +void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nPos) +{ + auto aRange = m_aBookmarksWithPosStart.equal_range(nPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + DoWriteBookmarkTagStart(aIter->second); + m_rOpenedBookmarksIds[aIter->second] = m_nNextBookmarkId; + m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(aIter->second), RTL_TEXTENCODING_UTF8).getStr(); + m_nNextBookmarkId++; + } +} + +void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nPos) +{ + auto aRange = m_aBookmarksWithPosEnd.equal_range(nPos); + for( auto aIter = aRange.first; aIter != aRange.second; ++aIter) + { + // Get the id of the bookmark + auto pPos = m_rOpenedBookmarksIds.find(aIter->second); + if (pPos != m_rOpenedBookmarksIds.end()) + { + // Output the bookmark + DoWriteBookmarkTagEnd(aIter->second); + m_rOpenedBookmarksIds.erase(aIter->second); + } + } +} + /// Write the start bookmarks void DocxAttributeOutput::DoWriteBookmarksStart() { @@ -1662,7 +1700,7 @@ void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nP else { // Write the field start - if ( rInfos.pField && rInfos.pField->GetSubType() & FIXEDFLD ) + if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD ) { m_pSerializer->startElementNS( XML_w, XML_fldChar, FSNS( XML_w, XML_fldCharType ), "begin", @@ -7143,6 +7181,176 @@ 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 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 ) @@ -7248,6 +7456,12 @@ 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 8822d523dec3..eb9dcf4d6791 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -370,6 +370,7 @@ 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(); @@ -699,6 +700,8 @@ private: void DoWriteBookmarkTagEnd(const OUString & bookmarkName); void DoWriteBookmarksStart(); void DoWriteBookmarksEnd(); + void DoWriteBookmarkStartIfExist(sal_Int32 nPos); + void DoWriteBookmarkEndIfExist(sal_Int32 nPos); void DoWritePermissionTagStart(const OUString & permission); void DoWritePermissionTagEnd(const OUString & permission); @@ -730,6 +733,7 @@ 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 ); + 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, ... ); @@ -787,6 +791,10 @@ 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; + /// Permissions to output std::vector<OUString> m_rPermissionsStart; std::vector<OUString> m_rPermissionsEnd; diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index de017201ed78..df65f3d39e93 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -186,6 +186,8 @@ public: void WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp); + virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOCX; } + protected: /// Format-dependent part of the actual export. virtual void ExportDocument_Impl() override; diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx index 1520569a7dd3..0f25754927c4 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.hxx +++ b/sw/source/filter/ww8/rtfattributeoutput.hxx @@ -218,6 +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 {}; protected: /// Output frames - the implementation. diff --git a/sw/source/filter/ww8/rtfexport.hxx b/sw/source/filter/ww8/rtfexport.hxx index 0b58e27392ae..64cceef9b333 100644 --- a/sw/source/filter/ww8/rtfexport.hxx +++ b/sw/source/filter/ww8/rtfexport.hxx @@ -124,6 +124,8 @@ public: sal_uLong ReplaceCr(sal_uInt8 nChar) override; + ExportFormat GetExportFormat() const override { return ExportFormat::RTF; } + protected: /// Format-dependent part of the actual export. void ExportDocument_Impl() override; diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index 87c27db44c89..23208e90a8fc 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -1173,6 +1173,25 @@ void SwWW8AttrIter::OutSwFormatRefMark(const SwFormatRefMark& rAttr) &rAttr.GetRefName(), 0 )); } +void SwWW8AttrIter::SplitRun( sal_Int32 nSplitEndPos ) +{ + for(auto aIter = maCharRuns.begin(); aIter != maCharRuns.end(); ++aIter) + { + if(aIter->mnEndPos == nSplitEndPos) + return; + else if (aIter->mnEndPos > nSplitEndPos) + { + CharRunEntry aNewEntry = *aIter; + aIter->mnEndPos = nSplitEndPos; + maCharRuns.insert( ++aIter, aNewEntry); + maCharRunIter = maCharRuns.begin(); + IterToCurrent(); + nAktSwPos = SearchNext(1); + break; + } + } +} + void WW8AttributeOutput::FieldVanish( const OUString& rText, ww::eField /*eType*/ ) { ww::bytes aItems; @@ -2163,6 +2182,10 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) AppendBookmark( BookmarkToWord( sBkmkName ) ); } + // Call this before write out fields and runs + if(GetExportFormat() == ExportFormat::DOCX) + AttrOutput().GenerateBookmarksForSequenceField(rNode, aAttrIter); + const OUString& aStr( rNode.GetText() ); sal_Int32 nAktPos = 0; diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index c3f05caad3c9..c96dcd8299b0 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -783,6 +783,9 @@ public: /// Returns the index of a picture bullet, used in numberings. int GetGrfIndex(const SvxBrushItem& rBrush); + enum ExportFormat { DOC = 0, RTF = 1, DOCX = 2}; + virtual ExportFormat GetExportFormat() const = 0; + protected: /// Format-dependent part of the actual export. virtual void ExportDocument_Impl() = 0; @@ -1145,6 +1148,8 @@ public: const SwFrameFormat& rFormat, const SwFrameFormat& rLeftFormat, const SwFrameFormat& rFirstPageFormat, sal_uInt8 nBreakCode) override; + virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOC; } + protected: /// Output SwGrfNode virtual void OutputGrfNode( const SwGrfNode& ) override; @@ -1508,6 +1513,8 @@ public: bool IsWatermarkFrame(); bool IsAnchorLinkedToThisNode( sal_uLong nNodePos ); + + void SplitRun( sal_Int32 nSplitEndPos ); }; /// Class to collect and output the styles table. diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index dfa4c2d29aa2..ad4c300b0350 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -899,7 +899,12 @@ OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pNa } break; case REF_SEQUENCEFLD: - break; // ??? + { + assert(pName); + sRet += "Ref_"; + sRet += *pName; + break; + } case REF_BOOKMARK: if ( pName ) sRet = *pName; @@ -2779,6 +2784,63 @@ void AttributeOutputBase::TextField( const SwFormatField& rField ) break; } break; + case REF_SEQUENCEFLD: + { + // Have this only for DOCX format by now + if(!(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::DOCX)) + break; + + switch (pField->GetFormat()) + { + case REF_PAGE: + case REF_PAGE_PGDESC: + eField = ww::ePAGEREF; + break; + default: + eField = ww::eREF; + break; + } + // Generate a unique bookmark name + { + OUString sName(rRField.GetSetRefName()); + sName += OUString::number(rRField.GetSeqNo()); + switch (pField->GetFormat()) + { + case REF_PAGE: + case REF_PAGE_PGDESC: + case REF_CONTENT: + case REF_UPDOWN: + sName += "_full"; + break; + case REF_ONLYNUMBER: + sName += "_label_and_number"; + break; + case REF_ONLYCAPTION: + sName += "_caption_only"; + break; + case REF_ONLYSEQNO: + sName += "_number_only"; + break; + default: // Ingore other types of reference fields + eField = ww::eNONE; + break; + } + sStr = FieldString(eField) + MSWordExportBase::GetBookmarkName(nSubType, &sName, 0); + } + switch (pField->GetFormat()) + { + case REF_NUMBER: + sStr += " \\r"; + break; + case REF_NUMBER_NO_CONTEXT: + sStr += " \\n"; + break; + case REF_NUMBER_FULL_CONTEXT: + sStr += " \\w"; + break; + } + break; + } case REF_FOOTNOTE: case REF_ENDNOTE: switch (pField->GetFormat()) diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx index 9a4701d49199..d814fff62805 100644 --- a/sw/source/filter/ww8/ww8attributeoutput.hxx +++ b/sw/source/filter/ww8/ww8attributeoutput.hxx @@ -84,6 +84,8 @@ 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; |