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/XTextDocument.hpp>
16 #include <comphelper/propertyvalue.hxx>
17 #include <editeng/colritem.hxx>
18 #include <sfx2/viewfrm.hxx>
19 #include <sfx2/dispatch.hxx>
21 #include <IDocumentLayoutAccess.hxx>
22 #include <rootfrm.hxx>
23 #include <sortedobjs.hxx>
24 #include <pagefrm.hxx>
28 #include <formatcontentcontrol.hxx>
29 #include <textcontentcontrol.hxx>
35 /// Covers sw/source/core/text/itrform2.cxx fixes.
36 class Test
: public SwModelTestBase
40 : SwModelTestBase(u
"/sw/qa/core/text/data/"_ustr
)
45 CPPUNIT_TEST_FIXTURE(Test
, testFloattableWrapEmptyParagraph
)
47 // Given a document with 2 pages, a floating table on both pages:
48 createSwDoc("floattable-wrap-empty-para.docx");
50 // When calculating the layout:
53 // Then make sure that each page has exactly 1 floating table:
54 SwDoc
* pDoc
= getSwDoc();
55 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
56 auto pPage
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
57 CPPUNIT_ASSERT(pPage
);
58 CPPUNIT_ASSERT(pPage
->GetSortedObjs());
59 const SwSortedObjs
& rPageObjs
= *pPage
->GetSortedObjs();
60 // Without the accompanying fix in place, this test would have failed with:
63 // i.e. both tables were on page 1, leading to an overlap.
64 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs
.size());
65 auto pPage2
= dynamic_cast<SwPageFrame
*>(pPage
->GetNext());
66 CPPUNIT_ASSERT(pPage2
);
67 CPPUNIT_ASSERT(pPage2
->GetSortedObjs());
68 const SwSortedObjs
& rPageObjs2
= *pPage2
->GetSortedObjs();
69 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs2
.size());
72 CPPUNIT_TEST_FIXTURE(Test
, testFloattableLegacyWrapEmptyParagraph
)
74 // Given a document with 2 pages, a floating table on both pages (from DOC, so the table is
75 // shifted towards the left page edge slightly):
76 createSwDoc("floattable-wrap-empty-para-legacy.docx");
78 // When calculating the layout:
81 // Then make sure that each page has exactly 1 floating table:
82 SwDoc
* pDoc
= getSwDoc();
83 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
84 auto pPage
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
85 CPPUNIT_ASSERT(pPage
);
86 CPPUNIT_ASSERT(pPage
->GetSortedObjs());
87 const SwSortedObjs
& rPageObjs
= *pPage
->GetSortedObjs();
88 // Without the accompanying fix in place, this test would have failed with:
91 // i.e. both tables were on page 1, leading to an overlap.
92 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs
.size());
93 auto pPage2
= dynamic_cast<SwPageFrame
*>(pPage
->GetNext());
94 CPPUNIT_ASSERT(pPage2
);
95 CPPUNIT_ASSERT(pPage2
->GetSortedObjs());
96 const SwSortedObjs
& rPageObjs2
= *pPage2
->GetSortedObjs();
97 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs2
.size());
100 CPPUNIT_TEST_FIXTURE(Test
, testApplyTextAttrToEmptyLineAtEndOfParagraph
)
102 createSwDoc("A011-charheight.rtf");
106 SwDoc
* pDoc
= getSwDoc();
107 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
108 auto pPage
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
110 SwContentFrame
* pLastPara
= pPage
->FindLastBodyContent();
111 // wrong was 449 (11.5pt)
112 CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips
>(368), pLastPara
->getFrameArea().Height());
113 SwContentFrame
* pFirstPara
= pPage
->FindFirstBodyContent();
114 // wrong was 817 (11.5pt)
115 CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips
>(736), pFirstPara
->getFrameArea().Height());
118 CPPUNIT_TEST_FIXTURE(Test
, testFlyMinimalWrap
)
120 // Given a document with a first page that has a shape and a table in it (not floating table),
121 // some empty paragraphs wrapping around the shape:
122 createSwDoc("fly-minimal-wrap.docx");
124 // When calculating the layout:
127 // Then make sure the wrap happens, so the 2nd page only has 2 paragraphs:
128 SwDoc
* pDoc
= getSwDoc();
129 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
130 auto pPage
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
131 CPPUNIT_ASSERT(pPage
);
132 CPPUNIT_ASSERT(pPage
->GetSortedObjs());
133 const SwSortedObjs
& rPageObjs
= *pPage
->GetSortedObjs();
134 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), rPageObjs
.size());
135 auto pPage2
= dynamic_cast<SwPageFrame
*>(pPage
->GetNext());
136 CPPUNIT_ASSERT(pPage2
);
137 CPPUNIT_ASSERT(!pPage2
->GetSortedObjs());
138 SwLayoutFrame
* pBody2
= pPage2
->FindBodyCont();
139 SwFrame
* pPage2Para1
= pBody2
->GetLower();
140 CPPUNIT_ASSERT(pPage2Para1
);
141 SwFrame
* pPage2Para2
= pPage2Para1
->GetNext();
142 CPPUNIT_ASSERT(pPage2Para2
);
143 // Without the accompanying fix in place, this test would have failed, the second page had 19
144 // text frames in the body frame, not 2.
145 CPPUNIT_ASSERT(!pPage2Para2
->GetNext());
148 CPPUNIT_TEST_FIXTURE(Test
, testContentControlHeaderPDFExport
)
150 // Given a document with a content control in the header:
151 createSwDoc("content-control-header.docx");
153 // When exporting to PDF:
154 save(u
"writer_pdf_Export"_ustr
);
156 // Then make sure all the expected text is there on page 2:
157 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
162 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage2
= pPdfDocument
->openPage(1);
164 for (int i
= 0; i
< pPage2
->getObjectCount(); ++i
)
166 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pObject
= pPage2
->getObject(i
);
167 if (pObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
172 // Without the accompanying fix in place, this test would have failed with:
175 // i.e. not all of header, heading and body text was there on page 2, content was lost.
176 CPPUNIT_ASSERT_EQUAL(3, nTextCount
);
179 CPPUNIT_TEST_FIXTURE(Test
, testSplitFlyAnchorLeftMargin
)
181 // Given a document with a floating table, anchor para is followed by another para with a left
183 createSwDoc("floattable-anchor-left-margin.docx");
185 // When laying out that document:
188 // Then make sure that the left margin of this last paragraph is not lost:
189 SwDoc
* pDoc
= getSwDoc();
190 SwRootFrame
* pLayout
= pDoc
->getIDocumentLayoutAccess().GetCurrentLayout();
191 auto pPage
= dynamic_cast<SwPageFrame
*>(pLayout
->Lower());
192 CPPUNIT_ASSERT(pPage
);
193 SwContentFrame
* pLastPara
= pPage
->FindLastBodyContent();
194 // Without the accompanying fix in place, this test would have failed with:
197 // i.e. the left margin was lost.
198 CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips
>(6480), pLastPara
->getFramePrintArea().Left());
201 CPPUNIT_TEST_FIXTURE(Test
, testCheckedCheckboxContentControlPDF
)
203 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
207 SwExportFormFieldsGuard g
;
208 // Given a file with a checked checkbox content control:
210 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
211 pWrtShell
->InsertContentControl(SwContentControlType::CHECKBOX
);
212 // Toggle it, so we get a checked one:
213 SwTextContentControl
* pTextContentControl
= pWrtShell
->CursorInsideContentControl();
214 const SwFormatContentControl
& rFormatContentControl
= pTextContentControl
->GetContentControl();
215 pWrtShell
->GotoContentControl(rFormatContentControl
);
217 // When exporting to PDF:
218 save(u
"writer_pdf_Export"_ustr
);
220 // Then make sure that a checked checkbox form widget is emitted:
221 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
222 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
223 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
224 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
225 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Widget
, pAnnotation
->getSubType());
226 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::CheckBox
,
227 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
228 OUString aActual
= pAnnotation
->getFormFieldValue(pPdfDocument
.get());
229 // Without the accompanying fix in place, this test would have failed with:
232 // i.e. the /AP -> /N key of the checkbox widget annotation object didn't have a sub-key that
233 // would match /V, leading to not showing the checked state.
234 CPPUNIT_ASSERT_EQUAL(u
"Yes"_ustr
, aActual
);
237 CPPUNIT_TEST_FIXTURE(Test
, testContentControlPDFFontColor
)
239 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
243 SwExportFormFieldsGuard g
;
244 // Given a document with a custom orange font color and a content control:
246 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
247 SfxItemSetFixed
<RES_CHRATR_COLOR
, RES_CHRATR_COLOR
> aSet(pWrtShell
->GetAttrPool());
248 Color
nOrange(0xff6b00);
249 SvxColorItem
aItem(nOrange
, RES_CHRATR_COLOR
);
251 pWrtShell
->SetAttrSet(aSet
);
252 pWrtShell
->InsertContentControl(SwContentControlType::RICH_TEXT
);
254 // When exporting that document to PDF:
255 save(u
"writer_pdf_Export"_ustr
);
257 // Then make sure that the widget in the PDF result has that custom font color:
258 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
259 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
260 pPage
->onAfterLoadPage(pPdfDocument
.get());
261 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
262 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
263 // Without the accompanying fix in place, this test would have failed with:
264 // - Expected: rgba[ff6b00ff]
265 // - Actual : rgba[000000ff]
266 // i.e. the custom color was lost, the font color was black, not orange.
267 CPPUNIT_ASSERT_EQUAL(nOrange
, pAnnotation
->getFontColor(pPdfDocument
.get()));
270 CPPUNIT_TEST_FIXTURE(Test
, testContentControlPDFDropDownText
)
272 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
276 SwExportFormFieldsGuard g
;
277 // Given a document with a dropdown: custom default text and 3 items:
279 uno::Reference
<lang::XMultiServiceFactory
> xMSF(mxComponent
, uno::UNO_QUERY
);
280 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
281 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
282 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
283 xText
->insertString(xCursor
, u
"test"_ustr
, /*bAbsorb=*/false);
284 xCursor
->gotoStart(/*bExpand=*/false);
285 xCursor
->gotoEnd(/*bExpand=*/true);
286 uno::Reference
<text::XTextContent
> xContentControl(
287 xMSF
->createInstance(u
"com.sun.star.text.ContentControl"_ustr
), uno::UNO_QUERY
);
288 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
290 uno::Sequence
<beans::PropertyValues
> aListItems
= {
292 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"red"_ustr
)),
293 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"R"_ustr
)),
296 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"green"_ustr
)),
297 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"G"_ustr
)),
300 comphelper::makePropertyValue(u
"DisplayText"_ustr
, uno::Any(u
"blue"_ustr
)),
301 comphelper::makePropertyValue(u
"Value"_ustr
, uno::Any(u
"B"_ustr
)),
304 xContentControlProps
->setPropertyValue(u
"ListItems"_ustr
, uno::Any(aListItems
));
306 xText
->insertTextContent(xCursor
, xContentControl
, /*bAbsorb=*/true);
308 // When exporting that to PDF:
309 save(u
"writer_pdf_Export"_ustr
);
311 // Then make sure that the custom default is not lost:
312 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
313 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
314 pPage
->onAfterLoadPage(pPdfDocument
.get());
315 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
316 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
317 // Without the accompanying fix in place, this test would have failed with:
320 // i.e. only the 3 colors were exported, the default "test" text was not.
321 CPPUNIT_ASSERT_EQUAL(4, pAnnotation
->getOptionCount(pPdfDocument
.get()));
324 CPPUNIT_TEST_FIXTURE(Test
, testContentControlPDFComments
)
326 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
330 // Given a document with both a content control and a comment:
332 SwWrtShell
* pWrtShell
= getSwDocShell()->GetWrtShell();
333 pWrtShell
->InsertContentControl(SwContentControlType::RICH_TEXT
);
334 pWrtShell
->SttEndDoc(/*bStt=*/false);
335 SwDocShell
* pDocShell
= getSwDocShell();
336 SwView
* pView
= pDocShell
->GetView();
337 pView
->GetViewFrame().GetDispatcher()->Execute(FN_POSTIT
, SfxCallMode::SYNCHRON
);
339 // When exporting to PDF, exporting notes in master (and not as widgets):
340 uno::Sequence
<beans::PropertyValue
> aFilterData
= {
341 comphelper::makePropertyValue(u
"ExportFormFields"_ustr
, true),
342 comphelper::makePropertyValue(u
"ExportNotes"_ustr
, false),
343 comphelper::makePropertyValue(u
"ExportNotesInMargin"_ustr
, true),
346 comphelper::makePropertyValue(u
"FilterName"_ustr
, u
"writer_pdf_Export"_ustr
),
347 comphelper::makePropertyValue(u
"FilterData"_ustr
, aFilterData
),
350 // Then make sure the only widget for the content control has a correct position:
351 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
352 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
353 pPage
->onAfterLoadPage(pPdfDocument
.get());
354 CPPUNIT_ASSERT_EQUAL(1, pPage
->getAnnotationCount());
355 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
356 basegfx::B2DPoint aAnnotTopLeft
= pAnnotation
->getRectangle().getMinimum();
357 // Without the accompanying fix in place, this test would have failed with:
358 // - Expected: (41.749, 639.401)
359 // - Actual : (59.249,716.951)
360 // i.e. the content control rectangle was shifted towards the top right of the page, compared to
361 // where it's expected.
362 CPPUNIT_ASSERT_DOUBLES_EQUAL(41.749, aAnnotTopLeft
.getX(), 0.001);
363 CPPUNIT_ASSERT_DOUBLES_EQUAL(639.401, aAnnotTopLeft
.getY(), 0.001);
367 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */