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>
43 class Test
: public SwModelTestBase
46 Test() : SwModelTestBase("/sw/qa/extras/globalfilter/data/") {}
48 void testEmbeddedGraphicRoundtrip();
49 void testLinkedGraphicRT();
50 void testImageWithSpecialID();
51 void testGraphicShape();
52 void testMultipleIdenticalGraphics();
53 void testCharHighlight();
54 void testCharHighlightODF();
55 void testCharHighlightBody();
56 void testCharStyleHighlight();
57 void testMSCharBackgroundEditing();
58 void testCharBackgroundToHighlighting();
60 void testSkipImages();
62 void testNestedFieldmark();
63 void verifyText13(char const*);
65 void testRedlineFlags();
66 void testBulletAsImage();
67 void testTextFormField();
68 void testCheckBoxFormField();
69 void testDropDownFormField();
70 void testDateFormField();
71 void testDateFormFieldCharacterFormatting();
72 void testSvgImageSupport();
74 CPPUNIT_TEST_SUITE(Test
);
75 CPPUNIT_TEST(testEmbeddedGraphicRoundtrip
);
76 CPPUNIT_TEST(testLinkedGraphicRT
);
77 CPPUNIT_TEST(testImageWithSpecialID
);
78 CPPUNIT_TEST(testGraphicShape
);
79 CPPUNIT_TEST(testMultipleIdenticalGraphics
);
80 CPPUNIT_TEST(testCharHighlight
);
81 CPPUNIT_TEST(testCharHighlightODF
);
82 CPPUNIT_TEST(testMSCharBackgroundEditing
);
83 CPPUNIT_TEST(testCharBackgroundToHighlighting
);
85 CPPUNIT_TEST(testSkipImages
);
87 CPPUNIT_TEST(testNestedFieldmark
);
88 CPPUNIT_TEST(testODF13
);
89 CPPUNIT_TEST(testRedlineFlags
);
90 CPPUNIT_TEST(testBulletAsImage
);
91 CPPUNIT_TEST(testTextFormField
);
92 CPPUNIT_TEST(testCheckBoxFormField
);
93 CPPUNIT_TEST(testDropDownFormField
);
94 CPPUNIT_TEST(testDateFormField
);
95 CPPUNIT_TEST(testDateFormFieldCharacterFormatting
);
96 CPPUNIT_TEST(testSvgImageSupport
);
97 CPPUNIT_TEST_SUITE_END();
100 void Test::testEmbeddedGraphicRoundtrip()
102 OUString aFilterNames
[] = {
106 "Office Open XML Text",
109 for (OUString
const & rFilterName
: aFilterNames
)
111 // Check whether the export code swaps in the image which was swapped out before by auto mechanism
113 createSwDoc("document_with_two_images.odt");
115 // Export the document and import again for a check
116 saveAndReload(rFilterName
);
118 // Check whether graphic exported well after it was swapped out
119 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
120 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 2, getShapes());
123 uno::Reference
<drawing::XShape
> xImage(getShape(1), uno::UNO_QUERY
);
124 uno::Reference
< beans::XPropertySet
> XPropSet( xImage
, uno::UNO_QUERY_THROW
);
126 // Check graphic, size
128 uno::Reference
<graphic::XGraphic
> xGraphic
;
129 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
130 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
131 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
132 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
133 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
134 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(610), xBitmap
->getSize().Width
);
135 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(381), xBitmap
->getSize().Height
);
139 xImage
.set(getShape(2), uno::UNO_QUERY
);
140 XPropSet
.set( xImage
, uno::UNO_QUERY_THROW
);
142 // Check graphic, size
144 uno::Reference
<graphic::XGraphic
> xGraphic
;
145 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
146 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
147 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
148 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
149 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
150 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(900), xBitmap
->getSize().Width
);
151 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(600), xBitmap
->getSize().Height
);
156 void Test::testLinkedGraphicRT()
158 const OUString aFilterNames
[] = {
160 // "Rich Text Format", Note: picture is there, but SwGrfNode is not found?
162 "Office Open XML Text",
165 for (OUString
const & rFilterName
: aFilterNames
)
167 createSwDoc("document_with_linked_graphic.odt");
169 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
171 // Export the document and import again for a check
172 saveAndReload(rFilterName
);
174 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
175 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pTextDoc
);
176 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
177 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pDoc
);
178 SwNodes
& aNodes
= pDoc
->GetNodes();
181 bool bImageFound
= false;
183 for (SwNodeOffset
nIndex(0); nIndex
< aNodes
.Count(); ++nIndex
)
185 if (aNodes
[nIndex
]->IsGrfNode())
187 SwGrfNode
* pGrfNode
= aNodes
[nIndex
]->GetGrfNode();
188 CPPUNIT_ASSERT(pGrfNode
);
190 const GraphicObject
& rGraphicObj
= pGrfNode
->GetGrfObj(true);
191 aGraphic
= rGraphicObj
.GetGraphic();
196 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), bImageFound
);
198 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), GraphicType::Bitmap
, aGraphic
.GetType());
199 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_uLong(864900), aGraphic
.GetSizeBytes());
201 // Check if linked graphic is registered in LinkManager
202 SwEditShell
* const pEditShell(pTextDoc
->GetDocShell()->GetDoc()->GetEditShell());
203 CPPUNIT_ASSERT(pEditShell
);
204 sfx2::LinkManager
& rLinkManager
= pEditShell
->GetLinkManager();
205 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), size_t(1), rLinkManager
.GetLinks().size());
206 const tools::SvRef
<sfx2::SvBaseLink
> & rLink
= rLinkManager
.GetLinks()[0];
207 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), rLink
->GetLinkSourceName().indexOf("linked_graphic.jpg") >= 0);
211 void Test::testImageWithSpecialID()
213 // Check how LO handles when the imported graphic's ID is different from that one
214 // which is generated by LO.
216 const OUString aFilterNames
[] = {
220 "Office Open XML Text",
223 for (OUString
const & rFilterName
: aFilterNames
)
225 createSwDoc("images_with_special_IDs.odt");
227 // Export the document and import again for a check
228 saveAndReload(rFilterName
);
230 // Check whether graphic exported well
231 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
232 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 2, getShapes());
234 uno::Reference
<drawing::XShape
> xImage
= getShape(1);
235 uno::Reference
< beans::XPropertySet
> XPropSet( xImage
, uno::UNO_QUERY_THROW
);
237 // Check graphic, size
239 uno::Reference
<graphic::XGraphic
> xGraphic
;
240 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
241 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
242 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
243 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
244 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
245 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(610), xBitmap
->getSize().Width
);
246 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(381), xBitmap
->getSize().Height
);
250 xImage
.set(getShape(2), uno::UNO_QUERY
);
251 XPropSet
.set( xImage
, uno::UNO_QUERY_THROW
);
253 // Check graphic, size
255 uno::Reference
<graphic::XGraphic
> xGraphic
;
256 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
257 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
258 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), graphic::GraphicType::PIXEL
, xGraphic
->getType());
259 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
260 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
261 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(900), xBitmap
->getSize().Width
);
262 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(600), xBitmap
->getSize().Height
);
267 /// Gives the first embedded or linked image in a document.
268 uno::Reference
<drawing::XShape
> lcl_getShape(const uno::Reference
<lang::XComponent
>& xComponent
, bool bEmbedded
)
270 uno::Reference
<drawing::XShape
> xShape
;
272 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(xComponent
, uno::UNO_QUERY
);
273 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
274 for (sal_Int32 i
= 0; i
< xDrawPage
->getCount(); ++i
)
276 uno::Reference
<beans::XPropertySet
> xShapeProperties(xDrawPage
->getByIndex(i
), uno::UNO_QUERY
);
277 uno::Reference
<graphic::XGraphic
> xGraphic
;
278 xShapeProperties
->getPropertyValue("Graphic") >>= xGraphic
;
281 Graphic
aGraphic(xGraphic
);
283 if (bEmbedded
== aGraphic
.getOriginURL().isEmpty())
285 xShape
.set(xShapeProperties
, uno::UNO_QUERY
);
294 void Test::testGraphicShape()
296 // There are two kind of images in Writer: 1) Writer specific handled by SwGrfNode and
297 // 2) graphic shape handled by SdrGrafObj (e.g. after copy&paste from Impress).
299 const OUString aFilterNames
[] = {
303 "Office Open XML Text",
306 for (OUString
const & rFilterName
: aFilterNames
)
308 createSwDoc("graphic_shape.odt");
310 // Export the document and import again for a check
311 saveAndReload(rFilterName
);
313 // Check whether graphic exported well
314 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
315 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 2, getShapes());
317 uno::Reference
<drawing::XShape
> xImage
= lcl_getShape(mxComponent
, true);
318 CPPUNIT_ASSERT_MESSAGE("Couldn't load the shape/image", xImage
.is());
319 uno::Reference
< beans::XPropertySet
> XPropSet( xImage
, uno::UNO_QUERY
);
320 // First image is embedded
323 uno::Reference
<graphic::XGraphic
> xGraphic
;
324 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
325 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
326 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
327 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
328 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(610), xBitmap
->getSize().Width
);
329 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(381), xBitmap
->getSize().Height
);
332 // MS filters make this kind of linked images broken !?
333 if (rFilterName
!= "writer8")
336 // Second image is a linked one
337 xImage
= lcl_getShape(mxComponent
, false);
338 XPropSet
.set(xImage
, uno::UNO_QUERY
);
339 const OString sFailedImageLoad
= OString::Concat("Couldn't load the shape/image for ") + rFilterName
.toUtf8();
340 CPPUNIT_ASSERT_MESSAGE(sFailedImageLoad
.getStr(), xImage
.is());
344 uno::Reference
<graphic::XGraphic
> xGraphic
;
345 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
346 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
348 Graphic
aGraphic(xGraphic
);
349 OUString sURL
= aGraphic
.getOriginURL();
350 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), sURL
.endsWith("linked_graphic.jpg"));
352 uno::Reference
<awt::XBitmap
> xBitmap(xGraphic
, uno::UNO_QUERY
);
353 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
354 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(620), xBitmap
->getSize().Width
);
355 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(465), xBitmap
->getSize().Height
);
360 std::vector
<uno::Reference
<graphic::XGraphic
>>
361 lcl_getGraphics(const uno::Reference
<lang::XComponent
>& xComponent
)
363 std::vector
<uno::Reference
<graphic::XGraphic
>> aGraphics
;
365 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(xComponent
, uno::UNO_QUERY
);
366 uno::Reference
<drawing::XDrawPage
> xDrawPage
= xDrawPageSupplier
->getDrawPage();
367 for (sal_Int32 i
= 0; i
< xDrawPage
->getCount(); ++i
)
369 uno::Reference
<beans::XPropertySet
> xShapeProperties(xDrawPage
->getByIndex(i
), uno::UNO_QUERY
);
370 uno::Reference
<graphic::XGraphic
> xGraphic
;
371 xShapeProperties
->getPropertyValue("Graphic") >>= xGraphic
;
374 aGraphics
.push_back(xGraphic
);
381 void Test::testMultipleIdenticalGraphics()
383 // We have multiple identical graphics. When we save them we want
384 // them to be saved de-duplicated and the same should still be true
385 // after loading them again. This test check that the de-duplication
386 // works as expected.
388 const OUString aFilterNames
[] {
390 //"Rich Text Format", // doesn't work correctly for now
392 "Office Open XML Text",
395 for (OUString
const & rFilterName
: aFilterNames
)
397 createSwDoc("multiple_identical_graphics.odt");
399 // Export the document and import again for a check
400 saveAndReload(rFilterName
);
402 // Check whether graphic exported well
403 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
404 auto aGraphics
= lcl_getGraphics(mxComponent
);
406 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), size_t(5), aGraphics
.size());
408 // Get all GfxLink addresses, we expect all of them to be the same
409 // indicating we use the same graphic instance for all shapes
410 std::vector
<sal_Int64
> aGfxLinkAddresses
;
411 for (auto const & rxGraphic
: aGraphics
)
413 GfxLink
* pLink
= Graphic(rxGraphic
).GetSharedGfxLink().get();
414 aGfxLinkAddresses
.emplace_back(reinterpret_cast<sal_Int64
>(pLink
));
417 // Check all addresses are the same
418 bool bResult
= std::equal(aGfxLinkAddresses
.begin() + 1, aGfxLinkAddresses
.end(), aGfxLinkAddresses
.begin());
419 const OString sGraphicNotTheSameFailedMessage
= OString::Concat("Graphics not the same for filter: '") +
420 rFilterName
.toUtf8() + OString::Concat("'");
421 CPPUNIT_ASSERT_EQUAL_MESSAGE(sGraphicNotTheSameFailedMessage
.getStr(), true, bResult
);
425 void Test::testCharHighlightBody()
427 // MS Word has two kind of character backgrounds called character shading and highlighting
428 // MS filters handle these attributes separately, but ODF export merges them into one background attribute
430 const OUString aFilterNames
[] = {
434 "Office Open XML Text",
437 for (OUString
const & rFilterName
: aFilterNames
)
439 createSwDoc("char_highlight.docx");
441 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
443 // Export the document and import again for a check
444 saveAndReload(rFilterName
);
446 const uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
447 // Both highlight and background
448 const Color
nBackColor(0x4F81BD);
449 for( int nRun
= 1; nRun
<= 16; ++nRun
)
451 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,nRun
), uno::UNO_QUERY
);
452 Color nHighlightColor
;
455 case 1: nHighlightColor
= COL_BLACK
; break; //black 0x000000
456 case 2: nHighlightColor
= COL_LIGHTBLUE
; break; //light blue 0x0000ff
457 case 3: nHighlightColor
= COL_LIGHTCYAN
; break; //light cyan 0x00ffff
458 case 4: nHighlightColor
= COL_LIGHTGREEN
; break; //light green 0x00ff00
459 case 5: nHighlightColor
= COL_LIGHTMAGENTA
; break; //light magenta 0xff00ff
460 case 6: nHighlightColor
= COL_LIGHTRED
; break; //light red 0xff0000
461 case 7: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
462 case 8: nHighlightColor
= COL_WHITE
; break; //white 0xffffff
463 case 9: nHighlightColor
= COL_BLUE
; break;//blue 0x000080
464 case 10: nHighlightColor
= COL_CYAN
; break; //cyan 0x008080
465 case 11: nHighlightColor
= COL_GREEN
; break; //green 0x008000
466 case 12: nHighlightColor
= COL_MAGENTA
; break; //magenta 0x800080
467 case 13: nHighlightColor
= COL_RED
; break; //red 0x800000
468 case 14: nHighlightColor
= COL_BROWN
; break; //brown 0x808000
469 case 15: nHighlightColor
= COL_GRAY
; break; //dark gray 0x808080
470 case 16: nHighlightColor
= COL_LIGHTGRAY
; break; //light gray 0xC0C0C0
473 if (rFilterName
== "writer8")
475 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharHighlight"));
476 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nHighlightColor
, getProperty
<Color
>(xRun
,"CharBackColor"));
480 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nHighlightColor
, getProperty
<Color
>(xRun
,"CharHighlight"));
481 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xRun
,"CharBackColor"));
487 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,18), uno::UNO_QUERY
);
488 if (rFilterName
== "writer8")
490 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharHighlight"));
491 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,"CharBackColor"));
495 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,"CharHighlight"));
496 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharBackColor"));
502 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,19), uno::UNO_QUERY
);
503 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharHighlight"));
504 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTBLUE
, getProperty
<Color
>(xRun
,"CharBackColor"));
509 void Test::testCharStyleHighlight()
511 // MS Word has two kind of character backgrounds called character shading and highlighting.
512 // However, their character style can only accept shading. It ignores the highlighting value.
514 const OUString aFilterNames
[] = {
517 "Office Open XML Text",
520 for (OUString
const & rFilterName
: aFilterNames
)
522 createSwDoc("tdf138345_charstyle_highlight.odt");
524 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
526 // Export the document and import again for a check
527 saveAndReload(rFilterName
);
529 uno::Reference
<beans::XPropertySet
> xCharStyle
;
530 getStyles("CharacterStyles")->getByName("charBackground") >>= xCharStyle
;
531 const Color
nBackColor(0xFFDBB6); //orange-y
533 // Always export character style's background colour as shading, never as highlighting.
534 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xCharStyle
,"CharHighlight"));
535 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xCharStyle
,"CharBackColor"));
539 void Test::testCharHighlight()
541 auto batch
= comphelper::ConfigurationChanges::create();
542 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(false, batch
);
545 testCharHighlightBody();
546 testCharStyleHighlight();
548 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(true, batch
);
551 testCharHighlightBody();
552 testCharStyleHighlight();
555 void Test::testCharHighlightODF()
557 createSwDoc("char_background_editing.docx");
559 // don't check import, testMSCharBackgroundEditing already does that
561 uno::Reference
<text::XTextRange
> xPara
= getParagraph(1);
562 for (int i
= 1; i
<= 4; ++i
)
564 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
567 case 1: // non-transparent highlight
568 xRun
->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32
>(128)));
569 xRun
->setPropertyValue("CharBackTransparent", uno::Any(true));
570 xRun
->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32
>(64)));
573 case 2: // transparent backcolor
574 xRun
->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32
>(128)));
575 xRun
->setPropertyValue("CharBackTransparent", uno::Any(true));
576 xRun
->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32
>(COL_TRANSPARENT
)));
579 case 3: // non-transparent backcolor
580 xRun
->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32
>(128)));
581 xRun
->setPropertyValue("CharBackTransparent", uno::Any(false));
582 xRun
->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32
>(COL_TRANSPARENT
)));
585 case 4: // non-transparent highlight again
586 xRun
->setPropertyValue("CharBackColor", uno::Any(static_cast<sal_Int32
>(128)));
587 xRun
->setPropertyValue("CharBackTransparent", uno::Any(false));
588 xRun
->setPropertyValue("CharHighlight", uno::Any(static_cast<sal_Int32
>(64)));
593 saveAndReload("writer8");
595 xPara
.set(getParagraph(1));
596 for (int i
= 1; i
<= 4; ++i
)
598 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
599 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32
>(COL_TRANSPARENT
), getProperty
<sal_Int32
>(xRun
, "CharHighlight"));
602 case 1: // non-transparent highlight
603 CPPUNIT_ASSERT_EQUAL(Color(0x000040), getProperty
<Color
>(xRun
, "CharBackColor"));
604 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xRun
, "CharBackTransparent"));
606 case 2: // transparent backcolor
607 CPPUNIT_ASSERT_EQUAL(COL_TRANSPARENT
, getProperty
<Color
>(xRun
, "CharBackColor"));
608 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(xRun
, "CharBackTransparent"));
610 case 3: // non-transparent backcolor
611 CPPUNIT_ASSERT_EQUAL(COL_BLUE
, getProperty
<Color
>(xRun
, "CharBackColor"));
612 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xRun
, "CharBackTransparent"));
614 case 4: // non-transparent highlight again
615 CPPUNIT_ASSERT_EQUAL(Color(0x000040), getProperty
<Color
>(xRun
, "CharBackColor"));
616 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xRun
, "CharBackTransparent"));
622 void Test::testMSCharBackgroundEditing()
624 // Simulate the editing process of imported MSO character background attributes
625 // and check how export behaves.
627 const OUString aFilterNames
[] = {
631 "Office Open XML Text",
634 auto batch
= comphelper::ConfigurationChanges::create();
635 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(true, batch
);
638 for (OUString
const & rFilterName
: aFilterNames
)
640 createSwDoc("char_background_editing.docx");
642 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
644 // Check whether import was done on the right way
645 uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
647 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,1), uno::UNO_QUERY
);
648 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharHighlight"));
649 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,"CharBackColor"));
651 xRun
.set(getRun(xPara
,2), uno::UNO_QUERY
);
652 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTBLUE
, getProperty
<Color
>(xRun
,"CharHighlight"));
653 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharBackColor"));
655 xRun
.set(getRun(xPara
,3), uno::UNO_QUERY
);
656 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTBLUE
, getProperty
<Color
>(xRun
,"CharHighlight"));
657 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xRun
,"CharBackColor"));
659 xRun
.set(getRun(xPara
,4), uno::UNO_QUERY
);
660 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharHighlight"));
661 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharBackColor"));
665 for( int i
= 1; i
<= 4; ++i
)
667 uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
672 case 1: nBackColor
= COL_BLACK
; break; //black 0x000000
673 case 2: nBackColor
= COL_LIGHTCYAN
; break; //cyan 0x00ffff
674 case 3: nBackColor
= COL_LIGHTGREEN
; break; //green 0x00ff00
675 case 4: nBackColor
= COL_LIGHTMAGENTA
; break; //magenta 0xff00ff
677 xRun
->setPropertyValue("CharBackColor", uno::Any(nBackColor
));
678 // Remove highlighting
679 xRun
->setPropertyValue("CharHighlight", uno::Any(COL_TRANSPARENT
));
680 // Remove shading marker
681 uno::Sequence
<beans::PropertyValue
> aGrabBag
= getProperty
<uno::Sequence
<beans::PropertyValue
> >(xRun
,"CharInteropGrabBag");
682 for (beans::PropertyValue
& rProp
: asNonConstRange(aGrabBag
))
684 if (rProp
.Name
== "CharShadingMarker")
686 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), true, rProp
.Value
.get
<bool>());
687 rProp
.Value
<<= false;
690 xRun
->setPropertyValue("CharInteropGrabBag", uno::Any(aGrabBag
));
693 // Export the document and import again for a check
694 saveAndReload(rFilterName
);
696 // Check whether background was exported as highlighting
697 xPara
.set(getParagraph(1));
698 for( int i
= 1; i
<= 4; ++i
)
703 case 1: nBackColor
= COL_BLACK
; break; //black 0x000000
704 case 2: nBackColor
= COL_LIGHTCYAN
; break; //light cyan 0x00ffff
705 case 3: nBackColor
= COL_LIGHTGREEN
; break; //light green 0x00ff00
706 case 4: nBackColor
= COL_LIGHTMAGENTA
; break; //light magenta 0xff00ff
708 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,i
), uno::UNO_QUERY
);
709 if (rFilterName
== "writer8")
711 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharHighlight"));
712 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xRun
,"CharBackColor"));
716 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), nBackColor
, getProperty
<Color
>(xRun
,"CharHighlight"));
717 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_TRANSPARENT
, getProperty
<Color
>(xRun
,"CharBackColor"));
723 void Test::testCharBackgroundToHighlighting()
725 // MSO highlighting has less kind of values so let's see how LO character background is converted
728 const OUString aFilterNames
[] = {
731 "Office Open XML Text",
734 for (OUString
const & rFilterName
: aFilterNames
)
736 createSwDoc("char_background.odt");
738 OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
740 auto batch
= comphelper::ConfigurationChanges::create();
741 officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::set(true, batch
);
744 // Export the document and import again for a check
745 saveAndReload(rFilterName
);
747 // Check highlight color
748 const uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
749 for( int nRun
= 1; nRun
<= 19; ++nRun
)
751 const uno::Reference
<beans::XPropertySet
> xRun(getRun(xPara
,nRun
), uno::UNO_QUERY
);
752 Color nHighlightColor
;
755 case 1: nHighlightColor
= COL_BLACK
; break; //black 0x000000
756 case 2: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
757 case 3: nHighlightColor
= COL_LIGHTMAGENTA
; break; //light magenta 0xff00ff
758 case 4: nHighlightColor
= COL_LIGHTCYAN
; break; //light cyan 0x00ffff
759 case 5: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
760 case 6: nHighlightColor
= COL_LIGHTRED
; break; //light red 0xff0000
761 case 7: nHighlightColor
= COL_LIGHTBLUE
; break; //light blue 0x0000ff
762 case 8: nHighlightColor
= COL_LIGHTGREEN
; break; //light green 0x00ff00
763 case 9: nHighlightColor
= COL_GREEN
; break; //dark green 0x008000
764 case 10: nHighlightColor
= COL_MAGENTA
; break; //dark magenta 0x800080
765 case 11: nHighlightColor
= COL_BLUE
; break; //dark blue 0x000080
766 case 12: nHighlightColor
= COL_BROWN
; break; //brown 0x808000
767 case 13: nHighlightColor
= COL_GRAY
; break; //dark gray 0x808080
768 case 14: nHighlightColor
= COL_BLACK
; break; //black 0x000000
769 case 15: nHighlightColor
= COL_LIGHTRED
; break; //light red 0xff0000
770 case 16: nHighlightColor
= COL_LIGHTGRAY
; break; //light gray 0xC0C0C0
771 case 17: nHighlightColor
= COL_RED
; break; //dark red 0x800000
772 case 18: nHighlightColor
= COL_GRAY
; break; //dark gray 0x808080
773 case 19: nHighlightColor
= COL_YELLOW
; break; //yellow 0xffff00
775 const OString sMessage
= sFailedMessage
+". Index of run with unmatched color: " + OString::number(nRun
);
776 CPPUNIT_ASSERT_EQUAL_MESSAGE(sMessage
.getStr(), nHighlightColor
, getProperty
<Color
>(xRun
,"CharHighlight"));
782 void Test::testSkipImages()
784 // Check how LO skips image loading (but not texts of textboxes and custom shapes)
785 // during DOC and DOCX import, using the "SkipImages" FilterOptions.
787 std::pair
<OUString
, OUString
> aFilterNames
[] = {
788 { "skipimages.doc", "" },
789 { "skipimages.doc", "SkipImages" },
790 { "skipimages.docx", "" },
791 { "skipimages.docx", "SkipImages" }
794 for (auto const & rFilterNamePair
: aFilterNames
)
796 bool bSkipImages
= !rFilterNamePair
.second
.isEmpty();
797 OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterNamePair
.first
.toUtf8();
799 setImportFilterOptions(rFilterNamePair
.second
);
800 createSwDoc(rFilterNamePair
.first
.toUtf8().getStr());
801 sFailedMessage
+= " - " + rFilterNamePair
.second
.toUtf8();
803 // Check shapes (images, textboxes, custom shapes)
804 uno::Reference
<drawing::XShape
> xShape
;
805 uno::Reference
<graphic::XGraphic
> xGraphic
;
806 uno::Reference
< beans::XPropertySet
> XPropSet
;
807 uno::Reference
<awt::XBitmap
> xBitmap
;
809 bool bHasTextboxText
= false;
810 bool bHasCustomShapeText
= false;
811 sal_Int32 nImageCount
= 0;
813 for (int i
= 1; i
<= getShapes(); i
++)
815 xShape
= getShape(i
);
816 XPropSet
.set( xShape
, uno::UNO_QUERY_THROW
);
819 XPropSet
->getPropertyValue("Graphic") >>= xGraphic
;
820 xBitmap
.set(xGraphic
, uno::UNO_QUERY
);
824 catch (beans::UnknownPropertyException
&)
827 uno::Reference
<text::XTextRange
> xText(xShape
, uno::UNO_QUERY
);
830 OUString shapeText
= xText
->getString();
831 if (shapeText
.startsWith("Lorem ipsum"))
832 bHasTextboxText
= true;
833 else if (shapeText
.startsWith("Nam pretium"))
834 bHasCustomShapeText
= true;
838 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), bHasTextboxText
);
839 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), bHasCustomShapeText
);
840 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), static_cast<sal_Int32
>(bSkipImages
? 0 : 3), nImageCount
);
845 auto verifyNestedFieldmark(OUString
const& rTestName
,
846 uno::Reference
<lang::XComponent
> const& xComponent
) -> void
848 SwDoc
const*const pDoc(dynamic_cast<SwXTextDocument
&>(*xComponent
).GetDocShell()->GetDoc());
849 IDocumentMarkAccess
const& rIDMA(*pDoc
->getIDocumentMarkAccess());
851 // no spurious bookmarks have been created
852 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(),
853 sal_Int32(0), rIDMA
.getBookmarksCount());
855 // check inner fieldmark
856 SwNodeIndex
const node1(*pDoc
->GetNodes().GetEndOfContent().StartOfSectionNode(), +2);
857 SwPosition
const innerPos(*node1
.GetNode().GetTextNode(),
858 node1
.GetNode().GetTextNode()->GetText().indexOf(CH_TXT_ATR_FIELDSTART
));
859 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(),
860 sal_Int32(1), innerPos
.GetContentIndex());
861 ::sw::mark::IFieldmark
*const pInner(rIDMA
.getFieldmarkAt(innerPos
));
862 CPPUNIT_ASSERT_MESSAGE(rTestName
.toUtf8().getStr(), pInner
);
863 OUString
const innerString(SwPaM(pInner
->GetMarkPos(), pInner
->GetOtherMarkPos()).GetText());
864 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(), OUString(
865 OUStringChar(CH_TXT_ATR_FIELDSTART
) + u
" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
866 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
867 + u
"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP
) + u
"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
868 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
869 + u
"baz" + OUStringChar(CH_TXT_ATR_FIELDEND
)), innerString
);
871 // check outer fieldmark
872 SwNodeIndex
const node2(node1
, -1);
873 SwPosition
const outerPos(*node2
.GetNode().GetTextNode(),
874 node2
.GetNode().GetTextNode()->GetText().indexOf(CH_TXT_ATR_FIELDSTART
));
875 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(),
876 sal_Int32(0), outerPos
.GetContentIndex());
877 ::sw::mark::IFieldmark
const*const pOuter(rIDMA
.getFieldmarkAt(outerPos
));
878 CPPUNIT_ASSERT_MESSAGE(rTestName
.toUtf8().getStr(), pOuter
);
879 OUString
const outerString(SwPaM(pOuter
->GetMarkPos(), pOuter
->GetOtherMarkPos()).GetText());
880 CPPUNIT_ASSERT_EQUAL_MESSAGE(rTestName
.toUtf8().getStr(), OUString(
881 OUStringChar(CH_TXT_ATR_FIELDSTART
) + u
" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
882 + u
" " + OUStringChar(CH_TXT_ATR_FIELDSTART
) + u
" QUOTE \"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
883 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
884 + u
"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP
) + u
"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
885 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
886 + u
"baz" + OUStringChar(CH_TXT_ATR_FIELDEND
) + OUStringChar(CH_TXTATR_NEWLINE
)
887 + u
"bar " + OUStringChar(CH_TXTATR_NEWLINE
)
888 + u
"baz\" " + OUStringChar(CH_TXT_ATR_FIELDSEP
) + u
"foo " + OUStringChar(CH_TXTATR_NEWLINE
)
889 + u
" foo " + OUStringChar(CH_TXTATR_NEWLINE
)
890 + u
" bar " + OUStringChar(CH_TXTATR_NEWLINE
)
891 + u
"baz" + OUStringChar(CH_TXTATR_NEWLINE
)
892 + u
"bar " + OUStringChar(CH_TXTATR_NEWLINE
)
893 + u
"baz" + OUStringChar(CH_TXT_ATR_FIELDEND
)), outerString
);
895 // must return innermost mark
896 CPPUNIT_ASSERT_EQUAL(pInner
, rIDMA
.getInnerFieldmarkFor(innerPos
));
899 void Test::testNestedFieldmark()
901 // experimental config setting
904 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(
905 comphelper::ConfigurationChanges::create());
906 officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::set(false, pBatch
);
907 return pBatch
->commit();
909 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(comphelper::ConfigurationChanges::create());
910 officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::set(true, pBatch
);
913 std::pair
<OUString
, OUString
> const aFilterNames
[] = {
914 {"writer8", "fieldmark_QUOTE_nest.fodt"},
915 {"Office Open XML Text", "fieldmark_QUOTE_nest.docx"},
916 {"Rich Text Format", "fieldmark_QUOTE_nest.rtf"},
919 for (auto const & rFilterName
: aFilterNames
)
921 createSwDoc(rFilterName
.second
.toUtf8().getStr());
923 verifyNestedFieldmark(rFilterName
.first
+ ", load", mxComponent
);
925 // Export the document and import again
926 saveAndReload(rFilterName
.first
);
928 verifyNestedFieldmark(rFilterName
.first
+ " exported-reload", mxComponent
);
932 auto Test::verifyText13(char const*const pTestName
) -> void
934 // OFFICE-3789 style:header-first/style:footer-first
935 uno::Reference
<beans::XPropertySet
> xPageStyle
;
936 getStyles("PageStyles")->getByName("Standard") >>= xPageStyle
;
937 uno::Reference
<text::XText
> xHF(getProperty
<uno::Reference
<text::XText
>>(xPageStyle
, "HeaderTextFirst"));
938 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("Header first"), xHF
->getString());
939 uno::Reference
<text::XText
> xFF(getProperty
<uno::Reference
<text::XText
>>(xPageStyle
, "FooterTextFirst"));
940 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("Footer first"), xFF
->getString());
941 // OFFICE-3767 text:contextual-spacing
942 uno::Reference
<text::XTextRange
> xPara(getParagraph(1));
943 CPPUNIT_ASSERT_MESSAGE(pTestName
, getProperty
<bool>(xPara
, "ParaContextMargin"));
944 // OFFICE-3776 meta:creator-initials
945 uno::Reference
<text::XTextRange
> xRun(getRun(xPara
, 1));
946 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("Annotation"), getProperty
<OUString
>(xRun
, "TextPortionType"));
947 uno::Reference
<beans::XPropertySet
> xComment(getProperty
<uno::Reference
<beans::XPropertySet
>>(xRun
, "TextField"));
948 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("dj"), getProperty
<OUString
>(xComment
, "Initials"));
949 // OFFICE-3941 text:index-entry-link-start/text:index-entry-link-end
950 uno::Reference
<text::XDocumentIndexesSupplier
> xDIS(mxComponent
, uno::UNO_QUERY
);
951 uno::Reference
<container::XIndexAccess
> xIndexes(xDIS
->getDocumentIndexes());
952 uno::Reference
<text::XDocumentIndex
> xIndex(xIndexes
->getByIndex(0), uno::UNO_QUERY
);
953 uno::Reference
<container::XIndexReplace
> xLevels(getProperty
<uno::Reference
<container::XIndexReplace
>>(xIndex
, "LevelFormat"));
954 uno::Sequence
<beans::PropertyValues
> format
;
955 xLevels
->getByIndex(1) >>= format
; // 1-based?
956 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("TokenType"), format
[0][0].Name
);
957 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("TokenHyperlinkStart"), format
[0][0].Value
.get
<OUString
>());
958 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("TokenType"), format
[4][0].Name
);
959 CPPUNIT_ASSERT_EQUAL_MESSAGE(pTestName
, OUString("TokenHyperlinkEnd"), format
[4][0].Value
.get
<OUString
>());
962 // test ODF 1.3 new text document features
963 void Test::testODF13()
966 createSwDoc("text13e.odt");
969 verifyText13("import");
972 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(
973 comphelper::ConfigurationChanges::create());
974 officecfg::Office::Common::Save::ODF::DefaultVersion::set(3, pBatch
);
975 return pBatch
->commit();
980 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(
981 comphelper::ConfigurationChanges::create());
982 officecfg::Office::Common::Save::ODF::DefaultVersion::set(10, pBatch
);
985 saveAndReload("writer8");
988 xmlDocUniquePtr pContentXml
= parseExport("content.xml");
989 assertXPath(pContentXml
, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties[@style:contextual-spacing='true']"_ostr
);
990 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials"_ostr
);
991 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials"_ostr
, 0);
992 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"_ostr
);
993 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"_ostr
, 0);
994 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"_ostr
);
995 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"_ostr
, 0);
996 xmlDocUniquePtr pStylesXml
= parseExport("styles.xml");
997 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:header-first"_ostr
);
998 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:header-first"_ostr
, 0);
999 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:footer-first"_ostr
);
1000 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first"_ostr
, 0);
1003 verifyText13("1.3 reload");
1006 // export ODF 1.2 extended
1007 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(
1008 comphelper::ConfigurationChanges::create());
1009 officecfg::Office::Common::Save::ODF::DefaultVersion::set(9, pBatch
);
1012 // FIXME: it's not possible to use 'reload' here because the validation fails with
1013 // Error: unexpected attribute "loext:contextual-spacing"
1014 utl::MediaDescriptor aMediaDescriptor
;
1015 aMediaDescriptor
["FilterName"] <<= OUString("writer8");
1017 uno::Reference
<frame::XStorable
> const xStorable(mxComponent
, uno::UNO_QUERY
);
1018 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
1021 xmlDocUniquePtr pContentXml
= parseExport("content.xml");
1022 assertXPath(pContentXml
, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties[@loext:contextual-spacing='true']"_ostr
);
1023 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials"_ostr
);
1024 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials"_ostr
, 0);
1025 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"_ostr
);
1026 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"_ostr
, 0);
1027 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"_ostr
);
1028 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"_ostr
, 0);
1029 xmlDocUniquePtr pStylesXml
= parseExport("styles.xml");
1030 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:header-first"_ostr
);
1031 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:header-first"_ostr
, 0);
1032 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first"_ostr
);
1033 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:footer-first"_ostr
, 0);
1036 mxComponent
->dispose();
1037 mxComponent
= loadFromDesktop(maTempFile
.GetURL(), "com.sun.star.text.TextDocument");
1040 verifyText13("1.2 Extended reload");
1044 std::shared_ptr
<comphelper::ConfigurationChanges
> pBatch(
1045 comphelper::ConfigurationChanges::create());
1046 officecfg::Office::Common::Save::ODF::DefaultVersion::set(4, pBatch
);
1049 // don't reload - no point
1053 xmlDocUniquePtr pContentXml
= parseExport("content.xml");
1054 assertXPathNoAttribute(pContentXml
, "/office:document-content/office:automatic-styles/style:style/style:paragraph-properties"_ostr
, "contextual-spacing"_ostr
);
1055 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/meta:creator-initials"_ostr
, 0);
1056 assertXPath(pContentXml
, "/office:document-content/office:body/office:text/text:p/office:annotation/loext:sender-initials"_ostr
, 0);
1057 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"_ostr
, 0);
1058 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"_ostr
, 0);
1059 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"_ostr
, 0);
1060 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"_ostr
, 0);
1061 xmlDocUniquePtr pStylesXml
= parseExport("styles.xml");
1062 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:header-first"_ostr
, 0);
1063 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:header-first"_ostr
, 0);
1064 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/style:footer-first"_ostr
, 0);
1065 assertXPath(pStylesXml
, "/office:document-styles/office:master-styles/style:master-page/loext:footer-first"_ostr
, 0);
1069 void Test::testRedlineFlags()
1071 const OUString aFilterNames
[] = {
1075 "Office Open XML Text",
1079 SwDoc
* pDoc
= getSwDoc();
1081 SwPaM
pam(SwPosition(pDoc
->GetNodes().GetEndOfContent(), SwNodeOffset(-1)));
1082 pDoc
->getIDocumentContentOperations().InsertString(pam
, "foo bar baz");
1084 IDocumentRedlineAccess
& rIDRA(pDoc
->getIDocumentRedlineAccess());
1085 // enable change tracking
1086 rIDRA
.SetRedlineFlags(rIDRA
.GetRedlineFlags()
1087 | RedlineFlags::On
| RedlineFlags::ShowDelete
);
1089 // need a delete redline to trigger mode switching
1090 pam
.Move(fnMoveForward
, GoInDoc
);
1092 pam
.Move(fnMoveBackward
, GoInDoc
);
1093 pDoc
->getIDocumentContentOperations().DeleteAndJoin(pam
);
1095 // hide delete redlines
1096 RedlineFlags
const nRedlineFlags
=
1097 rIDRA
.GetRedlineFlags() & ~RedlineFlags::ShowDelete
;
1098 rIDRA
.SetRedlineFlags(nRedlineFlags
);
1100 for (OUString
const & rFilterName
: aFilterNames
)
1102 // export the document
1105 // tdf#97103 check that redline mode is properly restored
1106 CPPUNIT_ASSERT_EQUAL_MESSAGE(
1107 OString(OString::Concat("redline mode not restored in ") + rFilterName
.toUtf8()).getStr(),
1108 static_cast<int>(nRedlineFlags
), static_cast<int>(rIDRA
.GetRedlineFlags()));
1112 void Test::testBulletAsImage()
1114 OUString aFilterNames
[] = {
1117 "Office Open XML Text",
1121 for (OUString
const & rFilterName
: aFilterNames
)
1123 OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1125 createSwDoc("BulletAsImage.odt");
1127 // Check if import was successful
1129 uno::Reference
<text::XTextRange
> xPara(getParagraph(1));
1130 uno::Reference
<beans::XPropertySet
> xPropertySet(xPara
, uno::UNO_QUERY
);
1131 uno::Reference
<container::XIndexAccess
> xLevels
;
1132 xLevels
.set(xPropertySet
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
1133 uno::Sequence
<beans::PropertyValue
> aProperties
;
1134 xLevels
->getByIndex(0) >>= aProperties
;
1135 uno::Reference
<awt::XBitmap
> xBitmap
;
1137 sal_Int16 nNumberingType
= -1;
1139 for (beans::PropertyValue
const& rProperty
: aProperties
)
1141 if (rProperty
.Name
== "NumberingType")
1143 nNumberingType
= rProperty
.Value
.get
<sal_Int16
>();
1145 else if (rProperty
.Name
== "GraphicBitmap")
1147 if (rProperty
.Value
.has
<uno::Reference
<awt::XBitmap
>>())
1149 xBitmap
= rProperty
.Value
.get
<uno::Reference
<awt::XBitmap
>>();
1152 else if (rProperty
.Name
== "GraphicSize")
1154 aSize
= rProperty
.Value
.get
<awt::Size
>();
1158 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), style::NumberingType::BITMAP
, nNumberingType
);
1161 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
1162 Graphic
aGraphic(uno::Reference
<graphic::XGraphic
>(xBitmap
, uno::UNO_QUERY
));
1163 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), GraphicType::Bitmap
, aGraphic
.GetType());
1164 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), aGraphic
.GetSizeBytes() > o3tl::make_unsigned(0));
1165 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Width());
1166 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Height());
1169 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Width
);
1170 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Height
);
1173 // Export the document and import again for a check
1174 saveAndReload(rFilterName
);
1177 uno::Reference
<text::XTextRange
> xPara(getParagraph(1));
1178 uno::Reference
<beans::XPropertySet
> xPropertySet(xPara
, uno::UNO_QUERY
);
1179 uno::Reference
<container::XIndexAccess
> xLevels
;
1180 xLevels
.set(xPropertySet
->getPropertyValue("NumberingRules"), uno::UNO_QUERY
);
1181 uno::Sequence
<beans::PropertyValue
> aProperties
;
1182 xLevels
->getByIndex(0) >>= aProperties
;
1183 uno::Reference
<awt::XBitmap
> xBitmap
;
1185 sal_Int16 nNumberingType
= -1;
1187 for (beans::PropertyValue
const& rProperty
: aProperties
)
1189 if (rProperty
.Name
== "NumberingType")
1191 nNumberingType
= rProperty
.Value
.get
<sal_Int16
>();
1193 else if (rProperty
.Name
== "GraphicBitmap")
1195 if (rProperty
.Value
.has
<uno::Reference
<awt::XBitmap
>>())
1197 xBitmap
= rProperty
.Value
.get
<uno::Reference
<awt::XBitmap
>>();
1200 else if (rProperty
.Name
== "GraphicSize")
1202 aSize
= rProperty
.Value
.get
<awt::Size
>();
1206 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), style::NumberingType::BITMAP
, nNumberingType
);
1209 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xBitmap
.is());
1210 Graphic
aGraphic(uno::Reference
<graphic::XGraphic
>(xBitmap
, uno::UNO_QUERY
));
1211 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), GraphicType::Bitmap
, aGraphic
.GetType());
1212 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), aGraphic
.GetSizeBytes() > o3tl::make_unsigned(0));
1213 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Width());
1214 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), tools::Long(16), aGraphic
.GetSizePixel().Height());
1217 if (rFilterName
== "write8") // ODT is correct
1219 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Width
);
1220 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(400), aSize
.Height
);
1222 // FIXME: MS Filters don't work correctly for graphic bullet size
1223 else if (rFilterName
== "Office Open XML Text" || rFilterName
== "Rich Text Format")
1225 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(279), aSize
.Width
);
1226 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(279), aSize
.Height
);
1228 else if (rFilterName
== "MS Word 97")
1230 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(296), aSize
.Width
);
1231 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(296), aSize
.Height
);
1237 CPPUNIT_TEST_FIXTURE(Test
, testListLabelPDFExport
)
1241 uno::Reference
<text::XTextDocument
> xDoc(mxComponent
, uno::UNO_QUERY_THROW
);
1242 uno::Reference
<text::XText
> xText(xDoc
->getText());
1243 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY_THROW
);
1244 uno::Reference
<container::XIndexReplace
> xNumRule(
1245 xFactory
->createInstance("com.sun.star.text.NumberingRules"),
1246 uno::UNO_QUERY_THROW
);
1247 OUString listFormat
;
1248 for (sal_Int32 i
= 0; i
< xNumRule
->getCount(); ++i
)
1250 uno::Sequence
<beans::PropertyValue
> format
;
1252 xNumRule
->getByIndex(i
) >>= format
;
1254 auto it(::std::find_if(format
.begin(), format
.end(),
1255 [](auto const& r
) { return r
.Name
== "NumberingType"; }));
1256 // need something RTL
1257 const_cast<uno::Any
&>(it
->Value
) <<= style::NumberingType::CHARS_ARABIC
;
1261 // this doesn't work any more
1262 auto it(::std::find_if(format
.begin(), format
.end(),
1263 [](auto const& r
) { return r
.Name
== "ParentNumbering"; }));
1264 const_cast<uno::Any
&>(it
->Value
) <<= sal_Int16(i
+ 1);
1266 listFormat
+= "%" + OUString::number(i
+1) + "%.";
1267 auto it(::std::find_if(format
.begin(), format
.end(),
1268 [](auto const& r
) { return r
.Name
== "ListFormat"; }));
1269 const_cast<uno::Any
&>(it
->Value
) <<= listFormat
;
1271 xNumRule
->replaceByIndex(i
, uno::Any(format
));
1273 uno::Reference
<beans::XPropertySet
>(getParagraph(1), uno::UNO_QUERY_THROW
)->setPropertyValue("NumberingRules", uno::Any(xNumRule
));
1274 xText
->insertControlCharacter(xText
->getEnd(), text::ControlCharacter::PARAGRAPH_BREAK
, false);
1275 uno::Reference
<beans::XPropertySet
>(getParagraph(2), uno::UNO_QUERY_THROW
)->setPropertyValue("NumberingLevel", uno::Any(sal_Int16(1)));
1276 xText
->insertControlCharacter(xText
->getEnd(), text::ControlCharacter::PARAGRAPH_BREAK
, false);
1277 uno::Reference
<beans::XPropertySet
>(getParagraph(3), uno::UNO_QUERY_THROW
)->setPropertyValue("NumberingLevel", uno::Any(sal_Int16(2)));
1279 // check PDF export of the list items (label in particular)
1280 utl::MediaDescriptor aMediaDescriptor
;
1281 aMediaDescriptor
["FilterName"] <<= OUString("writer_pdf_Export");
1283 uno::Sequence
<beans::PropertyValue
> aFilterData(
1284 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1285 aMediaDescriptor
["FilterData"] <<= aFilterData
;
1286 css::uno::Reference
<frame::XStorable
> xStorable(mxComponent
, css::uno::UNO_QUERY_THROW
);
1287 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
1289 // Parse the export result with pdfium.
1290 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1292 // Non-NULL pPdfDocument means pdfium is available.
1293 if (pPdfDocument
!= nullptr)
1295 // The document has one page.
1296 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1297 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1298 CPPUNIT_ASSERT(pPdfPage
);
1300 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1301 CPPUNIT_ASSERT(pPdfTextPage
);
1303 int nChars
= pPdfTextPage
->countChars();
1304 CPPUNIT_ASSERT_EQUAL(22, nChars
);
1306 // Check that the label strings were exported correctly
1307 std::vector
<sal_uInt32
> aChars(nChars
);
1308 for (int i
= 0; i
< nChars
; i
++)
1309 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1310 OUString
aText(aChars
.data(), aChars
.size());
1311 CPPUNIT_ASSERT_EQUAL(u
"\u0623\r\n.\r\n\u0623.\u0623\r\n.\r\n\u0623.\u0623.\u0623\r\n."_ustr
, aText
);
1314 // Parse the document again to get its raw content
1315 // TODO: get the content from PDFiumPage somehow
1316 vcl::filter::PDFDocument aDocument
;
1317 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
1318 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
1320 // The document has one page.
1321 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1322 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1324 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1325 CPPUNIT_ASSERT(pContents
);
1326 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1327 CPPUNIT_ASSERT(pStream
);
1328 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1330 SvMemoryStream aUncompressed
;
1332 aZCodec
.BeginCompression();
1333 rObjectStream
.Seek(0);
1334 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1335 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1337 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1338 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1352 std::vector
<int> mcids
;
1356 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1361 std::string_view
const line(pStart
, pLine
- pStart
);
1363 if (!line
.empty() && line
[0] != '%')
1365 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1366 if (o3tl::starts_with(line
, "/Lbl<</MCID") && o3tl::ends_with(line
, ">>BDC"))
1368 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1369 mcids
.push_back(o3tl::toInt32(line
.substr(12)));
1373 else if (state
== Lbl
)
1375 auto const endj(line
.find(">Tj"));
1376 if (endj
!= ::std::string_view::npos
)
1378 state
= LblFoundText
;
1383 auto const endJ(line
.find("]TJ"));
1384 if (endJ
!= ::std::string_view::npos
)
1386 state
= LblFoundText
;
1391 else if (state
!= Default
&& line
== "EMC")
1393 CPPUNIT_ASSERT_EQUAL_MESSAGE("missing text", LblFoundText
, state
);
1398 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1399 // ideally there should be 3 but apparently every text portion gets its own
1400 // tag - this should not be a problem if these are grouped in the structure
1402 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nLbl
)>(6), nLbl
);
1403 // these are quite arbitrary?
1404 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nLbl
)>(6), nLblTJ
+ nLblTj
);
1407 for (const auto& rDocElement
: aDocument
.GetElements())
1409 auto pObject0
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1412 auto pType0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject0
->Lookup("Type"_ostr
));
1413 if (!pType0
|| pType0
->GetValue() != "StructElem")
1417 auto pS0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject0
->Lookup("S"_ostr
));
1418 if (!pS0
|| pS0
->GetValue() != "Document")
1422 auto pKids0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject0
->Lookup("K"_ostr
));
1423 CPPUNIT_ASSERT(pKids0
);
1425 for (const auto& pKid0
: pKids0
->GetElements())
1427 auto pRefKid0
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKid0
);
1428 CPPUNIT_ASSERT(pRefKid0
);
1429 auto pObject1
= pRefKid0
->LookupObject();
1430 CPPUNIT_ASSERT(pObject1
);
1431 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
1432 CPPUNIT_ASSERT(pType1
);
1434 if (pType1
&& pType1
->GetValue() == "StructElem")
1436 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
1437 if (pS1
&& pS1
->GetValue() == "L")
1440 auto pKids1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1
->Lookup("K"_ostr
));
1441 CPPUNIT_ASSERT(pKids1
);
1442 // this is purely structural so there should be 1 child
1443 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids1
->GetElements().size());
1445 auto pRefKid11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1
->GetElements()[0]);
1446 CPPUNIT_ASSERT(pRefKid11
);
1447 auto pObject11
= pRefKid11
->LookupObject();
1448 CPPUNIT_ASSERT(pObject11
);
1449 auto pType11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("Type"_ostr
));
1450 CPPUNIT_ASSERT(pType11
);
1451 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11
->GetValue());
1452 auto pS11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("S"_ostr
));
1453 CPPUNIT_ASSERT(pS11
);
1454 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS11
->GetValue());
1455 // LI has 2 children: Lbl and LBody
1456 auto pKids11
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11
->Lookup("K"_ostr
));
1457 CPPUNIT_ASSERT(pKids11
);
1458 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids11
->GetElements().size());
1460 auto pRefKid111
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11
->GetElements()[0]);
1461 CPPUNIT_ASSERT(pRefKid111
);
1462 auto pObject111
= pRefKid111
->LookupObject();
1463 CPPUNIT_ASSERT(pObject111
);
1464 auto pType111
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject111
->Lookup("Type"_ostr
));
1465 CPPUNIT_ASSERT(pType111
);
1466 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType111
->GetValue());
1467 auto pS111
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject111
->Lookup("S"_ostr
));
1468 CPPUNIT_ASSERT(pS111
);
1469 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS111
->GetValue());
1470 // Lbl has 2 children: the first 2 mcids (in order)
1471 auto pKids111
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject111
->Lookup("K"_ostr
));
1472 CPPUNIT_ASSERT(pKids111
);
1473 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids111
->GetElements().size());
1475 auto pRefKid1111
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids111
->GetElements()[0]);
1476 CPPUNIT_ASSERT(pRefKid1111
);
1477 CPPUNIT_ASSERT_EQUAL(mcids
[0], int(pRefKid1111
->GetValue()));
1478 auto pRefKid1112
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids111
->GetElements()[1]);
1479 CPPUNIT_ASSERT(pRefKid1112
);
1480 CPPUNIT_ASSERT_EQUAL(mcids
[1], int(pRefKid1112
->GetValue()));
1482 auto pRefKid112
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11
->GetElements()[1]);
1483 CPPUNIT_ASSERT(pRefKid112
);
1484 auto pObject112
= pRefKid112
->LookupObject();
1485 CPPUNIT_ASSERT(pObject112
);
1486 auto pType112
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112
->Lookup("Type"_ostr
));
1487 CPPUNIT_ASSERT(pType112
);
1488 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112
->GetValue());
1489 auto pS112
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112
->Lookup("S"_ostr
));
1490 CPPUNIT_ASSERT(pS112
);
1491 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS112
->GetValue());
1492 // LBody has 2 children: paragraph and nested L (in order)
1493 auto pKids112
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112
->Lookup("K"_ostr
));
1494 CPPUNIT_ASSERT(pKids112
);
1495 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112
->GetElements().size());
1497 auto pRefKid1121
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112
->GetElements()[0]);
1498 CPPUNIT_ASSERT(pRefKid1121
);
1499 auto pObject1121
= pRefKid1121
->LookupObject();
1500 CPPUNIT_ASSERT(pObject1121
);
1501 auto pType1121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1121
->Lookup("Type"_ostr
));
1502 CPPUNIT_ASSERT(pType1121
);
1503 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1121
->GetValue());
1504 auto pS1121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1121
->Lookup("S"_ostr
));
1505 CPPUNIT_ASSERT(pS1121
);
1506 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1121
->GetValue());
1508 auto pRefKid1122
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112
->GetElements()[1]);
1509 CPPUNIT_ASSERT(pRefKid1122
);
1510 auto pObject1122
= pRefKid1122
->LookupObject();
1511 CPPUNIT_ASSERT(pObject1122
);
1512 auto pType1122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122
->Lookup("Type"_ostr
));
1513 CPPUNIT_ASSERT(pType1122
);
1514 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122
->GetValue());
1515 auto pS1122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122
->Lookup("S"_ostr
));
1516 CPPUNIT_ASSERT(pS1122
);
1517 CPPUNIT_ASSERT_EQUAL("L"_ostr
, pS1122
->GetValue());
1518 auto pKids1122
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1122
->Lookup("K"_ostr
));
1519 CPPUNIT_ASSERT(pKids1122
);
1520 // this is purely structural so there should be 1 child
1521 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids1122
->GetElements().size());
1523 auto pRefKid11221
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1122
->GetElements()[0]);
1524 CPPUNIT_ASSERT(pRefKid11221
);
1525 auto pObject11221
= pRefKid11221
->LookupObject();
1526 CPPUNIT_ASSERT(pObject11221
);
1527 auto pType11221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221
->Lookup("Type"_ostr
));
1528 CPPUNIT_ASSERT(pType11221
);
1529 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11221
->GetValue());
1530 auto pS11221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221
->Lookup("S"_ostr
));
1531 CPPUNIT_ASSERT(pS11221
);
1532 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS11221
->GetValue());
1533 // LI has 2 children: Lbl and LBody
1534 auto pKids11221
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11221
->Lookup("K"_ostr
));
1535 CPPUNIT_ASSERT(pKids11221
);
1536 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids11221
->GetElements().size());
1538 auto pRefKid112211
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221
->GetElements()[0]);
1539 CPPUNIT_ASSERT(pRefKid112211
);
1540 auto pObject112211
= pRefKid112211
->LookupObject();
1541 CPPUNIT_ASSERT(pObject112211
);
1542 auto pType112211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112211
->Lookup("Type"_ostr
));
1543 CPPUNIT_ASSERT(pType112211
);
1544 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112211
->GetValue());
1545 auto pS112211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112211
->Lookup("S"_ostr
));
1546 CPPUNIT_ASSERT(pS112211
);
1547 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS112211
->GetValue());
1548 // Lbl has 2 children: the first 2 mcids (in order)
1549 auto pKids112211
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112211
->Lookup("K"_ostr
));
1550 CPPUNIT_ASSERT(pKids112211
);
1551 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112211
->GetElements().size());
1553 auto pRefKid1122111
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112211
->GetElements()[0]);
1554 CPPUNIT_ASSERT(pRefKid1122111
);
1555 CPPUNIT_ASSERT_EQUAL(mcids
[2], int(pRefKid1122111
->GetValue()));
1556 auto pRefKid1122112
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112211
->GetElements()[1]);
1557 CPPUNIT_ASSERT(pRefKid1122112
);
1558 CPPUNIT_ASSERT_EQUAL(mcids
[3], int(pRefKid1122112
->GetValue()));
1560 auto pRefKid112212
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221
->GetElements()[1]);
1561 CPPUNIT_ASSERT(pRefKid112212
);
1562 auto pObject112212
= pRefKid112212
->LookupObject();
1563 CPPUNIT_ASSERT(pObject112212
);
1564 auto pType112212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212
->Lookup("Type"_ostr
));
1565 CPPUNIT_ASSERT(pType112212
);
1566 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112212
->GetValue());
1567 auto pS112212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212
->Lookup("S"_ostr
));
1568 CPPUNIT_ASSERT(pS112212
);
1569 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS112212
->GetValue());
1570 // LBody has 2 children: paragraph and nested L (in order)
1571 auto pKids112212
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112212
->Lookup("K"_ostr
));
1572 CPPUNIT_ASSERT(pKids112212
);
1573 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112212
->GetElements().size());
1575 auto pRefKid1122121
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112212
->GetElements()[0]);
1576 CPPUNIT_ASSERT(pRefKid1122121
);
1577 auto pObject1122121
= pRefKid1122121
->LookupObject();
1578 CPPUNIT_ASSERT(pObject1122121
);
1579 auto pType1122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122121
->Lookup("Type"_ostr
));
1580 CPPUNIT_ASSERT(pType1122121
);
1581 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122121
->GetValue());
1582 auto pS1122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122121
->Lookup("S"_ostr
));
1583 CPPUNIT_ASSERT(pS1122121
);
1584 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1122121
->GetValue());
1586 auto pRefKid1122122
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112212
->GetElements()[1]);
1587 CPPUNIT_ASSERT(pRefKid1122122
);
1588 auto pObject1122122
= pRefKid1122122
->LookupObject();
1589 CPPUNIT_ASSERT(pObject1122122
);
1590 auto pType1122122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122
->Lookup("Type"_ostr
));
1591 CPPUNIT_ASSERT(pType1122122
);
1592 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122122
->GetValue());
1593 auto pS1122122
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122
->Lookup("S"_ostr
));
1594 CPPUNIT_ASSERT(pS1122122
);
1595 CPPUNIT_ASSERT_EQUAL("L"_ostr
, pS1122122
->GetValue());
1596 auto pKids1122122
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1122122
->Lookup("K"_ostr
));
1597 CPPUNIT_ASSERT(pKids1122122
);
1598 // this is purely structural so there should be 1 child
1599 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids1122122
->GetElements().size());
1601 auto pRefKid11221221
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1122122
->GetElements()[0]);
1602 CPPUNIT_ASSERT(pRefKid11221221
);
1603 auto pObject11221221
= pRefKid11221221
->LookupObject();
1604 CPPUNIT_ASSERT(pObject11221221
);
1605 auto pType11221221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221221
->Lookup("Type"_ostr
));
1606 CPPUNIT_ASSERT(pType11221221
);
1607 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11221221
->GetValue());
1608 auto pS11221221
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11221221
->Lookup("S"_ostr
));
1609 CPPUNIT_ASSERT(pS11221221
);
1610 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS11221221
->GetValue());
1611 // LI has 2 children: Lbl and LBody
1612 auto pKids11221221
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11221221
->Lookup("K"_ostr
));
1613 CPPUNIT_ASSERT(pKids11221221
);
1614 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids11221221
->GetElements().size());
1616 auto pRefKid112212211
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221221
->GetElements()[0]);
1617 CPPUNIT_ASSERT(pRefKid112212211
);
1618 auto pObject112212211
= pRefKid112212211
->LookupObject();
1619 CPPUNIT_ASSERT(pObject112212211
);
1620 auto pType112212211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212211
->Lookup("Type"_ostr
));
1621 CPPUNIT_ASSERT(pType112212211
);
1622 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112212211
->GetValue());
1623 auto pS112212211
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212211
->Lookup("S"_ostr
));
1624 CPPUNIT_ASSERT(pS112212211
);
1625 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS112212211
->GetValue());
1626 // Lbl has 2 children: the first 2 mcids (in order)
1627 auto pKids112212211
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112212211
->Lookup("K"_ostr
));
1628 CPPUNIT_ASSERT(pKids112212211
);
1629 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids112212211
->GetElements().size());
1631 auto pRefKid1122122111
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112212211
->GetElements()[0]);
1632 CPPUNIT_ASSERT(pRefKid1122122111
);
1633 CPPUNIT_ASSERT_EQUAL(mcids
[4], int(pRefKid1122122111
->GetValue()));
1634 auto pRefKid1122122112
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids112212211
->GetElements()[1]);
1635 CPPUNIT_ASSERT(pRefKid1122122112
);
1636 CPPUNIT_ASSERT_EQUAL(mcids
[5], int(pRefKid1122122112
->GetValue()));
1638 auto pRefKid112212212
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids11221221
->GetElements()[1]);
1639 CPPUNIT_ASSERT(pRefKid112212212
);
1640 auto pObject112212212
= pRefKid112212212
->LookupObject();
1641 CPPUNIT_ASSERT(pObject112212212
);
1642 auto pType112212212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212212
->Lookup("Type"_ostr
));
1643 CPPUNIT_ASSERT(pType112212212
);
1644 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType112212212
->GetValue());
1645 auto pS112212212
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject112212212
->Lookup("S"_ostr
));
1646 CPPUNIT_ASSERT(pS112212212
);
1647 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS112212212
->GetValue());
1648 // inner LBody has 1 children: paragraph
1649 auto pKids112212212
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject112212212
->Lookup("K"_ostr
));
1650 CPPUNIT_ASSERT(pKids112212212
);
1651 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids112212212
->GetElements().size());
1653 auto pRefKid1122122121
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids112212212
->GetElements()[0]);
1654 CPPUNIT_ASSERT(pRefKid1122122121
);
1655 auto pObject1122122121
= pRefKid1122122121
->LookupObject();
1656 CPPUNIT_ASSERT(pObject1122122121
);
1657 auto pType1122122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122121
->Lookup("Type"_ostr
));
1658 CPPUNIT_ASSERT(pType1122122121
);
1659 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1122122121
->GetValue());
1660 auto pS1122122121
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1122122121
->Lookup("S"_ostr
));
1661 CPPUNIT_ASSERT(pS1122122121
);
1662 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1122122121
->GetValue());
1667 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nL
)>(1), nL
);
1670 CPPUNIT_TEST_FIXTURE(Test
, testTdf143311
)
1672 createSwDoc("tdf143311-1.docx");
1673 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(1), "Decorative"));
1675 // add another one that's a SdrObject
1676 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
1677 uno::Reference
<drawing::XShape
> xShape(
1678 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
1679 uno::Reference
<beans::XPropertySet
> xShapeProps(xShape
, uno::UNO_QUERY
);
1680 xShapeProps
->setPropertyValue("Decorative", uno::Any(true));
1681 uno::Reference
<drawing::XDrawPageSupplier
> xDrawPageSupplier(mxComponent
, uno::UNO_QUERY
);
1682 uno::Reference
<drawing::XDrawPage
> xDrawPage(xDrawPageSupplier
->getDrawPage());
1683 xDrawPage
->add(xShape
);
1685 // check DOCX filters
1686 saveAndReload("Office Open XML Text");
1687 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(1), "Decorative"));
1688 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(2), "Decorative"));
1690 // tdf#153925 not imported - check default and set it to test ODF filters
1691 uno::Reference
<beans::XPropertySet
> const xStyle(getStyles("FrameStyles")->getByName("Formula"), uno::UNO_QUERY_THROW
);
1692 CPPUNIT_ASSERT_EQUAL(false, getProperty
<bool>(xStyle
, "Decorative"));
1693 xStyle
->setPropertyValue("Decorative", uno::Any(true));
1695 // check ODF filters
1696 saveAndReload("writer8");
1697 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(1), "Decorative"));
1698 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getShape(2), "Decorative"));
1699 CPPUNIT_ASSERT_EQUAL(true, getProperty
<bool>(getStyles("FrameStyles")->getByName("Formula"), "Decorative"));
1702 utl::MediaDescriptor aMediaDescriptor
;
1703 aMediaDescriptor
["FilterName"] <<= OUString("writer_pdf_Export");
1705 uno::Sequence
<beans::PropertyValue
> aFilterData(
1706 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1707 aMediaDescriptor
["FilterData"] <<= aFilterData
;
1708 css::uno::Reference
<frame::XStorable
> xStorable(mxComponent
, css::uno::UNO_QUERY_THROW
);
1709 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
1711 vcl::filter::PDFDocument aDocument
;
1712 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
1713 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
1715 // The document has one page.
1716 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1717 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1719 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1720 CPPUNIT_ASSERT(pContents
);
1721 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1722 CPPUNIT_ASSERT(pStream
);
1723 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1725 SvMemoryStream aUncompressed
;
1727 aZCodec
.BeginCompression();
1728 rObjectStream
.Seek(0);
1729 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1730 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1732 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1733 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1749 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1754 std::string_view
const line(pStart
, pLine
- pStart
);
1756 if (!line
.empty() && line
[0] != '%')
1758 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1759 if (line
== "/Artifact BMC")
1761 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1765 else if (o3tl::starts_with(line
, "/Standard<</MCID") && o3tl::ends_with(line
, ">>BDC"))
1767 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1771 else if (line
== "EMC")
1773 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
1776 else if (nLine
> 1) // first line is expected "0.1 w"
1778 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
1782 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1783 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged
)>(25), nTagged
); // text in body
1784 // 1 decorative image + 1 decorative shape + 1 pre-existing rectangle border or something
1785 CPPUNIT_ASSERT(nArtifacts
>= 3);
1788 void Test::testTextFormField()
1790 const OUString aFilterNames
[] = {
1793 "Office Open XML Text",
1796 for (const OUString
& rFilterName
: aFilterNames
)
1798 createSwDoc("text_form_field.odt");
1800 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1802 // Export the document and import again for a check
1803 saveAndReload(rFilterName
);
1805 // Check the document after round trip
1806 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1807 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pTextDoc
);
1808 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
1809 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1811 // We have two text form fields
1812 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(2), pMarkAccess
->getAllMarksCount());
1814 // Check whether all fieldmarks are text form fields
1815 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1817 ::sw::mark::IFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::IFieldmark
*>(*aIter
);
1818 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1819 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMTEXT
, pFieldmark
->GetFieldname());
1822 // In the first paragraph we have an empty text form field with the placeholder spaces
1823 const uno::Reference
< text::XTextRange
> xPara
= getParagraph(1);
1824 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("TextFieldStart"), getProperty
<OUString
>(getRun(xPara
, 1), "TextPortionType"));
1825 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("TextFieldSeparator"), getProperty
<OUString
>(getRun(xPara
, 2), "TextPortionType"));
1826 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("Text"), getProperty
<OUString
>(getRun(xPara
, 3), "TextPortionType"));
1827 sal_Unicode vEnSpaces
[5] = {8194, 8194, 8194, 8194, 8194};
1828 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString(vEnSpaces
, 5), getRun(xPara
, 3)->getString());
1829 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("TextFieldEnd"), getProperty
<OUString
>(getRun(xPara
, 4), "TextPortionType"));
1831 // In the second paragraph we have a set text
1832 const uno::Reference
< text::XTextRange
> xPara2
= getParagraph(2);
1833 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("TextFieldStart"), getProperty
<OUString
>(getRun(xPara2
, 1), "TextPortionType"));
1834 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("TextFieldSeparator"), getProperty
<OUString
>(getRun(xPara2
, 2), "TextPortionType"));
1835 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("Text"), getProperty
<OUString
>(getRun(xPara2
, 3), "TextPortionType"));
1836 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("xxxxx"), getRun(xPara2
, 3)->getString());
1837 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("TextFieldEnd"), getProperty
<OUString
>(getRun(xPara2
, 4), "TextPortionType"));
1841 void Test::testCheckBoxFormField()
1843 const OUString aFilterNames
[] = {
1846 "Office Open XML Text",
1849 for (const OUString
& rFilterName
: aFilterNames
)
1851 createSwDoc("checkbox_form_field.odt");
1853 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1855 // Export the document and import again for a check
1856 saveAndReload(rFilterName
);
1858 // Check the document after round trip
1859 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1860 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pTextDoc
);
1861 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
1862 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1864 // We have two check box form fields
1865 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(2), pMarkAccess
->getAllMarksCount());
1868 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1870 ::sw::mark::IFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::IFieldmark
*>(*aIter
);
1872 if(rFilterName
== "Office Open XML Text") // OOXML import also generates bookmarks
1878 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1879 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMCHECKBOX
, pFieldmark
->GetFieldname());
1880 ::sw::mark::ICheckboxFieldmark
* pCheckBox
= dynamic_cast< ::sw::mark::ICheckboxFieldmark
* >(pFieldmark
);
1881 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pCheckBox
);
1883 // The first one is unchecked, the other one is checked
1885 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), !pCheckBox
->IsChecked());
1887 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pCheckBox
->IsChecked());
1890 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(2), nIndex
);
1894 void Test::testDropDownFormField()
1896 const OUString aFilterNames
[] = {
1899 "Office Open XML Text",
1902 for (const OUString
& rFilterName
: aFilterNames
)
1904 createSwDoc("dropdown_form_field.odt");
1906 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1908 // Export the document and import again for a check
1909 saveAndReload(rFilterName
);
1911 // Check the document after round trip
1912 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1913 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pTextDoc
);
1914 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
1915 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1917 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(2), pMarkAccess
->getAllMarksCount());
1920 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1922 ::sw::mark::IFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::IFieldmark
*>(*aIter
);
1927 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
1928 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMDROPDOWN
, pFieldmark
->GetFieldname());
1930 // Check drop down field's parameters.
1931 const sw::mark::IFieldmark::parameter_map_t
* const pParameters
= pFieldmark
->GetParameters();
1932 css::uno::Sequence
<OUString
> vListEntries
;
1933 sal_Int32 nSelection
= -1;
1934 auto pListEntries
= pParameters
->find(ODF_FORMDROPDOWN_LISTENTRY
);
1935 if (pListEntries
!= pParameters
->end())
1937 pListEntries
->second
>>= vListEntries
;
1939 if(vListEntries
.hasElements())
1941 auto pResult
= pParameters
->find(ODF_FORMDROPDOWN_RESULT
);
1942 if (pResult
!= pParameters
->end())
1944 pResult
->second
>>= nSelection
;
1949 // The first one is empty
1952 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), !vListEntries
.hasElements());
1953 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(-1), nSelection
);
1955 else // The second one has list and also a selected item
1957 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(4), vListEntries
.getLength());
1958 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(1), nSelection
);
1959 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("1000"), vListEntries
[0]);
1960 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("2000"), vListEntries
[1]);
1961 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("3000"), vListEntries
[2]);
1962 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("4000"), vListEntries
[3]);
1966 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(2), nIndex
);
1970 void Test::testDateFormField()
1972 const OUString aFilterNames
[] = {
1974 "Office Open XML Text",
1977 for (const OUString
& rFilterName
: aFilterNames
)
1979 createSwDoc("date_form_field.odt");
1981 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
1983 // Export the document and import again for a check
1984 saveAndReload(rFilterName
);
1986 // Check the document after round trip
1987 if (rFilterName
== "writer8")
1989 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
1990 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pTextDoc
);
1991 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
1992 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
1994 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(5), pMarkAccess
->getAllMarksCount());
1997 for(auto aIter
= pMarkAccess
->getAllMarksBegin(); aIter
!= pMarkAccess
->getAllMarksEnd(); ++aIter
)
1999 ::sw::mark::IDateFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::IDateFieldmark
*>(*aIter
);
2000 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
2001 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMDATE
, pFieldmark
->GetFieldname());
2003 // Check date form field's parameters.
2004 const sw::mark::IFieldmark::parameter_map_t
* const pParameters
= pFieldmark
->GetParameters();
2005 OUString sDateFormat
;
2006 auto pResult
= pParameters
->find(ODF_FORMDATE_DATEFORMAT
);
2007 if (pResult
!= pParameters
->end())
2009 pResult
->second
>>= sDateFormat
;
2013 pResult
= pParameters
->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE
);
2014 if (pResult
!= pParameters
->end())
2016 pResult
->second
>>= sLang
;
2019 OUString sCurrentDate
= pFieldmark
->GetContent();
2021 // The first one has the default field content
2025 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("MM/DD/YY"), sDateFormat
);
2026 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("en-US"), sLang
);
2027 sal_Unicode vEnSpaces
[ODF_FORMFIELD_DEFAULT_LENGTH
] = {8194, 8194, 8194, 8194, 8194};
2028 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString(vEnSpaces
, 5), sCurrentDate
);
2030 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2031 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(5), pFieldmark
->GetMarkStart().GetContentIndex());
2033 else if (nIndex
== 1) // The second has the default format
2035 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("MM/DD/YY"), sDateFormat
);
2036 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("en-US"), sLang
);
2037 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("06/12/19"), sCurrentDate
);
2039 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2040 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(20), pFieldmark
->GetMarkStart().GetContentIndex());
2042 else if (nIndex
== 2) // The third one has special format
2044 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("[NatNum12 MMMM=abbreviation]YYYY\". \"MMMM D."), sDateFormat
);
2045 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("hu-HU"), sLang
);
2046 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("2019. febr. 12."), sCurrentDate
);
2048 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2049 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(40), pFieldmark
->GetMarkStart().GetContentIndex());
2052 else if (nIndex
== 3) // The fourth one has placeholder text
2054 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("D, MMM YY"), sDateFormat
);
2055 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("bm-ML"), sLang
);
2056 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("[select date]"), sCurrentDate
);
2058 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2059 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(62), pFieldmark
->GetMarkStart().GetContentIndex());
2062 else // The last one is really empty
2064 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("MM/DD/YY"), sDateFormat
);
2065 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("en-US"), sLang
);
2066 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString(""), sCurrentDate
);
2068 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), SwNodeOffset(9), pFieldmark
->GetMarkStart().GetNodeIndex());
2069 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(82), pFieldmark
->GetMarkStart().GetContentIndex());
2074 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(5), nIndex
);
2078 // Import from DOCX, so the fieldmark is now a content control.
2079 uno::Reference
<container::XEnumerationAccess
> xEnumAccess(getParagraph(1), uno::UNO_QUERY
);
2080 uno::Reference
<container::XEnumeration
> xTextPortions
= xEnumAccess
->createEnumeration();
2083 while (xTextPortions
->hasMoreElements())
2085 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
2086 OUString aPortionType
;
2087 xTextPortion
->getPropertyValue("TextPortionType") >>= aPortionType
;
2088 if (aPortionType
!= "ContentControl")
2093 uno::Reference
<text::XTextContent
> xContentControl
;
2094 xTextPortion
->getPropertyValue("ContentControl") >>= xContentControl
;
2095 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
2098 xContentControlProps
->getPropertyValue("Date") >>= bDate
;
2099 CPPUNIT_ASSERT(bDate
);
2101 // Check date form field's parameters.
2102 OUString sDateFormat
;
2103 xContentControlProps
->getPropertyValue("DateFormat") >>= sDateFormat
;
2106 xContentControlProps
->getPropertyValue("DateLanguage") >>= sLang
;
2108 uno::Reference
<container::XEnumerationAccess
> xContentControlEnumAccess(xContentControl
,
2110 uno::Reference
<container::XEnumeration
> xContentControlEnum
2111 = xContentControlEnumAccess
->createEnumeration();
2112 uno::Reference
<text::XTextRange
> xContentControlTextPortion(xContentControlEnum
->nextElement(), uno::UNO_QUERY
);
2113 OUString sCurrentDate
= xContentControlTextPortion
->getString();
2115 // The first one has the default field content
2118 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("MM/DD/YY"), sDateFormat
);
2119 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("en-US"), sLang
);
2120 sal_Unicode vEnSpaces
[ODF_FORMFIELD_DEFAULT_LENGTH
] = {8194, 8194, 8194, 8194, 8194};
2121 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString(vEnSpaces
, 5), sCurrentDate
);
2123 else if (nIndex
== 1) // The second has the default format
2125 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("MM/DD/YY"), sDateFormat
);
2126 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("en-US"), sLang
);
2127 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("06/12/19"), sCurrentDate
);
2129 else if (nIndex
== 2) // The third one has special format
2131 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("[NatNum12 MMMM=abbreviation]YYYY\". \"MMMM D."), sDateFormat
);
2132 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("hu-HU"), sLang
);
2133 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("2019. febr. 12."), sCurrentDate
);
2135 else if (nIndex
== 3) // The fourth one has placeholder text
2137 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("D, MMM YY"), sDateFormat
);
2138 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("bm-ML"), sLang
);
2139 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("[select date]"), sCurrentDate
);
2141 else // The last one is really empty
2143 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("MM/DD/YY"), sDateFormat
);
2144 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString("en-US"), sLang
);
2145 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), OUString(""), sCurrentDate
);
2149 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), int(5), nIndex
);
2154 void Test::testDateFormFieldCharacterFormatting()
2156 const OUString aFilterNames
[] = {
2158 "Office Open XML Text",
2161 for (const OUString
& rFilterName
: aFilterNames
)
2163 createSwDoc("date_form_field_char_formatting.odt");
2165 const OString sFailedMessage
= OString::Concat("Failed on filter: ") + rFilterName
.toUtf8();
2167 // Export the document and import again for a check
2168 saveAndReload(rFilterName
);
2170 // Check the document after round trip
2171 if (rFilterName
== "writer8")
2173 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
2174 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pTextDoc
);
2175 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
2176 IDocumentMarkAccess
* pMarkAccess
= pDoc
->getIDocumentMarkAccess();
2178 // Check that we have the field at the right place
2179 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(1), pMarkAccess
->getAllMarksCount());
2180 ::sw::mark::IDateFieldmark
* pFieldmark
= dynamic_cast<::sw::mark::IDateFieldmark
*>(*pMarkAccess
->getAllMarksBegin());
2181 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pFieldmark
);
2182 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), ODF_FORMDATE
, pFieldmark
->GetFieldname());
2183 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(0), pFieldmark
->GetMarkStart().GetContentIndex());
2184 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), sal_Int32(11), pFieldmark
->GetMarkEnd().GetContentIndex());
2186 // We have one date field, first half of the field has bold character weight and second part has red character color
2187 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::BOLD
, getProperty
<float>(getRun(getParagraph(1), 3), "CharWeight"));
2188 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_AUTO
, getProperty
<Color
>(getRun(getParagraph(1), 3), "CharColor"));
2189 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::NORMAL
, getProperty
<float>(getRun(getParagraph(1), 4), "CharWeight"));
2190 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(getRun(getParagraph(1), 4), "CharColor"));
2194 uno::Reference
<beans::XPropertySet
> xTextPortion(getRun(getParagraph(1), 1), uno::UNO_QUERY
);
2195 OUString aPortionType
;
2196 xTextPortion
->getPropertyValue("TextPortionType") >>= aPortionType
;
2197 CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType
);
2199 uno::Reference
<text::XTextContent
> xContentControl
;
2200 xTextPortion
->getPropertyValue("ContentControl") >>= xContentControl
;
2201 uno::Reference
<beans::XPropertySet
> xContentControlProps(xContentControl
, uno::UNO_QUERY
);
2203 xContentControlProps
->getPropertyValue("Date") >>= bDate
;
2204 CPPUNIT_ASSERT(bDate
);
2206 uno::Reference
<container::XEnumerationAccess
> xContentControlEnumAccess(xContentControl
,
2208 uno::Reference
<container::XEnumeration
> xContentControlEnum
2209 = xContentControlEnumAccess
->createEnumeration();
2210 xTextPortion
.set(xContentControlEnum
->nextElement(), uno::UNO_QUERY
);
2211 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::BOLD
, getProperty
<float>(xTextPortion
, "CharWeight"));
2212 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_AUTO
, getProperty
<Color
>(xTextPortion
, "CharColor"));
2213 xTextPortion
.set(xContentControlEnum
->nextElement(), uno::UNO_QUERY
);
2214 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), awt::FontWeight::NORMAL
, getProperty
<float>(xTextPortion
, "CharWeight"));
2215 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), COL_LIGHTRED
, getProperty
<Color
>(xTextPortion
, "CharColor"));
2220 void Test::testSvgImageSupport()
2222 OUString aFilterNames
[] = {
2224 "Office Open XML Text",
2227 for (OUString
const & rFilterName
: aFilterNames
)
2229 // Use case to import a document containing a SVG image, export in target format, import and check if the
2230 // SVG image is present and as expected in the document
2233 createSwDoc("SvgImageTest.odt");
2235 // Export the document in target format and import again
2236 saveAndReload(rFilterName
);
2238 // Prepare fail message (writing which import/export filter was used)
2239 const OString sFailedMessage
= "Failed on filter: "_ostr
+ rFilterName
.toUtf8();
2241 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), 1, getShapes());
2244 uno::Reference
<drawing::XShape
> xImage(getShape(1), uno::UNO_QUERY
);
2245 uno::Reference
<beans::XPropertySet
> xPropertySet(xImage
, uno::UNO_QUERY_THROW
);
2247 // Convert to a XGraphic
2248 uno::Reference
<graphic::XGraphic
> xGraphic
;
2249 xPropertySet
->getPropertyValue("Graphic") >>= xGraphic
;
2250 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), xGraphic
.is());
2252 // Access the Graphic
2253 Graphic
aGraphic(xGraphic
);
2255 // Check if it contains a VectorGraphicData struct
2256 auto pVectorGraphic
= aGraphic
.getVectorGraphicData();
2257 CPPUNIT_ASSERT_MESSAGE(sFailedMessage
.getStr(), pVectorGraphic
);
2259 // Which should be of type SVG, which means we have a SVG file
2260 CPPUNIT_ASSERT_EQUAL_MESSAGE(sFailedMessage
.getStr(), VectorGraphicDataType::Svg
, pVectorGraphic
->getType());
2264 } // end of anonymous namespace
2265 CPPUNIT_TEST_SUITE_REGISTRATION(Test
);
2267 CPPUNIT_PLUGIN_IMPLEMENT();
2269 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */