summaryrefslogtreecommitdiffstats
path: root/sw/qa/extras/globalfilter/globalfilter.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sw/qa/extras/globalfilter/globalfilter.cxx1909
1 files changed, 1909 insertions, 0 deletions
diff --git a/sw/qa/extras/globalfilter/globalfilter.cxx b/sw/qa/extras/globalfilter/globalfilter.cxx
new file mode 100644
index 000000000..fefc1fbda
--- /dev/null
+++ b/sw/qa/extras/globalfilter/globalfilter.cxx
@@ -0,0 +1,1909 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <swmodeltestbase.hxx>
+
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicType.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XDocumentIndex.hpp>
+#include <o3tl/safeint.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <editsh.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <doc.hxx>
+#include <ndgrf.hxx>
+#include <ndtxt.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <xmloff/odffields.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IMark.hxx>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <unotools/mediadescriptor.hxx>
+
+class Test : public SwModelTestBase
+{
+public:
+ Test() : SwModelTestBase() {}
+
+ void testEmbeddedGraphicRoundtrip();
+ void testLinkedGraphicRT();
+ void testImageWithSpecialID();
+ void testGraphicShape();
+ void testMultipleIdenticalGraphics();
+ void testCharHighlight();
+ void testCharHighlightODF();
+ void testCharHighlightBody();
+ void testCharStyleHighlight();
+ void testMSCharBackgroundEditing();
+ void testCharBackgroundToHighlighting();
+#if !defined(_WIN32)
+ void testSkipImages();
+#endif
+ void testNestedFieldmark();
+ void verifyText13(char const*);
+ void testODF13();
+ void testRedlineFlags();
+ void testBulletAsImage();
+ void testTextFormField();
+ void testCheckBoxFormField();
+ void testDropDownFormField();
+ void testDateFormField();
+ void testDateFormFieldCharacterFormatting();
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testEmbeddedGraphicRoundtrip);
+ CPPUNIT_TEST(testLinkedGraphicRT);
+ CPPUNIT_TEST(testImageWithSpecialID);
+ CPPUNIT_TEST(testGraphicShape);
+ CPPUNIT_TEST(testMultipleIdenticalGraphics);
+ CPPUNIT_TEST(testCharHighlight);
+ CPPUNIT_TEST(testCharHighlightODF);
+ CPPUNIT_TEST(testMSCharBackgroundEditing);
+ CPPUNIT_TEST(testCharBackgroundToHighlighting);
+#if !defined(_WIN32)
+ CPPUNIT_TEST(testSkipImages);
+#endif
+ CPPUNIT_TEST(testNestedFieldmark);
+ CPPUNIT_TEST(testODF13);
+ CPPUNIT_TEST(testRedlineFlags);
+ CPPUNIT_TEST(testBulletAsImage);
+ CPPUNIT_TEST(testTextFormField);
+ CPPUNIT_TEST(testCheckBoxFormField);
+ CPPUNIT_TEST(testDropDownFormField);
+ CPPUNIT_TEST(testDateFormField);
+ CPPUNIT_TEST(testDateFormFieldCharacterFormatting);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void Test::testEmbeddedGraphicRoundtrip()
+{
+ OUString aFilterNames[] = {
+ "writer8",
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ // Check whether the export code swaps in the image which was swapped out before by auto mechanism
+
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/document_with_two_images.odt"), "com.sun.star.text.TextDocument");
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check whether graphic exported well after it was swapped out
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), 2, getShapes());
+
+ // First image
+ uno::Reference<drawing::XShape> xImage(getShape(1), uno::UNO_QUERY);
+ uno::Reference< beans::XPropertySet > XPropSet( xImage, uno::UNO_QUERY_THROW );
+
+ // Check graphic, size
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xGraphic.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), graphic::GraphicType::PIXEL, xGraphic->getType());
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(610), xBitmap->getSize().Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(381), xBitmap->getSize().Height);
+ }
+
+ // Second Image
+ xImage.set(getShape(2), uno::UNO_QUERY);
+ XPropSet.set( xImage, uno::UNO_QUERY_THROW );
+
+ // Check graphic, size
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xGraphic.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), graphic::GraphicType::PIXEL, xGraphic->getType());
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(900), xBitmap->getSize().Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(600), xBitmap->getSize().Height);
+ }
+ }
+}
+
+void Test::testLinkedGraphicRT()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+// "Rich Text Format", Note: picture is there, but SwGrfNode is not found?
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/document_with_linked_graphic.odt"), "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pDoc);
+ SwNodes& aNodes = pDoc->GetNodes();
+
+ // Find the image
+ bool bImageFound = false;
+ Graphic aGraphic;
+ for (SwNodeOffset nIndex(0); nIndex < aNodes.Count(); ++nIndex)
+ {
+ if (aNodes[nIndex]->IsGrfNode())
+ {
+ SwGrfNode* pGrfNode = aNodes[nIndex]->GetGrfNode();
+ CPPUNIT_ASSERT(pGrfNode);
+
+ const GraphicObject& rGraphicObj = pGrfNode->GetGrfObj(true);
+ aGraphic = rGraphicObj.GetGraphic();
+ bImageFound = true;
+ }
+ }
+
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), bImageFound);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), GraphicType::Bitmap, aGraphic.GetType());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_uLong(864900), aGraphic.GetSizeBytes());
+
+ // Check if linked graphic is registered in LinkManager
+ sfx2::LinkManager& rLinkManager = pTextDoc->GetDocShell()->GetDoc()->GetEditShell()->GetLinkManager();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), size_t(1), rLinkManager.GetLinks().size());
+ const tools::SvRef<sfx2::SvBaseLink> & rLink = rLinkManager.GetLinks()[0];
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), rLink->GetLinkSourceName().indexOf("linked_graphic.jpg") >= 0);
+ }
+}
+
+void Test::testImageWithSpecialID()
+{
+ // Check how LO handles when the imported graphic's ID is different from that one
+ // which is generated by LO.
+
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/images_with_special_IDs.odt"), "com.sun.star.text.TextDocument");
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check whether graphic exported well
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), 2, getShapes());
+
+ uno::Reference<drawing::XShape> xImage = getShape(1);
+ uno::Reference< beans::XPropertySet > XPropSet( xImage, uno::UNO_QUERY_THROW );
+
+ // Check graphic, size
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xGraphic.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), graphic::GraphicType::PIXEL, xGraphic->getType());
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(610), xBitmap->getSize().Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(381), xBitmap->getSize().Height);
+ }
+
+ // Second Image
+ xImage.set(getShape(2), uno::UNO_QUERY);
+ XPropSet.set( xImage, uno::UNO_QUERY_THROW );
+
+ // Check graphic, size
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xGraphic.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), graphic::GraphicType::PIXEL, xGraphic->getType());
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(900), xBitmap->getSize().Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(600), xBitmap->getSize().Height);
+ }
+ }
+}
+
+/// Gives the first embedded or linked image in a document.
+static uno::Reference<drawing::XShape> lcl_getShape(const uno::Reference<lang::XComponent>& xComponent, bool bEmbedded)
+{
+ uno::Reference<drawing::XShape> xShape;
+
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ for (sal_Int32 i = 0; i < xDrawPage->getCount(); ++i)
+ {
+ uno::Reference<beans::XPropertySet> xShapeProperties(xDrawPage->getByIndex(i), uno::UNO_QUERY);
+ uno::Reference<graphic::XGraphic> xGraphic;
+ xShapeProperties->getPropertyValue("Graphic") >>= xGraphic;
+ if (xGraphic.is())
+ {
+ Graphic aGraphic(xGraphic);
+
+ if (bEmbedded == aGraphic.getOriginURL().isEmpty())
+ {
+ xShape.set(xShapeProperties, uno::UNO_QUERY);
+ return xShape;
+ }
+ }
+ }
+
+ return xShape;
+}
+
+void Test::testGraphicShape()
+{
+ // There are two kind of images in Writer: 1) Writer specific handled by SwGrfNode and
+ // 2) graphic shape handled by SdrGrafObj (e.g. after copy&paste from Impress).
+
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/graphic_shape.odt"), "com.sun.star.text.TextDocument");
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check whether graphic exported well
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), 2, getShapes());
+
+ uno::Reference<drawing::XShape> xImage = lcl_getShape(mxComponent, true);
+ CPPUNIT_ASSERT_MESSAGE("Couldn't load the shape/image", xImage.is());
+ uno::Reference< beans::XPropertySet > XPropSet( xImage, uno::UNO_QUERY );
+ // First image is embedded
+ // Check size
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xGraphic.is());
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(610), xBitmap->getSize().Width );
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(381), xBitmap->getSize().Height );
+ }
+
+ // MS filters make this kind of linked images broken !?
+ if (rFilterName != "writer8")
+ return;
+
+ // Second image is a linked one
+ xImage = lcl_getShape(mxComponent, false);
+ XPropSet.set(xImage, uno::UNO_QUERY);
+ const OString sFailedImageLoad = OString::Concat("Couldn't load the shape/image for ") + rFilterName.toUtf8();
+ CPPUNIT_ASSERT_MESSAGE(sFailedImageLoad.getStr(), xImage.is());
+
+ // Check size
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xGraphic.is());
+
+ Graphic aGraphic(xGraphic);
+ OUString sURL = aGraphic.getOriginURL();
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), sURL.endsWith("linked_graphic.jpg"));
+
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(620), xBitmap->getSize().Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(465), xBitmap->getSize().Height);
+ }
+ }
+}
+
+namespace
+{
+
+std::vector<uno::Reference<graphic::XGraphic>>
+ lcl_getGraphics(const uno::Reference<lang::XComponent>& xComponent)
+{
+ std::vector<uno::Reference<graphic::XGraphic>> aGraphics;
+
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xComponent, uno::UNO_QUERY);
+ uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
+ for (sal_Int32 i = 0; i < xDrawPage->getCount(); ++i)
+ {
+ uno::Reference<beans::XPropertySet> xShapeProperties(xDrawPage->getByIndex(i), uno::UNO_QUERY);
+ uno::Reference<graphic::XGraphic> xGraphic;
+ xShapeProperties->getPropertyValue("Graphic") >>= xGraphic;
+ if (xGraphic.is())
+ {
+ aGraphics.push_back(xGraphic);
+ }
+ }
+
+ return aGraphics;
+}
+
+}
+
+void Test::testMultipleIdenticalGraphics()
+{
+ // We have multiple identical graphics. When we save them we want
+ // them to be saved de-duplicated and the same should still be true
+ // after loading them again. This test check that the de-duplication
+ // works as expected.
+
+ const OUString aFilterNames[] {
+ "writer8",
+ //"Rich Text Format", // doesn't work correctly for now
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/multiple_identical_graphics.odt"), "com.sun.star.text.TextDocument");
+
+ // Export the document and import again for a check
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ mxComponent->dispose();
+
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check whether graphic exported well
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+ auto aGraphics = lcl_getGraphics(mxComponent);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), size_t(5), aGraphics.size());
+
+ // Get all GfxLink addresses, we expect all of them to be the same
+ // indicating we use the same graphic instance for all shapes
+ std::vector<sal_Int64> aGfxLinkAddresses;
+ for (auto const & rxGraphic : aGraphics)
+ {
+ GfxLink* pLink = Graphic(rxGraphic).GetSharedGfxLink().get();
+ aGfxLinkAddresses.emplace_back(reinterpret_cast<sal_Int64>(pLink));
+ }
+
+ // Check all addresses are the same
+ bool bResult = std::equal(aGfxLinkAddresses.begin() + 1, aGfxLinkAddresses.end(), aGfxLinkAddresses.begin());
+ const OString sGraphicNotTheSameFailedMessage = OString::Concat("Graphics not the same for filter: '") +
+ rFilterName.toUtf8() + OString::Concat("'");
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sGraphicNotTheSameFailedMessage.getStr(), true, bResult);
+ }
+}
+
+void Test::testCharHighlightBody()
+{
+ // MS Word has two kind of character backgrounds called character shading and highlighting
+ // MS filters handle these attributes separately, but ODF export merges them into one background attribute
+
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/char_highlight.docx"),
+ "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ const uno::Reference< text::XTextRange > xPara = getParagraph(1);
+ // Both highlight and background
+ const Color nBackColor(0x4F81BD);
+ for( int nRun = 1; nRun <= 16; ++nRun )
+ {
+ const uno::Reference<beans::XPropertySet> xRun(getRun(xPara,nRun), uno::UNO_QUERY);
+ Color nHighlightColor;
+ switch( nRun )
+ {
+ case 1: nHighlightColor = COL_BLACK; break; //black 0x000000
+ case 2: nHighlightColor = COL_LIGHTBLUE; break; //light blue 0x0000ff
+ case 3: nHighlightColor = COL_LIGHTCYAN; break; //light cyan 0x00ffff
+ case 4: nHighlightColor = COL_LIGHTGREEN; break; //light green 0x00ff00
+ case 5: nHighlightColor = COL_LIGHTMAGENTA; break; //light magenta 0xff00ff
+ case 6: nHighlightColor = COL_LIGHTRED; break; //light red 0xff0000
+ case 7: nHighlightColor = COL_YELLOW; break; //yellow 0xffff00
+ case 8: nHighlightColor = COL_WHITE; break; //white 0xffffff
+ case 9: nHighlightColor = COL_BLUE; break;//blue 0x000080
+ case 10: nHighlightColor = COL_CYAN; break; //cyan 0x008080
+ case 11: nHighlightColor = COL_GREEN; break; //green 0x008000
+ case 12: nHighlightColor = COL_MAGENTA; break; //magenta 0x800080
+ case 13: nHighlightColor = COL_RED; break; //red 0x800000
+ case 14: nHighlightColor = COL_BROWN; break; //brown 0x808000
+ case 15: nHighlightColor = COL_GRAY; break; //dark gray 0x808080
+ case 16: nHighlightColor = COL_LIGHTGRAY; break; //light gray 0xC0C0C0
+ }
+
+ if (rFilterName == "writer8")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), nHighlightColor, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ else // MS filters
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), nHighlightColor, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), nBackColor, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ }
+
+ // Only highlight
+ {
+ const uno::Reference<beans::XPropertySet> xRun(getRun(xPara,18), uno::UNO_QUERY);
+ if (rFilterName == "writer8")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTRED, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ else
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTRED, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ }
+
+ // Only background
+ {
+ const uno::Reference<beans::XPropertySet> xRun(getRun(xPara,19), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTBLUE, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ }
+}
+
+void Test::testCharStyleHighlight()
+{
+ // MS Word has two kind of character backgrounds called character shading and highlighting.
+ // However, their character style can only accept shading. It ignores the highlighting value.
+
+ const OUString aFilterNames[] = {
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/tdf138345_charstyle_highlight.odt"),
+ "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ uno::Reference<beans::XPropertySet> xCharStyle;
+ getStyles("CharacterStyles")->getByName("charBackground") >>= xCharStyle;
+ const Color nBackColor(0xFFDBB6); //orange-y
+
+ // Always export character style's background colour as shading, never as highlighting.
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xCharStyle,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), nBackColor, getProperty<Color>(xCharStyle,"CharBackColor"));
+ }
+}
+
+void Test::testCharHighlight()
+{
+ SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ rOpt.SetCharBackground2Shading();
+
+ testCharHighlightBody();
+ testCharStyleHighlight();
+
+ rOpt.SetCharBackground2Highlighting();
+
+ testCharHighlightBody();
+ testCharStyleHighlight();
+}
+
+void Test::testCharHighlightODF()
+{
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/char_background_editing.docx"),
+ "com.sun.star.text.TextDocument");
+
+ // don't check import, testMSCharBackgroundEditing already does that
+
+ uno::Reference<text::XTextRange> xPara = getParagraph(1);
+ for (int i = 1; i <= 4; ++i)
+ {
+ uno::Reference<beans::XPropertySet> xRun(getRun(xPara,i), uno::UNO_QUERY);
+ switch (i)
+ {
+ case 1: // non-transparent highlight
+ xRun->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32>(128)));
+ xRun->setPropertyValue("CharBackTransparent", uno::Any(true));
+ xRun->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32>(64)));
+ break;
+
+ case 2: // transparent backcolor
+ xRun->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32>(128)));
+ xRun->setPropertyValue("CharBackTransparent", uno::Any(true));
+ xRun->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32>(COL_TRANSPARENT)));
+ break;
+
+ case 3: // non-transparent backcolor
+ xRun->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32>(128)));
+ xRun->setPropertyValue("CharBackTransparent", uno::Any(false));
+ xRun->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32>(COL_TRANSPARENT)));
+ break;
+
+ case 4: // non-transparent highlight again
+ xRun->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32>(128)));
+ xRun->setPropertyValue("CharBackTransparent", uno::Any(false));
+ xRun->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32>(64)));
+ break;
+ }
+ }
+
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer8");
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ xPara.set(getParagraph(1));
+ for (int i = 1; i <= 4; ++i)
+ {
+ uno::Reference<beans::XPropertySet> xRun(getRun(xPara,i), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(COL_TRANSPARENT), getProperty<sal_Int32>(xRun, "CharHighlight"));
+ switch (i)
+ {
+ case 1: // non-transparent highlight
+ CPPUNIT_ASSERT_EQUAL(Color(0x000040), getProperty<Color>(xRun, "CharBackColor"));
+ CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xRun, "CharBackTransparent"));
+ break;
+ case 2: // transparent backcolor
+ CPPUNIT_ASSERT_EQUAL(COL_TRANSPARENT, getProperty<Color>(xRun, "CharBackColor"));
+ CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xRun, "CharBackTransparent"));
+ break;
+ case 3: // non-transparent backcolor
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, getProperty<Color>(xRun, "CharBackColor"));
+ CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xRun, "CharBackTransparent"));
+ break;
+ case 4: // non-transparent highlight again
+ CPPUNIT_ASSERT_EQUAL(Color(0x000040), getProperty<Color>(xRun, "CharBackColor"));
+ CPPUNIT_ASSERT_EQUAL(false, getProperty<bool>(xRun, "CharBackTransparent"));
+ break;
+ }
+ }
+}
+
+void Test::testMSCharBackgroundEditing()
+{
+ // Simulate the editing process of imported MSO character background attributes
+ // and check how export behaves.
+
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/char_background_editing.docx"),
+ "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Check whether import was done on the right way
+ uno::Reference< text::XTextRange > xPara = getParagraph(1);
+ {
+ uno::Reference<beans::XPropertySet> xRun(getRun(xPara,1), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTRED, getProperty<Color>(xRun,"CharBackColor"));
+
+ xRun.set(getRun(xPara,2), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTBLUE, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharBackColor"));
+
+ xRun.set(getRun(xPara,3), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTBLUE, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_LIGHTRED, getProperty<Color>(xRun,"CharBackColor"));
+
+ xRun.set(getRun(xPara,4), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharBackColor"));
+ }
+
+ // Simulate editing
+ for( int i = 1; i <= 4; ++i )
+ {
+ uno::Reference<beans::XPropertySet> xRun(getRun(xPara,i), uno::UNO_QUERY);
+ // Change background
+ Color nBackColor;
+ switch( i )
+ {
+ case 1: nBackColor = COL_BLACK; break; //black 0x000000
+ case 2: nBackColor = COL_LIGHTCYAN; break; //cyan 0x00ffff
+ case 3: nBackColor = COL_LIGHTGREEN; break; //green 0x00ff00
+ case 4: nBackColor = COL_LIGHTMAGENTA; break; //magenta 0xff00ff
+ }
+ xRun->setPropertyValue("CharBackColor", uno::Any(nBackColor));
+ // Remove highlighting
+ xRun->setPropertyValue("CharHighlight", uno::Any(COL_TRANSPARENT));
+ // Remove shading marker
+ uno::Sequence<beans::PropertyValue> aGrabBag = getProperty<uno::Sequence<beans::PropertyValue> >(xRun,"CharInteropGrabBag");
+ for (beans::PropertyValue& rProp : asNonConstRange(aGrabBag))
+ {
+ if (rProp.Name == "CharShadingMarker")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), true, rProp.Value.get<bool>());
+ rProp.Value <<= false;
+ }
+ }
+ xRun->setPropertyValue("CharInteropGrabBag", uno::Any(aGrabBag));
+ }
+
+ SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ rOpt.SetCharBackground2Highlighting();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check whether background was exported as highlighting
+ xPara.set(getParagraph(1));
+ for( int i = 1; i <= 4; ++i )
+ {
+ Color nBackColor;
+ switch( i )
+ {
+ case 1: nBackColor = COL_BLACK; break; //black 0x000000
+ case 2: nBackColor = COL_LIGHTCYAN; break; //light cyan 0x00ffff
+ case 3: nBackColor = COL_LIGHTGREEN; break; //light green 0x00ff00
+ case 4: nBackColor = COL_LIGHTMAGENTA; break; //light magenta 0xff00ff
+ }
+ const uno::Reference<beans::XPropertySet> xRun(getRun(xPara,i), uno::UNO_QUERY);
+ if (rFilterName == "writer8")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), nBackColor, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ else
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), nBackColor, getProperty<Color>(xRun,"CharHighlight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_TRANSPARENT, getProperty<Color>(xRun,"CharBackColor"));
+ }
+ }
+ }
+}
+
+void Test::testCharBackgroundToHighlighting()
+{
+ // MSO highlighting has less kind of values so let's see how LO character background is converted
+ // to these values
+
+ const OUString aFilterNames[] = {
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/char_background.odt"),
+ "com.sun.star.text.TextDocument");
+
+ OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+
+ SvtFilterOptions& rOpt = SvtFilterOptions::Get();
+ rOpt.SetCharBackground2Highlighting();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check highlight color
+ const uno::Reference< text::XTextRange > xPara = getParagraph(1);
+ for( int nRun = 1; nRun <= 19; ++nRun )
+ {
+ const uno::Reference<beans::XPropertySet> xRun(getRun(xPara,nRun), uno::UNO_QUERY);
+ Color nHighlightColor;
+ switch( nRun )
+ {
+ case 1: nHighlightColor = COL_BLACK; break; //black 0x000000
+ case 2: nHighlightColor = COL_YELLOW; break; //yellow 0xffff00
+ case 3: nHighlightColor = COL_LIGHTMAGENTA; break; //light magenta 0xff00ff
+ case 4: nHighlightColor = COL_LIGHTCYAN; break; //light cyan 0x00ffff
+ case 5: nHighlightColor = COL_YELLOW; break; //yellow 0xffff00
+ case 6: nHighlightColor = COL_LIGHTRED; break; //light red 0xff0000
+ case 7: nHighlightColor = COL_LIGHTBLUE; break; //light blue 0x0000ff
+ case 8: nHighlightColor = COL_LIGHTGREEN; break; //light green 0x00ff00
+ case 9: nHighlightColor = COL_GREEN; break; //dark green 0x008000
+ case 10: nHighlightColor = COL_MAGENTA; break; //dark magenta 0x800080
+ case 11: nHighlightColor = COL_BLUE; break; //dark blue 0x000080
+ case 12: nHighlightColor = COL_BROWN; break; //brown 0x808000
+ case 13: nHighlightColor = COL_GRAY; break; //dark gray 0x808080
+ case 14: nHighlightColor = COL_BLACK; break; //black 0x000000
+ case 15: nHighlightColor = COL_LIGHTRED; break; //light red 0xff0000
+ case 16: nHighlightColor = COL_LIGHTGRAY; break; //light gray 0xC0C0C0
+ case 17: nHighlightColor = COL_RED; break; //dark red 0x800000
+ case 18: nHighlightColor = COL_GRAY; break; //dark gray 0x808080
+ case 19: nHighlightColor = COL_YELLOW; break; //yellow 0xffff00
+ }
+ const OString sMessage = sFailedMessage +". Index of run with unmatched color: " + OString::number(nRun);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage.getStr(), nHighlightColor, getProperty<Color>(xRun,"CharHighlight"));
+ }
+ }
+}
+
+#if !defined(_WIN32)
+void Test::testSkipImages()
+{
+ // Check how LO skips image loading (but not texts of textboxes and custom shapes)
+ // during DOC and DOCX import, using the "SkipImages" FilterOptions.
+
+ std::pair<OUString, OUString> aFilterNames[] = {
+ { "/sw/qa/extras/globalfilter/data/skipimages.doc", "" },
+ { "/sw/qa/extras/globalfilter/data/skipimages.doc", "SkipImages" },
+ { "/sw/qa/extras/globalfilter/data/skipimages.docx", "" },
+ { "/sw/qa/extras/globalfilter/data/skipimages.docx", "SkipImages" }
+ };
+
+ for (auto const & rFilterNamePair : aFilterNames)
+ {
+ bool bSkipImages = !rFilterNamePair.second.isEmpty();
+ OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterNamePair.first.toUtf8();
+
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ if (bSkipImages)
+ {
+ // FilterOptions parameter
+ uno::Sequence<beans::PropertyValue> args(comphelper::InitPropertySequence({
+ { "FilterOptions", uno::Any(rFilterNamePair.second) }
+ }));
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(rFilterNamePair.first), "com.sun.star.text.TextDocument", args);
+ sFailedMessage += " - " + rFilterNamePair.second.toUtf8();
+ }
+ else
+ {
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(rFilterNamePair.first), "com.sun.star.text.TextDocument");
+ }
+
+ // Check shapes (images, textboxes, custom shapes)
+ uno::Reference<drawing::XShape> xShape;
+ uno::Reference<graphic::XGraphic> xGraphic;
+ uno::Reference< beans::XPropertySet > XPropSet;
+ uno::Reference<awt::XBitmap> xBitmap;
+
+ bool bHasTextboxText = false;
+ bool bHasCustomShapeText = false;
+ sal_Int32 nImageCount = 0;
+
+ for (int i = 1; i<= getShapes(); i++)
+ {
+ xShape = getShape(i);
+ XPropSet.set( xShape, uno::UNO_QUERY_THROW );
+ try
+ {
+ XPropSet->getPropertyValue("Graphic") >>= xGraphic;
+ xBitmap.set(xGraphic, uno::UNO_QUERY);
+ if (xBitmap.is())
+ nImageCount++;
+ }
+ catch (beans::UnknownPropertyException &)
+ { /* ignore */ }
+
+ uno::Reference<text::XTextRange> xText(xShape, uno::UNO_QUERY);
+ if (xText.is())
+ {
+ OUString shapeText = xText->getString();
+ if (shapeText.startsWith("Lorem ipsum"))
+ bHasTextboxText = true;
+ else if (shapeText.startsWith("Nam pretium"))
+ bHasCustomShapeText = true;
+ }
+ }
+
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), bHasTextboxText);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), bHasCustomShapeText);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), static_cast<sal_Int32>(bSkipImages ? 0 : 3), nImageCount );
+ }
+}
+#endif
+
+static auto verifyNestedFieldmark(OUString const& rTestName,
+ uno::Reference<lang::XComponent> const& xComponent) -> void
+{
+ SwDoc const*const pDoc(dynamic_cast<SwXTextDocument&>(*xComponent).GetDocShell()->GetDoc());
+ IDocumentMarkAccess const& rIDMA(*pDoc->getIDocumentMarkAccess());
+
+ // no spurious bookmarks have been created
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName.toUtf8().getStr(),
+ sal_Int32(0), rIDMA.getBookmarksCount());
+
+ // check inner fieldmark
+ SwNodeIndex const node1(*pDoc->GetNodes().GetEndOfContent().StartOfSectionNode(), +2);
+ SwPosition const innerPos(*node1.GetNode().GetTextNode(),
+ node1.GetNode().GetTextNode()->GetText().indexOf(CH_TXT_ATR_FIELDSTART));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName.toUtf8().getStr(),
+ sal_Int32(1), innerPos.nContent.GetIndex());
+ ::sw::mark::IFieldmark *const pInner(rIDMA.getFieldmarkAt(innerPos));
+ CPPUNIT_ASSERT_MESSAGE(rTestName.toUtf8().getStr(), pInner);
+ OUString const innerString(SwPaM(pInner->GetMarkPos(), pInner->GetOtherMarkPos()).GetText());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName.toUtf8().getStr(), OUString(
+ OUStringChar(CH_TXT_ATR_FIELDSTART) + u" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP) + u"foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz" + OUStringChar(CH_TXT_ATR_FIELDEND)), innerString);
+
+ // check outer fieldmark
+ SwNodeIndex const node2(node1, -1);
+ SwPosition const outerPos(*node2.GetNode().GetTextNode(),
+ node2.GetNode().GetTextNode()->GetText().indexOf(CH_TXT_ATR_FIELDSTART));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName.toUtf8().getStr(),
+ sal_Int32(0), outerPos.nContent.GetIndex());
+ ::sw::mark::IFieldmark const*const pOuter(rIDMA.getFieldmarkAt(outerPos));
+ CPPUNIT_ASSERT_MESSAGE(rTestName.toUtf8().getStr(), pOuter);
+ OUString const outerString(SwPaM(pOuter->GetMarkPos(), pOuter->GetOtherMarkPos()).GetText());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName.toUtf8().getStr(), OUString(
+ OUStringChar(CH_TXT_ATR_FIELDSTART) + u" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" " + OUStringChar(CH_TXT_ATR_FIELDSTART) + u" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP) + u"foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz" + OUStringChar(CH_TXT_ATR_FIELDEND) + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP) + u"foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" foo " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u" bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz" + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"bar " + OUStringChar(CH_TXTATR_NEWLINE)
+ + u"baz" + OUStringChar(CH_TXT_ATR_FIELDEND)), outerString);
+
+ // must return innermost mark
+ CPPUNIT_ASSERT_EQUAL(pInner, rIDMA.getFieldmarkFor(innerPos));
+}
+
+void Test::testNestedFieldmark()
+{
+ // experimental config setting
+ Resetter resetter(
+ [] () {
+ std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::set(false, pBatch);
+ return pBatch->commit();
+ });
+ std::shared_ptr<comphelper::ConfigurationChanges> pBatch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::set(true, pBatch);
+ pBatch->commit();
+
+ std::pair<OUString, OUString> const aFilterNames[] = {
+ {"writer8", "fieldmark_QUOTE_nest.fodt"},
+ {"Office Open XML Text", "fieldmark_QUOTE_nest.docx"},
+ {"Rich Text Format", "fieldmark_QUOTE_nest.rtf"},
+ };
+
+ for (auto const & rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ {
+ mxComponent->dispose();
+ }
+
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(
+ OUStringConcatenation("/sw/qa/extras/globalfilter/data/" + rFilterName.second)),
+ "com.sun.star.text.TextDocument");
+
+ verifyNestedFieldmark(rFilterName.first + ", load", mxComponent);
+
+ // Export the document and import again
+ uno::Reference<frame::XStorable> const xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName.first;
+
+ utl::TempFile aTempFile;
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ mxComponent->dispose();
+
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ verifyNestedFieldmark(rFilterName.first + " exported-reload", mxComponent);
+ aTempFile.EnableKillingFile();
+ }
+}
+
+auto Test::verifyText13(char const*const pTestName) -> void
+{
+ // OFFICE-3789 style:header-first/style:footer-first
+ uno::Reference<beans::XPropertySet> xPageStyle;
+ getStyles("PageStyles")->getByName("Standard") >>= xPageStyle;
+ uno::Reference<text::XText> xHF(getProperty<uno::Reference<text::XText>>(xPageStyle, "HeaderTextFirst"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("Header first"), xHF->getString());
+ uno::Reference<text::XText> xFF(getProperty<uno::Reference<text::XText>>(xPageStyle, "FooterTextFirst"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("Footer first"), xFF->getString());
+ // OFFICE-3767 text:contextual-spacing
+ uno::Reference<text::XTextRange> xPara(getParagraph(1));
+ CPPUNIT_ASSERT_MESSAGE(pTestName, getProperty<bool>(xPara, "ParaContextMargin"));
+ // OFFICE-3776 meta:creator-initials
+ uno::Reference<text::XTextRange> xRun(getRun(xPara, 1));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("Annotation"), getProperty<OUString>(xRun, "TextPortionType"));
+ uno::Reference<beans::XPropertySet> xComment(getProperty<uno::Reference<beans::XPropertySet>>(xRun, "TextField"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("dj"), getProperty<OUString>(xComment, "Initials"));
+ // OFFICE-3941 text:index-entry-link-start/text:index-entry-link-end
+ uno::Reference<text::XDocumentIndexesSupplier> xDIS(mxComponent, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xIndexes(xDIS->getDocumentIndexes());
+ uno::Reference<text::XDocumentIndex> xIndex(xIndexes->getByIndex(0), uno::UNO_QUERY);
+ uno::Reference<container::XIndexReplace> xLevels(getProperty<uno::Reference<container::XIndexReplace>>(xIndex, "LevelFormat"));
+ uno::Sequence<beans::PropertyValues> format;
+ xLevels->getByIndex(1) >>= format; // 1-based?
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("TokenType"), format[0][0].Name);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("TokenHyperlinkStart"), format[0][0].Value.get<OUString>());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("TokenType"), format[4][0].Name);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName, OUString("TokenHyperlinkEnd"), format[4][0].Value.get<OUString>());
+}
+
+// test ODF 1.3 new text document features
+void Test::testODF13()
+{
+ // import
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(
+ u"/sw/qa/extras/globalfilter/data/text13e.odt"),
+ "com.sun.star.text.TextDocument");
+
+ // check model
+ verifyText13("import");
+
+ Resetter _([]() {
+ std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Save::ODF::DefaultVersion::set(3, pBatch);
+ return pBatch->commit();
+ });
+
+ {
+ // export ODF 1.3
+ std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Save::ODF::DefaultVersion::set(10, pBatch);
+ pBatch->commit();
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer8");
+
+ utl::TempFile aTempFile;
+ uno::Reference<frame::XStorable> const xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // check XML
+ xmlDocUniquePtr pContentXml = parseExportInternal(aTempFile.GetURL(), "content.xml");
+ assertXPath(pContentXml, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties[@style:contextual-spacing='true']");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-start");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-start", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-end");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-end", 0);
+ xmlDocUniquePtr pStylesXml = parseExportInternal(aTempFile.GetURL(), "styles.xml");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/style:header-first");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/loext:header-first", 0);
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/style:footer-first");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first", 0);
+
+ // reload
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // check model
+ verifyText13("1.3 reload");
+ }
+ {
+ // export ODF 1.2 extended
+ std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Save::ODF::DefaultVersion::set(9, pBatch);
+ pBatch->commit();
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer8");
+
+ utl::TempFile aTempFile;
+ uno::Reference<frame::XStorable> const xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // check XML
+ xmlDocUniquePtr pContentXml = parseExportInternal(aTempFile.GetURL(), "content.xml");
+ assertXPath(pContentXml, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties[@loext:contextual-spacing='true']");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-start");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-start", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-end");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-end", 0);
+ xmlDocUniquePtr pStylesXml = parseExportInternal(aTempFile.GetURL(), "styles.xml");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/loext:header-first");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/style:header-first", 0);
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/style:footer-first", 0);
+
+ // reload
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // check model
+ verifyText13("1.2 Extended reload");
+ }
+ {
+ // export ODF 1.2
+ std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Save::ODF::DefaultVersion::set(4, pBatch);
+ pBatch->commit();
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer8");
+
+ utl::TempFile aTempFile;
+ uno::Reference<frame::XStorable> const xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // check XML
+ xmlDocUniquePtr pContentXml = parseExportInternal(aTempFile.GetURL(), "content.xml");
+ assertXPathNoAttribute(pContentXml, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties", "contextual-spacing");
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-start", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-start", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-end", 0);
+ assertXPath(pContentXml, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-end", 0);
+ xmlDocUniquePtr pStylesXml = parseExportInternal(aTempFile.GetURL(), "styles.xml");
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/style:header-first", 0);
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/loext:header-first", 0);
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/style:footer-first", 0);
+ assertXPath(pStylesXml, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first", 0);
+
+ // don't reload - no point
+ }
+}
+
+void Test::testRedlineFlags()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Rich Text Format",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument");
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT(pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+
+ SwPaM pam(SwPosition(SwNodeIndex(pDoc->GetNodes().GetEndOfContent(), -1)));
+ pDoc->getIDocumentContentOperations().InsertString(pam, "foo bar baz");
+
+ IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess());
+ // enable change tracking
+ rIDRA.SetRedlineFlags(rIDRA.GetRedlineFlags()
+ | RedlineFlags::On | RedlineFlags::ShowDelete);
+
+ // need a delete redline to trigger mode switching
+ pam.Move(fnMoveForward, GoInDoc);
+ pam.SetMark();
+ pam.Move(fnMoveBackward, GoInDoc);
+ pDoc->getIDocumentContentOperations().DeleteAndJoin(pam);
+
+ // hide delete redlines
+ RedlineFlags const nRedlineFlags =
+ rIDRA.GetRedlineFlags() & ~RedlineFlags::ShowDelete;
+ rIDRA.SetRedlineFlags(nRedlineFlags);
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ // export the document
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(),
+ aMediaDescriptor.getAsConstPropertyValueList());
+
+ // tdf#97103 check that redline mode is properly restored
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(
+ OString(OString::Concat("redline mode not restored in ") + rFilterName.toUtf8()).getStr(),
+ static_cast<int>(nRedlineFlags), static_cast<int>(rIDRA.GetRedlineFlags()));
+ }
+}
+
+void Test::testBulletAsImage()
+{
+ OUString aFilterNames[] = {
+ "writer8",
+ "MS Word 97",
+ "Office Open XML Text",
+ "Rich Text Format",
+ };
+
+ for (OUString const & rFilterName : aFilterNames)
+ {
+ OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/BulletAsImage.odt"), "com.sun.star.text.TextDocument");
+
+ // Check if import was successful
+ {
+ uno::Reference<text::XTextRange> xPara(getParagraph(1));
+ uno::Reference<beans::XPropertySet> xPropertySet(xPara, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xLevels;
+ xLevels.set(xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aProperties;
+ xLevels->getByIndex(0) >>= aProperties;
+ uno::Reference<awt::XBitmap> xBitmap;
+ awt::Size aSize;
+ sal_Int16 nNumberingType = -1;
+
+ for (beans::PropertyValue const & rProperty : std::as_const(aProperties))
+ {
+ if (rProperty.Name == "NumberingType")
+ {
+ nNumberingType = rProperty.Value.get<sal_Int16>();
+ }
+ else if (rProperty.Name == "GraphicBitmap")
+ {
+ if (rProperty.Value.has<uno::Reference<awt::XBitmap>>())
+ {
+ xBitmap = rProperty.Value.get<uno::Reference<awt::XBitmap>>();
+ }
+ }
+ else if (rProperty.Name == "GraphicSize")
+ {
+ aSize = rProperty.Value.get<awt::Size>();
+ }
+ }
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), style::NumberingType::BITMAP, nNumberingType);
+
+ // Graphic Bitmap
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ Graphic aGraphic(uno::Reference<graphic::XGraphic>(xBitmap, uno::UNO_QUERY));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), GraphicType::Bitmap, aGraphic.GetType());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), aGraphic.GetSizeBytes() > o3tl::make_unsigned(0));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), tools::Long(16), aGraphic.GetSizePixel().Width());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), tools::Long(16), aGraphic.GetSizePixel().Height());
+
+ // Graphic Size
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(400), aSize.Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(400), aSize.Height);
+ }
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+
+
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ {
+ uno::Reference<text::XTextRange> xPara(getParagraph(1));
+ uno::Reference<beans::XPropertySet> xPropertySet(xPara, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> xLevels;
+ xLevels.set(xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aProperties;
+ xLevels->getByIndex(0) >>= aProperties;
+ uno::Reference<awt::XBitmap> xBitmap;
+ awt::Size aSize;
+ sal_Int16 nNumberingType = -1;
+
+ for (beans::PropertyValue const & rProperty : std::as_const(aProperties))
+ {
+ if (rProperty.Name == "NumberingType")
+ {
+ nNumberingType = rProperty.Value.get<sal_Int16>();
+ }
+ else if (rProperty.Name == "GraphicBitmap")
+ {
+ if (rProperty.Value.has<uno::Reference<awt::XBitmap>>())
+ {
+ xBitmap = rProperty.Value.get<uno::Reference<awt::XBitmap>>();
+ }
+ }
+ else if (rProperty.Name == "GraphicSize")
+ {
+ aSize = rProperty.Value.get<awt::Size>();
+ }
+ }
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), style::NumberingType::BITMAP, nNumberingType);
+
+ // Graphic Bitmap
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), xBitmap.is());
+ Graphic aGraphic(uno::Reference<graphic::XGraphic>(xBitmap, uno::UNO_QUERY));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), GraphicType::Bitmap, aGraphic.GetType());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), aGraphic.GetSizeBytes() > o3tl::make_unsigned(0));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), tools::Long(16), aGraphic.GetSizePixel().Width());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), tools::Long(16), aGraphic.GetSizePixel().Height());
+
+ // Graphic Size
+ if (rFilterName == "write8") // ODT is correct
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(400), aSize.Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(400), aSize.Height);
+ }
+ // FIXME: MS Filters don't work correctly for graphic bullet size
+ else if (rFilterName == "Office Open XML Text" || rFilterName == "Rich Text Format")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(279), aSize.Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(279), aSize.Height);
+ }
+ else if (rFilterName == "MS Word 97")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(296), aSize.Width);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(296), aSize.Height);
+ }
+ }
+ }
+}
+
+void Test::testTextFormField()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (const OUString& rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/text_form_field.odt"), "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check the document after round trip
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+ // We have two text form fields
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(2), pMarkAccess->getAllMarksCount());
+
+ // Check whether all fieldmarks are text form fields
+ for(auto aIter = pMarkAccess->getAllMarksBegin(); aIter != pMarkAccess->getAllMarksEnd(); ++aIter)
+ {
+ ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pFieldmark);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(ODF_FORMTEXT), pFieldmark->GetFieldname());
+ }
+
+ // In the first paragraph we have an empty text form field with the placeholder spaces
+ const uno::Reference< text::XTextRange > xPara = getParagraph(1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("TextFieldStart"), getProperty<OUString>(getRun(xPara, 1), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("TextFieldSeparator"), getProperty<OUString>(getRun(xPara, 2), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("Text"), getProperty<OUString>(getRun(xPara, 3), "TextPortionType"));
+ sal_Unicode vEnSpaces[5] = {8194, 8194, 8194, 8194, 8194};
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(vEnSpaces, 5), getRun(xPara, 3)->getString());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("TextFieldEnd"), getProperty<OUString>(getRun(xPara, 4), "TextPortionType"));
+
+ // In the second paragraph we have a set text
+ const uno::Reference< text::XTextRange > xPara2 = getParagraph(2);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("TextFieldStart"), getProperty<OUString>(getRun(xPara2, 1), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("TextFieldSeparator"), getProperty<OUString>(getRun(xPara2, 2), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("Text"), getProperty<OUString>(getRun(xPara2, 3), "TextPortionType"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("xxxxx"), getRun(xPara2, 3)->getString());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("TextFieldEnd"), getProperty<OUString>(getRun(xPara2, 4), "TextPortionType"));
+ }
+}
+
+void Test::testCheckBoxFormField()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (const OUString& rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/checkbox_form_field.odt"), "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check the document after round trip
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+ // We have two check box form fields
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(2), pMarkAccess->getAllMarksCount());
+
+ int nIndex = 0;
+ for(auto aIter = pMarkAccess->getAllMarksBegin(); aIter != pMarkAccess->getAllMarksEnd(); ++aIter)
+ {
+ ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter);
+
+ if(rFilterName == "Office Open XML Text") // OOXML import also generates bookmarks
+ {
+ if(!pFieldmark)
+ continue;
+ }
+
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pFieldmark);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(ODF_FORMCHECKBOX), pFieldmark->GetFieldname());
+ ::sw::mark::ICheckboxFieldmark* pCheckBox = dynamic_cast< ::sw::mark::ICheckboxFieldmark* >(pFieldmark);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pCheckBox);
+
+ // The first one is unchecked, the other one is checked
+ if(nIndex == 0)
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), !pCheckBox->IsChecked());
+ else
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pCheckBox->IsChecked());
+ ++nIndex;
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), int(2), nIndex);
+ }
+}
+
+void Test::testDropDownFormField()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+ "MS Word 97",
+ "Office Open XML Text",
+ };
+
+ for (const OUString& rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/dropdown_form_field.odt"), "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check the document after round trip
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(2), pMarkAccess->getAllMarksCount());
+
+ int nIndex = 0;
+ for(auto aIter = pMarkAccess->getAllMarksBegin(); aIter != pMarkAccess->getAllMarksEnd(); ++aIter)
+ {
+ ::sw::mark::IFieldmark* pFieldmark = dynamic_cast<::sw::mark::IFieldmark*>(*aIter);
+
+ if(!pFieldmark)
+ continue;
+
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pFieldmark);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(ODF_FORMDROPDOWN), pFieldmark->GetFieldname());
+
+ // Check drop down field's parameters.
+ const sw::mark::IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters();
+ css::uno::Sequence<OUString> vListEntries;
+ sal_Int32 nSelection = -1;
+ auto pListEntries = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
+ if (pListEntries != pParameters->end())
+ {
+ pListEntries->second >>= vListEntries;
+
+ if(vListEntries.hasElements())
+ {
+ auto pResult = pParameters->find(ODF_FORMDROPDOWN_RESULT);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= nSelection;
+ }
+ }
+ }
+
+ // The first one is empty
+ if(nIndex == 0)
+ {
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), !vListEntries.hasElements());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(-1), nSelection);
+ }
+ else // The second one has list and also a selected item
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(4), vListEntries.getLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(1), nSelection);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("1000"), vListEntries[0]);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("2000"), vListEntries[1]);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("3000"), vListEntries[2]);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("4000"), vListEntries[3]);
+ }
+ ++nIndex;
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), int(2), nIndex);
+ }
+}
+
+void Test::testDateFormField()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Office Open XML Text",
+ };
+
+ for (const OUString& rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/date_form_field.odt"), "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check the document after round trip
+ if (rFilterName == "writer8")
+ {
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(5), pMarkAccess->getAllMarksCount());
+
+ int nIndex = 0;
+ for(auto aIter = pMarkAccess->getAllMarksBegin(); aIter != pMarkAccess->getAllMarksEnd(); ++aIter)
+ {
+ ::sw::mark::IDateFieldmark* pFieldmark = dynamic_cast<::sw::mark::IDateFieldmark*>(*aIter);
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pFieldmark);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(ODF_FORMDATE), pFieldmark->GetFieldname());
+
+ // Check date form field's parameters.
+ const sw::mark::IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters();
+ OUString sDateFormat;
+ auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sDateFormat;
+ }
+
+ OUString sLang;
+ pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sLang;
+ }
+
+ OUString sCurrentDate = pFieldmark->GetContent();
+
+ // The first one has the default field content
+ if(nIndex == 0)
+ {
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("MM/DD/YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("en-US"), sLang);
+ sal_Unicode vEnSpaces[ODF_FORMFIELD_DEFAULT_LENGTH] = {8194, 8194, 8194, 8194, 8194};
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(vEnSpaces, 5), sCurrentDate);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), SwNodeOffset(9), pFieldmark->GetMarkStart().nNode.GetIndex());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(5), pFieldmark->GetMarkStart().nContent.GetIndex());
+ }
+ else if (nIndex == 1) // The second has the default format
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("MM/DD/YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("en-US"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("06/12/19"), sCurrentDate);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), SwNodeOffset(9), pFieldmark->GetMarkStart().nNode.GetIndex());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(20), pFieldmark->GetMarkStart().nContent.GetIndex());
+ }
+ else if (nIndex == 2) // The third one has special format
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("[NatNum12 MMMM=abbreviation]YYYY\". \"MMMM D."), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("hu-HU"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("2019. febr. 12."), sCurrentDate);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), SwNodeOffset(9), pFieldmark->GetMarkStart().nNode.GetIndex());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(40), pFieldmark->GetMarkStart().nContent.GetIndex());
+
+ }
+ else if (nIndex == 3) // The fourth one has placeholder text
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("D, MMM YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("bm-ML"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("[select date]"), sCurrentDate);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), SwNodeOffset(9), pFieldmark->GetMarkStart().nNode.GetIndex());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(62), pFieldmark->GetMarkStart().nContent.GetIndex());
+
+ }
+ else // The last one is really empty
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("MM/DD/YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("en-US"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(""), sCurrentDate);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), SwNodeOffset(9), pFieldmark->GetMarkStart().nNode.GetIndex());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(82), pFieldmark->GetMarkStart().nContent.GetIndex());
+
+ }
+ ++nIndex;
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), int(5), nIndex);
+ }
+ else
+ {
+ // Import from DOCX, so the fieldmark is now a content control.
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getParagraph(1), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xTextPortions = xEnumAccess->createEnumeration();
+
+ int nIndex = 0;
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aPortionType;
+ xTextPortion->getPropertyValue("TextPortionType") >>= aPortionType;
+ if (aPortionType != "ContentControl")
+ {
+ continue;
+ }
+
+ uno::Reference<text::XTextContent> xContentControl;
+ xTextPortion->getPropertyValue("ContentControl") >>= xContentControl;
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+
+ bool bDate{};
+ xContentControlProps->getPropertyValue("Date") >>= bDate;
+ CPPUNIT_ASSERT(bDate);
+
+ // Check date form field's parameters.
+ OUString sDateFormat;
+ xContentControlProps->getPropertyValue("DateFormat") >>= sDateFormat;
+
+ OUString sLang;
+ xContentControlProps->getPropertyValue("DateLanguage") >>= sLang;
+
+ uno::Reference<container::XEnumerationAccess> xContentControlEnumAccess(xContentControl,
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xContentControlEnum
+ = xContentControlEnumAccess->createEnumeration();
+ uno::Reference<text::XTextRange> xContentControlTextPortion(xContentControlEnum->nextElement(), uno::UNO_QUERY);
+ OUString sCurrentDate = xContentControlTextPortion->getString();
+
+ // The first one has the default field content
+ if(nIndex == 0)
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("MM/DD/YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("en-US"), sLang);
+ sal_Unicode vEnSpaces[ODF_FORMFIELD_DEFAULT_LENGTH] = {8194, 8194, 8194, 8194, 8194};
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(vEnSpaces, 5), sCurrentDate);
+ }
+ else if (nIndex == 1) // The second has the default format
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("MM/DD/YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("en-US"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("06/12/19"), sCurrentDate);
+ }
+ else if (nIndex == 2) // The third one has special format
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("[NatNum12 MMMM=abbreviation]YYYY\". \"MMMM D."), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("hu-HU"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("2019. febr. 12."), sCurrentDate);
+ }
+ else if (nIndex == 3) // The fourth one has placeholder text
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("D, MMM YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("bm-ML"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("[select date]"), sCurrentDate);
+ }
+ else // The last one is really empty
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("MM/DD/YY"), sDateFormat);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString("en-US"), sLang);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(""), sCurrentDate);
+ }
+ ++nIndex;
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), int(5), nIndex);
+ }
+ }
+}
+
+void Test::testDateFormFieldCharacterFormatting()
+{
+ const OUString aFilterNames[] = {
+ "writer8",
+ "Office Open XML Text",
+ };
+
+ for (const OUString& rFilterName : aFilterNames)
+ {
+ if (mxComponent.is())
+ mxComponent->dispose();
+ mxComponent = loadFromDesktop(m_directories.getURLFromSrc(u"/sw/qa/extras/globalfilter/data/date_form_field_char_formatting.odt"), "com.sun.star.text.TextDocument");
+
+ const OString sFailedMessage = OString::Concat("Failed on filter: ") + rFilterName.toUtf8();
+
+ // Export the document and import again for a check
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= rFilterName;
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+ uno::Reference< lang::XComponent > xComponent(xStorable, uno::UNO_QUERY);
+ xComponent->dispose();
+ mxComponent = loadFromDesktop(aTempFile.GetURL(), "com.sun.star.text.TextDocument");
+
+ // Check the document after round trip
+ if (rFilterName == "writer8")
+ {
+ SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pTextDoc);
+ SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+
+ // Check that we have the field at the right place
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(1), pMarkAccess->getAllMarksCount());
+ ::sw::mark::IDateFieldmark* pFieldmark = dynamic_cast<::sw::mark::IDateFieldmark*>(*pMarkAccess->getAllMarksBegin());
+ CPPUNIT_ASSERT_MESSAGE(sFailedMessage.getStr(), pFieldmark);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), OUString(ODF_FORMDATE), pFieldmark->GetFieldname());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(0), pFieldmark->GetMarkStart().nContent.GetIndex());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), sal_Int32(11), pFieldmark->GetMarkEnd().nContent.GetIndex());
+
+ // We have one date field, first half of the field has bold character weight and second part has red character color
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), awt::FontWeight::BOLD, getProperty<float>(getRun(getParagraph(1), 3), "CharWeight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_AUTO, getProperty<Color>(getRun(getParagraph(1), 3), "CharColor"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), awt::FontWeight::NORMAL, getProperty<float>(getRun(getParagraph(1), 4), "CharWeight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), Color(0xff0000), getProperty<Color>(getRun(getParagraph(1), 4), "CharColor"));
+ }
+ else
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(getRun(getParagraph(1), 1), uno::UNO_QUERY);
+ OUString aPortionType;
+ xTextPortion->getPropertyValue("TextPortionType") >>= aPortionType;
+ CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType);
+
+ uno::Reference<text::XTextContent> xContentControl;
+ xTextPortion->getPropertyValue("ContentControl") >>= xContentControl;
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ bool bDate{};
+ xContentControlProps->getPropertyValue("Date") >>= bDate;
+ CPPUNIT_ASSERT(bDate);
+
+ uno::Reference<container::XEnumerationAccess> xContentControlEnumAccess(xContentControl,
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xContentControlEnum
+ = xContentControlEnumAccess->createEnumeration();
+ xTextPortion.set(xContentControlEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), awt::FontWeight::BOLD, getProperty<float>(xTextPortion, "CharWeight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), COL_AUTO, getProperty<Color>(xTextPortion, "CharColor"));
+ xTextPortion.set(xContentControlEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), awt::FontWeight::NORMAL, getProperty<float>(xTextPortion, "CharWeight"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage.getStr(), Color(0xff0000), getProperty<Color>(xTextPortion, "CharColor"));
+ }
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */