/* -*- 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 "mailmergetestbase.cxx" #include #include #include #include #include #include #include namespace { class MMTest2 : public MailMergeTestBase { }; DECLARE_SHELL_MAILMERGE_TEST(tdf125522_shell, "tdf125522.odt", "10-testing-addresses.ods", "testing-addresses") { // prepare unit test and run executeMailMerge(); // there should be no any text frame in output CPPUNIT_ASSERT(mxSwTextDocument); const auto & rNodes = mxSwTextDocument->GetDocShell()->GetDoc()->GetNodes(); for (SwNodeOffset nodeIndex(0); nodeIndexStartOfSectionNode()) { CPPUNIT_ASSERT(!aNode->StartOfSectionNode()->GetFlyFormat()); } } } DECLARE_SHELL_MAILMERGE_TEST(testTd78611_shell, "tdf78611.odt", "10-testing-addresses.ods", "testing-addresses") { // prepare unit test and run executeMailMerge(); // check: each page (one page is one sub doc) has different paragraphs and header paragraphs. // All header paragraphs should have numbering. xmlDocUniquePtr pXmlDoc = parseLayoutDump(static_cast(mxSwTextDocument.get())); // check first page assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.1"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.2"); // check some other pages assertXPath(pXmlDoc, "/root/page[3]/body/txt[6]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1"); assertXPath(pXmlDoc, "/root/page[5]/body/txt[8]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.1"); assertXPath(pXmlDoc, "/root/page[7]/body/txt[10]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.2"); } DECLARE_FILE_MAILMERGE_TEST(testTd78611_file, "tdf78611.odt", "10-testing-addresses.ods", "testing-addresses") { executeMailMerge(true); for (int doc = 0; doc < 10; ++doc) { loadMailMergeDocument( doc ); xmlDocUniquePtr pXmlDoc = parseLayoutDump(static_cast(mxSwTextDocument.get())); assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.1"); assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.2"); } } DECLARE_SHELL_MAILMERGE_TEST(testTdf122156_shell, "linked-with-condition.odt", "5-with-blanks.ods", "names") { // A document with a linked section hidden on an "empty field" condition // For combined documents, hidden sections are removed completely executeMailMerge(); CPPUNIT_ASSERT(mxSwTextDocument); // 5 documents 1 page each, starting at odd page numbers => 9 CPPUNIT_ASSERT_EQUAL(sal_uInt16(9), mxSwTextDocument->GetDocShell()->GetWrtShell()->GetPhyPageNum()); uno::Reference xSections(mxSwTextDocument->getTextSections(), uno::UNO_QUERY_THROW); // 2 out of 5 dataset records have empty "Title" field => no sections in respective documents CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xSections->getCount()); } DECLARE_FILE_MAILMERGE_TEST(testTdf122156_file, "linked-with-condition.odt", "5-with-blanks.ods", "names") { // A document with a linked section hidden on an "empty field" condition // For separate documents, the sections are removed executeMailMerge(); { loadMailMergeDocument(0); uno::Reference xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount()); } { loadMailMergeDocument(1); uno::Reference xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); uno::Reference xSect(xSections->getByIndex(0), uno::UNO_QUERY_THROW); // Record 2 has non-empty "Title" field => section is shown CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect, u"IsVisible"_ustr)); } { loadMailMergeDocument(2); uno::Reference xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); uno::Reference xSect(xSections->getByIndex(0), uno::UNO_QUERY_THROW); // Record 3 has non-empty "Title" field => section is shown CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect, u"IsVisible"_ustr)); } { loadMailMergeDocument(3); uno::Reference xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount()); } { loadMailMergeDocument(4); uno::Reference xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); uno::Reference xSect(xSections->getByIndex(0), uno::UNO_QUERY_THROW); // Record 5 has non-empty "Title" field => section is shown CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect, u"IsVisible"_ustr)); } } DECLARE_SHELL_MAILMERGE_TEST(exportDirectToPDF_shell, "linked-with-condition.odt", "5-with-blanks.ods", "names") { executeMailMerge(); CPPUNIT_ASSERT(mxSwTextDocument.is()); uno::Reference xController(mxSwTextDocument->getCurrentController()); CPPUNIT_ASSERT(xController.is()); uno::Reference xSupplier(xController, uno::UNO_QUERY); CPPUNIT_ASSERT(xSupplier.is()); uno::Reference xPageCursor(xSupplier->getViewCursor(), uno::UNO_QUERY); CPPUNIT_ASSERT(xPageCursor.is()); xPageCursor->jumpToFirstPage(); CPPUNIT_ASSERT_EQUAL(sal_Int16(1), xPageCursor->getPage()); uno::Reference xFrame(xController->getFrame()); CPPUNIT_ASSERT(xFrame.is()); uno::Reference xDispatchProvider(xFrame, uno::UNO_QUERY); CPPUNIT_ASSERT(xDispatchProvider.is()); util::URL aURL; aURL.Complete = ".uno:ExportDirectToPDF"; { uno::Reference xParser(css::util::URLTransformer::create( comphelper::getProcessComponentContext())); CPPUNIT_ASSERT(xParser.is()); xParser->parseStrict(aURL); } uno::Reference xDispatch = xDispatchProvider->queryDispatch(aURL, OUString(), 0); CPPUNIT_ASSERT(xDispatch.is()); const OUString sExportTo(msMailMergeOutputURL + "/ExportDirectToPDF.pdf"); uno::Sequence aArgs { comphelper::makePropertyValue(u"SynchronMode"_ustr, true), comphelper::makePropertyValue(u"URL"_ustr, sExportTo) }; xDispatch->dispatch(aURL, aArgs); CPPUNIT_ASSERT(comphelper::DirectoryHelper::fileExists(sExportTo)); SvFileStream aPDFFile(sExportTo, StreamMode::READ); SvMemoryStream aMemory; aMemory.WriteStream(aPDFFile); std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; std::unique_ptr pPdfDocument = pPDFium->openDocument(aMemory.GetData(), aMemory.GetSize(), OString()); CPPUNIT_ASSERT(pPdfDocument); CPPUNIT_ASSERT_EQUAL(5, pPdfDocument->getPageCount()); std::unique_ptr pPdfPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT(pPdfPage); CPPUNIT_ASSERT_EQUAL(4, pPdfPage->getObjectCount()); } DECLARE_SHELL_MAILMERGE_TEST(testTdf121168, "section_ps.odt", "4_v01.ods", "Tabelle1") { // A document starting with a section on a page with non-default page style with header executeMailMerge(); CPPUNIT_ASSERT(mxSwTextDocument); // 4 documents 1 page each, starting at odd page numbers => 7 CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), mxSwTextDocument->GetDocShell()->GetWrtShell()->GetPhyPageNum()); SwDoc* pDocMM = mxSwTextDocument->GetDocShell()->GetDoc(); SwNodeOffset nSizeMM = pDocMM->GetNodes().GetEndOfContent().GetIndex() - pDocMM->GetNodes().GetEndOfExtras().GetIndex() - 2; CPPUNIT_ASSERT_EQUAL(SwNodeOffset(16), nSizeMM); // All even pages should be empty, all sub-documents have one page const SwRootFrame* pLayout = pDocMM->getIDocumentLayoutAccess().GetCurrentLayout(); const SwPageFrame* pPageFrm = static_cast(pLayout->Lower()); while (pPageFrm) { sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum(); bool bOdd = (1 == (nPageNum % 2)); CPPUNIT_ASSERT_EQUAL(!bOdd, pPageFrm->IsEmptyPage()); CPPUNIT_ASSERT_EQUAL(sal_uInt16(bOdd ? 1 : 2), pPageFrm->GetVirtPageNum()); if (bOdd) { const SwPageDesc* pDesc = pPageFrm->GetPageDesc(); CPPUNIT_ASSERT_EQUAL(OUString("Teststyle" + OUString::number(nPageNum / 2 + 1)), pDesc->GetName()); } pPageFrm = static_cast(pPageFrm->GetNext()); } } DECLARE_FILE_MAILMERGE_TEST(testTdf81782_file, "tdf78611.odt", "10-testing-addresses.ods", "testing-addresses") { executeMailMerge(true); for (int doc = 0; doc < 10; ++doc) { loadMailMergeDocument( doc ); // get document properties uno::Reference xDocumentPropertiesSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xDocumentProperties(xDocumentPropertiesSupplier->getDocumentProperties()); // check if properties were set uno::Sequence aKeywords(xDocumentProperties->getKeywords()); CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aKeywords.getLength()); CPPUNIT_ASSERT_EQUAL(u"one two"_ustr, aKeywords[0]); // check title and subject CPPUNIT_ASSERT_EQUAL(u"my title"_ustr, xDocumentProperties->getTitle()); CPPUNIT_ASSERT_EQUAL(u"my subject"_ustr, xDocumentProperties->getSubject()); } } // problem was: field content was duplicated & truncated DECLARE_SHELL_MAILMERGE_TEST(testTdf81750_shell, "tdf81750.odt", "10-testing-addresses.ods", "testing-addresses") { // prepare unit test and run executeMailMerge(); // check several pages page OUString aExpected(u"Text: Foo "_ustr); xmlDocUniquePtr pXmlDoc = parseLayoutDump(static_cast(mxSwTextDocument.get())); assertXPathContent(pXmlDoc, "/root/page[1]/body/txt[2]", aExpected); assertXPathContent(pXmlDoc, "/root/page[3]/body/txt[2]", aExpected); assertXPathContent(pXmlDoc, "/root/page[5]/body/txt[2]", aExpected); assertXPathContent(pXmlDoc, "/root/page[7]/body/txt[2]", aExpected); assertXPathContent(pXmlDoc, "/root/page[9]/body/txt[2]", aExpected); } DECLARE_FILE_MAILMERGE_TEST(testTdf123057_file, "pagecounttest.ott", "db_pagecounttest.ods", "Sheet1") { uno::Reference xSect0, xSect1; executeMailMerge(true); for (int doc = 0; doc < 4; ++doc) { loadMailMergeDocument(doc); // get document properties uno::Reference xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW); uno::Reference xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW); switch (doc) { case 0: CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount()); xSect0.set(xSections->getByIndex(0), uno::UNO_QUERY_THROW); xSect1.set(xSections->getByIndex(1), uno::UNO_QUERY_THROW); // both sections visible, page num is 2 CPPUNIT_ASSERT_EQUAL(2, getPages()); CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect0, u"IsVisible"_ustr)); CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect1, u"IsVisible"_ustr)); break; case 1: CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); xSect0.set(xSections->getByIndex(0), uno::UNO_QUERY_THROW); // second section removed, page num is 1 CPPUNIT_ASSERT_EQUAL(1, getPages()); CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect0, u"IsVisible"_ustr)); break; case 2: CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount()); xSect0.set(xSections->getByIndex(0), uno::UNO_QUERY_THROW); // first section removed, page num is 1 CPPUNIT_ASSERT_EQUAL(1, getPages()); CPPUNIT_ASSERT_EQUAL(true, getProperty(xSect0, u"IsVisible"_ustr)); break; case 3: CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount()); // both sections removed, page num is 1 CPPUNIT_ASSERT_EQUAL(1, getPages()); break; } } } // The document has a header with page number and total page count on page 2 // (which uses page style "Default Style") but doesn't have a header set // for the first page (which uses page style "First Page"). // Fields in the header hadn't been replaced properly. DECLARE_SHELL_MAILMERGE_TEST(testTdf128148, "tdf128148.odt", "4_v01.ods", "Tabelle1") { executeMailMerge(); CPPUNIT_ASSERT(mxSwTextDocument); // 4 documents with 2 pages each => 8 pages in total CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), mxSwTextDocument->GetDocShell()->GetWrtShell()->GetPhyPageNum()); SwDoc* pDocMM = mxSwTextDocument->GetDocShell()->GetDoc(); rtl::Reference xModel = mxSwTextDocument->GetDocShell()->GetBaseModel(); uno::Reference xStyleFamilies = xModel->getStyleFamilies(); uno::Reference xStyleFamily(xStyleFamilies->getByName(u"PageStyles"_ustr), uno::UNO_QUERY); // All odd pages have no header, all even pages should have header with text "Page 2 of 2" const SwRootFrame* pLayout = pDocMM->getIDocumentLayoutAccess().GetCurrentLayout(); const SwPageFrame* pPageFrm = static_cast(pLayout->Lower()); while (pPageFrm) { const sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum(); const bool bIsEvenPage = ((nPageNum % 2) == 0); const OUString& sPageStyle = pPageFrm->GetPageDesc()->GetName(); uno::Reference xPageStyle(xStyleFamily->getByName(sPageStyle), uno::UNO_QUERY); bool bHeaderIsOn = false; xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn; // first page for every data record shouldn't have header, second should CPPUNIT_ASSERT_EQUAL(bIsEvenPage, bHeaderIsOn); if (bIsEvenPage) { // text in header on even pages with correctly replaced fields is "Page 2 of 2" uno::Reference xHeaderText; xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText; const OUString sHeaderText = xHeaderText->getString(); CPPUNIT_ASSERT_EQUAL(u"Page 2 of 2"_ustr, sHeaderText); } pPageFrm = static_cast(pPageFrm->GetNext()); } } DECLARE_MAILMERGE_TEST(testGrabBag, "grabbagtest.docx", "onecell.xlsx", "Sheet1", "MS Word 2007 XML", MMTest2, 0, nullptr) { executeMailMerge(true); loadMailMergeDocument(0, ".docx"); CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), getSwDocShell()->GetWrtShell()->GetPhyPageNum()); // check grabbag uno::Reference const xModel( mxComponent, uno::UNO_QUERY_THROW); uno::Sequence aInteropGrabBag; uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); uno::Reference xTextDocumentPropertySet(xTextDocument, uno::UNO_QUERY); xTextDocumentPropertySet->getPropertyValue(u"InteropGrabBag"_ustr) >>= aInteropGrabBag; CPPUNIT_ASSERT_EQUAL(sal_Int32(12), aInteropGrabBag.getLength()); // check table border - comes from table style "Tabellenraster" uno::Reference const xTable(getParagraphOrTable(1, xTextDocument->getText()), uno::UNO_QUERY_THROW); uno::Reference const xTableProps(xTable, uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(table::TableBorder( table::BorderLine(util::Color(0), 0, 18, 0), true, table::BorderLine(util::Color(0), 0, 18, 0), true, table::BorderLine(util::Color(0), 0, 18, 0), true, table::BorderLine(util::Color(0), 0, 18, 0), true, table::BorderLine(util::Color(0), 0, 18, 0), true, table::BorderLine(util::Color(0), 0, 0, 0), true, sal_Int16(191), true), getProperty(xTableProps, u"TableBorder"_ustr)); // check font is Arial - comes from theme (wrong result was "" - nothing) uno::Reference const xCell(xTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY_THROW); uno::Reference const xParaA1(getParagraphOrTable(1, xCell->getText()), uno::UNO_QUERY_THROW); CPPUNIT_ASSERT_EQUAL(u"Arial"_ustr, getProperty(xParaA1, u"CharFontName"_ustr)); } } // end of anonymous namespace namespace com::sun::star::table { static std::ostream& operator<<(std::ostream& rStream, table::BorderLine const& rLine) { rStream << "BorderLine(" << rLine.Color << "," << rLine.InnerLineWidth << "," << rLine.OuterLineWidth << "," << rLine.LineDistance << ")"; return rStream; } static std::ostream& operator<<(std::ostream& rStream, table::TableBorder const& rBorder) { rStream << "TableBorder(\n " << rBorder.TopLine << "," << static_cast(rBorder.IsTopLineValid) << ",\n " << rBorder.BottomLine << "," << static_cast(rBorder.IsBottomLineValid) << ",\n " << rBorder.LeftLine << "," << static_cast(rBorder.IsLeftLineValid) << ",\n " << rBorder.RightLine << "," << static_cast(rBorder.IsRightLineValid) << ",\n " << rBorder.HorizontalLine << "," << static_cast(rBorder.IsHorizontalLineValid) << ",\n " << rBorder.VerticalLine << "," << static_cast(rBorder.IsVerticalLineValid) << ",\n " << rBorder.Distance << "," << static_cast(rBorder.IsDistanceValid) << ")"; return rStream; } } CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */