1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <swmodeltestbase.hxx>
14 #include <com/sun/star/text/BibliographyDataField.hpp>
15 #include <com/sun/star/text/BibliographyDataType.hpp>
16 #include <com/sun/star/text/ControlCharacter.hpp>
17 #include <com/sun/star/text/WritingMode2.hpp>
18 #include <com/sun/star/text/XDocumentIndex.hpp>
20 #include <vcl/gdimtf.hxx>
21 #include <vcl/metaact.hxx>
22 #include <vcl/filter/PDFiumLibrary.hxx>
23 #include <comphelper/propertyvalue.hxx>
24 #include <editeng/fhgtitem.hxx>
25 #include <editeng/wghtitem.hxx>
28 #include <unotxdoc.hxx>
30 #include <IDocumentLayoutAccess.hxx>
31 #include <rootfrm.hxx>
34 #include <pormulti.hxx>
35 #include <formatlinebreak.hxx>
36 #include <sortedobjs.hxx>
37 #include <anchoredobject.hxx>
38 #include <fmtcntnt.hxx>
39 #include <fmtfsize.hxx>
40 #include <IDocumentRedlineAccess.hxx>
41 #include <formatcontentcontrol.hxx>
42 #include <strings.hrc>
44 #include <txatbase.hxx>
45 #include <textcontentcontrol.hxx>
46 #include <pagefrm.hxx>
48 /// Covers sw/source/core/text/ fixes.
49 class SwCoreTextTest
: public SwModelTestBase
53 : SwModelTestBase("/sw/qa/core/text/data/")
58 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testFootnoteConnect
)
60 createSwDoc("footnote-connect.fodt");
61 SwDoc
* pDoc
= getSwDoc();
62 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
63 // Jump to the start of the next page.
64 pWrtShell
->SttNxtPg();
65 // Remove the page break.
67 // Split the multi-line text frame, containing an endnote.
69 // Join the split text frame.
71 // Turn the 3 page document into a 2 page one, so the endnote frame is moved.
72 // Without the accompanying fix in place, this test would have crashed due to a use-after-free
73 // in SwFootnoteFrame::GetRef().
77 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testSemiTransparentText
)
79 // Create an in-memory empty document.
82 // Set text to half-transparent and type a character.
83 uno::Reference
<beans::XPropertySet
> xParagraph(getParagraph(1), uno::UNO_QUERY
);
84 CPPUNIT_ASSERT(xParagraph
.is());
85 sal_Int16 nTransparence
= 50;
86 xParagraph
->setPropertyValue("CharTransparence", uno::Any(nTransparence
));
87 uno::Reference
<text::XTextRange
> xTextRange(xParagraph
, uno::UNO_QUERY
);
88 CPPUNIT_ASSERT(xTextRange
.is());
89 xTextRange
->setString("x");
91 // Render the document to a metafile.
92 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
93 CPPUNIT_ASSERT(pTextDoc
);
94 SwDocShell
* pDocShell
= pTextDoc
->GetDocShell();
95 CPPUNIT_ASSERT(pDocShell
);
96 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pDocShell
->GetPreviewMetaFile();
97 CPPUNIT_ASSERT(xMetaFile
);
99 // Make sure that DrawTransparent() was used during rendering.
100 MetafileXmlDump dumper
;
101 xmlDocUniquePtr pXmlDoc
= dumpAndParse(dumper
, *xMetaFile
);
102 CPPUNIT_ASSERT(pXmlDoc
);
103 assertXPath(pXmlDoc
, "//floattransparent");
106 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testBibliographyUrlPdfExport
)
108 // Given a document with a bibliography entry field:
109 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
115 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
116 uno::Reference
<beans::XPropertySet
> xField(
117 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
118 uno::Sequence
<beans::PropertyValue
> aFields
= {
119 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
120 comphelper::makePropertyValue("Identifier", OUString("AT")),
121 comphelper::makePropertyValue("Author", OUString("Author")),
122 comphelper::makePropertyValue("Title", OUString("Title")),
123 comphelper::makePropertyValue("URL", OUString("http://www.example.com/test.pdf#page=1")),
125 xField
->setPropertyValue("Fields", uno::Any(aFields
));
126 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
127 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
128 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
129 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
130 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
132 // When exporting to PDF:
133 save("writer_pdf_Export");
135 // Then make sure the field links the source.
136 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
137 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
138 // Without the accompanying fix in place, this test would have failed, the field was not
139 // clickable (while it was clickable on the UI).
140 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
143 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testBibliographyUrlPdfExport2
)
145 // Given a document with a bibliography entry field:
146 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
152 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
153 uno::Reference
<beans::XPropertySet
> xField(
154 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
155 uno::Sequence
<beans::PropertyValue
> aFields
= {
156 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
157 comphelper::makePropertyValue("Identifier", OUString("AT")),
158 comphelper::makePropertyValue("Author", OUString("Author")),
159 comphelper::makePropertyValue("Title", OUString("Title")),
160 comphelper::makePropertyValue("URL", OUString("#page=1")),
162 xField
->setPropertyValue("Fields", uno::Any(aFields
));
163 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
164 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
165 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
166 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
167 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
169 // When exporting to PDF:
170 save("writer_pdf_Export");
172 // Then make sure the field links when the Target URL is set
173 // (this test is important, isn't the same as the one above)
174 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
175 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
176 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
179 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testBibliographyUrlPdfExport3
)
181 // Given a document with a bibliography entry field:
182 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
188 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
189 uno::Reference
<beans::XPropertySet
> xField(
190 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
191 uno::Sequence
<beans::PropertyValue
> aFields
= {
192 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
193 comphelper::makePropertyValue("Identifier", OUString("AT")),
194 comphelper::makePropertyValue("Author", OUString("Author")),
195 comphelper::makePropertyValue("Title", OUString("Title")),
196 comphelper::makePropertyValue("TargetURL", OUString("#page=1")),
198 xField
->setPropertyValue("Fields", uno::Any(aFields
));
199 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
200 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
201 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
202 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
203 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
205 // When exporting to PDF:
206 save("writer_pdf_Export");
208 // Then make sure there are no links since UseTargetURL is not set
209 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
210 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
211 CPPUNIT_ASSERT(!pPdfPage
->hasLinks());
214 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testBibliographyUrlPdfExport4
)
216 // Given a document with a bibliography entry field:
217 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
223 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
224 uno::Reference
<beans::XPropertySet
> xField(
225 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
226 uno::Sequence
<beans::PropertyValue
> aFields
= {
227 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
228 comphelper::makePropertyValue("Identifier", OUString("AT")),
229 comphelper::makePropertyValue("Author", OUString("Author")),
230 comphelper::makePropertyValue("Title", OUString("Title")),
231 comphelper::makePropertyValue("TargetType", OUString("1")), // 1 == UseTargetURL
232 comphelper::makePropertyValue("TargetURL", OUString("#page=1")),
234 xField
->setPropertyValue("Fields", uno::Any(aFields
));
235 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
236 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
237 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
238 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
239 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
241 // When exporting to PDF:
242 save("writer_pdf_Export");
244 // Then make sure the field links when the Target URL is set
245 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
246 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
247 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
250 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testBibliographyUrlPdfExport5
)
252 // Given a document with a bibliography entry field:
253 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
259 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
260 uno::Reference
<beans::XPropertySet
> xField(
261 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
262 uno::Sequence
<beans::PropertyValue
> aFields
= {
263 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
264 comphelper::makePropertyValue("Identifier", OUString("AT")),
265 comphelper::makePropertyValue("Author", OUString("Author")),
266 comphelper::makePropertyValue("Title", OUString("Title")),
267 comphelper::makePropertyValue("TargetType", OUString("3")), // 3 == BibliographyTableRow
269 xField
->setPropertyValue("Fields", uno::Any(aFields
));
270 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
271 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
272 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
273 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
274 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
275 // Create a bibliography table.
276 uno::Reference
<text::XTextContent
> xTable(
277 xFactory
->createInstance("com.sun.star.text.Bibliography"), uno::UNO_QUERY
);
278 xCursor
->gotoEnd(/*bExpand=*/false);
279 xText
->insertControlCharacter(xCursor
, text::ControlCharacter::APPEND_PARAGRAPH
,
281 xText
->insertTextContent(xCursor
, xTable
, /*bAbsorb=*/false);
283 uno::Reference
<text::XDocumentIndex
> xTableIndex(xTable
, uno::UNO_QUERY
);
284 xTableIndex
->update();
286 // When exporting to PDF:
287 save("writer_pdf_Export");
289 // Then make sure the mark links to the table when table is present
290 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
291 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
292 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
295 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testBibliographyUrlPdfExport6
)
297 // Given a document with a bibliography entry field:
298 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
304 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
306 // Create a bibliography mark
307 uno::Reference
<beans::XPropertySet
> xField(
308 xFactory
->createInstance("com.sun.star.text.TextField.Bibliography"), uno::UNO_QUERY
);
309 uno::Sequence
<beans::PropertyValue
> aFields
= {
310 comphelper::makePropertyValue("BibiliographicType", text::BibliographyDataType::WWW
),
311 comphelper::makePropertyValue("Identifier", OUString("AT")),
312 comphelper::makePropertyValue("Author", OUString("AuthorName")),
313 comphelper::makePropertyValue("Title", OUString("Title")),
314 comphelper::makePropertyValue("TargetType", OUString("3")), // 3 == BibliographyTableRow
316 xField
->setPropertyValue("Fields", uno::Any(aFields
));
317 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
318 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
319 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
320 uno::Reference
<text::XTextContent
> xContent(xField
, uno::UNO_QUERY
);
321 xText
->insertTextContent(xCursor
, xContent
, /*bAbsorb=*/false);
323 // Create a bibliography table.
324 uno::Reference
<text::XTextContent
> xTable(
325 xFactory
->createInstance("com.sun.star.text.Bibliography"), uno::UNO_QUERY
);
326 xCursor
->gotoEnd(/*bExpand=*/false);
327 xText
->insertControlCharacter(xCursor
, text::ControlCharacter::APPEND_PARAGRAPH
,
329 xText
->insertTextContent(xCursor
, xTable
, /*bAbsorb=*/false);
331 // Set formatting for AUTH_TYPE_WWW to include tab stop
332 uno::Reference
<beans::XPropertySet
> xTableAsPropertySet(xTable
, uno::UNO_QUERY_THROW
);
333 uno::Reference
<container::XIndexReplace
> aAllPatterns(
334 xTableAsPropertySet
->getPropertyValue("LevelFormat"), uno::UNO_QUERY
);
336 uno::Sequence
<uno::Sequence
<beans::PropertyValue
>> aFormattingPattern
338 comphelper::makePropertyValue("TokenType", OUString("TokenBibliographyDataField")),
339 comphelper::makePropertyValue("BibliographyDataField",
340 text::BibliographyDataField::AUTHOR
),
341 comphelper::makePropertyValue("CharacterStyleName", OUString("")),
344 comphelper::makePropertyValue("TokenType", OUString("TokenTabStop")),
345 comphelper::makePropertyValue("TabStopRightAligned", true),
346 comphelper::makePropertyValue("CharacterStyleName", OUString("")),
349 comphelper::makePropertyValue("TokenType", OUString("TokenBibliographyDataField")),
350 comphelper::makePropertyValue("BibliographyDataField",
351 text::BibliographyDataField::TITLE
),
352 comphelper::makePropertyValue("CharacterStyleName", OUString("")),
355 comphelper::makePropertyValue("TokenType", OUString("TokenTabStop")),
356 comphelper::makePropertyValue("TabStopRightAligned", false),
357 comphelper::makePropertyValue("TabStopFillCharacter", OUString(".")),
358 comphelper::makePropertyValue("CharacterStyleName", OUString("")),
361 comphelper::makePropertyValue("TokenType", OUString("TokenText")),
362 comphelper::makePropertyValue("Text", OUString("FixedText")),
363 comphelper::makePropertyValue("CharacterStyleName", OUString("")),
366 aAllPatterns
->replaceByIndex(AUTH_TYPE_WWW
+ 1, uno::Any(aFormattingPattern
));
367 xTableAsPropertySet
->setPropertyValue("LevelFormat", uno::Any(aAllPatterns
));
370 uno::Reference
<text::XDocumentIndex
> xTableIndex(xTable
, uno::UNO_QUERY
);
371 xTableIndex
->update();
373 // Assert the table updated correctly
374 OUString
sExpectedPattern("AuthorName\tTitle\tFixedText");
375 OUString sDocumentText
= xTextDocument
->getText()->getString();
376 sal_Int32 iTabPos
= sDocumentText
.indexOf(sExpectedPattern
);
377 CPPUNIT_ASSERT(iTabPos
>= 0);
378 CPPUNIT_ASSERT_EQUAL(iTabPos
, sDocumentText
.lastIndexOf(sExpectedPattern
));
380 // When exporting to PDF:
381 save("writer_pdf_Export");
383 // Then make sure the mark links to the table even when format contains tab stop
384 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
385 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
386 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
389 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testTabOverMarginSection
)
391 createSwDoc("tabovermargin-section.fodt");
392 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
394 = getXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/child::*[@type='PortionType::TabRight']",
397 // Without the accompanying fix in place, this test would have failed with:
398 // - Expected less than: 5000
400 // i.e. the tab portion width was not the expected 4386, but much larger, so the number after
401 // the tab portion was not visible.
402 CPPUNIT_ASSERT_LESS(static_cast<sal_Int32
>(5000), nWidth
);
405 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testLineHeight
)
407 // Given a document with an as-char image, height in twips not fitting into sal_uInt16:
408 createSwDoc("line-height.fodt");
410 // When laying out that document:
411 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
413 // Then make sure its top is the top of the page:
414 // Without the accompanying fix in place, this test would have failed with:
417 // due to various unsigned integer truncations.
418 assertXPath(pXmlDoc
, "//fly/infos/bounds", "top", OUString::number(DOCUMENTBORDER
));
421 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testLineWidth
)
423 // Given a document with an as-char image, width in twips not fitting into sal_uInt16:
424 createSwDoc("line-width.fodt");
425 SwDoc
* pDoc
= getSwDoc();
426 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
427 sal_Int32 nOldLeft
= pWrtShell
->GetCharRect().Left();
429 // When moving the cursor to the right:
430 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 1, /*bBasicCall=*/false);
432 // Then make sure we move to the right by the image width:
433 sal_Int32 nNewLeft
= pWrtShell
->GetCharRect().Left();
434 // Without the accompanying fix in place, this test would have failed with:
435 // - Expected greater than: 65536
437 // i.e. the width (around 67408 twips) was truncated.
438 CPPUNIT_ASSERT_GREATER(static_cast<sal_Int32
>(65536), nNewLeft
- nOldLeft
);
441 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testChineseAutoFirstLineIndent
)
443 // The test document contains two simple multi-line paragraph. For both paragraphs, the first line indent
444 // is set to 'auto'. Line spacing is 100% for the 1st paragraph and 200% for the 2nd paragraph.
445 // Also, there is a "AutoFirstLineIndentDisregardLineSpace" capability flag set in the document.
446 createSwDoc("firstLineIndent-withFlag.fodt");
448 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
450 // Get the line width of the first line for the 1st paragraph.
451 sal_Int32 nFirstLineWidth
452 = getXPath(pXmlDoc
, "//body/txt[1]/SwParaPortion/SwLineLayout[1]", "width").toInt32();
453 // Get the line width of the first line for the 2nd paragraph.
454 sal_Int32 nSecondLineWidth
455 = getXPath(pXmlDoc
, "//body/txt[2]/SwParaPortion/SwLineLayout[1]", "width").toInt32();
457 // Tdf#129448: the changing of line-height should not affect the auto first line indent.
458 // As a result, the first line width of the two paragraphs should be the same.
459 CPPUNIT_ASSERT_EQUAL(nSecondLineWidth
, nFirstLineWidth
);
462 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testRuby
)
464 // Given a document with multiple ruby portions:
465 createSwDoc("ruby.fodt");
466 SwDoc
* pDoc
= getSwDoc();
468 // When laying out that document:
469 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
471 // Then make sure that no unwanted margin portions are created, making the actual text
473 SwFrame
* pPageFrame
= pLayout
->GetLower();
474 SwFrame
* pBodyFrame
= pPageFrame
->GetLower();
475 SwFrame
* pFrame
= pBodyFrame
->GetLower();
476 CPPUNIT_ASSERT(pFrame
->IsTextFrame());
477 auto pTextFrame
= static_cast<SwTextFrame
*>(pFrame
);
478 SwParaPortion
* pPara
= pTextFrame
->GetPara();
480 for (SwLinePortion
* pPor
= pPara
->GetFirstPortion(); pPor
; pPor
= pPor
->GetNextPortion())
482 // Look for multi-portions in the only paragraph of the document.
483 if (pPor
->GetWhichPor() != PortionType::Multi
)
494 // The second multi-portion has two lines, check the start of the second line.
495 auto pMulti
= static_cast<SwMultiPortion
*>(pPor
);
496 // Without the accompanying fix in place, this test would have failed, as the portion was a
497 // margin portion, not a text portion. The margin was so large that the actual text portion was
498 // hidden. No margin is needed here at all.
499 CPPUNIT_ASSERT(pMulti
->GetRoot().GetNext()->GetFirstPortion()->IsTextPortion());
503 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testEmptyNumberingPageSplit
)
505 // Given a document with 2 pages: the only para on page 1 is a numbering without a number
507 createSwDoc("empty-numbering-page-split.fodt");
509 // When inserting an image that doesn't fit the body frame:
510 // Then make sure that the layout update after insertion finishes:
511 uno::Sequence
<beans::PropertyValue
> aArgs
= {
512 comphelper::makePropertyValue("FileName", createFileURL(u
"image.png")),
514 // Without the accompanying fix in place, this never finished.
515 dispatchCommand(mxComponent
, ".uno:InsertGraphic", aArgs
);
518 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testClearingLineBreak
)
520 // Given a document with a fly frame and two characters wrapped around it:
521 createSwDoc("clearing-break.fodt");
522 // Insert a clearing break between "A" and "B":
523 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
524 uno::Reference
<text::XText
> xText
= xDocument
->getText();
525 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
526 xCursor
->gotoEnd(/*bSelect=*/false);
527 xCursor
->goLeft(/*nCount=*/1, /*bSelect=*/false);
528 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
529 uno::Reference
<text::XTextContent
> xLineBreak(
530 xFactory
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
531 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
532 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
533 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
534 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
536 // When laying out that document:
537 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
539 // Then make sure that the second line "jumps down", below the fly frame:
540 // Without the accompanying fix in place, this test would have failed with:
543 // i.e. the line height wasn't the twips value of the 1.806 cm from the file, but was based on
544 // the font size of the text, which is only correct for non-clearing breaks.
545 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout[1]", "height", "1024");
548 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testClearingLineBreakAtStart
)
550 // Given a document with a fly frame and a character wrapped around it:
551 createSwDoc("clearing-break-start.fodt");
552 // Insert a clearing break before "X":
553 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
554 uno::Reference
<text::XText
> xText
= xDocument
->getText();
555 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
556 xCursor
->gotoEnd(/*bSelect=*/false);
557 xCursor
->goLeft(/*nCount=*/1, /*bSelect=*/false);
558 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
559 uno::Reference
<text::XTextContent
> xLineBreak(
560 xFactory
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
561 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
562 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
563 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
564 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
566 // When laying out that document:
567 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
569 // Then make sure that the second line "jumps down", below the fly frame:
570 // Without the accompanying fix in place, this test would have failed with:
573 // i.e. the line height was too small, but only in case the full line was a fly and a break
574 // portion, without any real content.
575 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout[1]", "height", "1024");
578 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testClearingLineBreakLeft
)
580 // Given a document with two anchored objects (left height is 5cm, right height is 7.5cm) and a
581 // clearing break (type=left):
583 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
584 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
585 uno::Reference
<text::XText
> xText
= xDocument
->getText();
586 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
588 uno::Reference
<drawing::XShape
> xShape(
589 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
590 xShape
->setSize(awt::Size(5000, 5000));
591 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
592 xShapeProps
->setPropertyValue("AnchorType",
593 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
594 uno::Reference
<text::XTextContent
> xShapeContent(xShape
, uno::UNO_QUERY
);
595 xText
->insertTextContent(xCursor
, xShapeContent
, /*bAbsorb=*/false);
598 uno::Reference
<drawing::XShape
> xShape(
599 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
600 xShape
->setSize(awt::Size(5000, 7500));
601 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
602 xShapeProps
->setPropertyValue("AnchorType",
603 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
604 xShapeProps
->setPropertyValue("HoriOrientPosition", uno::Any(sal_Int32(10000)));
605 uno::Reference
<text::XTextContent
> xShapeContent2(xShape
, uno::UNO_QUERY
);
606 xText
->insertTextContent(xCursor
, xShapeContent2
, /*bAbsorb=*/false);
608 uno::Reference
<text::XTextContent
> xLineBreak(
609 xFactory
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
610 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
611 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::LEFT
);
612 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
613 xText
->insertString(xCursor
, "foo", /*bAbsorb=*/false);
614 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
615 xText
->insertString(xCursor
, "bar", /*bAbsorb=*/false);
617 // When laying out that document:
620 // Then make sure the "bar" jumps down below the left shape, but not below the right shape (due
622 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
623 // Without the accompanying fix in place, this test would have failed with:
626 // i.e. any non-none type was handled as type=all, and this was jumping below both shapes.
627 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout[1]", "height", "2837");
630 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testClearingLineBreakLeftRTL
)
632 // Given a document with an anchored object in an RTL para and a clearing break (type=left):
634 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
635 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
636 uno::Reference
<text::XText
> xText
= xDocument
->getText();
637 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
638 uno::Reference
<beans::XPropertySet
> xCursorProps(xCursor
, uno::UNO_QUERY
);
639 xCursorProps
->setPropertyValue("WritingMode", uno::Any(text::WritingMode2::RL_TB
));
641 uno::Reference
<drawing::XShape
> xShape(
642 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
643 xShape
->setSize(awt::Size(5000, 5000));
644 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
645 xShapeProps
->setPropertyValue("AnchorType",
646 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
647 uno::Reference
<text::XTextContent
> xShapeContent(xShape
, uno::UNO_QUERY
);
648 xText
->insertTextContent(xCursor
, xShapeContent
, /*bAbsorb=*/false);
650 uno::Reference
<text::XTextContent
> xLineBreak(
651 xFactory
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
652 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
653 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::RIGHT
);
654 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
655 xText
->insertString(xCursor
, "foo", /*bAbsorb=*/false);
656 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
657 xText
->insertString(xCursor
, "bar", /*bAbsorb=*/false);
659 // When laying out that document:
662 // Then make sure the "bar" does not jump down (due to type=left && RTL):
663 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
664 // Without the accompanying fix in place, this test would have failed with:
667 // i.e. left/right was not ignored in the RTL case.
668 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout[1]", "height", "276");
671 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testClearingLineBreakVertical
)
673 // Given a document with an anchored object in a vertical page and a clearing break (type=all):
675 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
676 uno::Reference
<text::XTextDocument
> xDocument(mxComponent
, uno::UNO_QUERY
);
677 uno::Reference
<text::XText
> xText
= xDocument
->getText();
678 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
679 uno::Reference
<beans::XPropertySet
> xStandard(getStyles("PageStyles")->getByName("Standard"),
681 xStandard
->setPropertyValue("WritingMode", uno::Any(text::WritingMode2::TB_RL
));
683 uno::Reference
<drawing::XShape
> xShape(
684 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
685 xShape
->setSize(awt::Size(5000, 5000));
686 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
687 xShapeProps
->setPropertyValue("AnchorType",
688 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
689 uno::Reference
<text::XTextContent
> xShapeContent(xShape
, uno::UNO_QUERY
);
690 xText
->insertTextContent(xCursor
, xShapeContent
, /*bAbsorb=*/false);
692 uno::Reference
<text::XTextContent
> xLineBreak(
693 xFactory
->createInstance("com.sun.star.text.LineBreak"), uno::UNO_QUERY
);
694 uno::Reference
<beans::XPropertySet
> xLineBreakProps(xLineBreak
, uno::UNO_QUERY
);
695 auto eClear
= static_cast<sal_Int16
>(SwLineBreakClear::ALL
);
696 xLineBreakProps
->setPropertyValue("Clear", uno::Any(eClear
));
697 xText
->insertString(xCursor
, "foo", /*bAbsorb=*/false);
698 xText
->insertTextContent(xCursor
, xLineBreak
, /*bAbsorb=*/false);
699 xText
->insertString(xCursor
, "bar", /*bAbsorb=*/false);
701 // When laying out that document:
704 // Then make sure the "bar" does jump (logic) down the correct amount:
705 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
706 // Without the accompanying fix in place, this test would have failed with:
709 // i.e. the expected break height is the twips value of the 5cm rectangle size, it was much
711 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout[1]/SwBreakPortion", "height", "2837");
714 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testClearingLineBreakHeader
)
716 // Given a document with a shape in the header and a clearing break in the body text:
717 createSwDoc("clearing-break-header.fodt");
719 // When laying out that document:
720 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
722 // Then make sure that the shape from the header is ignored while calculating the line height:
723 // Without the accompanying fix in place, this test would have failed with:
726 // i.e. the shape was in the background, but we failed to ignore it for the break portion.
727 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout[1]", "height", "276");
730 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testAsCharImageDocModelFromViewPoint
)
732 // Given a document with an as-char image:
734 SwDoc
* pDoc
= getSwDoc();
735 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
736 uno::Reference
<beans::XPropertySet
> xTextGraphic(
737 xFactory
->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY
);
738 // Only set the anchor type, the actual bitmap content is not interesting.
739 xTextGraphic
->setPropertyValue("AnchorType",
740 uno::Any(text::TextContentAnchorType_AS_CHARACTER
));
741 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
742 uno::Reference
<text::XText
> xBodyText
= xTextDocument
->getText();
743 uno::Reference
<text::XTextCursor
> xCursor(xBodyText
->createTextCursor());
744 uno::Reference
<text::XTextContent
> xTextContent(xTextGraphic
, uno::UNO_QUERY
);
745 xBodyText
->insertTextContent(xCursor
, xTextContent
, false);
746 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
747 SwRootFrame
* pRootFrame
= pWrtShell
->GetLayout();
748 SwFrame
* pPageFrame
= pRootFrame
->GetLower();
749 SwFrame
* pBodyFrame
= pPageFrame
->GetLower();
750 SwFrame
* pTextFrame
= pBodyFrame
->GetLower();
751 const SwSortedObjs
& rSortedObjs
= *pTextFrame
->GetDrawObjs();
752 const SwAnchoredObject
* pAnchoredObject
= rSortedObjs
[0];
753 // The content points to the start node, the next node is the graphic node.
754 SwNodeIndex aGraphicNode
= *pAnchoredObject
->GetFrameFormat().GetContent().GetContentIdx();
756 tools::Rectangle aFlyFrame
= pAnchoredObject
->GetDrawObj()->GetLastBoundRect();
757 Point aDocPos
= aFlyFrame
.Center();
759 // When translating the view point to the model position:
760 pWrtShell
->SttCursorMove();
761 pWrtShell
->CallSetCursor(&aDocPos
, /*bOnlyText=*/false);
762 pWrtShell
->EndCursorMove();
764 // Then make sure that we find the graphic node, and not its anchor:
765 SwShellCursor
* pShellCursor
= pWrtShell
->getShellCursor(/*bBlock=*/false);
766 // Without the accompanying fix in place, this test would have failed with:
767 // - Expected: SwNodeIndex (node 6)
768 // - Actual : SwNodeIndex (node 12)
769 // i.e. the cursor position was the text node hosting the as-char image, not the graphic node of
771 CPPUNIT_ASSERT_EQUAL(aGraphicNode
.GetIndex(), pShellCursor
->GetMark()->GetNodeIndex());
774 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testRedlineDelete
)
776 // Given a document with A4 paper size, some text, redlining on, but hidden:
778 SwDoc
* pDoc
= getSwDoc();
779 SwDocShell
* pDocShell
= pDoc
->GetDocShell();
780 SwWrtShell
* pWrtShell
= pDocShell
->GetWrtShell();
782 // Set page size to A4.
783 size_t nCurIdx
= pWrtShell
->GetCurPageDesc();
784 SwPageDesc
aPageDesc(pWrtShell
->GetPageDesc(nCurIdx
));
785 SwFrameFormat
& rMaster
= aPageDesc
.GetMaster();
786 SwFormatFrameSize
aSize(SwFrameSize::Fixed
);
787 aSize
.SetSize(Size(11906, 16838));
788 rMaster
.SetFormatAttr(aSize
);
789 pWrtShell
->ChgPageDesc(nCurIdx
, aPageDesc
);
791 OUString
aBefore("aaaaaaaaa aaaaaaaaaa aa aa aa ");
792 OUString
aDelete("delete eeeeeeeeeee ee eeeeeeeeeee ee eeeeee");
793 pWrtShell
->Insert(aBefore
+ " " + aDelete
794 + " zz zzz zzzzzzzzz zzz zzzz zzzz zzzzzzzzz zzzzzz zzz zzzzzzzzzzz zzz");
796 pDocShell
->SetChangeRecording(/*bActivate=*/true);
798 pWrtShell
->StartAllAction();
799 pWrtShell
->GetLayout()->SetHideRedlines(true);
800 pWrtShell
->EndAllAction();
802 // When deleting content in the middle of the paragraph:
803 pWrtShell
->SttEndDoc(/*bStt=*/true);
804 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, /*nCount=*/aBefore
.getLength(),
805 /*bBasicCall=*/false);
806 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/true, /*nCount=*/aDelete
.getLength(),
807 /*bBasicCall=*/false);
808 // Without the accompanying fix in place, this test would have crashed:
811 // Then make sure that the redline is created:
812 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1),
813 pDoc
->getIDocumentRedlineAccess().GetRedlineTable().size());
816 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testTdf120715_CursorMoveWhenTypingSpaceAtCenteredLineEnd
)
818 createSwDoc("tdf43100_tdf120715_cursorOnSpacesOverMargin.docx");
819 SwDoc
* pDoc
= getSwDoc();
820 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
822 // Make a paint to force the call of AddExtraBlankWidth, that calculate width for holePortions.
823 pDoc
->GetDocShell()->GetPreviewBitmap();
825 // Move the cursor to the last character of the document.
826 pWrtShell
->EndOfSection();
828 //Press space and check if the cursor move right with the additional space.
829 sal_Int32 nOldCursorPos
= pWrtShell
->GetCharRect().Left();
830 pWrtShell
->Insert(" ");
831 sal_Int32 nNewCursorPos
= pWrtShell
->GetCharRect().Left();
832 CPPUNIT_ASSERT_GREATER(nOldCursorPos
, nNewCursorPos
);
835 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testTdf43100_CursorMoveToSpacesOverMargin
)
837 // Test the cursor movement over the right margin in several different paragraphs.
838 // These differences are based on its paragraphs
839 // - alignment (left, center, right, justified),
840 // - line count (1 line, 2 lines, blank line containing only spaces)
841 createSwDoc("tdf43100_tdf120715_cursorOnSpacesOverMargin.docx");
842 SwDoc
* pDoc
= getSwDoc();
843 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
845 // Make a paint to force the call of AddExtraBlankWidth, that calculate width for holePortions.
846 pDoc
->GetDocShell()->GetPreviewBitmap();
848 // Move the cursor to the 2. line.
849 pWrtShell
->Down(/*bSelect=*/false, 1, /*bBasicCall=*/false);
850 // Move the cursor to the right margin.
851 pWrtShell
->RightMargin(false, false);
853 sal_Int32 nMarginPos
= pWrtShell
->GetCharRect().Left();
854 sal_Int32 nLastCursorPos
= nMarginPos
;
856 // Move the cursor right 5 times, every step should increase the cursor x position.
857 // Before this fix, the cursor stopped at the margin.
858 for (int i
= 0; i
< 5; i
++)
860 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 1, /*bBasicCall=*/false);
861 sal_Int32 nNewCursorPos
= pWrtShell
->GetCharRect().Left();
862 CPPUNIT_ASSERT_GREATER(nLastCursorPos
, nNewCursorPos
);
863 nLastCursorPos
= nNewCursorPos
;
866 // Move down the cursor several lines, and check if it will keep nearly its horizontal position.
867 // Some of the lines are not reach beyond the margin, there the cursor won't be able to keep its
868 // original position.
869 bool aLineReachOverMargin
[] = { false, true, true, false, false, true, true, false, true,
870 true, true, true, false, true, true, false, false };
871 // Cursor position can be a bit inaccurate, because it can only be positioned on characters,
872 // that is based on the actual line layout, therefore the actual cursor position
873 // is checked against a more distinct position instead of the nMarginPos.
874 sal_Int32 nAvgLeft
= (nMarginPos
+ nLastCursorPos
) / 2;
875 for (int i
= 2; i
< 17; i
++)
877 pWrtShell
->Down(/*bSelect=*/false, 1, /*bBasicCall=*/false);
878 sal_Int32 nNewCursorPos
= pWrtShell
->GetCharRect().Left();
879 if (aLineReachOverMargin
[i
])
880 CPPUNIT_ASSERT_GREATER(nAvgLeft
, nNewCursorPos
);
882 CPPUNIT_ASSERT_LESS(nAvgLeft
, nNewCursorPos
);
886 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testContentControlPDF
)
888 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
892 // Given a file with a content control:
894 SwDoc
* pDoc
= getSwDoc();
895 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
896 pWrtShell
->InsertContentControl(SwContentControlType::RICH_TEXT
);
897 pWrtShell
->SttEndDoc(/*bStt=*/true);
898 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 1, /*bBasicCall=*/false);
899 sal_Int32 nPlaceHolderLen
= SwResId(STR_CONTENT_CONTROL_PLACEHOLDER
).getLength();
900 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/true, nPlaceHolderLen
,
901 /*bBasicCall=*/false);
902 pWrtShell
->Insert("mycontent");
903 const SwPosition
* pStart
= pWrtShell
->GetCursor()->Start();
904 SwTextNode
* pTextNode
= pStart
->GetNode().GetTextNode();
905 sal_Int32 nIndex
= pStart
->GetContentIndex();
907 = pTextNode
->GetTextAttrAt(nIndex
, RES_TXTATR_CONTENTCONTROL
, sw::GetTextAttrMode::Parent
);
908 auto pTextContentControl
= static_txtattr_cast
<SwTextContentControl
*>(pAttr
);
909 const SwFormatContentControl
& rFormatContentControl
= pTextContentControl
->GetContentControl();
910 std::shared_ptr
<SwContentControl
> pContentControl
= rFormatContentControl
.GetContentControl();
911 // Alias/title, to be mapped to PDF's description.
912 pContentControl
->SetAlias("mydesc");
914 // When exporting to PDF:
915 save("writer_pdf_Export");
917 // Then make sure that a fillable form widget is emitted:
918 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
919 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
920 // Without the accompanying fix in place, this test would have failed with:
923 // i.e. the content control was just exported as normal text.
924 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
925 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
926 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
928 // Also verify that the widget description is correct, it was empty:
929 CPPUNIT_ASSERT_EQUAL(OUString("mydesc"),
930 pAnnotation
->getFormFieldAlternateName(pPdfDocument
.get()));
933 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testContentControlPlaceholderPDF
)
935 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
939 // Given a file with a content control, in placeholder mode:
941 SwDoc
* pDoc
= getSwDoc();
942 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
943 pWrtShell
->InsertContentControl(SwContentControlType::RICH_TEXT
);
945 // When exporting to PDF:
946 save("writer_pdf_Export");
948 // Then make sure that a fillable form widget is emitted with the expected value:
949 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
950 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
951 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
952 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
953 // Without the accompanying fix in place, this test would have failed with:
954 // - Expected: Click here to enter text
956 // i.e. the value of the content control was empty, the placeholder value was lost.
957 CPPUNIT_ASSERT_EQUAL(SwResId(STR_CONTENT_CONTROL_PLACEHOLDER
),
958 pAnnotation
->getFormFieldValue(pPdfDocument
.get()));
961 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testCheckboxContentControlPDF
)
963 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
967 // Given a file with a checkbox content control:
969 SwDoc
* pDoc
= getSwDoc();
970 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
971 pWrtShell
->InsertContentControl(SwContentControlType::CHECKBOX
);
973 // When exporting to PDF:
974 save("writer_pdf_Export");
976 // Then make sure that a checkbox form widget is emitted:
977 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
978 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
979 // Without the accompanying fix in place, this test would have failed with:
982 // i.e. the checkbox content control was just exported as normal text.
983 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
984 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
985 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
986 // Also check the form widget type:
987 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::CheckBox
,
988 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
991 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testDropdownContentControlPDF
)
993 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
997 // Given a file with a dropdown content control:
999 SwDoc
* pDoc
= getSwDoc();
1000 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1001 pWrtShell
->InsertContentControl(SwContentControlType::DROP_DOWN_LIST
);
1003 // When exporting to PDF:
1004 save("writer_pdf_Export");
1006 // Then make sure that a dropdown form widget is emitted:
1007 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1008 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1009 // Without the accompanying fix in place, this test would have failed with:
1012 // i.e. the dropdown content control was just exported as normal text.
1013 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
1014 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
1015 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
1016 // Also check the form widget type (our dropdown is called combo in PDF terms):
1017 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::ComboBox
,
1018 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
1021 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testDropdownContentControlPDF2
)
1023 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
1027 createSwDoc("tdf153040.docx");
1029 save("writer_pdf_Export");
1031 // Make sure that a dropdown form widget is emitted:
1032 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1033 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1035 CPPUNIT_ASSERT_EQUAL(4, pPage
->getAnnotationCount());
1036 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
1037 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
1038 // Also check the form widget type (our dropdown is called combo in PDF terms):
1039 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::ComboBox
,
1040 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
1041 // Without tdf#153040's fix, this would have been the empty OUString()
1042 CPPUNIT_ASSERT_EQUAL(OUString("Apfel"), pAnnotation
->getFormFieldValue(pPdfDocument
.get()));
1045 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testDateContentControlPDF
)
1047 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
1051 // Given a file with a date content control:
1053 SwDoc
* pDoc
= getSwDoc();
1054 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1055 pWrtShell
->InsertContentControl(SwContentControlType::DATE
);
1057 // When exporting to PDF:
1058 save("writer_pdf_Export");
1060 // Then make sure that a date form widget is emitted:
1061 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1062 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1063 // Without the accompanying fix in place, this test would have failed with:
1066 // i.e. the date content control was just exported as normal text.
1067 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
1068 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
1069 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
1070 // Also check the form widget type (our date is a mode of text in PDF terms):
1071 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::TextField
,
1072 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
1073 OUString aAction
= pAnnotation
->getFormAdditionalActionJavaScript(
1074 pPdfDocument
.get(), vcl::pdf::PDFAnnotAActionType::KeyStroke
);
1075 CPPUNIT_ASSERT_EQUAL(OUString("AFDate_KeystrokeEx(\"mm/dd/yy\");"), aAction
);
1078 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testContentControlPDFFont
)
1080 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
1084 // Given a document with a custom 24pt font size and a content control:
1086 SwDoc
* pDoc
= getSwDoc();
1087 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1088 SfxItemSetFixed
<RES_CHRATR_FONTSIZE
, RES_CHRATR_FONTSIZE
> aSet(pWrtShell
->GetAttrPool());
1089 SvxFontHeightItem
aItem(480, 100, RES_CHRATR_FONTSIZE
);
1091 pWrtShell
->SetAttrSet(aSet
);
1092 pWrtShell
->InsertContentControl(SwContentControlType::RICH_TEXT
);
1094 // When exporting that document to PDF:
1095 save("writer_pdf_Export");
1097 // Then make sure that the widget in the PDF result has that custom font size:
1098 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1099 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1100 pPage
->onAfterLoadPage(pPdfDocument
.get());
1101 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
1102 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
1103 // Without the accompanying fix in place, this test would have failed with:
1106 // i.e. i.e. the font size was some default, not the 24pt specified in the model.
1107 CPPUNIT_ASSERT_EQUAL(24.0f
, pAnnotation
->getFontSize(pPdfDocument
.get()));
1110 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testComboContentControlPDF
)
1112 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
1116 // Given a file with a combo box content control:
1118 SwDoc
* pDoc
= getSwDoc();
1119 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1120 pWrtShell
->InsertContentControl(SwContentControlType::COMBO_BOX
);
1122 // When exporting to PDF:
1123 save("writer_pdf_Export");
1125 // Then make sure that a combo box form widget is emitted:
1126 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1127 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1128 // Without the accompanying fix in place, this test would have failed with:
1131 // i.e. the combo box content control was exported as plain text.
1132 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
1133 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
1134 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
1135 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::ComboBox
,
1136 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
1137 // 19th bit: combo box, not dropdown.
1138 CPPUNIT_ASSERT(pAnnotation
->getFormFieldFlags(pPdfDocument
.get()) & 0x00040000);
1141 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testRichContentControlPDF
)
1143 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
1147 // Given a file with a rich content control, its value set to "xxx<b>yyy</b>":
1149 SwDoc
* pDoc
= getSwDoc();
1150 SwWrtShell
* pWrtShell
= pDoc
->GetDocShell()->GetWrtShell();
1151 pWrtShell
->InsertContentControl(SwContentControlType::RICH_TEXT
);
1152 pWrtShell
->SttEndDoc(/*bStt=*/true);
1153 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/false, 1, /*bBasicCall=*/false);
1154 sal_Int32 nPlaceHolderLen
= SwResId(STR_CONTENT_CONTROL_PLACEHOLDER
).getLength();
1155 pWrtShell
->Right(SwCursorSkipMode::Chars
, /*bSelect=*/true, nPlaceHolderLen
,
1156 /*bBasicCall=*/false);
1157 pWrtShell
->Insert("xxxyyy");
1158 pWrtShell
->Left(SwCursorSkipMode::Chars
, /*bSelect=*/true, 3, /*bBasicCall=*/false);
1159 SfxItemSetFixed
<RES_CHRATR_WEIGHT
, RES_CHRATR_WEIGHT
> aSet(pWrtShell
->GetAttrPool());
1160 SvxWeightItem
aItem(WEIGHT_BOLD
, RES_CHRATR_WEIGHT
);
1162 pWrtShell
->SetAttrSet(aSet
);
1164 // When exporting to PDF:
1165 save("writer_pdf_Export");
1167 // Then make sure that a single fillable form widget is emitted:
1168 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1169 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1170 // Without the accompanying fix in place, this test would have failed with:
1173 // i.e. "xxx<b>yyy</b>" was exported as 2 widgets, not 1.
1174 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
1177 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testPlaceholderFieldPDF
)
1179 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
1183 // Given a file with a text-type placeholder field:
1184 createSwDoc("placeholder.fodt");
1186 // When exporting to PDF (default setting is "create a PDF form"):
1187 save("writer_pdf_Export");
1189 // Then make sure that a fillable form widget is emitted:
1190 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1191 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
1192 // Without the accompanying fix in place, this test would have failed with:
1195 // i.e. the placeholder field was just exported as normal text.
1196 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
1197 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
1198 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
1200 // Also verify that the widget description is correct:
1201 CPPUNIT_ASSERT_EQUAL(OUString("reference text"),
1202 pAnnotation
->getFormFieldAlternateName(pPdfDocument
.get()));
1205 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testNumberPortionFormat
)
1207 // Given a document with a single paragraph, direct formatting asks 24pt font size for the
1208 // numbering and the text portion:
1209 createSwDoc("number-portion-format.odt");
1211 // When laying out that document:
1212 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1214 // Then make sure that the numbering portion has the correct font size:
1215 // Without the accompanying fix in place, this test would have failed with:
1218 // i.e. the numbering portion font size was 12pt, not 24pt (but only when the doc had a
1220 assertXPath(pXmlDoc
,
1221 "//SwParaPortion/SwLineLayout/child::*[@type='PortionType::Number']/SwFont",
1225 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testNumberPortionNoformat
)
1227 // Given a document with a numbering and a single paragraph, the entire run is red:
1228 createSwDoc("number-portion-noformat.docx");
1230 // When laying out that document:
1231 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1233 // Then make sure that just because the entire run is red, the numbering portion is not red:
1234 // Without the accompanying fix in place, this test would have failed with:
1235 // - Expected: ffffffff (COL_AUTO)
1236 // - Actual : 00ff0000 (COL_LIGHTRED)
1237 // i.e. the run color affected the color of the number portion in Writer, but not in Word.
1238 CPPUNIT_ASSERT_EQUAL(
1239 OUString("ffffffff"),
1240 getXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwFieldPortion/SwFont", "color"));
1243 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testFloattableOverlap
)
1245 // Given a document with 2 floating tables, not overlapping in Word's "Word 2010" compat mode,
1246 // because the first empty paragraph is below the first floating table:
1247 createSwDoc("floattable-overlap.docx");
1249 // When laying out that document:
1252 // Then make sure they don't overlap in Writer, either:
1253 SwDoc
* pDoc
= getSwDoc();
1254 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
1255 auto pPage1
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
1256 CPPUNIT_ASSERT(pPage1
);
1257 CPPUNIT_ASSERT(pPage1
->GetSortedObjs());
1258 const SwSortedObjs
& rPage1Objs
= *pPage1
->GetSortedObjs();
1259 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPage1Objs
.size());
1260 SwAnchoredObject
* pPage1Obj1
= rPage1Objs
[0];
1261 const SwRect
& rRect1
= pPage1Obj1
->GetObjRectWithSpaces();
1262 SwAnchoredObject
* pPage1Obj2
= rPage1Objs
[1];
1263 const SwRect
& rRect2
= pPage1Obj2
->GetObjRectWithSpaces();
1264 // Without the accompanying fix in place, this test would have failed, the empty paragraph,
1265 // which is after the floating table in the document model went above the floating table in the
1266 // layout, which resulted in an overlap.
1267 CPPUNIT_ASSERT(!rRect1
.Overlaps(rRect2
));
1270 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testFloattableAnchorNextPage
)
1272 // Given a document with 3 floating tables, the last one has a negative vertical offset, so the
1273 // floating table is on page 1, but its anchor frame is effectively on page 2:
1274 createSwDoc("floattable-anchor-next-page.docx");
1276 // When laying out that document:
1279 // Then make sure all 3 floating tables are on page 1:
1280 SwDoc
* pDoc
= getSwDoc();
1281 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
1282 auto pPage1
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
1283 CPPUNIT_ASSERT(pPage1
);
1284 CPPUNIT_ASSERT(pPage1
->GetSortedObjs());
1285 const SwSortedObjs
& rPage1Objs
= *pPage1
->GetSortedObjs();
1286 // Without the accompanying fix in place, this test would have failed with:
1289 // i.e. the last floating table was on the wrong page (page 2).
1290 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), rPage1Objs
.size());
1293 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testParaUpperMarginFlyIntersect
)
1295 // Given a document with 2 paragraphs, the paragraphs have both upper and lower spacing of 567
1297 createSwDoc("para-upper-margin-fly-intersect.docx");
1299 // When laying out that document:
1302 // Then make sure that we shift down the text in the second paragraph only based on the 2nd para
1303 // upper margin, not based on the 1st para lower margin:
1304 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1306 = getXPathContent(pXmlDoc
,
1307 "count(//SwParaPortion/SwLineLayout/child::*[@type='PortionType::Fly'])")
1310 for (int i
= 1; i
<= nFlyCount
; ++i
)
1312 OString xPath
= "(//SwParaPortion/SwLineLayout/child::*[@type='PortionType::Fly'])["
1313 + OString::number(i
) + "]";
1314 nHeight
+= getXPath(pXmlDoc
, xPath
, "height").toInt32();
1316 // Without the accompanying fix in place, this test would have failed with:
1317 // - Expected: 521 (~500)
1318 // - Actual : 857 (~1000)
1319 // I.e. both upper and lower margin was taken into account.
1320 CPPUNIT_ASSERT_EQUAL(521, nHeight
);
1323 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testTdf129810
)
1325 // Load the document, which embeds a CJK font.
1326 createSwDoc("tdf129810.odt");
1328 // Render the document to a metafile.
1329 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1330 SwDocShell
* pShell
= pTextDoc
->GetDocShell();
1331 std::shared_ptr
<GDIMetaFile
> xMetaFile
= pShell
->GetPreviewMetaFile();
1332 CPPUNIT_ASSERT(xMetaFile
);
1334 // Find the fist text array action
1335 for (size_t nAction
= 0; nAction
< xMetaFile
->GetActionSize(); nAction
++)
1337 auto pAction
= xMetaFile
->GetAction(nAction
);
1338 if (pAction
->GetType() == MetaActionType::TEXTARRAY
)
1340 auto pTextArrayAction
= static_cast<MetaTextArrayAction
*>(pAction
);
1341 auto pDXArray
= pTextArrayAction
->GetDXArray();
1343 // There should be 13 chars on the first line
1344 CPPUNIT_ASSERT_GREATER(size_t(13), pDXArray
.size());
1346 // Assert we are using the expected width for uncompressed chars
1347 CPPUNIT_ASSERT_EQUAL(sal_Int32(720), pDXArray
[0]);
1348 // Assert we are using the expected width for compressed chars
1349 CPPUNIT_ASSERT_EQUAL(sal_Int32(500), pDXArray
[6] - pDXArray
[5]);
1355 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testScriptinfosurrogatePairs
)
1357 createSwDoc("scriptinfo-surrogate-pairs.fodt");
1358 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1360 // Test that a dotted circle is grouped with the mark after it, even if the
1361 // mark is outside Unicode’s Basic Multilingual Plan (i.e. a surrogate pair
1364 // Without the fix it fails with:
1367 assertXPath(pXmlDoc
, "//txt[1]/SwParaPortion/SwLineLayout/SwLinePortion[1]", "portion", u
"11");
1368 assertXPath(pXmlDoc
, "//txt[1]/SwParaPortion/SwLineLayout/SwLinePortion[2]", "portion",
1369 u
"\u25CC\U00010A01");
1371 // Without the fix this would crash because we got a lone surrogate that
1372 // can’t be converted to UTF-8, but if it were not for that it might fail
1373 // with something like:
1376 assertXPath(pXmlDoc
, "//txt[2]/SwParaPortion/SwLineLayout/SwLinePortion[1]", "portion", u
"11");
1377 assertXPath(pXmlDoc
, "//txt[2]/SwParaPortion/SwLineLayout/SwLinePortion[2]", "portion",
1378 u
"\U0001D400\u064E");
1381 CPPUNIT_TEST_FIXTURE(SwCoreTextTest
, testTdf112594
)
1383 createSwDoc("tdf112594.fodt");
1384 xmlDocUniquePtr pXmlDoc
= parseLayoutDump();
1386 // Test that a NNBSP is grouped with the Mongolian characters after it
1388 // Without the fix it fails with:
1390 // - Actual : 11\u202F
1391 // (U+020F is a space, so might not be visible)
1392 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwLinePortion[1]", "portion", u
"11");
1393 assertXPath(pXmlDoc
, "//SwParaPortion/SwLineLayout/SwLinePortion[2]", "portion",
1397 CPPUNIT_PLUGIN_IMPLEMENT();
1399 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */