tdf#154285 Check upper bound of arguments in SbRtl_Minute function
[LibreOffice.git] / sw / qa / extras / mailmerge / mailmerge2.cxx
blob1ba13874dfc1318d7a42aac2cf75b18d4bd935e4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "mailmergetestbase.cxx"
12 #include <com/sun/star/text/XPageCursor.hpp>
13 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
14 #include <com/sun/star/util/URLTransformer.hpp>
15 #include <comphelper/sequence.hxx>
16 #include <comphelper/processfactory.hxx>
17 #include <comphelper/propertyvalue.hxx>
18 #include <comphelper/DirectoryHelper.hxx>
20 namespace
23 class MMTest2 : public MailMergeTestBase
27 DECLARE_SHELL_MAILMERGE_TEST(tdf125522_shell, "tdf125522.odt", "10-testing-addresses.ods", "testing-addresses")
29 // prepare unit test and run
30 executeMailMerge();
32 // there should be no any text frame in output
33 CPPUNIT_ASSERT(mxSwTextDocument);
35 const auto & rNodes = mxSwTextDocument->GetDocShell()->GetDoc()->GetNodes();
36 for (SwNodeOffset nodeIndex(0); nodeIndex<rNodes.Count(); nodeIndex++)
38 SwNode* aNode = rNodes[nodeIndex];
39 if (aNode->StartOfSectionNode())
41 CPPUNIT_ASSERT(!aNode->StartOfSectionNode()->GetFlyFormat());
46 DECLARE_SHELL_MAILMERGE_TEST(testTd78611_shell, "tdf78611.odt", "10-testing-addresses.ods", "testing-addresses")
48 // prepare unit test and run
49 executeMailMerge();
51 // check: each page (one page is one sub doc) has different paragraphs and header paragraphs.
52 // All header paragraphs should have numbering.
53 xmlDocUniquePtr pXmlDoc = parseLayoutDump(static_cast<SfxBaseModel*>(mxSwTextDocument.get()));
55 // check first page
56 assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1");
57 assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.1");
58 assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.2");
60 // check some other pages
61 assertXPath(pXmlDoc, "/root/page[3]/body/txt[6]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1");
62 assertXPath(pXmlDoc, "/root/page[5]/body/txt[8]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.1");
63 assertXPath(pXmlDoc, "/root/page[7]/body/txt[10]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.2");
67 DECLARE_FILE_MAILMERGE_TEST(testTd78611_file, "tdf78611.odt", "10-testing-addresses.ods", "testing-addresses")
69 executeMailMerge(true);
70 for (int doc = 0; doc < 10; ++doc)
72 loadMailMergeDocument( doc );
73 xmlDocUniquePtr pXmlDoc = parseLayoutDump(static_cast<SfxBaseModel*>(mxSwTextDocument.get()));
74 assertXPath(pXmlDoc, "/root/page[1]/body/txt[6]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1");
75 assertXPath(pXmlDoc, "/root/page[1]/body/txt[8]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.1");
76 assertXPath(pXmlDoc, "/root/page[1]/body/txt[10]/SwParaPortion/SwLineLayout/SwFieldPortion", "expand", u"1.2");
80 DECLARE_SHELL_MAILMERGE_TEST(testTdf122156_shell, "linked-with-condition.odt", "5-with-blanks.ods",
81 "names")
83 // A document with a linked section hidden on an "empty field" condition
84 // For combined documents, hidden sections are removed completely
85 executeMailMerge();
86 CPPUNIT_ASSERT(mxSwTextDocument);
87 // 5 documents 1 page each, starting at odd page numbers => 9
88 CPPUNIT_ASSERT_EQUAL(sal_uInt16(9), mxSwTextDocument->GetDocShell()->GetWrtShell()->GetPhyPageNum());
89 uno::Reference<container::XIndexAccess> xSections(mxSwTextDocument->getTextSections(),
90 uno::UNO_QUERY_THROW);
91 // 2 out of 5 dataset records have empty "Title" field => no sections in respective documents
92 CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xSections->getCount());
95 DECLARE_FILE_MAILMERGE_TEST(testTdf122156_file, "linked-with-condition.odt", "5-with-blanks.ods",
96 "names")
98 // A document with a linked section hidden on an "empty field" condition
99 // For separate documents, the sections are removed
100 executeMailMerge();
102 loadMailMergeDocument(0);
103 uno::Reference<text::XTextSectionsSupplier> xSectionsSupplier(mxComponent,
104 uno::UNO_QUERY_THROW);
105 uno::Reference<container::XIndexAccess> xSections(xSectionsSupplier->getTextSections(),
106 uno::UNO_QUERY_THROW);
107 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount());
110 loadMailMergeDocument(1);
111 uno::Reference<text::XTextSectionsSupplier> xSectionsSupplier(mxComponent,
112 uno::UNO_QUERY_THROW);
113 uno::Reference<container::XIndexAccess> xSections(xSectionsSupplier->getTextSections(),
114 uno::UNO_QUERY_THROW);
115 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
116 uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), uno::UNO_QUERY_THROW);
117 // Record 2 has non-empty "Title" field => section is shown
118 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, u"IsVisible"_ustr));
121 loadMailMergeDocument(2);
122 uno::Reference<text::XTextSectionsSupplier> xSectionsSupplier(mxComponent,
123 uno::UNO_QUERY_THROW);
124 uno::Reference<container::XIndexAccess> xSections(xSectionsSupplier->getTextSections(),
125 uno::UNO_QUERY_THROW);
126 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
127 uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), uno::UNO_QUERY_THROW);
128 // Record 3 has non-empty "Title" field => section is shown
129 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, u"IsVisible"_ustr));
132 loadMailMergeDocument(3);
133 uno::Reference<text::XTextSectionsSupplier> xSectionsSupplier(mxComponent,
134 uno::UNO_QUERY_THROW);
135 uno::Reference<container::XIndexAccess> xSections(xSectionsSupplier->getTextSections(),
136 uno::UNO_QUERY_THROW);
137 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount());
140 loadMailMergeDocument(4);
141 uno::Reference<text::XTextSectionsSupplier> xSectionsSupplier(mxComponent,
142 uno::UNO_QUERY_THROW);
143 uno::Reference<container::XIndexAccess> xSections(xSectionsSupplier->getTextSections(),
144 uno::UNO_QUERY_THROW);
145 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
146 uno::Reference<beans::XPropertySet> xSect(xSections->getByIndex(0), uno::UNO_QUERY_THROW);
147 // Record 5 has non-empty "Title" field => section is shown
148 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect, u"IsVisible"_ustr));
152 DECLARE_SHELL_MAILMERGE_TEST(exportDirectToPDF_shell, "linked-with-condition.odt", "5-with-blanks.ods",
153 "names")
155 executeMailMerge();
157 CPPUNIT_ASSERT(mxSwTextDocument.is());
159 uno::Reference<css::frame::XController> xController(mxSwTextDocument->getCurrentController());
160 CPPUNIT_ASSERT(xController.is());
162 uno::Reference<css::text::XTextViewCursorSupplier> xSupplier(xController, uno::UNO_QUERY);
163 CPPUNIT_ASSERT(xSupplier.is());
165 uno::Reference<css::text::XPageCursor> xPageCursor(xSupplier->getViewCursor(), uno::UNO_QUERY);
166 CPPUNIT_ASSERT(xPageCursor.is());
168 xPageCursor->jumpToFirstPage();
169 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), xPageCursor->getPage());
171 uno::Reference<css::frame::XFrame> xFrame(xController->getFrame());
172 CPPUNIT_ASSERT(xFrame.is());
174 uno::Reference<css::frame::XDispatchProvider> xDispatchProvider(xFrame, uno::UNO_QUERY);
175 CPPUNIT_ASSERT(xDispatchProvider.is());
177 util::URL aURL;
178 aURL.Complete = ".uno:ExportDirectToPDF";
180 uno::Reference<css::util::XURLTransformer> xParser(css::util::URLTransformer::create(
181 comphelper::getProcessComponentContext()));
182 CPPUNIT_ASSERT(xParser.is());
183 xParser->parseStrict(aURL);
186 uno::Reference<css::frame::XDispatch> xDispatch = xDispatchProvider->queryDispatch(aURL, OUString(), 0);
187 CPPUNIT_ASSERT(xDispatch.is());
189 const OUString sExportTo(msMailMergeOutputURL + "/ExportDirectToPDF.pdf");
190 uno::Sequence <css::beans::PropertyValue> aArgs {
191 comphelper::makePropertyValue(u"SynchronMode"_ustr, true),
192 comphelper::makePropertyValue(u"URL"_ustr, sExportTo)
195 xDispatch->dispatch(aURL, aArgs);
196 CPPUNIT_ASSERT(comphelper::DirectoryHelper::fileExists(sExportTo));
198 SvFileStream aPDFFile(sExportTo, StreamMode::READ);
199 SvMemoryStream aMemory;
200 aMemory.WriteStream(aPDFFile);
201 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
202 if (!pPDFium)
203 return;
205 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
206 = pPDFium->openDocument(aMemory.GetData(), aMemory.GetSize(), OString());
207 CPPUNIT_ASSERT(pPdfDocument);
208 CPPUNIT_ASSERT_EQUAL(5, pPdfDocument->getPageCount());
210 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(0);
211 CPPUNIT_ASSERT(pPdfPage);
212 CPPUNIT_ASSERT_EQUAL(4, pPdfPage->getObjectCount());
215 DECLARE_SHELL_MAILMERGE_TEST(testTdf121168, "section_ps.odt", "4_v01.ods", "Tabelle1")
217 // A document starting with a section on a page with non-default page style with header
218 executeMailMerge();
219 CPPUNIT_ASSERT(mxSwTextDocument);
220 // 4 documents 1 page each, starting at odd page numbers => 7
221 CPPUNIT_ASSERT_EQUAL(sal_uInt16(7), mxSwTextDocument->GetDocShell()->GetWrtShell()->GetPhyPageNum());
223 SwDoc* pDocMM = mxSwTextDocument->GetDocShell()->GetDoc();
224 SwNodeOffset nSizeMM = pDocMM->GetNodes().GetEndOfContent().GetIndex()
225 - pDocMM->GetNodes().GetEndOfExtras().GetIndex() - 2;
226 CPPUNIT_ASSERT_EQUAL(SwNodeOffset(16), nSizeMM);
228 // All even pages should be empty, all sub-documents have one page
229 const SwRootFrame* pLayout = pDocMM->getIDocumentLayoutAccess().GetCurrentLayout();
230 const SwPageFrame* pPageFrm = static_cast<const SwPageFrame*>(pLayout->Lower());
231 while (pPageFrm)
233 sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum();
234 bool bOdd = (1 == (nPageNum % 2));
235 CPPUNIT_ASSERT_EQUAL(!bOdd, pPageFrm->IsEmptyPage());
236 CPPUNIT_ASSERT_EQUAL(sal_uInt16(bOdd ? 1 : 2), pPageFrm->GetVirtPageNum());
237 if (bOdd)
239 const SwPageDesc* pDesc = pPageFrm->GetPageDesc();
240 CPPUNIT_ASSERT_EQUAL(OUString("Teststyle" + OUString::number(nPageNum / 2 + 1)),
241 pDesc->GetName());
243 pPageFrm = static_cast<const SwPageFrame*>(pPageFrm->GetNext());
248 DECLARE_FILE_MAILMERGE_TEST(testTdf81782_file, "tdf78611.odt", "10-testing-addresses.ods", "testing-addresses")
250 executeMailMerge(true);
251 for (int doc = 0; doc < 10; ++doc)
253 loadMailMergeDocument( doc );
255 // get document properties
256 uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(mxComponent, uno::UNO_QUERY);
257 uno::Reference<document::XDocumentProperties> xDocumentProperties(xDocumentPropertiesSupplier->getDocumentProperties());
259 // check if properties were set
260 uno::Sequence<OUString> aKeywords(xDocumentProperties->getKeywords());
261 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aKeywords.getLength());
262 CPPUNIT_ASSERT_EQUAL(u"one two"_ustr, aKeywords[0]);
264 // check title and subject
265 CPPUNIT_ASSERT_EQUAL(u"my title"_ustr, xDocumentProperties->getTitle());
266 CPPUNIT_ASSERT_EQUAL(u"my subject"_ustr, xDocumentProperties->getSubject());
270 // problem was: field content was duplicated & truncated
271 DECLARE_SHELL_MAILMERGE_TEST(testTdf81750_shell, "tdf81750.odt", "10-testing-addresses.ods", "testing-addresses")
273 // prepare unit test and run
274 executeMailMerge();
276 // check several pages page
277 OUString aExpected(u"Text: Foo "_ustr);
278 xmlDocUniquePtr pXmlDoc = parseLayoutDump(static_cast<SfxBaseModel*>(mxSwTextDocument.get()));
279 assertXPathContent(pXmlDoc, "/root/page[1]/body/txt[2]", aExpected);
280 assertXPathContent(pXmlDoc, "/root/page[3]/body/txt[2]", aExpected);
281 assertXPathContent(pXmlDoc, "/root/page[5]/body/txt[2]", aExpected);
282 assertXPathContent(pXmlDoc, "/root/page[7]/body/txt[2]", aExpected);
283 assertXPathContent(pXmlDoc, "/root/page[9]/body/txt[2]", aExpected);
287 DECLARE_FILE_MAILMERGE_TEST(testTdf123057_file, "pagecounttest.ott", "db_pagecounttest.ods", "Sheet1")
289 uno::Reference<beans::XPropertySet> xSect0, xSect1;
290 executeMailMerge(true);
292 for (int doc = 0; doc < 4; ++doc)
294 loadMailMergeDocument(doc);
296 // get document properties
297 uno::Reference<text::XTextSectionsSupplier> xSectionsSupplier(mxComponent, uno::UNO_QUERY_THROW);
298 uno::Reference<container::XIndexAccess> xSections(xSectionsSupplier->getTextSections(), uno::UNO_QUERY_THROW);
300 switch (doc)
302 case 0:
303 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xSections->getCount());
304 xSect0.set(xSections->getByIndex(0), uno::UNO_QUERY_THROW);
305 xSect1.set(xSections->getByIndex(1), uno::UNO_QUERY_THROW);
307 // both sections visible, page num is 2
308 CPPUNIT_ASSERT_EQUAL(2, getPages());
309 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, u"IsVisible"_ustr));
310 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect1, u"IsVisible"_ustr));
311 break;
312 case 1:
313 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
314 xSect0.set(xSections->getByIndex(0), uno::UNO_QUERY_THROW);
316 // second section removed, page num is 1
317 CPPUNIT_ASSERT_EQUAL(1, getPages());
318 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, u"IsVisible"_ustr));
319 break;
320 case 2:
321 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xSections->getCount());
322 xSect0.set(xSections->getByIndex(0), uno::UNO_QUERY_THROW);
324 // first section removed, page num is 1
325 CPPUNIT_ASSERT_EQUAL(1, getPages());
326 CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xSect0, u"IsVisible"_ustr));
327 break;
328 case 3:
329 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xSections->getCount());
330 // both sections removed, page num is 1
331 CPPUNIT_ASSERT_EQUAL(1, getPages());
332 break;
337 // The document has a header with page number and total page count on page 2
338 // (which uses page style "Default Style") but doesn't have a header set
339 // for the first page (which uses page style "First Page").
340 // Fields in the header hadn't been replaced properly.
341 DECLARE_SHELL_MAILMERGE_TEST(testTdf128148, "tdf128148.odt", "4_v01.ods", "Tabelle1")
343 executeMailMerge();
344 CPPUNIT_ASSERT(mxSwTextDocument);
346 // 4 documents with 2 pages each => 8 pages in total
347 CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), mxSwTextDocument->GetDocShell()->GetWrtShell()->GetPhyPageNum());
349 SwDoc* pDocMM = mxSwTextDocument->GetDocShell()->GetDoc();
350 rtl::Reference<SwXTextDocument> xModel = mxSwTextDocument->GetDocShell()->GetBaseModel();
351 uno::Reference<container::XNameAccess> xStyleFamilies = xModel->getStyleFamilies();
352 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(u"PageStyles"_ustr), uno::UNO_QUERY);
354 // All odd pages have no header, all even pages should have header with text "Page 2 of 2"
355 const SwRootFrame* pLayout = pDocMM->getIDocumentLayoutAccess().GetCurrentLayout();
356 const SwPageFrame* pPageFrm = static_cast<const SwPageFrame*>(pLayout->Lower());
357 while (pPageFrm)
359 const sal_uInt16 nPageNum = pPageFrm->GetPhyPageNum();
360 const bool bIsEvenPage = ((nPageNum % 2) == 0);
362 const OUString& sPageStyle = pPageFrm->GetPageDesc()->GetName();
363 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(sPageStyle), uno::UNO_QUERY);
365 bool bHeaderIsOn = false;
366 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
368 // first page for every data record shouldn't have header, second should
369 CPPUNIT_ASSERT_EQUAL(bIsEvenPage, bHeaderIsOn);
370 if (bIsEvenPage)
372 // text in header on even pages with correctly replaced fields is "Page 2 of 2"
373 uno::Reference<text::XText> xHeaderText;
374 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
375 const OUString sHeaderText = xHeaderText->getString();
376 CPPUNIT_ASSERT_EQUAL(u"Page 2 of 2"_ustr, sHeaderText);
379 pPageFrm = static_cast<const SwPageFrame*>(pPageFrm->GetNext());
383 DECLARE_MAILMERGE_TEST(testGrabBag, "grabbagtest.docx", "onecell.xlsx", "Sheet1", "MS Word 2007 XML", MMTest2, 0, nullptr)
385 executeMailMerge(true);
387 loadMailMergeDocument(0, ".docx");
389 CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), getSwDocShell()->GetWrtShell()->GetPhyPageNum());
391 // check grabbag
392 uno::Reference<beans::XPropertySet> const xModel(
393 mxComponent, uno::UNO_QUERY_THROW);
394 uno::Sequence<beans::PropertyValue> aInteropGrabBag;
395 uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
396 uno::Reference<beans::XPropertySet> xTextDocumentPropertySet(xTextDocument, uno::UNO_QUERY);
397 xTextDocumentPropertySet->getPropertyValue(u"InteropGrabBag"_ustr) >>= aInteropGrabBag;
398 CPPUNIT_ASSERT_EQUAL(sal_Int32(12), aInteropGrabBag.getLength());
400 // check table border - comes from table style "Tabellenraster"
401 uno::Reference<text::XTextTable> const xTable(getParagraphOrTable(1, xTextDocument->getText()), uno::UNO_QUERY_THROW);
402 uno::Reference<beans::XPropertySet> const xTableProps(xTable, uno::UNO_QUERY_THROW);
403 CPPUNIT_ASSERT_EQUAL(table::TableBorder(
404 table::BorderLine(util::Color(0), 0, 18, 0), true,
405 table::BorderLine(util::Color(0), 0, 18, 0), true,
406 table::BorderLine(util::Color(0), 0, 18, 0), true,
407 table::BorderLine(util::Color(0), 0, 18, 0), true,
408 table::BorderLine(util::Color(0), 0, 18, 0), true,
409 table::BorderLine(util::Color(0), 0, 0, 0), true,
410 sal_Int16(191), true),
411 getProperty<table::TableBorder>(xTableProps, u"TableBorder"_ustr));
413 // check font is Arial - comes from theme (wrong result was "" - nothing)
414 uno::Reference<text::XText> const xCell(xTable->getCellByName(u"A1"_ustr), uno::UNO_QUERY_THROW);
415 uno::Reference<beans::XPropertySet> const xParaA1(getParagraphOrTable(1, xCell->getText()), uno::UNO_QUERY_THROW);
416 CPPUNIT_ASSERT_EQUAL(u"Arial"_ustr, getProperty<OUString>(xParaA1, u"CharFontName"_ustr));
419 } // end of anonymous namespace
420 namespace com::sun::star::table {
422 static std::ostream& operator<<(std::ostream& rStream, table::BorderLine const& rLine)
424 rStream << "BorderLine(" << rLine.Color << "," << rLine.InnerLineWidth << "," << rLine.OuterLineWidth << "," << rLine.LineDistance << ")";
425 return rStream;
428 static std::ostream& operator<<(std::ostream& rStream, table::TableBorder const& rBorder)
430 rStream << "TableBorder(\n "
431 << rBorder.TopLine << "," << static_cast<bool>(rBorder.IsTopLineValid) << ",\n "
432 << rBorder.BottomLine << "," << static_cast<bool>(rBorder.IsBottomLineValid) << ",\n "
433 << rBorder.LeftLine << "," << static_cast<bool>(rBorder.IsLeftLineValid) << ",\n "
434 << rBorder.RightLine << "," << static_cast<bool>(rBorder.IsRightLineValid) << ",\n "
435 << rBorder.HorizontalLine << "," << static_cast<bool>(rBorder.IsHorizontalLineValid) << ",\n "
436 << rBorder.VerticalLine << "," << static_cast<bool>(rBorder.IsVerticalLineValid) << ",\n "
437 << rBorder.Distance << "," << static_cast<bool>(rBorder.IsDistanceValid) << ")";
438 return rStream;
443 CPPUNIT_PLUGIN_IMPLEMENT();
444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */