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>
12 #include <com/sun/star/awt/XBitmap.hpp>
13 #include <com/sun/star/graphic/XGraphic.hpp>
14 #include <com/sun/star/graphic/GraphicType.hpp>
15 #include <com/sun/star/text/ControlCharacter.hpp>
16 #include <com/sun/star/text/XText.hpp>
17 #include <com/sun/star/text/XDocumentIndex.hpp>
18 #include <o3tl/safeint.hxx>
19 #include <o3tl/string_view.hxx>
20 #include <officecfg/Office/Common.hxx>
21 #include <tools/zcodec.hxx>
22 #include <vcl/filter/pdfdocument.hxx>
23 #include <sfx2/linkmgr.hxx>
24 #include <comphelper/propertysequence.hxx>
25 #include <unotxdoc.hxx>
28 #include <IDocumentRedlineAccess.hxx>
29 #include <IDocumentContentOperations.hxx>
33 #include <ndindex.hxx>
35 #include <xmloff/odffields.hxx>
36 #include <IDocumentMarkAccess.hxx>
38 #include <com/sun/star/awt/FontWeight.hpp>
39 #include <unotools/mediadescriptor.hxx>
40 #include <unotools/saveopt.hxx>
44 class Test
: public SwModelTestBase
47 Test() : SwModelTestBase(u
"/sw/qa/extras/globalfilter/data/"_ustr
) {}
49 void testEmbeddedGraphicRoundtrip();
50 void testLinkedGraphicRT();
51 void testImageWithSpecialID();
52 void testGraphicShape();
53 void testMultipleIdenticalGraphics();
54 void testCharHighlight();
55 void testCharHighlightODF();
56 void testCharHighlightBody();
57 void testCharStyleHighlight();
58 void testMSCharBackgroundEditing();
59 void testCharBackgroundToHighlighting();
61 void testSkipImages();
63 void testNestedFieldmark();
64 void verifyText13(char const*);
66 void testRedlineFlags();
67 void testBulletAsImage();
68 void testTextFormField();
69 void testCheckBoxFormField();
70 void testDropDownFormField();
71 void testDateFormField();
72 void testDateFormFieldCharacterFormatting();
73 void testSvgImageSupport();
75 CPPUNIT_TEST_SUITE(Test
);
76 CPPUNIT_TEST(testEmbeddedGraphicRoundtrip
);
77 CPPUNIT_TEST(testLinkedGraphicRT
);
78 CPPUNIT_TEST(testImageWithSpecialID
);
79 CPPUNIT_TEST(testGraphicShape
);
80 CPPUNIT_TEST(testMultipleIdenticalGraphics
);
81 CPPUNIT_TEST(testCharHighlight
);
82 CPPUNIT_TEST(testCharHighlightODF
);
83 CPPUNIT_TEST(testMSCharBackgroundEditing
);
84 CPPUNIT_TEST(testCharBackgroundToHighlighting
);
86 CPPUNIT_TEST(testSkipImages
);
88 CPPUNIT_TEST(testNestedFieldmark
);
89 CPPUNIT_TEST(testODF13
);
90 CPPUNIT_TEST(testRedlineFlags
);
91 CPPUNIT_TEST(testBulletAsImage
);
92 CPPUNIT_TEST(testTextFormField
);
93 CPPUNIT_TEST(testCheckBoxFormField
);
94 CPPUNIT_TEST(testDropDownFormField
);
95 CPPUNIT_TEST(testDateFormField
);
96 CPPUNIT_TEST(testDateFormFieldCharacterFormatting
);
97 CPPUNIT_TEST(testSvgImageSupport
);
98 CPPUNIT_TEST_SUITE_END();
101 void Test::testEmbeddedGraphicRoundtrip()
103 OUString aFilterNames
[] = {
105 u
"Rich Text Format"_ustr
,
107 u
"Office Open XML Text"_ustr
,
110 for (OUString
const & rFilterName
: aFilterNames
)
112 // Check whether the export code swaps in the image which was swapped out before by auto mechanism
114 createSwDoc("document_with_two_images.odt");
116 // Export the document and import again for a check
117 saveAndReload(rFilterName
);
119 // Check whether graphic exported well after it was swapped out
120 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
121 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 2, getShapes());
124 uno::Reference
<drawing::XShape
> xImage(getShape(1), uno::UNO_QUERY
);
125 uno::Reference
< beans::XPropertySet
> XPropSet( xImage
, uno::UNO_QUERY_THROW
);
127 // Check graphic, size
129 uno::Reference
<graphic::XGraphic
> xGraphic
;
130 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
131 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
132 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
133 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
134 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
135 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(610), xBitmap
->getSize().Width
);
136 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(381), xBitmap
->getSize().Height
);
140 xImage
.set(getShape(2), uno::UNO_QUERY
);
141 XPropSet
.set( xImage
, uno::UNO_QUERY_THROW
);
143 // Check graphic, size
145 uno::Reference
<graphic::XGraphic
> xGraphic
;
146 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
147 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
148 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
149 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
150 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
151 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(900), xBitmap
->getSize().Width
);
152 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(600), xBitmap
->getSize().Height
);
157 void Test::testLinkedGraphicRT()
159 const OUString aFilterNames
[] = {
161 // "Rich Text Format", Note: picture is there, but SwGrfNode is not found?
163 u
"Office Open XML Text"_ustr
,
166 for (OUString
const & rFilterName
: aFilterNames
)
168 createSwDoc("document_with_linked_graphic.odt");
170 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
172 // Export the document and import again for a check
173 saveAndReload(rFilterName
);
175 SwDoc
* pDoc
= getSwDoc();
176 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pDoc
);
177 SwNodes
& aNodes
= pDoc
->GetNodes();
180 bool bImageFound
= false;
182 for (SwNodeOffset
nIndex(0); nIndex
< aNodes
.Count(); ++nIndex
)
184 if (aNodes
[nIndex
]->IsGrfNode())
186 SwGrfNode
* pGrfNode
= aNodes
[nIndex
]->GetGrfNode();
187 CPPUNIT_ASSERT(pGrfNode
);
189 const GraphicObject
& rGraphicObj
= pGrfNode
->GetGrfObj(true);
190 aGraphic
= rGraphicObj
.GetGraphic();
195 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), bImageFound
);
197 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), GraphicType::Bitmap
, aGraphic
.GetType());
198 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_uLong(864900), aGraphic
.GetSizeBytes());
200 // Check if linked graphic is registered in LinkManager
201 SwEditShell
* const pEditShell(getSwDoc()->GetEditShell());
202 CPPUNIT_ASSERT(pEditShell
);
203 sfx2::LinkManager
& rLinkManager
= pEditShell
->GetLinkManager();
204 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), size_t(1), rLinkManager
.GetLinks().size());
205 const tools::SvRef
<sfx2::SvBaseLink
> & rLink
= rLinkManager
.GetLinks()[0];
206 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), rLink
->GetLinkSourceName().indexOf("linked_graphic.jpg") >= 0);
210 void Test::testImageWithSpecialID()
212 // Check how LO handles when the imported graphic's ID is different from that one
213 // which is generated by LO.
215 const OUString aFilterNames
[] = {
217 u
"Rich Text Format"_ustr
,
219 u
"Office Open XML Text"_ustr
,
222 for (OUString
const & rFilterName
: aFilterNames
)
224 createSwDoc("images_with_special_IDs.odt");
226 // Export the document and import again for a check
227 saveAndReload(rFilterName
);
229 // Check whether graphic exported well
230 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
231 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 2, getShapes());
233 uno::Reference
<drawing::XShape
> xImage
= getShape(1);
234 uno::Reference
< beans::XPropertySet
> XPropSet( xImage
, uno::UNO_QUERY_THROW
);
236 // Check graphic, size
238 uno::Reference
<graphic::XGraphic
> xGraphic
;
239 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
240 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
241 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
242 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
243 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
244 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(610), xBitmap
->getSize().Width
);
245 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(381), xBitmap
->getSize().Height
);
249 xImage
.set(getShape(2), uno::UNO_QUERY
);
250 XPropSet
.set( xImage
, uno::UNO_QUERY_THROW
);
252 // Check graphic, size
254 uno::Reference
<graphic::XGraphic
> xGraphic
;
255 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
256 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
257 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
258 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
259 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
260 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(900), xBitmap
->getSize().Width
);
261 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(600), xBitmap
->getSize().Height
);
266 /// Gives the first embedded or linked image in a document.
267 uno::Reference
<drawing::XShape
> lcl_getShape(const uno::Reference
<lang::XComponent
>& xComponent
, bool bEmbedded
)
269 uno::Reference
<drawing::XShape
> xShape
;
271 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(xComponent
, uno::UNO_QUERY
);
272 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
273 for (sal_Int32 i
= 0; i
< xDrawPage
->getCount(); ++i
)
275 uno::Reference
<beans::XPropertySet
> xShapeProperties(xDrawPage
->getByIndex(i
), uno::UNO_QUERY
);
276 uno::Reference
<graphic::XGraphic
> xGraphic
;
277 xShapeProperties
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
280 Graphic
aGraphic(xGraphic
);
282 if (bEmbedded
== aGraphic
.getOriginURL().isEmpty())
284 xShape
.set(xShapeProperties
, uno::UNO_QUERY
);
293 void Test::testGraphicShape()
295 // There are two kind of images in Writer: 1) Writer specific handled by SwGrfNode and
296 // 2) graphic shape handled by SdrGrafObj (e.g. after copy&paste from Impress).
298 const OUString aFilterNames
[] = {
300 u
"Rich Text Format"_ustr
,
302 u
"Office Open XML Text"_ustr
,
305 for (OUString
const & rFilterName
: aFilterNames
)
307 createSwDoc("graphic_shape.odt");
309 // Export the document and import again for a check
310 saveAndReload(rFilterName
);
312 // Check whether graphic exported well
313 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
314 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 2, getShapes());
316 uno::Reference
<drawing::XShape
> xImage
= lcl_getShape(mxComponent
, true);
317 CPPUNIT_ASSERT_MESSAGE("Couldn't load the shape/image", xImage
.is());
318 uno::Reference
< beans::XPropertySet
> XPropSet( xImage
, uno::UNO_QUERY
);
319 // First image is embedded
322 uno::Reference
<graphic::XGraphic
> xGraphic
;
323 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
324 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
325 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
326 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
327 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(610), xBitmap
->getSize().Width
);
328 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(381), xBitmap
->getSize().Height
);
331 // MS filters make this kind of linked images broken !?
332 if (rFilterName
!= "writer8")
335 // Second image is a linked one
336 xImage
= lcl_getShape(mxComponent
, false);
337 XPropSet
.set(xImage
, uno::UNO_QUERY
);
338 const OString sFailedImageLoad
= OString::Concat("Couldn't load the shape/image for ") + rFilterName
.toUtf8();
339 CPPUNIT_ASSERT_MESSAGE(sFailedImageLoad
.getStr(), xImage
.is());
343 uno::Reference
<graphic::XGraphic
> xGraphic
;
344 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
345 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
347 Graphic
aGraphic(xGraphic
);
348 OUString sURL
= aGraphic
.getOriginURL();
349 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), sURL
.endsWith("linked_graphic.jpg"));
351 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
352 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
353 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(620), xBitmap
->getSize().Width
);
354 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(465), xBitmap
->getSize().Height
);
359 std::vector
<uno::Reference
<graphic::XGraphic
>>
360 lcl_getGraphics(const uno::Reference
<lang::XComponent
>& xComponent
)
362 std::vector
<uno::Reference
<graphic::XGraphic
>> aGraphics
;
364 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(xComponent
, uno::UNO_QUERY
);
365 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
366 for (sal_Int32 i
= 0; i
< xDrawPage
->getCount(); ++i
)
368 uno::Reference
<beans::XPropertySet
> xShapeProperties(xDrawPage
->getByIndex(i
), uno::UNO_QUERY
);
369 uno::Reference
<graphic::XGraphic
> xGraphic
;
370 xShapeProperties
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
373 aGraphics
.push_back(xGraphic
);
380 void Test::testMultipleIdenticalGraphics()
382 // We have multiple identical graphics. When we save them we want
383 // them to be saved de-duplicated and the same should still be true
384 // after loading them again. This test check that the de-duplication
385 // works as expected.
387 const OUString aFilterNames
[] {
389 //"Rich Text Format", // doesn't work correctly for now
391 u
"Office Open XML Text"_ustr
,
394 for (OUString
const & rFilterName
: aFilterNames
)
396 createSwDoc("multiple_identical_graphics.odt");
398 // Export the document and import again for a check
399 saveAndReload(rFilterName
);
401 // Check whether graphic exported well
402 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
403 auto aGraphics
= lcl_getGraphics(mxComponent
);
405 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), size_t(5), aGraphics
.size());
407 // Get all GfxLink addresses, we expect all of them to be the same
408 // indicating we use the same graphic instance for all shapes
409 std::vector
<sal_Int64
> aGfxLinkAddresses
;
410 for (auto const & rxGraphic
: aGraphics
)
412 GfxLink
* pLink
= Graphic(rxGraphic
).GetSharedGfxLink().get();
413 aGfxLinkAddresses
.emplace_back(reinterpret_cast<sal_Int64
>(pLink
));
416 // Check all addresses are the same
417 bool bResult
= std::equal(aGfxLinkAddresses
.begin() + 1, aGfxLinkAddresses
.end(), aGfxLinkAddresses
.begin());
418 const OString sGraphicNotTheSameFailedMessage
= OString::Concat("Graphics not the same for filter: '") +
419 rFilterName
.toUtf8() + OString::Concat("'");
420 CPPUNIT_ASSERT_EQUAL_MESSAGE(sGraphicNotTheSameFailedMessage
.getStr(), true, bResult
);
424 void Test::testCharHighlightBody()
426 // MS Word has two kind of character backgrounds called character shading and highlighting
427 // MS filters handle these attributes separately, but ODF export merges them into one background attribute
429 const OUString aFilterNames
[] = {
431 u
"Rich Text Format"_ustr
,
433 u
"Office Open XML Text"_ustr
,
436 for (OUString
const & rFilterName
: aFilterNames
)
438 createSwDoc("char_highlight.docx");
440 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
442 // Export the document and import again for a check
443 saveAndReload(rFilterName
);
445 const uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
446 // Both highlight and background
447 const Color
nBackColor(0x4F81BD);
448 for( int nRun
= 1; nRun
<= 16; ++nRun
)
450 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,nRun
), uno::UNO_QUERY
);
451 Color nHighlightColor
;
454 case 1: nHighlightColor
= COL_BLACK
; break; //black 0x000000
455 case 2: nHighlightColor
= COL_LIGHTBLUE
; break; //light blue 0x0000ff
456 case 3: nHighlightColor
= COL_LIGHTCYAN
; break; //light cyan 0x00ffff
457 case 4: nHighlightColor
= COL_LIGHTGREEN
; break; //light green 0x00ff00
458 case 5: nHighlightColor
= COL_LIGHTMAGENTA
; break; //light magenta 0xff00ff
459 case 6: nHighlightColor
= COL_LIGHTRED
; break; //light red 0xff0000
460 case 7: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
461 case 8: nHighlightColor
= COL_WHITE
; break; //white 0xffffff
462 case 9: nHighlightColor
= COL_BLUE
; break;//blue 0x000080
463 case 10: nHighlightColor
= COL_CYAN
; break; //cyan 0x008080
464 case 11: nHighlightColor
= COL_GREEN
; break; //green 0x008000
465 case 12: nHighlightColor
= COL_MAGENTA
; break; //magenta 0x800080
466 case 13: nHighlightColor
= COL_RED
; break; //red 0x800000
467 case 14: nHighlightColor
= COL_BROWN
; break; //brown 0x808000
468 case 15: nHighlightColor
= COL_GRAY
; break; //dark gray 0x808080
469 case 16: nHighlightColor
= COL_LIGHTGRAY
; break; //light gray 0xC0C0C0
472 if (rFilterName
== "writer8")
474 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
475 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nHighlightColor
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
479 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nHighlightColor
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
480 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
486 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,18), uno::UNO_QUERY
);
487 if (rFilterName
== "writer8")
489 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
490 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
494 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
495 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
501 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,19), uno::UNO_QUERY
);
502 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
503 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTBLUE
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
508 void Test::testCharStyleHighlight()
510 // MS Word has two kind of character backgrounds called character shading and highlighting.
511 // However, their character style can only accept shading. It ignores the highlighting value.
513 const OUString aFilterNames
[] = {
514 u
"Rich Text Format"_ustr
,
516 u
"Office Open XML Text"_ustr
,
519 for (OUString
const & rFilterName
: aFilterNames
)
521 createSwDoc("tdf138345_charstyle_highlight.odt");
523 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
525 // Export the document and import again for a check
526 saveAndReload(rFilterName
);
528 uno::Reference
<beans::XPropertySet
> xCharStyle
;
529 getStyles(u
"CharacterStyles"_ustr
)->getByName(u
"charBackground"_ustr
) >>= xCharStyle
;
530 const Color
nBackColor(0xFFDBB6); //orange-y
532 // Always export character style's background colour as shading, never as highlighting.
533 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xCharStyle
,u
"CharHighlight"_ustr
));
534 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xCharStyle
,u
"CharBackColor"_ustr
));
538 void Test::testCharHighlight()
540 auto batch
= comphelper::ConfigurationChanges::create();
541 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(false, batch
);
544 testCharHighlightBody();
545 testCharStyleHighlight();
547 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(true, batch
);
550 testCharHighlightBody();
551 testCharStyleHighlight();
554 void Test::testCharHighlightODF()
556 createSwDoc("char_background_editing.docx");
558 // don't check import, testMSCharBackgroundEditing already does that
560 uno::Reference
<text::XTextRange
> xPara
= getParagraph(1);
561 for (int i
= 1; i
<= 4; ++i
)
563 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
566 case 1: // non-transparent highlight
567 xRun
->setPropertyValue(u
"CharBackColor"_ustr
, uno::Any(static_cast<sal_Int32
>(128)));
568 xRun
->setPropertyValue(u
"CharBackTransparent"_ustr
, uno::Any(true));
569 xRun
->setPropertyValue(u
"CharHighlight"_ustr
, uno::Any(static_cast<sal_Int32
>(64)));
572 case 2: // transparent backcolor
573 xRun
->setPropertyValue(u
"CharBackColor"_ustr
, uno::Any(static_cast<sal_Int32
>(128)));
574 xRun
->setPropertyValue(u
"CharBackTransparent"_ustr
, uno::Any(true));
575 xRun
->setPropertyValue(u
"CharHighlight"_ustr
, uno::Any(static_cast<sal_Int32
>(COL_TRANSPARENT
)));
578 case 3: // non-transparent backcolor
579 xRun
->setPropertyValue(u
"CharBackColor"_ustr
, uno::Any(static_cast<sal_Int32
>(128)));
580 xRun
->setPropertyValue(u
"CharBackTransparent"_ustr
, uno::Any(false));
581 xRun
->setPropertyValue(u
"CharHighlight"_ustr
, uno::Any(static_cast<sal_Int32
>(COL_TRANSPARENT
)));
584 case 4: // non-transparent highlight again
585 xRun
->setPropertyValue(u
"CharBackColor"_ustr
, uno::Any(static_cast<sal_Int32
>(128)));
586 xRun
->setPropertyValue(u
"CharBackTransparent"_ustr
, uno::Any(false));
587 xRun
->setPropertyValue(u
"CharHighlight"_ustr
, uno::Any(static_cast<sal_Int32
>(64)));
592 saveAndReload(u
"writer8"_ustr
);
594 xPara
.set(getParagraph(1));
595 for (int i
= 1; i
<= 4; ++i
)
597 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
598 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(COL_TRANSPARENT
), getProperty
<sal_Int32
>(xRun
, u
"CharHighlight"_ustr
));
601 case 1: // non-transparent highlight
602 CPPUNIT_ASSERT_EQUAL(Color(0x000040), getProperty
<Color
>(xRun
, u
"CharBackColor"_ustr
));
603 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xRun
, u
"CharBackTransparent"_ustr
));
605 case 2: // transparent backcolor
606 CPPUNIT_ASSERT_EQUAL(COL_TRANSPARENT
, getProperty
<Color
>(xRun
, u
"CharBackColor"_ustr
));
607 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(xRun
, u
"CharBackTransparent"_ustr
));
609 case 3: // non-transparent backcolor
610 CPPUNIT_ASSERT_EQUAL(COL_BLUE
, getProperty
<Color
>(xRun
, u
"CharBackColor"_ustr
));
611 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xRun
, u
"CharBackTransparent"_ustr
));
613 case 4: // non-transparent highlight again
614 CPPUNIT_ASSERT_EQUAL(Color(0x000040), getProperty
<Color
>(xRun
, u
"CharBackColor"_ustr
));
615 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xRun
, u
"CharBackTransparent"_ustr
));
621 void Test::testMSCharBackgroundEditing()
623 // Simulate the editing process of imported MSO character background attributes
624 // and check how export behaves.
626 const OUString aFilterNames
[] = {
628 u
"Rich Text Format"_ustr
,
630 u
"Office Open XML Text"_ustr
,
633 auto batch
= comphelper::ConfigurationChanges::create();
634 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(true, batch
);
637 for (OUString
const & rFilterName
: aFilterNames
)
639 createSwDoc("char_background_editing.docx");
641 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
643 // Check whether import was done on the right way
644 uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
646 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,1), uno::UNO_QUERY
);
647 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
648 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
650 xRun
.set(getRun(xPara
,2), uno::UNO_QUERY
);
651 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTBLUE
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
652 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
654 xRun
.set(getRun(xPara
,3), uno::UNO_QUERY
);
655 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTBLUE
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
656 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
658 xRun
.set(getRun(xPara
,4), uno::UNO_QUERY
);
659 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
660 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
664 for( int i
= 1; i
<= 4; ++i
)
666 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
671 case 1: nBackColor
= COL_BLACK
; break; //black 0x000000
672 case 2: nBackColor
= COL_LIGHTCYAN
; break; //cyan 0x00ffff
673 case 3: nBackColor
= COL_LIGHTGREEN
; break; //green 0x00ff00
674 case 4: nBackColor
= COL_LIGHTMAGENTA
; break; //magenta 0xff00ff
676 xRun
->setPropertyValue(u
"CharBackColor"_ustr
, uno::Any(nBackColor
));
677 // Remove highlighting
678 xRun
->setPropertyValue(u
"CharHighlight"_ustr
, uno::Any(COL_TRANSPARENT
));
679 // Remove shading marker
680 uno::Sequence
<beans::PropertyValue
> aGrabBag
= getProperty
<uno::Sequence
<beans::PropertyValue
> >(xRun
,u
"CharInteropGrabBag"_ustr
);
681 for (beans::PropertyValue
& rProp
: asNonConstRange(aGrabBag
))
683 if (rProp
.Name
== "CharShadingMarker")
685 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), true, rProp
.Value
.get
<bool>());
686 rProp
.Value
<<= false;
689 xRun
->setPropertyValue(u
"CharInteropGrabBag"_ustr
, uno::Any(aGrabBag
));
692 // Export the document and import again for a check
693 saveAndReload(rFilterName
);
695 // Check whether background was exported as highlighting
696 xPara
.set(getParagraph(1));
697 for( int i
= 1; i
<= 4; ++i
)
702 case 1: nBackColor
= COL_BLACK
; break; //black 0x000000
703 case 2: nBackColor
= COL_LIGHTCYAN
; break; //light cyan 0x00ffff
704 case 3: nBackColor
= COL_LIGHTGREEN
; break; //light green 0x00ff00
705 case 4: nBackColor
= COL_LIGHTMAGENTA
; break; //light magenta 0xff00ff
707 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
708 if (rFilterName
== "writer8")
710 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
711 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
715 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
716 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,u
"CharBackColor"_ustr
));
722 void Test::testCharBackgroundToHighlighting()
724 // MSO highlighting has less kind of values so let's see how LO character background is converted
727 const OUString aFilterNames
[] = {
728 u
"Rich Text Format"_ustr
,
730 u
"Office Open XML Text"_ustr
,
733 for (OUString
const & rFilterName
: aFilterNames
)
735 createSwDoc("char_background.odt");
737 OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
739 auto batch
= comphelper::ConfigurationChanges::create();
740 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(true, batch
);
743 // Export the document and import again for a check
744 saveAndReload(rFilterName
);
746 // Check highlight color
747 const uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
748 for( int nRun
= 1; nRun
<= 19; ++nRun
)
750 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,nRun
), uno::UNO_QUERY
);
751 Color nHighlightColor
;
754 case 1: nHighlightColor
= COL_BLACK
; break; //black 0x000000
755 case 2: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
756 case 3: nHighlightColor
= COL_LIGHTMAGENTA
; break; //light magenta 0xff00ff
757 case 4: nHighlightColor
= COL_LIGHTCYAN
; break; //light cyan 0x00ffff
758 case 5: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
759 case 6: nHighlightColor
= COL_LIGHTRED
; break; //light red 0xff0000
760 case 7: nHighlightColor
= COL_LIGHTBLUE
; break; //light blue 0x0000ff
761 case 8: nHighlightColor
= COL_LIGHTGREEN
; break; //light green 0x00ff00
762 case 9: nHighlightColor
= COL_GREEN
; break; //dark green 0x008000
763 case 10: nHighlightColor
= COL_MAGENTA
; break; //dark magenta 0x800080
764 case 11: nHighlightColor
= COL_BLUE
; break; //dark blue 0x000080
765 case 12: nHighlightColor
= COL_BROWN
; break; //brown 0x808000
766 case 13: nHighlightColor
= COL_GRAY
; break; //dark gray 0x808080
767 case 14: nHighlightColor
= COL_BLACK
; break; //black 0x000000
768 case 15: nHighlightColor
= COL_LIGHTRED
; break; //light red 0xff0000
769 case 16: nHighlightColor
= COL_LIGHTGRAY
; break; //light gray 0xC0C0C0
770 case 17: nHighlightColor
= COL_RED
; break; //dark red 0x800000
771 case 18: nHighlightColor
= COL_GRAY
; break; //dark gray 0x808080
772 case 19: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
774 const OString sMessage
= sFailedMessage
+". Index of run with unmatched color: " + OString::number(nRun
);
775 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), nHighlightColor
, getProperty
<Color
>(xRun
,u
"CharHighlight"_ustr
));
781 void Test::testSkipImages()
783 // Check how LO skips image loading (but not texts of textboxes and custom shapes)
784 // during DOC and DOCX import, using the "SkipImages" FilterOptions.
786 std::pair
<OUString
, OUString
> aFilterNames
[] = {
787 { "skipimages.doc", "" },
788 { "skipimages.doc", "SkipImages" },
789 { "skipimages.docx", "" },
790 { "skipimages.docx", "SkipImages" }
793 for (auto const & rFilterNamePair
: aFilterNames
)
795 bool bSkipImages
= !rFilterNamePair
.second
.isEmpty();
796 OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterNamePair
.first
.toUtf8();
798 setImportFilterOptions(rFilterNamePair
.second
);
799 createSwDoc(rFilterNamePair
.first
.toUtf8().getStr());
800 sFailedMessage
+= " - " + rFilterNamePair
.second
.toUtf8();
802 // Check shapes (images, textboxes, custom shapes)
803 uno::Reference
<drawing::XShape
> xShape
;
804 uno::Reference
<graphic::XGraphic
> xGraphic
;
805 uno::Reference
< beans::XPropertySet
> XPropSet
;
806 uno::Reference
<awt::XBitmap
> xBitmap
;
808 bool bHasTextboxText
= false;
809 bool bHasCustomShapeText
= false;
810 sal_Int32 nImageCount
= 0;
812 for (int i
= 1; i
<= getShapes(); i
++)
814 xShape
= getShape(i
);
815 XPropSet
.set( xShape
, uno::UNO_QUERY_THROW
);
818 XPropSet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
819 xBitmap
.set(xGraphic
, uno::UNO_QUERY
);
823 catch (beans::UnknownPropertyException
&)
826 uno::Reference
<text::XTextRange
> xText(xShape
, uno::UNO_QUERY
);
829 OUString shapeText
= xText
->getString();
830 if (shapeText
.startsWith("Lorem ipsum"))
831 bHasTextboxText
= true;
832 else if (shapeText
.startsWith("Nam pretium"))
833 bHasCustomShapeText
= true;
837 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), bHasTextboxText
);
838 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), bHasCustomShapeText
);
839 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(bSkipImages
? 0 : 3), nImageCount
);
844 void Test::testNestedFieldmark()
846 // experimental config setting
849 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(
850 comphelper::ConfigurationChanges::create());
851 officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::set(false, pBatch
);
852 return pBatch
->commit();
854 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(comphelper::ConfigurationChanges::create());
855 officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::set(true, pBatch
);
858 auto verify
= [this](OUString
const& rTestName
) {
859 SwDoc
* pDoc
= getSwDoc();
860 IDocumentMarkAccess
const& rIDMA(*pDoc
->getIDocumentMarkAccess());
862 // no spurious bookmarks have been created
863 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(),
864 sal_Int32(0), rIDMA
.getBookmarksCount());
866 // check inner fieldmark
867 SwNodeIndex
const node1(*pDoc
->GetNodes().GetEndOfContent().StartOfSectionNode(), +2);
868 SwPosition
const innerPos(*node1
.GetNode().GetTextNode(),
869 node1
.GetNode().GetTextNode()->GetText().indexOf(CH_TXT_ATR_FIELDSTART
));
870 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(),
871 sal_Int32(1), innerPos
.GetContentIndex());
872 ::sw::mark::Fieldmark
*const pInner(rIDMA
.getFieldmarkAt(innerPos
));
873 CPPUNIT_ASSERT_MESSAGE(rTestName
.toUtf8().getStr(), pInner
);
874 OUString
const innerString(SwPaM(pInner
->GetMarkPos(), pInner
->GetOtherMarkPos()).GetText());
875 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(), OUString(
876 OUStringChar(CH_TXT_ATR_FIELDSTART
) + u
" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
877 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
878 + u
"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP
) + u
"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
879 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
880 + u
"baz" + OUStringChar(CH_TXT_ATR_FIELDEND
)), innerString
);
882 // check outer fieldmark
883 SwNodeIndex
const node2(node1
, -1);
884 SwPosition
const outerPos(*node2
.GetNode().GetTextNode(),
885 node2
.GetNode().GetTextNode()->GetText().indexOf(CH_TXT_ATR_FIELDSTART
));
886 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(),
887 sal_Int32(0), outerPos
.GetContentIndex());
888 ::sw::mark::Fieldmark
const*const pOuter(rIDMA
.getFieldmarkAt(outerPos
));
889 CPPUNIT_ASSERT_MESSAGE(rTestName
.toUtf8().getStr(), pOuter
);
890 OUString
const outerString(SwPaM(pOuter
->GetMarkPos(), pOuter
->GetOtherMarkPos()).GetText());
891 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(), OUString(
892 OUStringChar(CH_TXT_ATR_FIELDSTART
) + u
" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
893 + u
" " + OUStringChar(CH_TXT_ATR_FIELDSTART
) + u
" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
894 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
895 + u
"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP
) + u
"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
896 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
897 + u
"baz" + OUStringChar(CH_TXT_ATR_FIELDEND
) + OUStringChar(CH_TXTATR_NEWLINE
)
898 + u
"bar " + OUStringChar(CH_TXTATR_NEWLINE
)
899 + u
"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP
) + u
"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
900 + u
" foo " + OUStringChar(CH_TXTATR_NEWLINE
)
901 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
902 + u
"baz" + OUStringChar(CH_TXTATR_NEWLINE
)
903 + u
"bar " + OUStringChar(CH_TXTATR_NEWLINE
)
904 + u
"baz" + OUStringChar(CH_TXT_ATR_FIELDEND
)), outerString
);
906 // must return innermost mark
907 CPPUNIT_ASSERT_EQUAL(pInner
, rIDMA
.getInnerFieldmarkFor(innerPos
));
910 std::pair
<OUString
, OUString
> const aFilterNames
[] = {
911 {"writer8", "fieldmark_QUOTE_nest.fodt"},
912 {"Office Open XML Text", "fieldmark_QUOTE_nest.docx"},
913 {"Rich Text Format", "fieldmark_QUOTE_nest.rtf"},
916 for (auto const & rFilterName
: aFilterNames
)
918 createSwDoc(rFilterName
.second
.toUtf8().getStr());
920 verify(rFilterName
.first
+ ", load");
922 // Export the document and import again
923 saveAndReload(rFilterName
.first
);
925 verify(rFilterName
.first
+ " exported-reload");
929 auto Test::verifyText13(char const*const pTestName
) -> void
931 // OFFICE-3789 style:header-first/style:footer-first
932 uno::Reference
<beans::XPropertySet
> xPageStyle
;
933 getStyles(u
"PageStyles"_ustr
)->getByName(u
"Standard"_ustr
) >>= xPageStyle
;
934 uno::Reference
<text::XText
> xHF(getProperty
<uno::Reference
<text::XText
>>(xPageStyle
, u
"HeaderTextFirst"_ustr
));
935 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"Header first"_ustr
, xHF
->getString());
936 uno::Reference
<text::XText
> xFF(getProperty
<uno::Reference
<text::XText
>>(xPageStyle
, u
"FooterTextFirst"_ustr
));
937 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"Footer first"_ustr
, xFF
->getString());
938 // OFFICE-3767 text:contextual-spacing
939 uno::Reference
<text::XTextRange
> xPara(getParagraph(1));
940 CPPUNIT_ASSERT_MESSAGE(pTestName
, getProperty
<bool>(xPara
, u
"ParaContextMargin"_ustr
));
941 // OFFICE-3776 meta:creator-initials
942 uno::Reference
<text::XTextRange
> xRun(getRun(xPara
, 1));
943 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"Annotation"_ustr
, getProperty
<OUString
>(xRun
, u
"TextPortionType"_ustr
));
944 uno::Reference
<beans::XPropertySet
> xComment(getProperty
<uno::Reference
<beans::XPropertySet
>>(xRun
, u
"TextField"_ustr
));
945 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"dj"_ustr
, getProperty
<OUString
>(xComment
, u
"Initials"_ustr
));
946 // OFFICE-3941 text:index-entry-link-start/text:index-entry-link-end
947 uno::Reference
<text::XDocumentIndexesSupplier
> xDIS(mxComponent
, uno::UNO_QUERY
);
948 uno::Reference
<container::XIndexAccess
> xIndexes(xDIS
->getDocumentIndexes());
949 uno::Reference
<text::XDocumentIndex
> xIndex(xIndexes
->getByIndex(0), uno::UNO_QUERY
);
950 uno::Reference
<container::XIndexReplace
> xLevels(getProperty
<uno::Reference
<container::XIndexReplace
>>(xIndex
, u
"LevelFormat"_ustr
));
951 uno::Sequence
<beans::PropertyValues
> format
;
952 xLevels
->getByIndex(1) >>= format
; // 1-based?
953 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"TokenType"_ustr
, format
[0][0].Name
);
954 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"TokenHyperlinkStart"_ustr
, format
[0][0].Value
.get
<OUString
>());
955 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"TokenType"_ustr
, format
[4][0].Name
);
956 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, u
"TokenHyperlinkEnd"_ustr
, format
[4][0].Value
.get
<OUString
>());
959 // test ODF 1.3 new text document features
960 void Test::testODF13()
962 Resetter
resetter([]() { SetODFDefaultVersion(SvtSaveOptions::ODFVER_LATEST
); });
965 createSwDoc("text13e.odt");
968 verifyText13("import");
972 SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_013
);
974 saveAndReload(u
"writer8"_ustr
);
977 xmlDocUniquePtr pContentXml
= parseExport(u
"content.xml"_ustr
);
978 assertXPath(pContentXml
, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties[@style:contextual-spacing='true']");
979 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials");
980 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials", 0);
981 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-start");
982 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-start", 0);
983 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-end");
984 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-end", 0);
985 xmlDocUniquePtr pStylesXml
= parseExport(u
"styles.xml"_ustr
);
986 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:header-first");
987 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:header-first", 0);
988 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:footer-first");
989 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first", 0);
992 verifyText13("1.3 reload");
995 // export ODF 1.2 extended
996 SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_012_EXTENDED
);
998 // FIXME: it's not possible to use 'reload' here because the validation fails with
999 // Error: unexpected attribute "loext:contextual-spacing"
1000 utl::MediaDescriptor aMediaDescriptor
;
1001 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer8"_ustr
;
1003 uno::Reference
<frame::XStorable
> const xStorable(mxComponent
, uno::UNO_QUERY
);
1004 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
1007 xmlDocUniquePtr pContentXml
= parseExport(u
"content.xml"_ustr
);
1008 assertXPath(pContentXml
, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties[@loext:contextual-spacing='true']");
1009 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials");
1010 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials", 0);
1011 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-start");
1012 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-start", 0);
1013 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-end");
1014 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-end", 0);
1015 xmlDocUniquePtr pStylesXml
= parseExport(u
"styles.xml"_ustr
);
1016 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:header-first");
1017 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:header-first", 0);
1018 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first");
1019 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:footer-first", 0);
1022 loadFromURL(maTempFile
.GetURL());
1025 verifyText13("1.2 Extended reload");
1029 SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_012
);
1031 // don't reload - no point
1032 save(u
"writer8"_ustr
);
1035 xmlDocUniquePtr pContentXml
= parseExport(u
"content.xml"_ustr
);
1036 assertXPathNoAttribute(pContentXml
, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties", "contextual-spacing");
1037 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials", 0);
1038 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials", 0);
1039 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-start", 0);
1040 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-start", 0);
1041 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/text:index-entry-link-end", 0);
1042 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:illustration-index/text:illustration-index-source/text:illustration-index-entry-template/loext:index-entry-link-end", 0);
1043 xmlDocUniquePtr pStylesXml
= parseExport(u
"styles.xml"_ustr
);
1044 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:header-first", 0);
1045 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:header-first", 0);
1046 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:footer-first", 0);
1047 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first", 0);
1051 void Test::testRedlineFlags()
1053 const OUString aFilterNames
[] = {
1055 u
"Rich Text Format"_ustr
,
1057 u
"Office Open XML Text"_ustr
,
1061 SwDoc
* pDoc
= getSwDoc();
1063 SwPaM
pam(SwPosition(pDoc
->GetNodes().GetEndOfContent(), SwNodeOffset(-1)));
1064 pDoc
->getIDocumentContentOperations().InsertString(pam
, u
"foo bar baz"_ustr
);
1066 IDocumentRedlineAccess
& rIDRA(pDoc
->getIDocumentRedlineAccess());
1067 // enable change tracking
1068 rIDRA
.SetRedlineFlags(rIDRA
.GetRedlineFlags()
1069 | RedlineFlags::On
| RedlineFlags::ShowDelete
);
1071 // need a delete redline to trigger mode switching
1072 pam
.Move(fnMoveForward
, GoInDoc
);
1074 pam
.Move(fnMoveBackward
, GoInDoc
);
1075 pDoc
->getIDocumentContentOperations().DeleteAndJoin(pam
);
1077 // hide delete redlines
1078 RedlineFlags
const nRedlineFlags
=
1079 rIDRA
.GetRedlineFlags() & ~RedlineFlags::ShowDelete
;
1080 rIDRA
.SetRedlineFlags(nRedlineFlags
);
1082 for (OUString
const & rFilterName
: aFilterNames
)
1084 // export the document
1087 // tdf#97103 check that redline mode is properly restored
1088 CPPUNIT_ASSERT_EQUAL_MESSAGE(
1089 OString(OString::Concat("redline mode not restored in ") + rFilterName
.toUtf8()).getStr(),
1090 static_cast<int>(nRedlineFlags
), static_cast<int>(rIDRA
.GetRedlineFlags()));
1094 void Test::testBulletAsImage()
1096 OUString aFilterNames
[] = {
1099 u
"Office Open XML Text"_ustr
,
1100 u
"Rich Text Format"_ustr
,
1103 for (OUString
const & rFilterName
: aFilterNames
)
1105 OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1107 createSwDoc("BulletAsImage.odt");
1109 // Check if import was successful
1111 uno::Reference
<text::XTextRange
> xPara(getParagraph(1));
1112 uno::Reference
<beans::XPropertySet
> xPropertySet(xPara
, uno::UNO_QUERY
);
1113 uno::Reference
<container::XIndexAccess
> xLevels
;
1114 xLevels
.set(xPropertySet
->getPropertyValue(u
"NumberingRules"_ustr
), uno::UNO_QUERY
);
1115 uno::Sequence
<beans::PropertyValue
> aProperties
;
1116 xLevels
->getByIndex(0) >>= aProperties
;
1117 uno::Reference
<awt::XBitmap
> xBitmap
;
1119 sal_Int16 nNumberingType
= -1;
1121 for (beans::PropertyValue
const& rProperty
: aProperties
)
1123 if (rProperty
.Name
== "NumberingType")
1125 nNumberingType
= rProperty
.Value
.get
<sal_Int16
>();
1127 else if (rProperty
.Name
== "GraphicBitmap")
1129 if (rProperty
.Value
.has
<uno::Reference
<awt::XBitmap
>>())
1131 xBitmap
= rProperty
.Value
.get
<uno::Reference
<awt::XBitmap
>>();
1134 else if (rProperty
.Name
== "GraphicSize")
1136 aSize
= rProperty
.Value
.get
<awt::Size
>();
1140 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), style::NumberingType::BITMAP
, nNumberingType
);
1143 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
1144 Graphic
aGraphic(uno::Reference
<graphic::XGraphic
>(xBitmap
, uno::UNO_QUERY
));
1145 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), GraphicType::Bitmap
, aGraphic
.GetType());
1146 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), aGraphic
.GetSizeBytes() > o3tl::make_unsigned(0));
1147 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Width());
1148 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Height());
1151 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Width
);
1152 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Height
);
1155 // Export the document and import again for a check
1156 saveAndReload(rFilterName
);
1159 uno::Reference
<text::XTextRange
> xPara(getParagraph(1));
1160 uno::Reference
<beans::XPropertySet
> xPropertySet(xPara
, uno::UNO_QUERY
);
1161 uno::Reference
<container::XIndexAccess
> xLevels
;
1162 xLevels
.set(xPropertySet
->getPropertyValue(u
"NumberingRules"_ustr
), uno::UNO_QUERY
);
1163 uno::Sequence
<beans::PropertyValue
> aProperties
;
1164 xLevels
->getByIndex(0) >>= aProperties
;
1165 uno::Reference
<awt::XBitmap
> xBitmap
;
1167 sal_Int16 nNumberingType
= -1;
1169 for (beans::PropertyValue
const& rProperty
: aProperties
)
1171 if (rProperty
.Name
== "NumberingType")
1173 nNumberingType
= rProperty
.Value
.get
<sal_Int16
>();
1175 else if (rProperty
.Name
== "GraphicBitmap")
1177 if (rProperty
.Value
.has
<uno::Reference
<awt::XBitmap
>>())
1179 xBitmap
= rProperty
.Value
.get
<uno::Reference
<awt::XBitmap
>>();
1182 else if (rProperty
.Name
== "GraphicSize")
1184 aSize
= rProperty
.Value
.get
<awt::Size
>();
1188 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), style::NumberingType::BITMAP
, nNumberingType
);
1191 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
1192 Graphic
aGraphic(uno::Reference
<graphic::XGraphic
>(xBitmap
, uno::UNO_QUERY
));
1193 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), GraphicType::Bitmap
, aGraphic
.GetType());
1194 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), aGraphic
.GetSizeBytes() > o3tl::make_unsigned(0));
1195 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Width());
1196 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Height());
1199 if (rFilterName
== "write8") // ODT is correct
1201 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Width
);
1202 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Height
);
1204 // FIXME: MS Filters don't work correctly for graphic bullet size
1205 else if (rFilterName
== "Office Open XML Text" || rFilterName
== "Rich Text Format")
1207 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(279), aSize
.Width
);
1208 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(279), aSize
.Height
);
1210 else if (rFilterName
== "MS Word 97")
1212 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(296), aSize
.Width
);
1213 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(296), aSize
.Height
);
1219 CPPUNIT_TEST_FIXTURE(Test
, testListLabelPDFExport
)
1223 uno::Reference
<text::XTextDocument
> xDoc(mxComponent
, uno::UNO_QUERY_THROW
);
1224 uno::Reference
<text::XText
> xText(xDoc
->getText());
1225 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY_THROW
);
1226 uno::Reference
<container::XIndexReplace
> xNumRule(
1227 xFactory
->createInstance(u
"com.sun.star.text.NumberingRules"_ustr
),
1228 uno::UNO_QUERY_THROW
);
1229 OUString listFormat
;
1230 for (sal_Int32 i
= 0; i
< xNumRule
->getCount(); ++i
)
1232 uno::Sequence
<beans::PropertyValue
> format
;
1234 xNumRule
->getByIndex(i
) >>= format
;
1236 auto it(::std::find_if(format
.begin(), format
.end(),
1237 [](auto const& r
) { return r
.Name
== "NumberingType"; }));
1238 // need something RTL
1239 const_cast<uno::Any
&>(it
->Value
) <<= style::NumberingType::CHARS_ARABIC
;
1243 // this doesn't work any more
1244 auto it(::std::find_if(format
.begin(), format
.end(),
1245 [](auto const& r
) { return r
.Name
== "ParentNumbering"; }));
1246 const_cast<uno::Any
&>(it
->Value
) <<= sal_Int16(i
+ 1);
1248 listFormat
+= "%" + OUString::number(i
+1) + "%.";
1249 auto it(::std::find_if(format
.begin(), format
.end(),
1250 [](auto const& r
) { return r
.Name
== "ListFormat"; }));
1251 const_cast<uno::Any
&>(it
->Value
) <<= listFormat
;
1253 xNumRule
->replaceByIndex(i
, uno::Any(format
));
1255 uno::Reference
<beans::XPropertySet
>(getParagraph(1), uno::UNO_QUERY_THROW
)->setPropertyValue(u
"NumberingRules"_ustr
, uno::Any(xNumRule
));
1256 xText
->insertControlCharacter(xText
->getEnd(), text::ControlCharacter::PARAGRAPH_BREAK
, false);
1257 uno::Reference
<beans::XPropertySet
>(getParagraph(2), uno::UNO_QUERY_THROW
)->setPropertyValue(u
"NumberingLevel"_ustr
, uno::Any(sal_Int16(1)));
1258 xText
->insertControlCharacter(xText
->getEnd(), text::ControlCharacter::PARAGRAPH_BREAK
, false);
1259 uno::Reference
<beans::XPropertySet
>(getParagraph(3), uno::UNO_QUERY_THROW
)->setPropertyValue(u
"NumberingLevel"_ustr
, uno::Any(sal_Int16(2)));
1261 // check PDF export of the list items (label in particular)
1262 utl::MediaDescriptor aMediaDescriptor
;
1263 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
1265 uno::Sequence
<beans::PropertyValue
> aFilterData(
1266 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1267 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1268 css::uno::Reference
<frame::XStorable
> xStorable(mxComponent
, css::uno::UNO_QUERY_THROW
);
1269 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
1271 // Parse the export result with pdfium.
1272 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1274 // Non-NULL pPdfDocument means pdfium is available.
1275 if (pPdfDocument
!= nullptr)
1277 // The document has one page.
1278 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1279 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1280 CPPUNIT_ASSERT(pPdfPage
);
1282 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1283 CPPUNIT_ASSERT(pPdfTextPage
);
1285 int nChars
= pPdfTextPage
->countChars();
1286 CPPUNIT_ASSERT_EQUAL(22, nChars
);
1288 // Check that the label strings were exported correctly
1289 std::vector
<sal_uInt32
> aChars(nChars
);
1290 for (int i
= 0; i
< nChars
; i
++)
1291 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1292 OUString
aText(aChars
.data(), aChars
.size());
1293 CPPUNIT_ASSERT_EQUAL(u
"\u0623\r\n.\r\n\u0623.\u0623\r\n.\r\n\u0623.\u0623.\u0623\r\n."_ustr
, aText
);
1296 // Parse the document again to get its raw content
1297 // TODO: get the content from PDFiumPage somehow
1298 vcl::filter::PDFDocument aDocument
;
1299 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
1300 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
1302 // The document has one page.
1303 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1304 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1306 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1307 CPPUNIT_ASSERT(pContents
);
1308 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1309 CPPUNIT_ASSERT(pStream
);
1310 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1312 SvMemoryStream aUncompressed
;
1314 aZCodec
.BeginCompression();
1315 rObjectStream
.Seek(0);
1316 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1317 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1319 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1320 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1334 std::vector
<int> mcids
;
1338 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1343 std::string_view
const line(pStart
, pLine
- pStart
);
1345 if (!line
.empty() && line
[0] != '%')
1347 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1348 if (o3tl::starts_with(line
, "/Lbl<</MCID") && o3tl::ends_with(line
, ">>BDC"))
1350 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1351 mcids
.push_back(o3tl::toInt32(line
.substr(12)));
1355 else if (state
== Lbl
)
1357 auto const endj(line
.find(">Tj"));
1358 if (endj
!= ::std::string_view::npos
)
1360 state
= LblFoundText
;
1365 auto const endJ(line
.find("]TJ"));
1366 if (endJ
!= ::std::string_view::npos
)
1368 state
= LblFoundText
;
1373 else if (state
!= Default
&& line
== "EMC")
1375 CPPUNIT_ASSERT_EQUAL_MESSAGE("missing text", LblFoundText
, state
);
1380 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1381 // ideally there should be 3 but apparently every text portion gets its own
1382 // tag - this should not be a problem if these are grouped in the structure
1384 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nLbl
)>(6), nLbl
);
1385 // these are quite arbitrary?
1386 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nLbl
)>(6), nLblTJ
+ nLblTj
);
1389 for (const auto& rDocElement
: aDocument
.GetElements())
1391 auto pObject0
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1394 auto pType0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject0
->Lookup("Type"_ostr
));
1395 if (!pType0
|| pType0
->GetValue() != "StructElem")
1399 auto pS0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject0
->Lookup("S"_ostr
));
1400 if (!pS0
|| pS0
->GetValue() != "Document")
1404 auto pKids0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject0
->Lookup("K"_ostr
));
1405 CPPUNIT_ASSERT(pKids0
);
1407 for (const auto& pKid0
: pKids0
->GetElements())
1409 auto pRefKid0
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKid0
);
1410 CPPUNIT_ASSERT(pRefKid0
);
1411 auto pObject1
= pRefKid0
->LookupObject();
1412 CPPUNIT_ASSERT(pObject1
);
1413 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
1414 CPPUNIT_ASSERT(pType1
);
1416 if (pType1
&& pType1
->GetValue() == "StructElem")
1418 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
1419 if (pS1
&& pS1
->GetValue() == "L")
1422 auto pKids1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1
->Lookup("K"_ostr
));
1423 CPPUNIT_ASSERT(pKids1
);
1424 // this is purely structural so there should be 1 child
1425 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids1
->GetElements().size());
1427 auto pRefKid11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1
->GetElements()[0]);
1428 CPPUNIT_ASSERT(pRefKid11
);
1429 auto pObject11
= pRefKid11
->LookupObject();
1430 CPPUNIT_ASSERT(pObject11
);
1431 auto pType11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("Type"_ostr
));
1432 CPPUNIT_ASSERT(pType11
);
1433 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11
->GetValue());
1434 auto pS11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("S"_ostr
));
1435 CPPUNIT_ASSERT(pS11
);
1436 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS11
->GetValue());
1437 // LI has 2 children: Lbl and LBody
1438 auto pKids11
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11
->Lookup("K"_ostr
));
1439 CPPUNIT_ASSERT(pKids11
);
1440 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids11
->GetElements().size());
1442 auto pRefKid111
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11
->GetElements()[0]);
1443 CPPUNIT_ASSERT(pRefKid111
);
1444 auto pObject111
= pRefKid111
->LookupObject();
1445 CPPUNIT_ASSERT(pObject111
);
1446 auto pType111
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject111
->Lookup("Type"_ostr
));
1447 CPPUNIT_ASSERT(pType111
);
1448 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType111
->GetValue());
1449 auto pS111
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject111
->Lookup("S"_ostr
));
1450 CPPUNIT_ASSERT(pS111
);
1451 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS111
->GetValue());
1452 // Lbl has 2 children: the first 2 mcids (in order)
1453 auto pKids111
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject111
->Lookup("K"_ostr
));
1454 CPPUNIT_ASSERT(pKids111
);
1455 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids111
->GetElements().size());
1457 auto pRefKid1111
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids111
->GetElements()[0]);
1458 CPPUNIT_ASSERT(pRefKid1111
);
1459 CPPUNIT_ASSERT_EQUAL(mcids
[0], int(pRefKid1111
->GetValue()));
1460 auto pRefKid1112
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids111
->GetElements()[1]);
1461 CPPUNIT_ASSERT(pRefKid1112
);
1462 CPPUNIT_ASSERT_EQUAL(mcids
[1], int(pRefKid1112
->GetValue()));
1464 auto pRefKid112
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11
->GetElements()[1]);
1465 CPPUNIT_ASSERT(pRefKid112
);
1466 auto pObject112
= pRefKid112
->LookupObject();
1467 CPPUNIT_ASSERT(pObject112
);
1468 auto pType112
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112
->Lookup("Type"_ostr
));
1469 CPPUNIT_ASSERT(pType112
);
1470 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112
->GetValue());
1471 auto pS112
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112
->Lookup("S"_ostr
));
1472 CPPUNIT_ASSERT(pS112
);
1473 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS112
->GetValue());
1474 // LBody has 2 children: paragraph and nested L (in order)
1475 auto pKids112
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112
->Lookup("K"_ostr
));
1476 CPPUNIT_ASSERT(pKids112
);
1477 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112
->GetElements().size());
1479 auto pRefKid1121
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112
->GetElements()[0]);
1480 CPPUNIT_ASSERT(pRefKid1121
);
1481 auto pObject1121
= pRefKid1121
->LookupObject();
1482 CPPUNIT_ASSERT(pObject1121
);
1483 auto pType1121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1121
->Lookup("Type"_ostr
));
1484 CPPUNIT_ASSERT(pType1121
);
1485 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1121
->GetValue());
1486 auto pS1121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1121
->Lookup("S"_ostr
));
1487 CPPUNIT_ASSERT(pS1121
);
1488 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1121
->GetValue());
1490 auto pRefKid1122
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112
->GetElements()[1]);
1491 CPPUNIT_ASSERT(pRefKid1122
);
1492 auto pObject1122
= pRefKid1122
->LookupObject();
1493 CPPUNIT_ASSERT(pObject1122
);
1494 auto pType1122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122
->Lookup("Type"_ostr
));
1495 CPPUNIT_ASSERT(pType1122
);
1496 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122
->GetValue());
1497 auto pS1122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122
->Lookup("S"_ostr
));
1498 CPPUNIT_ASSERT(pS1122
);
1499 CPPUNIT_ASSERT_EQUAL("L"_ostr
, pS1122
->GetValue());
1500 auto pKids1122
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1122
->Lookup("K"_ostr
));
1501 CPPUNIT_ASSERT(pKids1122
);
1502 // this is purely structural so there should be 1 child
1503 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids1122
->GetElements().size());
1505 auto pRefKid11221
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1122
->GetElements()[0]);
1506 CPPUNIT_ASSERT(pRefKid11221
);
1507 auto pObject11221
= pRefKid11221
->LookupObject();
1508 CPPUNIT_ASSERT(pObject11221
);
1509 auto pType11221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221
->Lookup("Type"_ostr
));
1510 CPPUNIT_ASSERT(pType11221
);
1511 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11221
->GetValue());
1512 auto pS11221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221
->Lookup("S"_ostr
));
1513 CPPUNIT_ASSERT(pS11221
);
1514 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS11221
->GetValue());
1515 // LI has 2 children: Lbl and LBody
1516 auto pKids11221
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11221
->Lookup("K"_ostr
));
1517 CPPUNIT_ASSERT(pKids11221
);
1518 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids11221
->GetElements().size());
1520 auto pRefKid112211
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221
->GetElements()[0]);
1521 CPPUNIT_ASSERT(pRefKid112211
);
1522 auto pObject112211
= pRefKid112211
->LookupObject();
1523 CPPUNIT_ASSERT(pObject112211
);
1524 auto pType112211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112211
->Lookup("Type"_ostr
));
1525 CPPUNIT_ASSERT(pType112211
);
1526 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112211
->GetValue());
1527 auto pS112211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112211
->Lookup("S"_ostr
));
1528 CPPUNIT_ASSERT(pS112211
);
1529 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS112211
->GetValue());
1530 // Lbl has 2 children: the first 2 mcids (in order)
1531 auto pKids112211
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112211
->Lookup("K"_ostr
));
1532 CPPUNIT_ASSERT(pKids112211
);
1533 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112211
->GetElements().size());
1535 auto pRefKid1122111
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112211
->GetElements()[0]);
1536 CPPUNIT_ASSERT(pRefKid1122111
);
1537 CPPUNIT_ASSERT_EQUAL(mcids
[2], int(pRefKid1122111
->GetValue()));
1538 auto pRefKid1122112
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112211
->GetElements()[1]);
1539 CPPUNIT_ASSERT(pRefKid1122112
);
1540 CPPUNIT_ASSERT_EQUAL(mcids
[3], int(pRefKid1122112
->GetValue()));
1542 auto pRefKid112212
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221
->GetElements()[1]);
1543 CPPUNIT_ASSERT(pRefKid112212
);
1544 auto pObject112212
= pRefKid112212
->LookupObject();
1545 CPPUNIT_ASSERT(pObject112212
);
1546 auto pType112212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212
->Lookup("Type"_ostr
));
1547 CPPUNIT_ASSERT(pType112212
);
1548 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112212
->GetValue());
1549 auto pS112212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212
->Lookup("S"_ostr
));
1550 CPPUNIT_ASSERT(pS112212
);
1551 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS112212
->GetValue());
1552 // LBody has 2 children: paragraph and nested L (in order)
1553 auto pKids112212
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112212
->Lookup("K"_ostr
));
1554 CPPUNIT_ASSERT(pKids112212
);
1555 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112212
->GetElements().size());
1557 auto pRefKid1122121
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112212
->GetElements()[0]);
1558 CPPUNIT_ASSERT(pRefKid1122121
);
1559 auto pObject1122121
= pRefKid1122121
->LookupObject();
1560 CPPUNIT_ASSERT(pObject1122121
);
1561 auto pType1122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122121
->Lookup("Type"_ostr
));
1562 CPPUNIT_ASSERT(pType1122121
);
1563 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122121
->GetValue());
1564 auto pS1122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122121
->Lookup("S"_ostr
));
1565 CPPUNIT_ASSERT(pS1122121
);
1566 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1122121
->GetValue());
1568 auto pRefKid1122122
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112212
->GetElements()[1]);
1569 CPPUNIT_ASSERT(pRefKid1122122
);
1570 auto pObject1122122
= pRefKid1122122
->LookupObject();
1571 CPPUNIT_ASSERT(pObject1122122
);
1572 auto pType1122122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122
->Lookup("Type"_ostr
));
1573 CPPUNIT_ASSERT(pType1122122
);
1574 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122122
->GetValue());
1575 auto pS1122122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122
->Lookup("S"_ostr
));
1576 CPPUNIT_ASSERT(pS1122122
);
1577 CPPUNIT_ASSERT_EQUAL("L"_ostr
, pS1122122
->GetValue());
1578 auto pKids1122122
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1122122
->Lookup("K"_ostr
));
1579 CPPUNIT_ASSERT(pKids1122122
);
1580 // this is purely structural so there should be 1 child
1581 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids1122122
->GetElements().size());
1583 auto pRefKid11221221
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1122122
->GetElements()[0]);
1584 CPPUNIT_ASSERT(pRefKid11221221
);
1585 auto pObject11221221
= pRefKid11221221
->LookupObject();
1586 CPPUNIT_ASSERT(pObject11221221
);
1587 auto pType11221221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221221
->Lookup("Type"_ostr
));
1588 CPPUNIT_ASSERT(pType11221221
);
1589 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11221221
->GetValue());
1590 auto pS11221221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221221
->Lookup("S"_ostr
));
1591 CPPUNIT_ASSERT(pS11221221
);
1592 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS11221221
->GetValue());
1593 // LI has 2 children: Lbl and LBody
1594 auto pKids11221221
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11221221
->Lookup("K"_ostr
));
1595 CPPUNIT_ASSERT(pKids11221221
);
1596 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids11221221
->GetElements().size());
1598 auto pRefKid112212211
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221221
->GetElements()[0]);
1599 CPPUNIT_ASSERT(pRefKid112212211
);
1600 auto pObject112212211
= pRefKid112212211
->LookupObject();
1601 CPPUNIT_ASSERT(pObject112212211
);
1602 auto pType112212211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212211
->Lookup("Type"_ostr
));
1603 CPPUNIT_ASSERT(pType112212211
);
1604 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112212211
->GetValue());
1605 auto pS112212211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212211
->Lookup("S"_ostr
));
1606 CPPUNIT_ASSERT(pS112212211
);
1607 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS112212211
->GetValue());
1608 // Lbl has 2 children: the first 2 mcids (in order)
1609 auto pKids112212211
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112212211
->Lookup("K"_ostr
));
1610 CPPUNIT_ASSERT(pKids112212211
);
1611 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112212211
->GetElements().size());
1613 auto pRefKid1122122111
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112212211
->GetElements()[0]);
1614 CPPUNIT_ASSERT(pRefKid1122122111
);
1615 CPPUNIT_ASSERT_EQUAL(mcids
[4], int(pRefKid1122122111
->GetValue()));
1616 auto pRefKid1122122112
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112212211
->GetElements()[1]);
1617 CPPUNIT_ASSERT(pRefKid1122122112
);
1618 CPPUNIT_ASSERT_EQUAL(mcids
[5], int(pRefKid1122122112
->GetValue()));
1620 auto pRefKid112212212
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221221
->GetElements()[1]);
1621 CPPUNIT_ASSERT(pRefKid112212212
);
1622 auto pObject112212212
= pRefKid112212212
->LookupObject();
1623 CPPUNIT_ASSERT(pObject112212212
);
1624 auto pType112212212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212212
->Lookup("Type"_ostr
));
1625 CPPUNIT_ASSERT(pType112212212
);
1626 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112212212
->GetValue());
1627 auto pS112212212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212212
->Lookup("S"_ostr
));
1628 CPPUNIT_ASSERT(pS112212212
);
1629 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS112212212
->GetValue());
1630 // inner LBody has 1 children: paragraph
1631 auto pKids112212212
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112212212
->Lookup("K"_ostr
));
1632 CPPUNIT_ASSERT(pKids112212212
);
1633 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids112212212
->GetElements().size());
1635 auto pRefKid1122122121
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112212212
->GetElements()[0]);
1636 CPPUNIT_ASSERT(pRefKid1122122121
);
1637 auto pObject1122122121
= pRefKid1122122121
->LookupObject();
1638 CPPUNIT_ASSERT(pObject1122122121
);
1639 auto pType1122122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122121
->Lookup("Type"_ostr
));
1640 CPPUNIT_ASSERT(pType1122122121
);
1641 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122122121
->GetValue());
1642 auto pS1122122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122121
->Lookup("S"_ostr
));
1643 CPPUNIT_ASSERT(pS1122122121
);
1644 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1122122121
->GetValue());
1649 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nL
)>(1), nL
);
1652 CPPUNIT_TEST_FIXTURE(Test
, testTdf143311
)
1654 createSwDoc("tdf143311-1.docx");
1655 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(1), u
"Decorative"_ustr
));
1657 // add another one that's a SdrObject
1658 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1659 uno::Reference
<drawing::XShape
> xShape(
1660 xFactory
->createInstance(u
"com.sun.star.drawing.RectangleShape"_ustr
), uno::UNO_QUERY
);
1661 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
1662 xShapeProps
->setPropertyValue(u
"Decorative"_ustr
, uno::Any(true));
1663 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1664 uno::Reference
<drawing::XDrawPage
> xDrawPage(xDrawPageSupplier
->getDrawPage());
1665 xDrawPage
->add(xShape
);
1667 // check DOCX filters
1668 saveAndReload(u
"Office Open XML Text"_ustr
);
1669 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(1), u
"Decorative"_ustr
));
1670 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(2), u
"Decorative"_ustr
));
1672 // tdf#153925 not imported - check default and set it to test ODF filters
1673 uno::Reference
<beans::XPropertySet
> const xStyle(getStyles(u
"FrameStyles"_ustr
)->getByName(u
"Formula"_ustr
), uno::UNO_QUERY_THROW
);
1674 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xStyle
, u
"Decorative"_ustr
));
1675 xStyle
->setPropertyValue(u
"Decorative"_ustr
, uno::Any(true));
1677 // check ODF filters
1678 saveAndReload(u
"writer8"_ustr
);
1679 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(1), u
"Decorative"_ustr
));
1680 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(2), u
"Decorative"_ustr
));
1681 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getStyles(u
"FrameStyles"_ustr
)->getByName(u
"Formula"_ustr
), u
"Decorative"_ustr
));
1684 utl::MediaDescriptor aMediaDescriptor
;
1685 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
1687 uno::Sequence
<beans::PropertyValue
> aFilterData(
1688 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1689 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1690 css::uno::Reference
<frame::XStorable
> xStorable(mxComponent
, css::uno::UNO_QUERY_THROW
);
1691 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
1693 vcl::filter::PDFDocument aDocument
;
1694 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
1695 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
1697 // The document has one page.
1698 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1699 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1701 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1702 CPPUNIT_ASSERT(pContents
);
1703 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1704 CPPUNIT_ASSERT(pStream
);
1705 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1707 SvMemoryStream aUncompressed
;
1709 aZCodec
.BeginCompression();
1710 rObjectStream
.Seek(0);
1711 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1712 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1714 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1715 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1731 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1736 std::string_view
const line(pStart
, pLine
- pStart
);
1738 if (!line
.empty() && line
[0] != '%')
1740 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1741 if (line
== "/Artifact BMC")
1743 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1747 else if (o3tl::starts_with(line
, "/Standard<</MCID") && o3tl::ends_with(line
, ">>BDC"))
1749 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1753 else if (line
== "EMC")
1755 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
1758 else if (nLine
> 1) // first line is expected "0.1 w"
1760 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
1764 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1765 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged
)>(25), nTagged
); // text in body
1766 // 1 decorative image + 1 decorative shape + 1 pre-existing rectangle border or something
1767 CPPUNIT_ASSERT(nArtifacts
>= 3);
1770 void Test::testTextFormField()
1772 const OUString aFilterNames
[] = {
1775 u
"Office Open XML Text"_ustr
,
1778 for (const OUString
& rFilterName
: aFilterNames
)
1780 createSwDoc("text_form_field.odt");
1782 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1784 // Export the document and import again for a check
1785 saveAndReload(rFilterName
);
1787 // Check the document after round trip
1788 SwDoc
* pDoc
= getSwDoc();
1789 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1791 // We have two text form fields
1792 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(2), pMarkAccess
->getAllMarksCount());
1794 // Check whether all fieldmarks are text form fields
1795 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1797 ::sw::mark::Fieldmark
* pFieldmark
= dynamic_cast<::sw::mark::Fieldmark
*>(*aIter
);
1798 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1799 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMTEXT
, pFieldmark
->GetFieldname());
1802 // In the first paragraph we have an empty text form field with the placeholder spaces
1803 const uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
1804 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"TextFieldStart"_ustr
, getProperty
<OUString
>(getRun(xPara
, 1), u
"TextPortionType"_ustr
));
1805 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"TextFieldSeparator"_ustr
, getProperty
<OUString
>(getRun(xPara
, 2), u
"TextPortionType"_ustr
));
1806 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"Text"_ustr
, getProperty
<OUString
>(getRun(xPara
, 3), u
"TextPortionType"_ustr
));
1807 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), vEnSpaces
, getRun(xPara
, 3)->getString());
1808 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"TextFieldEnd"_ustr
, getProperty
<OUString
>(getRun(xPara
, 4), u
"TextPortionType"_ustr
));
1810 // In the second paragraph we have a set text
1811 const uno::Reference
< text::XTextRange
> xPara2
= getParagraph(2);
1812 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"TextFieldStart"_ustr
, getProperty
<OUString
>(getRun(xPara2
, 1), u
"TextPortionType"_ustr
));
1813 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"TextFieldSeparator"_ustr
, getProperty
<OUString
>(getRun(xPara2
, 2), u
"TextPortionType"_ustr
));
1814 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"Text"_ustr
, getProperty
<OUString
>(getRun(xPara2
, 3), u
"TextPortionType"_ustr
));
1815 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"xxxxx"_ustr
, getRun(xPara2
, 3)->getString());
1816 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"TextFieldEnd"_ustr
, getProperty
<OUString
>(getRun(xPara2
, 4), u
"TextPortionType"_ustr
));
1820 void Test::testCheckBoxFormField()
1822 const OUString aFilterNames
[] = {
1825 u
"Office Open XML Text"_ustr
,
1828 for (const OUString
& rFilterName
: aFilterNames
)
1830 createSwDoc("checkbox_form_field.odt");
1832 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1834 // Export the document and import again for a check
1835 saveAndReload(rFilterName
);
1837 // Check the document after round trip
1838 SwDoc
* pDoc
= getSwDoc();
1839 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1841 // We have two check box form fields
1842 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(2), pMarkAccess
->getAllMarksCount());
1845 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1847 ::sw::mark::Fieldmark
* pFieldmark
= dynamic_cast<::sw::mark::Fieldmark
*>(*aIter
);
1849 if(rFilterName
== "Office Open XML Text") // OOXML import also generates bookmarks
1855 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1856 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMCHECKBOX
, pFieldmark
->GetFieldname());
1857 ::sw::mark::CheckboxFieldmark
* pCheckBox
= dynamic_cast< ::sw::mark::CheckboxFieldmark
* >(pFieldmark
);
1858 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pCheckBox
);
1860 // The first one is unchecked, the other one is checked
1862 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), !pCheckBox
->IsChecked());
1864 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pCheckBox
->IsChecked());
1867 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(2), nIndex
);
1871 void Test::testDropDownFormField()
1873 const OUString aFilterNames
[] = {
1876 u
"Office Open XML Text"_ustr
,
1879 for (const OUString
& rFilterName
: aFilterNames
)
1881 createSwDoc("dropdown_form_field.odt");
1883 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1885 // Export the document and import again for a check
1886 saveAndReload(rFilterName
);
1888 // Check the document after round trip
1889 SwDoc
* pDoc
= getSwDoc();
1890 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1892 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(2), pMarkAccess
->getAllMarksCount());
1895 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1897 ::sw::mark::Fieldmark
* pFieldmark
= dynamic_cast<::sw::mark::Fieldmark
*>(*aIter
);
1902 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1903 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMDROPDOWN
, pFieldmark
->GetFieldname());
1905 // Check drop down field's parameters.
1906 const sw::mark::Fieldmark::parameter_map_t
* const pParameters
= pFieldmark
->GetParameters();
1907 css::uno::Sequence
<OUString
> vListEntries
;
1908 sal_Int32 nSelection
= -1;
1909 auto pListEntries
= pParameters
->find(ODF_FORMDROPDOWN_LISTENTRY
);
1910 if (pListEntries
!= pParameters
->end())
1912 pListEntries
->second
>>= vListEntries
;
1914 if(vListEntries
.hasElements())
1916 auto pResult
= pParameters
->find(ODF_FORMDROPDOWN_RESULT
);
1917 if (pResult
!= pParameters
->end())
1919 pResult
->second
>>= nSelection
;
1924 // The first one is empty
1927 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), !vListEntries
.hasElements());
1928 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(-1), nSelection
);
1930 else // The second one has list and also a selected item
1932 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(4), vListEntries
.getLength());
1933 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(1), nSelection
);
1934 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"1000"_ustr
, vListEntries
[0]);
1935 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"2000"_ustr
, vListEntries
[1]);
1936 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"3000"_ustr
, vListEntries
[2]);
1937 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"4000"_ustr
, vListEntries
[3]);
1941 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(2), nIndex
);
1945 void Test::testDateFormField()
1947 const OUString aFilterNames
[] = {
1949 u
"Office Open XML Text"_ustr
,
1952 for (const OUString
& rFilterName
: aFilterNames
)
1954 createSwDoc("date_form_field.odt");
1956 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1958 // Export the document and import again for a check
1959 saveAndReload(rFilterName
);
1961 // Check the document after round trip
1962 if (rFilterName
== "writer8")
1964 SwDoc
* pDoc
= getSwDoc();
1965 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1967 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(5), pMarkAccess
->getAllMarksCount());
1970 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1972 ::sw::mark::DateFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::DateFieldmark
*>(*aIter
);
1973 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1974 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMDATE
, pFieldmark
->GetFieldname());
1976 // Check date form field's parameters.
1977 const sw::mark::Fieldmark::parameter_map_t
* const pParameters
= pFieldmark
->GetParameters();
1978 OUString sDateFormat
;
1979 auto pResult
= pParameters
->find(ODF_FORMDATE_DATEFORMAT
);
1980 if (pResult
!= pParameters
->end())
1982 pResult
->second
>>= sDateFormat
;
1986 pResult
= pParameters
->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE
);
1987 if (pResult
!= pParameters
->end())
1989 pResult
->second
>>= sLang
;
1992 OUString sCurrentDate
= pFieldmark
->GetContent();
1994 // The first one has the default field content
1998 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"MM/DD/YY"_ustr
, sDateFormat
);
1999 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"en-US"_ustr
, sLang
);
2000 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), vEnSpaces
, sCurrentDate
);
2002 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2003 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(5), pFieldmark
->GetMarkStart().GetContentIndex());
2005 else if (nIndex
== 1) // The second has the default format
2007 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"MM/DD/YY"_ustr
, sDateFormat
);
2008 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"en-US"_ustr
, sLang
);
2009 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"06/12/19"_ustr
, sCurrentDate
);
2011 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2012 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(20), pFieldmark
->GetMarkStart().GetContentIndex());
2014 else if (nIndex
== 2) // The third one has special format
2016 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"[NatNum12 MMMM=abbreviation]YYYY\". \"MMMM D."_ustr
, sDateFormat
);
2017 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"hu-HU"_ustr
, sLang
);
2018 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"2019. febr. 12."_ustr
, sCurrentDate
);
2020 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2021 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(40), pFieldmark
->GetMarkStart().GetContentIndex());
2024 else if (nIndex
== 3) // The fourth one has placeholder text
2026 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"D, MMM YY"_ustr
, sDateFormat
);
2027 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"bm-ML"_ustr
, sLang
);
2028 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"[select date]"_ustr
, sCurrentDate
);
2030 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2031 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(62), pFieldmark
->GetMarkStart().GetContentIndex());
2034 else // The last one is really empty
2036 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"MM/DD/YY"_ustr
, sDateFormat
);
2037 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"en-US"_ustr
, sLang
);
2038 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
""_ustr
, sCurrentDate
);
2040 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2041 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(82), pFieldmark
->GetMarkStart().GetContentIndex());
2046 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(5), nIndex
);
2050 // Import from DOCX, so the fieldmark is now a content control.
2051 uno::Reference
<container::XEnumerationAccess
> xEnumAccess(getParagraph(1), uno::UNO_QUERY
);
2052 uno::Reference
<container::XEnumeration
> xTextPortions
= xEnumAccess
->createEnumeration();
2055 while (xTextPortions
->hasMoreElements())
2057 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
2058 OUString aPortionType
;
2059 xTextPortion
->getPropertyValue(u
"TextPortionType"_ustr
) >>= aPortionType
;
2060 if (aPortionType
!= "ContentControl")
2065 uno::Reference
<text::XTextContent
> xContentControl
;
2066 xTextPortion
->getPropertyValue(u
"ContentControl"_ustr
) >>= xContentControl
;
2067 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
2070 xContentControlProps
->getPropertyValue(u
"Date"_ustr
) >>= bDate
;
2071 CPPUNIT_ASSERT(bDate
);
2073 // Check date form field's parameters.
2074 OUString sDateFormat
;
2075 xContentControlProps
->getPropertyValue(u
"DateFormat"_ustr
) >>= sDateFormat
;
2078 xContentControlProps
->getPropertyValue(u
"DateLanguage"_ustr
) >>= sLang
;
2080 uno::Reference
<container::XEnumerationAccess
> xContentControlEnumAccess(xContentControl
,
2082 uno::Reference
<container::XEnumeration
> xContentControlEnum
2083 = xContentControlEnumAccess
->createEnumeration();
2084 uno::Reference
<text::XTextRange
> xContentControlTextPortion(xContentControlEnum
->nextElement(), uno::UNO_QUERY
);
2085 OUString sCurrentDate
= xContentControlTextPortion
->getString();
2087 // The first one has the default field content
2090 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"MM/DD/YY"_ustr
, sDateFormat
);
2091 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"en-US"_ustr
, sLang
);
2092 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), vEnSpaces
, sCurrentDate
);
2094 else if (nIndex
== 1) // The second has the default format
2096 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"MM/DD/YY"_ustr
, sDateFormat
);
2097 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"en-US"_ustr
, sLang
);
2098 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"06/12/19"_ustr
, sCurrentDate
);
2100 else if (nIndex
== 2) // The third one has special format
2102 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"[NatNum12 MMMM=abbreviation]YYYY\". \"MMMM D."_ustr
, sDateFormat
);
2103 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"hu-HU"_ustr
, sLang
);
2104 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"2019. febr. 12."_ustr
, sCurrentDate
);
2106 else if (nIndex
== 3) // The fourth one has placeholder text
2108 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"D, MMM YY"_ustr
, sDateFormat
);
2109 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"bm-ML"_ustr
, sLang
);
2110 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"[select date]"_ustr
, sCurrentDate
);
2112 else // The last one is really empty
2114 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"MM/DD/YY"_ustr
, sDateFormat
);
2115 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
"en-US"_ustr
, sLang
);
2116 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), u
""_ustr
, sCurrentDate
);
2120 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(5), nIndex
);
2125 void Test::testDateFormFieldCharacterFormatting()
2127 const OUString aFilterNames
[] = {
2129 u
"Office Open XML Text"_ustr
,
2132 for (const OUString
& rFilterName
: aFilterNames
)
2134 createSwDoc("date_form_field_char_formatting.odt");
2136 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
2138 // Export the document and import again for a check
2139 saveAndReload(rFilterName
);
2141 // Check the document after round trip
2142 if (rFilterName
== "writer8")
2144 SwDoc
* pDoc
= getSwDoc();
2145 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
2147 // Check that we have the field at the right place
2148 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(1), pMarkAccess
->getAllMarksCount());
2149 ::sw::mark::DateFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::DateFieldmark
*>(*pMarkAccess
->getAllMarksBegin());
2150 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
2151 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMDATE
, pFieldmark
->GetFieldname());
2152 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(0), pFieldmark
->GetMarkStart().GetContentIndex());
2153 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(11), pFieldmark
->GetMarkEnd().GetContentIndex());
2155 // We have one date field, first half of the field has bold character weight and second part has red character color
2156 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::BOLD
, getProperty
<float>(getRun(getParagraph(1), 3), u
"CharWeight"_ustr
));
2157 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_AUTO
, getProperty
<Color
>(getRun(getParagraph(1), 3), u
"CharColor"_ustr
));
2158 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::NORMAL
, getProperty
<float>(getRun(getParagraph(1), 4), u
"CharWeight"_ustr
));
2159 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(getRun(getParagraph(1), 4), u
"CharColor"_ustr
));
2163 uno::Reference
<beans::XPropertySet
> xTextPortion(getRun(getParagraph(1), 1), uno::UNO_QUERY
);
2164 OUString aPortionType
;
2165 xTextPortion
->getPropertyValue(u
"TextPortionType"_ustr
) >>= aPortionType
;
2166 CPPUNIT_ASSERT_EQUAL(u
"ContentControl"_ustr
, aPortionType
);
2168 uno::Reference
<text::XTextContent
> xContentControl
;
2169 xTextPortion
->getPropertyValue(u
"ContentControl"_ustr
) >>= xContentControl
;
2170 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
2172 xContentControlProps
->getPropertyValue(u
"Date"_ustr
) >>= bDate
;
2173 CPPUNIT_ASSERT(bDate
);
2175 uno::Reference
<container::XEnumerationAccess
> xContentControlEnumAccess(xContentControl
,
2177 uno::Reference
<container::XEnumeration
> xContentControlEnum
2178 = xContentControlEnumAccess
->createEnumeration();
2179 xTextPortion
.set(xContentControlEnum
->nextElement(), uno::UNO_QUERY
);
2180 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::BOLD
, getProperty
<float>(xTextPortion
, u
"CharWeight"_ustr
));
2181 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_AUTO
, getProperty
<Color
>(xTextPortion
, u
"CharColor"_ustr
));
2182 xTextPortion
.set(xContentControlEnum
->nextElement(), uno::UNO_QUERY
);
2183 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::NORMAL
, getProperty
<float>(xTextPortion
, u
"CharWeight"_ustr
));
2184 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xTextPortion
, u
"CharColor"_ustr
));
2189 void Test::testSvgImageSupport()
2191 OUString aFilterNames
[] = {
2193 u
"Office Open XML Text"_ustr
,
2196 for (OUString
const & rFilterName
: aFilterNames
)
2198 // Use case to import a document containing a SVG image, export in target format, import and check if the
2199 // SVG image is present and as expected in the document
2202 createSwDoc("SvgImageTest.odt");
2204 // Export the document in target format and import again
2205 saveAndReload(rFilterName
);
2207 // Prepare fail message (writing which import/export filter was used)
2208 const OString sFailedMessage
= "Failed on filter: "_ostr
+ rFilterName
.toUtf8();
2210 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 1, getShapes());
2213 uno::Reference
<drawing::XShape
> xImage(getShape(1), uno::UNO_QUERY
);
2214 uno::Reference
<beans::XPropertySet
> xPropertySet(xImage
, uno::UNO_QUERY_THROW
);
2216 // Convert to a XGraphic
2217 uno::Reference
<graphic::XGraphic
> xGraphic
;
2218 xPropertySet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
2219 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
2221 // Access the Graphic
2222 Graphic
aGraphic(xGraphic
);
2224 // Check if it contains a VectorGraphicData struct
2225 auto pVectorGraphic
= aGraphic
.getVectorGraphicData();
2226 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pVectorGraphic
);
2228 // Which should be of type SVG, which means we have a SVG file
2229 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), VectorGraphicDataType::Svg
, pVectorGraphic
->getType());
2233 } // end of anonymous namespace
2234 CPPUNIT_TEST_SUITE_REGISTRATION(Test
);
2236 CPPUNIT_PLUGIN_IMPLEMENT();
2238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */