Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
21 : #include <basegfx/tools/canvastools.hxx>
22 : #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
23 : #include <basegfx/color/bcolor.hxx>
24 : #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
25 : #include <vcl/lineinfo.hxx>
26 : #include <drawinglayer/attribute/lineattribute.hxx>
27 : #include <drawinglayer/attribute/strokeattribute.hxx>
28 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
29 : #include <vcl/metaact.hxx>
30 : #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
31 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
32 : #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
33 : #include <basegfx/polygon/b2dpolygontools.hxx>
34 : #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
35 : #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
36 : #include <vcl/salbtype.hxx>
37 : #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
38 : #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
39 : #include <vcl/svapp.hxx>
40 : #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
41 : #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
42 : #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
43 : #include <basegfx/polygon/b2dpolygonclipper.hxx>
44 : #include <drawinglayer/primitive2d/invertprimitive2d.hxx>
45 : #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
46 : #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
47 : #include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
48 : #include <drawinglayer/primitive2d/textprimitive2d.hxx>
49 : #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
50 : #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
51 : #include <i18nlangtag/languagetag.hxx>
52 : #include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
53 : #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
54 : #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
55 : #include <tools/fract.hxx>
56 : #include <numeric>
57 :
58 :
59 :
60 : using namespace com::sun::star;
61 :
62 :
63 :
64 : namespace
65 : {
66 : /** helper class for graphic context
67 :
68 : This class allows to hold a complete representation of classic
69 : VCL OutputDevice state. This data is needed for correct
70 : interpretation of the MetaFile action flow.
71 : */
72 114 : class PropertyHolder
73 : {
74 : private:
75 : /// current transformation (aka MapMode)
76 : basegfx::B2DHomMatrix maTransformation;
77 : MapUnit maMapUnit;
78 :
79 : /// current colors
80 : basegfx::BColor maLineColor;
81 : basegfx::BColor maFillColor;
82 : basegfx::BColor maTextColor;
83 : basegfx::BColor maTextFillColor;
84 : basegfx::BColor maTextLineColor;
85 : basegfx::BColor maOverlineColor;
86 :
87 : /// clipping
88 : basegfx::B2DPolyPolygon maClipPolyPoygon;
89 :
90 : /// font, etc.
91 : vcl::Font maFont;
92 : RasterOp maRasterOp;
93 : ComplexTextLayoutMode mnLayoutMode;
94 : LanguageType maLanguageType;
95 : PushFlags mnPushFlags;
96 :
97 : /// bitfield
98 : /// contains all active markers
99 : bool mbLineColor : 1;
100 : bool mbFillColor : 1;
101 : bool mbTextColor : 1;
102 : bool mbTextFillColor : 1;
103 : bool mbTextLineColor : 1;
104 : bool mbOverlineColor : 1;
105 : bool mbClipPolyPolygonActive : 1;
106 :
107 : public:
108 89 : PropertyHolder()
109 : : maTransformation(),
110 : maMapUnit(MAP_100TH_MM),
111 : maLineColor(),
112 : maFillColor(),
113 : maTextColor(COL_BLACK),
114 : maTextFillColor(),
115 : maTextLineColor(),
116 : maOverlineColor(),
117 : maClipPolyPoygon(),
118 : maFont(),
119 : maRasterOp(ROP_OVERPAINT),
120 : mnLayoutMode(TEXT_LAYOUT_DEFAULT),
121 : maLanguageType(0),
122 : mnPushFlags(PushFlags::NONE),
123 : mbLineColor(false),
124 : mbFillColor(false),
125 : mbTextColor(true),
126 : mbTextFillColor(false),
127 : mbTextLineColor(false),
128 : mbOverlineColor(false),
129 89 : mbClipPolyPolygonActive(false)
130 : {
131 89 : }
132 :
133 203 : ~PropertyHolder()
134 203 : {
135 203 : }
136 :
137 : /// read/write accesses
138 1891 : const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; }
139 116 : void setTransformation(const basegfx::B2DHomMatrix& rNew) { if(rNew != maTransformation) maTransformation = rNew; }
140 :
141 114 : MapUnit getMapUnit() const { return maMapUnit; }
142 195 : void setMapUnit(MapUnit eNew) { if(eNew != maMapUnit) maMapUnit = eNew; }
143 :
144 386 : const basegfx::BColor& getLineColor() const { return maLineColor; }
145 613 : void setLineColor(const basegfx::BColor& rNew) { if(rNew != maLineColor) maLineColor = rNew; }
146 815 : bool getLineColorActive() const { return mbLineColor; }
147 655 : void setLineColorActive(bool bNew) { if(bNew != mbLineColor) mbLineColor = bNew; }
148 :
149 793 : const basegfx::BColor& getFillColor() const { return maFillColor; }
150 855 : void setFillColor(const basegfx::BColor& rNew) { if(rNew != maFillColor) maFillColor = rNew; }
151 793 : bool getFillColorActive() const { return mbFillColor; }
152 879 : void setFillColorActive(bool bNew) { if(bNew != mbFillColor) mbFillColor = bNew; }
153 :
154 765 : const basegfx::BColor& getTextColor() const { return maTextColor; }
155 545 : void setTextColor(const basegfx::BColor& rNew) { if(rNew != maTextColor) maTextColor = rNew; }
156 765 : bool getTextColorActive() const { return mbTextColor; }
157 229 : void setTextColorActive(bool bNew) { if(bNew != mbTextColor) mbTextColor = bNew; }
158 :
159 114 : const basegfx::BColor& getTextFillColor() const { return maTextFillColor; }
160 430 : void setTextFillColor(const basegfx::BColor& rNew) { if(rNew != maTextFillColor) maTextFillColor = rNew; }
161 765 : bool getTextFillColorActive() const { return mbTextFillColor; }
162 791 : void setTextFillColorActive(bool bNew) { if(bNew != mbTextFillColor) mbTextFillColor = bNew; }
163 :
164 114 : const basegfx::BColor& getTextLineColor() const { return maTextLineColor; }
165 114 : void setTextLineColor(const basegfx::BColor& rNew) { if(rNew != maTextLineColor) maTextLineColor = rNew; }
166 122 : bool getTextLineColorActive() const { return mbTextLineColor; }
167 114 : void setTextLineColorActive(bool bNew) { if(bNew != mbTextLineColor) mbTextLineColor = bNew; }
168 :
169 114 : const basegfx::BColor& getOverlineColor() const { return maOverlineColor; }
170 114 : void setOverlineColor(const basegfx::BColor& rNew) { if(rNew != maOverlineColor) maOverlineColor = rNew; }
171 122 : bool getOverlineColorActive() const { return mbOverlineColor; }
172 114 : void setOverlineColorActive(bool bNew) { if(bNew != mbOverlineColor) mbOverlineColor = bNew; }
173 :
174 296 : const basegfx::B2DPolyPolygon& getClipPolyPolygon() const { return maClipPolyPoygon; }
175 123 : void setClipPolyPolygon(const basegfx::B2DPolyPolygon& rNew) { if(rNew != maClipPolyPoygon) maClipPolyPoygon = rNew; }
176 896 : bool getClipPolyPolygonActive() const { return mbClipPolyPolygonActive; }
177 171 : void setClipPolyPolygonActive(bool bNew) { if(bNew != mbClipPolyPolygonActive) mbClipPolyPolygonActive = bNew; }
178 :
179 1982 : const vcl::Font& getFont() const { return maFont; }
180 446 : void setFont(const vcl::Font& rFont) { if(rFont != maFont) maFont = rFont; }
181 :
182 114 : const RasterOp& getRasterOp() const { return maRasterOp; }
183 801 : void setRasterOp(const RasterOp& rRasterOp) { if(rRasterOp != maRasterOp) maRasterOp = rRasterOp; }
184 1374 : bool isRasterOpInvert() const { return (ROP_XOR == maRasterOp || ROP_INVERT == maRasterOp); }
185 1370 : bool isRasterOpForceBlack() const { return ROP_0 == maRasterOp; }
186 1374 : bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); }
187 :
188 1416 : ComplexTextLayoutMode getLayoutMode() const { return mnLayoutMode; }
189 114 : void setLayoutMode(ComplexTextLayoutMode nNew) { if(nNew != mnLayoutMode) mnLayoutMode = nNew; }
190 :
191 765 : LanguageType getLanguageType() const { return maLanguageType; }
192 116 : void setLanguageType(LanguageType aNew) { if(aNew != maLanguageType) maLanguageType = aNew; }
193 :
194 342 : PushFlags getPushFlags() const { return mnPushFlags; }
195 114 : void setPushFlags(PushFlags nNew) { if(nNew != mnPushFlags) mnPushFlags = nNew; }
196 :
197 679 : bool getLineOrFillActive() const { return (mbLineColor || mbFillColor); }
198 : };
199 : } // end of anonymous namespace
200 :
201 :
202 :
203 : namespace
204 : {
205 : /** stack for properites
206 :
207 : This class builds a stack based on the PropertyHolder
208 : class. It encapsulates the pointer/new/delete usage to
209 : make it safe and implements the push/pop as needed by a
210 : VCL Metafile interpreter. The critical part here are the
211 : flag values VCL OutputDevice uses here; not all stuff is
212 : pushed and thus needs to be copied at pop.
213 : */
214 : class PropertyHolders
215 : {
216 : private:
217 : std::vector< PropertyHolder* > maPropertyHolders;
218 :
219 : public:
220 81 : PropertyHolders()
221 81 : {
222 81 : maPropertyHolders.push_back(new PropertyHolder());
223 81 : }
224 :
225 0 : void PushDefault()
226 : {
227 0 : PropertyHolder* pNew = new PropertyHolder();
228 0 : maPropertyHolders.push_back(pNew);
229 0 : }
230 :
231 114 : void Push(PushFlags nPushFlags)
232 : {
233 114 : if(bool(nPushFlags))
234 : {
235 : OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)");
236 114 : if ( !maPropertyHolders.empty() )
237 : {
238 114 : PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back());
239 114 : pNew->setPushFlags(nPushFlags);
240 114 : maPropertyHolders.push_back(pNew);
241 : }
242 : }
243 114 : }
244 :
245 114 : void Pop()
246 : {
247 : OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)");
248 114 : const sal_uInt32 nSize(maPropertyHolders.size());
249 :
250 114 : if(nSize)
251 : {
252 114 : const PropertyHolder* pTip = maPropertyHolders.back();
253 114 : const PushFlags nPushFlags(pTip->getPushFlags());
254 :
255 114 : if(nPushFlags != PushFlags::NONE)
256 : {
257 114 : if(nSize > 1)
258 : {
259 : // copy back content for all non-set flags
260 114 : PropertyHolder* pLast = maPropertyHolders[nSize - 2];
261 :
262 114 : if(PushFlags::ALL != nPushFlags)
263 : {
264 114 : if(!(nPushFlags & PushFlags::LINECOLOR ))
265 : {
266 103 : pLast->setLineColor(pTip->getLineColor());
267 103 : pLast->setLineColorActive(pTip->getLineColorActive());
268 : }
269 114 : if(!(nPushFlags & PushFlags::FILLCOLOR ))
270 : {
271 114 : pLast->setFillColor(pTip->getFillColor());
272 114 : pLast->setFillColorActive(pTip->getFillColorActive());
273 : }
274 114 : if(!(nPushFlags & PushFlags::FONT ))
275 : {
276 114 : pLast->setFont(pTip->getFont());
277 : }
278 114 : if(!(nPushFlags & PushFlags::TEXTCOLOR ))
279 : {
280 114 : pLast->setTextColor(pTip->getTextColor());
281 114 : pLast->setTextColorActive(pTip->getTextColorActive());
282 : }
283 114 : if(!(nPushFlags & PushFlags::MAPMODE ))
284 : {
285 114 : pLast->setTransformation(pTip->getTransformation());
286 114 : pLast->setMapUnit(pTip->getMapUnit());
287 : }
288 114 : if(!(nPushFlags & PushFlags::CLIPREGION ))
289 : {
290 11 : pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
291 11 : pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
292 : }
293 114 : if(!(nPushFlags & PushFlags::RASTEROP ))
294 : {
295 114 : pLast->setRasterOp(pTip->getRasterOp());
296 : }
297 114 : if(!(nPushFlags & PushFlags::TEXTFILLCOLOR ))
298 : {
299 114 : pLast->setTextFillColor(pTip->getTextFillColor());
300 114 : pLast->setTextFillColorActive(pTip->getTextFillColorActive());
301 : }
302 114 : if(!(nPushFlags & PushFlags::TEXTALIGN ))
303 : {
304 114 : if(pLast->getFont().GetAlign() != pTip->getFont().GetAlign())
305 : {
306 0 : vcl::Font aFont(pLast->getFont());
307 0 : aFont.SetAlign(pTip->getFont().GetAlign());
308 0 : pLast->setFont(aFont);
309 : }
310 : }
311 114 : if(!(nPushFlags & PushFlags::REFPOINT ))
312 : {
313 : // not supported
314 : }
315 114 : if(!(nPushFlags & PushFlags::TEXTLINECOLOR ))
316 : {
317 114 : pLast->setTextLineColor(pTip->getTextLineColor());
318 114 : pLast->setTextLineColorActive(pTip->getTextLineColorActive());
319 : }
320 114 : if(!(nPushFlags & PushFlags::TEXTLAYOUTMODE ))
321 : {
322 114 : pLast->setLayoutMode(pTip->getLayoutMode());
323 : }
324 114 : if(!(nPushFlags & PushFlags::TEXTLANGUAGE ))
325 : {
326 114 : pLast->setLanguageType(pTip->getLanguageType());
327 : }
328 114 : if(!(nPushFlags & PushFlags::OVERLINECOLOR ))
329 : {
330 114 : pLast->setOverlineColor(pTip->getOverlineColor());
331 114 : pLast->setOverlineColorActive(pTip->getOverlineColorActive());
332 : }
333 : }
334 : }
335 : }
336 :
337 : // execute the pop
338 114 : delete maPropertyHolders.back();
339 114 : maPropertyHolders.pop_back();
340 : }
341 114 : }
342 :
343 11373 : PropertyHolder& Current()
344 : {
345 11373 : static PropertyHolder aDummy;
346 : OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)");
347 11373 : return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
348 : }
349 :
350 81 : ~PropertyHolders()
351 81 : {
352 243 : while(!maPropertyHolders.empty())
353 : {
354 81 : delete maPropertyHolders.back();
355 81 : maPropertyHolders.pop_back();
356 : }
357 81 : }
358 : };
359 : } // end of anonymous namespace
360 :
361 :
362 :
363 : namespace
364 : {
365 : /** helper to convert a vcl::Region to a B2DPolyPolygon
366 : when it does not yet contain one. In the future
367 : this may be expanded to merge the polygons created
368 : from rectangles or use a special algo to directly turn
369 : the spans of regions to a single, already merged
370 : PolyPolygon.
371 : */
372 107 : basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const vcl::Region& rRegion)
373 : {
374 107 : basegfx::B2DPolyPolygon aRetval;
375 :
376 107 : if(!rRegion.IsEmpty())
377 : {
378 107 : vcl::Region aRegion(rRegion);
379 :
380 107 : aRetval = aRegion.GetAsB2DPolyPolygon();
381 : }
382 :
383 107 : return aRetval;
384 : }
385 : } // end of anonymous namespace
386 :
387 :
388 :
389 : namespace
390 : {
391 : /** Helper class to buffer and hold a Primive target vector. It
392 : encapsulates the new/delete functionality and aloows to work
393 : on pointers of the implementation classes. All data will
394 : be converted to uno sequences of uno references when accessing the
395 : data.
396 : */
397 : class TargetHolder
398 : {
399 : private:
400 : std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargets;
401 :
402 : public:
403 205 : TargetHolder()
404 205 : : aTargets()
405 : {
406 205 : }
407 :
408 205 : ~TargetHolder()
409 205 : {
410 205 : const sal_uInt32 nCount(aTargets.size());
411 :
412 205 : for(sal_uInt32 a(0); a < nCount; a++)
413 : {
414 0 : delete aTargets[a];
415 : }
416 205 : }
417 :
418 116 : sal_uInt32 size() const
419 : {
420 116 : return aTargets.size();
421 : }
422 :
423 1770 : void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
424 : {
425 1770 : if(pCandidate)
426 : {
427 1770 : aTargets.push_back(pCandidate);
428 : }
429 1770 : }
430 :
431 194 : drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
432 : {
433 194 : const sal_uInt32 nCount(aTargets.size());
434 194 : drawinglayer::primitive2d::Primitive2DSequence xRetval(nCount);
435 :
436 1964 : for(sal_uInt32 a(0); a < nCount; a++)
437 : {
438 1770 : xRetval[a] = aTargets[a];
439 : }
440 :
441 : // All Targets were pointers, but do not need to be deleted since they
442 : // were converted to UNO API references now, so they stay as long as
443 : // referenced. Do NOT delete the C++ implementation classes here, but clear
444 : // the buffer to not delete them in the destructor.
445 194 : aTargets.clear();
446 :
447 194 : if(xRetval.hasElements() && rPropertyHolder.getClipPolyPolygonActive())
448 : {
449 109 : const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon();
450 :
451 109 : if(rClipPolyPolygon.count())
452 : {
453 : const drawinglayer::primitive2d::Primitive2DReference xMask(
454 : new drawinglayer::primitive2d::MaskPrimitive2D(
455 : rClipPolyPolygon,
456 109 : xRetval));
457 :
458 109 : xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
459 : }
460 : }
461 :
462 194 : return xRetval;
463 : }
464 : };
465 : } // end of anonymous namespace
466 :
467 :
468 :
469 : namespace
470 : {
471 : /** Helper class which builds a stack on the TargetHolder class */
472 : class TargetHolders
473 : {
474 : private:
475 : std::vector< TargetHolder* > maTargetHolders;
476 :
477 : public:
478 81 : TargetHolders()
479 81 : {
480 81 : maTargetHolders.push_back(new TargetHolder());
481 81 : }
482 :
483 197 : sal_uInt32 size() const
484 : {
485 197 : return maTargetHolders.size();
486 : }
487 :
488 116 : void Push()
489 : {
490 116 : maTargetHolders.push_back(new TargetHolder());
491 116 : }
492 :
493 116 : void Pop()
494 : {
495 : OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
496 116 : if(!maTargetHolders.empty())
497 : {
498 116 : delete maTargetHolders.back();
499 116 : maTargetHolders.pop_back();
500 : }
501 116 : }
502 :
503 1831 : TargetHolder& Current()
504 : {
505 1831 : static TargetHolder aDummy;
506 : OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
507 1831 : return maTargetHolders.empty() ? aDummy : *maTargetHolders.back();
508 : }
509 :
510 81 : ~TargetHolders()
511 81 : {
512 243 : while(!maTargetHolders.empty())
513 : {
514 81 : delete maTargetHolders.back();
515 81 : maTargetHolders.pop_back();
516 : }
517 81 : }
518 : };
519 : } // end of anonymous namespace
520 :
521 :
522 :
523 : namespace drawinglayer
524 : {
525 : namespace primitive2d
526 : {
527 : /** NonOverlappingFillGradientPrimitive2D class
528 :
529 : This is a special version of the FillGradientPrimitive2D which decomposes
530 : to a non-overlapping geometry version of the gradient. This needs to be
531 : used to support the old XOR paint-'trick'.
532 :
533 : It does not need an own identifier since a renderer who wants to interpret
534 : it itself may do so. It just overrides the decomposition of the C++
535 : implementation class to do an alternative decomposition.
536 : */
537 0 : class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
538 : {
539 : protected:
540 : /// local decomposition.
541 : virtual Primitive2DSequence create2DDecomposition(
542 : const geometry::ViewInformation2D& rViewInformation) const SAL_OVERRIDE;
543 :
544 : public:
545 : /// constructor
546 0 : NonOverlappingFillGradientPrimitive2D(
547 : const basegfx::B2DRange& rObjectRange,
548 : const attribute::FillGradientAttribute& rFillGradient)
549 0 : : FillGradientPrimitive2D(rObjectRange, rFillGradient)
550 : {
551 0 : }
552 : };
553 :
554 0 : Primitive2DSequence NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
555 : const geometry::ViewInformation2D& /*rViewInformation*/) const
556 : {
557 0 : if(!getFillGradient().isDefault())
558 : {
559 0 : return createFill(false);
560 : }
561 : else
562 : {
563 0 : return Primitive2DSequence();
564 : }
565 : }
566 : } // end of namespace primitive2d
567 : } // end of namespace drawinglayer
568 :
569 :
570 :
571 : namespace
572 : {
573 : /** helper to convert a MapMode to a transformation */
574 2 : basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
575 : {
576 2 : basegfx::B2DHomMatrix aMapping;
577 4 : const Fraction aNoScale(1, 1);
578 2 : const Point& rOrigin(rMapMode.GetOrigin());
579 :
580 2 : if(0 != rOrigin.X() || 0 != rOrigin.Y())
581 : {
582 2 : aMapping.translate(rOrigin.X(), rOrigin.Y());
583 : }
584 :
585 2 : if(rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
586 : {
587 : aMapping.scale(
588 0 : double(rMapMode.GetScaleX()),
589 0 : double(rMapMode.GetScaleY()));
590 : }
591 :
592 4 : return aMapping;
593 : }
594 :
595 : /** helper to create a PointArrayPrimitive2D based on current context */
596 0 : void createPointArrayPrimitive(
597 : const std::vector< basegfx::B2DPoint >& rPositions,
598 : TargetHolder& rTarget,
599 : PropertyHolder& rProperties,
600 : const basegfx::BColor& rBColor)
601 : {
602 0 : if(!rPositions.empty())
603 : {
604 0 : if(rProperties.getTransformation().isIdentity())
605 : {
606 : rTarget.append(
607 : new drawinglayer::primitive2d::PointArrayPrimitive2D(
608 : rPositions,
609 0 : rBColor));
610 : }
611 : else
612 : {
613 0 : std::vector< basegfx::B2DPoint > aPositions(rPositions);
614 :
615 0 : for(size_t a(0); a < aPositions.size(); a++)
616 : {
617 0 : aPositions[a] = rProperties.getTransformation() * aPositions[a];
618 : }
619 :
620 : rTarget.append(
621 : new drawinglayer::primitive2d::PointArrayPrimitive2D(
622 : aPositions,
623 0 : rBColor));
624 : }
625 : }
626 0 : }
627 :
628 : /** helper to create a PolygonHairlinePrimitive2D based on current context */
629 272 : void createHairlinePrimitive(
630 : const basegfx::B2DPolygon& rLinePolygon,
631 : TargetHolder& rTarget,
632 : PropertyHolder& rProperties)
633 : {
634 272 : if(rLinePolygon.count())
635 : {
636 272 : basegfx::B2DPolygon aLinePolygon(rLinePolygon);
637 272 : aLinePolygon.transform(rProperties.getTransformation());
638 : rTarget.append(
639 : new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
640 : aLinePolygon,
641 272 : rProperties.getLineColor()));
642 : }
643 272 : }
644 :
645 : /** helper to create a PolyPolygonColorPrimitive2D based on current context */
646 679 : void createFillPrimitive(
647 : const basegfx::B2DPolyPolygon& rFillPolyPolygon,
648 : TargetHolder& rTarget,
649 : PropertyHolder& rProperties)
650 : {
651 679 : if(rFillPolyPolygon.count())
652 : {
653 679 : basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon);
654 679 : aFillPolyPolygon.transform(rProperties.getTransformation());
655 : rTarget.append(
656 : new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
657 : aFillPolyPolygon,
658 679 : rProperties.getFillColor()));
659 : }
660 679 : }
661 :
662 : /** helper to create a PolygonStrokePrimitive2D based on current context */
663 34 : void createLinePrimitive(
664 : const basegfx::B2DPolygon& rLinePolygon,
665 : const LineInfo& rLineInfo,
666 : TargetHolder& rTarget,
667 : PropertyHolder& rProperties)
668 : {
669 34 : if(rLinePolygon.count())
670 : {
671 34 : const bool bDashDotUsed(LINE_DASH == rLineInfo.GetStyle());
672 34 : const bool bWidthUsed(rLineInfo.GetWidth() > 1);
673 :
674 34 : if(bDashDotUsed || bWidthUsed)
675 : {
676 11 : basegfx::B2DPolygon aLinePolygon(rLinePolygon);
677 11 : aLinePolygon.transform(rProperties.getTransformation());
678 : const drawinglayer::attribute::LineAttribute aLineAttribute(
679 11 : rProperties.getLineColor(),
680 11 : bWidthUsed ? rLineInfo.GetWidth() : 0.0,
681 : rLineInfo.GetLineJoin(),
682 44 : rLineInfo.GetLineCap());
683 :
684 11 : if(bDashDotUsed)
685 : {
686 0 : ::std::vector< double > fDotDashArray;
687 0 : const double fDashLen(rLineInfo.GetDashLen());
688 0 : const double fDotLen(rLineInfo.GetDotLen());
689 0 : const double fDistance(rLineInfo.GetDistance());
690 :
691 0 : for(sal_uInt16 a(0); a < rLineInfo.GetDashCount(); a++)
692 : {
693 0 : fDotDashArray.push_back(fDashLen);
694 0 : fDotDashArray.push_back(fDistance);
695 : }
696 :
697 0 : for(sal_uInt16 b(0); b < rLineInfo.GetDotCount(); b++)
698 : {
699 0 : fDotDashArray.push_back(fDotLen);
700 0 : fDotDashArray.push_back(fDistance);
701 : }
702 :
703 0 : const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
704 : const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
705 : fDotDashArray,
706 0 : fAccumulated);
707 :
708 : rTarget.append(
709 : new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
710 : aLinePolygon,
711 : aLineAttribute,
712 0 : aStrokeAttribute));
713 : }
714 : else
715 : {
716 : rTarget.append(
717 : new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
718 : aLinePolygon,
719 11 : aLineAttribute));
720 11 : }
721 : }
722 : else
723 : {
724 23 : createHairlinePrimitive(rLinePolygon, rTarget, rProperties);
725 : }
726 : }
727 34 : }
728 :
729 : /** helper to create needed line and fill primitives based on current context */
730 374 : void createHairlineAndFillPrimitive(
731 : const basegfx::B2DPolygon& rPolygon,
732 : TargetHolder& rTarget,
733 : PropertyHolder& rProperties)
734 : {
735 374 : if(rProperties.getFillColorActive())
736 : {
737 374 : createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties);
738 : }
739 :
740 374 : if(rProperties.getLineColorActive())
741 : {
742 0 : createHairlinePrimitive(rPolygon, rTarget, rProperties);
743 : }
744 374 : }
745 :
746 : /** helper to create needed line and fill primitives based on current context */
747 305 : void createHairlineAndFillPrimitive(
748 : const basegfx::B2DPolyPolygon& rPolyPolygon,
749 : TargetHolder& rTarget,
750 : PropertyHolder& rProperties)
751 : {
752 305 : if(rProperties.getFillColorActive())
753 : {
754 305 : createFillPrimitive(rPolyPolygon, rTarget, rProperties);
755 : }
756 :
757 305 : if(rProperties.getLineColorActive())
758 : {
759 498 : for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++)
760 : {
761 249 : createHairlinePrimitive(rPolyPolygon.getB2DPolygon(a), rTarget, rProperties);
762 : }
763 : }
764 305 : }
765 :
766 : /** helper to create DiscreteBitmapPrimitive2D based on current context.
767 : The DiscreteBitmapPrimitive2D is especially created for this usage
768 : since no other usage defines a bitmap visualisation based on top-left
769 : position and size in pixels. At the end it will create a view-dependent
770 : transformed embedding of a BitmapPrimitive2D.
771 : */
772 0 : void createBitmapExPrimitive(
773 : const BitmapEx& rBitmapEx,
774 : const Point& rPoint,
775 : TargetHolder& rTarget,
776 : PropertyHolder& rProperties)
777 : {
778 0 : if(!rBitmapEx.IsEmpty())
779 : {
780 0 : basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y());
781 0 : aPoint = rProperties.getTransformation() * aPoint;
782 :
783 : rTarget.append(
784 : new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
785 : rBitmapEx,
786 0 : aPoint));
787 : }
788 0 : }
789 :
790 : /** helper to create BitmapPrimitive2D based on current context */
791 44 : void createBitmapExPrimitive(
792 : const BitmapEx& rBitmapEx,
793 : const Point& rPoint,
794 : const Size& rSize,
795 : TargetHolder& rTarget,
796 : PropertyHolder& rProperties)
797 : {
798 44 : if(!rBitmapEx.IsEmpty())
799 : {
800 44 : basegfx::B2DHomMatrix aObjectTransform;
801 :
802 44 : aObjectTransform.set(0, 0, rSize.Width());
803 44 : aObjectTransform.set(1, 1, rSize.Height());
804 44 : aObjectTransform.set(0, 2, rPoint.X());
805 44 : aObjectTransform.set(1, 2, rPoint.Y());
806 :
807 44 : aObjectTransform = rProperties.getTransformation() * aObjectTransform;
808 :
809 : rTarget.append(
810 : new drawinglayer::primitive2d::BitmapPrimitive2D(
811 : rBitmapEx,
812 44 : aObjectTransform));
813 : }
814 44 : }
815 :
816 : /** helper to create a regular BotmapEx from a MaskAction (definitions
817 : which use a bitmap without transparence but define one of the colors as
818 : transparent)
819 : */
820 0 : BitmapEx createMaskBmpEx(const Bitmap& rBitmap, const Color& rMaskColor)
821 : {
822 0 : const Color aWhite(COL_WHITE);
823 0 : BitmapPalette aBiLevelPalette(2);
824 :
825 0 : aBiLevelPalette[0] = aWhite;
826 0 : aBiLevelPalette[1] = rMaskColor;
827 :
828 0 : Bitmap aMask(rBitmap.CreateMask(aWhite));
829 0 : Bitmap aSolid(rBitmap.GetSizePixel(), 1, &aBiLevelPalette);
830 :
831 0 : aSolid.Erase(rMaskColor);
832 :
833 0 : return BitmapEx(aSolid, aMask);
834 : }
835 :
836 : /** helper to convert from a VCL Gradient definition to the corresponding
837 : data for primitive representation
838 : */
839 0 : drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient)
840 : {
841 0 : const Color aStartColor(rGradient.GetStartColor());
842 0 : const sal_uInt16 nStartIntens(rGradient.GetStartIntensity());
843 0 : basegfx::BColor aStart(aStartColor.getBColor());
844 :
845 0 : if(nStartIntens != 100)
846 : {
847 0 : const basegfx::BColor aBlack;
848 0 : aStart = interpolate(aBlack, aStart, (double)nStartIntens * 0.01);
849 : }
850 :
851 0 : const Color aEndColor(rGradient.GetEndColor());
852 0 : const sal_uInt16 nEndIntens(rGradient.GetEndIntensity());
853 0 : basegfx::BColor aEnd(aEndColor.getBColor());
854 :
855 0 : if(nEndIntens != 100)
856 : {
857 0 : const basegfx::BColor aBlack;
858 0 : aEnd = interpolate(aBlack, aEnd, (double)nEndIntens * 0.01);
859 : }
860 :
861 0 : drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GRADIENTSTYLE_RECT);
862 :
863 0 : switch(rGradient.GetStyle())
864 : {
865 : case GradientStyle_LINEAR :
866 : {
867 0 : aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_LINEAR;
868 0 : break;
869 : }
870 : case GradientStyle_AXIAL :
871 : {
872 0 : aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_AXIAL;
873 0 : break;
874 : }
875 : case GradientStyle_RADIAL :
876 : {
877 0 : aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RADIAL;
878 0 : break;
879 : }
880 : case GradientStyle_ELLIPTICAL :
881 : {
882 0 : aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_ELLIPTICAL;
883 0 : break;
884 : }
885 : case GradientStyle_SQUARE :
886 : {
887 0 : aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_SQUARE;
888 0 : break;
889 : }
890 : default : // GradientStyle_RECT
891 : {
892 0 : aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RECT;
893 0 : break;
894 : }
895 : }
896 :
897 : return drawinglayer::attribute::FillGradientAttribute(
898 : aGradientStyle,
899 0 : (double)rGradient.GetBorder() * 0.01,
900 0 : (double)rGradient.GetOfsX() * 0.01,
901 0 : (double)rGradient.GetOfsY() * 0.01,
902 0 : (double)rGradient.GetAngle() * F_PI1800,
903 : aStart,
904 : aEnd,
905 0 : rGradient.GetSteps());
906 : }
907 :
908 : /** helper to convert from a VCL Hatch definition to the corresponding
909 : data for primitive representation
910 : */
911 0 : drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch)
912 : {
913 0 : drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HATCHSTYLE_SINGLE);
914 :
915 0 : switch(rHatch.GetStyle())
916 : {
917 : default : // case HATCH_SINGLE :
918 : {
919 0 : aHatchStyle = drawinglayer::attribute::HATCHSTYLE_SINGLE;
920 0 : break;
921 : }
922 : case HATCH_DOUBLE :
923 : {
924 0 : aHatchStyle = drawinglayer::attribute::HATCHSTYLE_DOUBLE;
925 0 : break;
926 : }
927 : case HATCH_TRIPLE :
928 : {
929 0 : aHatchStyle = drawinglayer::attribute::HATCHSTYLE_TRIPLE;
930 0 : break;
931 : }
932 : }
933 :
934 : return drawinglayer::attribute::FillHatchAttribute(
935 : aHatchStyle,
936 0 : (double)rHatch.GetDistance(),
937 0 : (double)rHatch.GetAngle() * F_PI1800,
938 0 : rHatch.GetColor().getBColor(),
939 : 3, // same default as VCL, a minimum of three discrete units (pixels) offset
940 0 : false);
941 : }
942 :
943 : /** helper to take needed action on ClipRegion change. This method needs to be called
944 : on any vcl::Region change, e.g. at the obvious actions doing this, but also at pop-calls
945 : which change the vcl::Region of the current context. It takes care of creating the
946 : current embedded context, set the new vcl::Region at the context and possibly prepare
947 : a new target for including new geometry into the current region
948 : */
949 160 : void HandleNewClipRegion(
950 : const basegfx::B2DPolyPolygon& rClipPolyPolygon,
951 : TargetHolders& rTargetHolders,
952 : PropertyHolders& rPropertyHolders)
953 : {
954 160 : const bool bNewActive(rClipPolyPolygon.count());
955 :
956 : // #i108636# The handlig of new ClipPolyPolygons was not done as good as possible
957 : // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
958 : // initially and then using a lot of push/pop actions, the pop always leads
959 : // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
960 : // of the properties next on the stack.
961 :
962 : // This ClipPolyPolygon is identical to the current one, so there is no need to
963 : // create a MaskPrimitive2D containing the up-to-now created primitives, but
964 : // this was done before. While this does not lead to wrong primitive
965 : // representations of the metafile data, it creates unnecessarily expensive
966 : // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
967 : // solves the problem.
968 :
969 160 : if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive)
970 : {
971 : // no active ClipPolyPolygon exchanged by no new one, done
972 0 : return;
973 : }
974 :
975 160 : if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive)
976 : {
977 : // active ClipPolyPolygon and new active ClipPolyPolygon
978 64 : if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon)
979 : {
980 : // new is the same as old, done
981 0 : return;
982 : }
983 : }
984 :
985 : // Here the old and the new are definitively different, maybe
986 : // old one and/or new one is not active.
987 :
988 : // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
989 : // belong to this active ClipPolyPolygon. These need to be embedded to a
990 : // MaskPrimitive2D accordingly.
991 160 : if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1)
992 : {
993 112 : drawinglayer::primitive2d::Primitive2DSequence aSubContent;
994 :
995 224 : if(rPropertyHolders.Current().getClipPolyPolygon().count()
996 112 : && rTargetHolders.Current().size())
997 : {
998 218 : aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
999 218 : rPropertyHolders.Current());
1000 : }
1001 :
1002 112 : rTargetHolders.Pop();
1003 :
1004 112 : if(aSubContent.hasElements())
1005 : {
1006 109 : rTargetHolders.Current().append(
1007 : new drawinglayer::primitive2d::GroupPrimitive2D(
1008 218 : aSubContent));
1009 112 : }
1010 : }
1011 :
1012 : // apply new settings to current properties by setting
1013 : // the new region now
1014 160 : rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive);
1015 :
1016 160 : if(bNewActive)
1017 : {
1018 112 : rPropertyHolders.Current().setClipPolyPolygon(rClipPolyPolygon);
1019 :
1020 : // prepare new content holder for new active region
1021 112 : rTargetHolders.Push();
1022 : }
1023 : }
1024 :
1025 : /** helper to handle the change of RasterOp. It takes care of encapsulating all current
1026 : geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
1027 : change. It will also start a new geometry target to embrace to the new RasterOp if
1028 : a changuing RasterOp is used. Currently, ROP_XOR and ROP_INVERT are supported using
1029 : InvertPrimitive2D, and ROP_0 by using a ModifiedColorPrimitive2D to force to black paint
1030 : */
1031 687 : void HandleNewRasterOp(
1032 : RasterOp aRasterOp,
1033 : TargetHolders& rTargetHolders,
1034 : PropertyHolders& rPropertyHolders)
1035 : {
1036 : // check if currently active
1037 687 : if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1)
1038 : {
1039 4 : drawinglayer::primitive2d::Primitive2DSequence aSubContent;
1040 :
1041 4 : if(rTargetHolders.Current().size())
1042 : {
1043 4 : aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
1044 : }
1045 :
1046 4 : rTargetHolders.Pop();
1047 :
1048 4 : if(aSubContent.hasElements())
1049 : {
1050 4 : if(rPropertyHolders.Current().isRasterOpForceBlack())
1051 : {
1052 : // force content to black
1053 0 : rTargetHolders.Current().append(
1054 : new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
1055 : aSubContent,
1056 : basegfx::BColorModifierSharedPtr(
1057 : new basegfx::BColorModifier_replace(
1058 0 : basegfx::BColor(0.0, 0.0, 0.0)))));
1059 : }
1060 : else // if(rPropertyHolders.Current().isRasterOpInvert())
1061 : {
1062 : // invert content
1063 4 : rTargetHolders.Current().append(
1064 : new drawinglayer::primitive2d::InvertPrimitive2D(
1065 8 : aSubContent));
1066 : }
1067 4 : }
1068 : }
1069 :
1070 : // apply new settings
1071 687 : rPropertyHolders.Current().setRasterOp(aRasterOp);
1072 :
1073 : // check if now active
1074 687 : if(rPropertyHolders.Current().isRasterOpActive())
1075 : {
1076 : // prepare new content holder for new invert
1077 4 : rTargetHolders.Push();
1078 : }
1079 687 : }
1080 :
1081 : /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1082 : It is a quite mighty action. This helper is for simple color filled background.
1083 : */
1084 0 : drawinglayer::primitive2d::BasePrimitive2D* CreateColorWallpaper(
1085 : const basegfx::B2DRange& rRange,
1086 : const basegfx::BColor& rColor,
1087 : PropertyHolder& rPropertyHolder)
1088 : {
1089 0 : basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(rRange));
1090 0 : aOutline.transform(rPropertyHolder.getTransformation());
1091 :
1092 : return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1093 : basegfx::B2DPolyPolygon(aOutline),
1094 0 : rColor);
1095 : }
1096 :
1097 : /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1098 : It is a quite mighty action. This helper is for gradient filled background.
1099 : */
1100 0 : drawinglayer::primitive2d::BasePrimitive2D* CreateGradientWallpaper(
1101 : const basegfx::B2DRange& rRange,
1102 : const Gradient& rGradient,
1103 : PropertyHolder& rPropertyHolder)
1104 : {
1105 0 : const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
1106 :
1107 0 : if(aAttribute.getStartColor() == aAttribute.getEndColor())
1108 : {
1109 : // not really a gradient. Create filled rectangle
1110 0 : return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder);
1111 : }
1112 : else
1113 : {
1114 : // really a gradient
1115 : drawinglayer::primitive2d::BasePrimitive2D* pRetval =
1116 : new drawinglayer::primitive2d::FillGradientPrimitive2D(
1117 : rRange,
1118 0 : aAttribute);
1119 :
1120 0 : if(!rPropertyHolder.getTransformation().isIdentity())
1121 : {
1122 0 : const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval);
1123 0 : const drawinglayer::primitive2d::Primitive2DSequence xSeq(&xPrim, 1);
1124 :
1125 : pRetval = new drawinglayer::primitive2d::TransformPrimitive2D(
1126 : rPropertyHolder.getTransformation(),
1127 0 : xSeq);
1128 : }
1129 :
1130 0 : return pRetval;
1131 0 : }
1132 : }
1133 :
1134 : /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1135 : It is a quite mighty action. This helper decides if color and/or gradient
1136 : background is needed for the wanted bitmap fill and then creates the needed
1137 : WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
1138 : takes over all needed logic of orientations and tiling.
1139 : */
1140 0 : void CreateAndAppendBitmapWallpaper(
1141 : basegfx::B2DRange aWallpaperRange,
1142 : const Wallpaper& rWallpaper,
1143 : TargetHolder& rTarget,
1144 : PropertyHolder& rProperty)
1145 : {
1146 0 : const BitmapEx aBitmapEx(rWallpaper.GetBitmap());
1147 0 : const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
1148 :
1149 : // if bitmap visualisation is transparent, maybe background
1150 : // needs to be filled. Create background
1151 0 : if(aBitmapEx.IsTransparent()
1152 0 : || (WALLPAPER_TILE != eWallpaperStyle && WALLPAPER_SCALE != eWallpaperStyle))
1153 : {
1154 0 : if(rWallpaper.IsGradient())
1155 : {
1156 : rTarget.append(
1157 : CreateGradientWallpaper(
1158 : aWallpaperRange,
1159 : rWallpaper.GetGradient(),
1160 0 : rProperty));
1161 : }
1162 0 : else if(!rWallpaper.GetColor().GetTransparency())
1163 : {
1164 : rTarget.append(
1165 : CreateColorWallpaper(
1166 : aWallpaperRange,
1167 0 : rWallpaper.GetColor().getBColor(),
1168 0 : rProperty));
1169 : }
1170 : }
1171 :
1172 : // use wallpaper rect if set
1173 0 : if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
1174 : {
1175 : aWallpaperRange = basegfx::B2DRange(
1176 0 : rWallpaper.GetRect().Left(), rWallpaper.GetRect().Top(),
1177 0 : rWallpaper.GetRect().Right(), rWallpaper.GetRect().Bottom());
1178 : }
1179 :
1180 : drawinglayer::primitive2d::BasePrimitive2D* pBitmapWallpaperFill =
1181 : new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
1182 : aWallpaperRange,
1183 : aBitmapEx,
1184 0 : eWallpaperStyle);
1185 :
1186 0 : if(rProperty.getTransformation().isIdentity())
1187 : {
1188 : // add directly
1189 0 : rTarget.append(pBitmapWallpaperFill);
1190 : }
1191 : else
1192 : {
1193 : // when a transformation is set, embed to it
1194 0 : const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill);
1195 :
1196 : rTarget.append(
1197 : new drawinglayer::primitive2d::TransformPrimitive2D(
1198 : rProperty.getTransformation(),
1199 0 : drawinglayer::primitive2d::Primitive2DSequence(&xPrim, 1)));
1200 0 : }
1201 0 : }
1202 :
1203 : /** helper to decide UnderlineAbove for text primitives */
1204 8 : bool isUnderlineAbove(const vcl::Font& rFont)
1205 : {
1206 8 : if(!rFont.IsVertical())
1207 : {
1208 8 : return false;
1209 : }
1210 :
1211 0 : if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
1212 : {
1213 : // the underline is right for Japanese only
1214 0 : return true;
1215 : }
1216 :
1217 0 : return false;
1218 : }
1219 :
1220 651 : void createFontAttributeTransformAndAlignment(
1221 : drawinglayer::attribute::FontAttribute& rFontAttribute,
1222 : basegfx::B2DHomMatrix& rTextTransform,
1223 : basegfx::B2DVector& rAlignmentOffset,
1224 : PropertyHolder& rProperty)
1225 : {
1226 651 : const vcl::Font& rFont = rProperty.getFont();
1227 651 : basegfx::B2DVector aFontScaling;
1228 :
1229 1953 : rFontAttribute = drawinglayer::attribute::FontAttribute(
1230 : drawinglayer::primitive2d::getFontAttributeFromVclFont(
1231 : aFontScaling,
1232 : rFont,
1233 651 : bool(rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL),
1234 1302 : bool(rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG)));
1235 :
1236 : // add FontScaling
1237 651 : rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
1238 :
1239 : // take text align into account
1240 651 : if(ALIGN_BASELINE != rFont.GetAlign())
1241 : {
1242 31 : drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
1243 31 : aTextLayouterDevice.setFont(rFont);
1244 :
1245 31 : if(ALIGN_TOP == rFont.GetAlign())
1246 : {
1247 31 : rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent());
1248 : }
1249 : else // ALIGN_BOTTOM
1250 : {
1251 0 : rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent());
1252 : }
1253 :
1254 31 : rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY());
1255 : }
1256 :
1257 : // add FontRotation (if used)
1258 651 : if(rFont.GetOrientation())
1259 : {
1260 0 : rTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
1261 651 : }
1262 651 : }
1263 :
1264 : /** helper which takes complete care for creating the needed text primitives. It
1265 : takes care of decorated stuff and all the geometry adaptions needed
1266 : */
1267 651 : void processMetaTextAction(
1268 : const Point& rTextStartPosition,
1269 : const OUString& rText,
1270 : sal_uInt16 nTextStart,
1271 : sal_uInt16 nTextLength,
1272 : const ::std::vector< double >& rDXArray,
1273 : TargetHolder& rTarget,
1274 : PropertyHolder& rProperty)
1275 : {
1276 651 : drawinglayer::primitive2d::BasePrimitive2D* pResult = 0;
1277 651 : const vcl::Font& rFont = rProperty.getFont();
1278 651 : basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
1279 :
1280 651 : if(nTextLength)
1281 : {
1282 651 : drawinglayer::attribute::FontAttribute aFontAttribute;
1283 1302 : basegfx::B2DHomMatrix aTextTransform;
1284 :
1285 : // fill parameters derived from current font
1286 : createFontAttributeTransformAndAlignment(
1287 : aFontAttribute,
1288 : aTextTransform,
1289 : aAlignmentOffset,
1290 651 : rProperty);
1291 :
1292 : // add TextStartPosition
1293 651 : aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
1294 :
1295 : // prepare FontColor and Locale
1296 1302 : const basegfx::BColor aFontColor(rProperty.getTextColor());
1297 651 : const Color aFillColor(rFont.GetFillColor());
1298 1302 : const com::sun::star::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale());
1299 651 : const bool bWordLineMode(rFont.IsWordLineMode());
1300 :
1301 : const bool bDecoratedIsNeeded(
1302 651 : UNDERLINE_NONE != rFont.GetOverline()
1303 651 : || UNDERLINE_NONE != rFont.GetUnderline()
1304 643 : || STRIKEOUT_NONE != rFont.GetStrikeout()
1305 643 : || EMPHASISMARK_NONE != (rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
1306 643 : || RELIEF_NONE != rFont.GetRelief()
1307 643 : || rFont.IsShadow()
1308 1294 : || bWordLineMode);
1309 :
1310 651 : if(bDecoratedIsNeeded)
1311 : {
1312 : // prepare overline, underline and srikeout data
1313 8 : const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetOverline()));
1314 8 : const drawinglayer::primitive2d::TextLine eFontUnderline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetUnderline()));
1315 8 : const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
1316 :
1317 : // check UndelineAbove
1318 8 : const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && isUnderlineAbove(rFont));
1319 :
1320 : // prepare emphasis mark data
1321 8 : drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
1322 :
1323 8 : switch(rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
1324 : {
1325 0 : case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
1326 0 : case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
1327 0 : case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
1328 0 : case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
1329 : }
1330 :
1331 8 : const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
1332 8 : const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
1333 :
1334 : // prepare font relief data
1335 8 : drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
1336 :
1337 8 : switch(rFont.GetRelief())
1338 : {
1339 0 : case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
1340 0 : case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
1341 8 : default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
1342 : }
1343 :
1344 : // prepare shadow/outline data
1345 8 : const bool bShadow(rFont.IsShadow());
1346 :
1347 : // TextDecoratedPortionPrimitive2D is needed, create one
1348 : pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1349 :
1350 : // attributes for TextSimplePortionPrimitive2D
1351 : aTextTransform,
1352 : rText,
1353 : nTextStart,
1354 : nTextLength,
1355 : rDXArray,
1356 : aFontAttribute,
1357 : aLocale,
1358 : aFontColor,
1359 : aFillColor,
1360 :
1361 : // attributes for TextDecoratedPortionPrimitive2D
1362 8 : rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
1363 8 : rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
1364 : eFontOverline,
1365 : eFontUnderline,
1366 : bUnderlineAbove,
1367 : eTextStrikeout,
1368 : bWordLineMode,
1369 : eTextEmphasisMark,
1370 : bEmphasisMarkAbove,
1371 : bEmphasisMarkBelow,
1372 : eTextRelief,
1373 16 : bShadow);
1374 : }
1375 : else
1376 : {
1377 : // TextSimplePortionPrimitive2D is enough
1378 : pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1379 : aTextTransform,
1380 : rText,
1381 : nTextStart,
1382 : nTextLength,
1383 : rDXArray,
1384 : aFontAttribute,
1385 : aLocale,
1386 643 : aFontColor);
1387 651 : }
1388 : }
1389 :
1390 651 : if(pResult && rProperty.getTextFillColorActive())
1391 : {
1392 : // text background is requested, add and encapsulate both to new primitive
1393 0 : drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
1394 0 : aTextLayouterDevice.setFont(rFont);
1395 :
1396 : // get text width
1397 0 : double fTextWidth(0.0);
1398 :
1399 0 : if(rDXArray.empty())
1400 : {
1401 0 : fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
1402 : }
1403 : else
1404 : {
1405 0 : fTextWidth = rDXArray.back();
1406 : }
1407 :
1408 0 : if(basegfx::fTools::more(fTextWidth, 0.0))
1409 : {
1410 : // build text range
1411 : const basegfx::B2DRange aTextRange(
1412 0 : 0.0, -aTextLayouterDevice.getFontAscent(),
1413 0 : fTextWidth, aTextLayouterDevice.getFontDescent());
1414 :
1415 : // create Transform
1416 0 : basegfx::B2DHomMatrix aTextTransform;
1417 :
1418 0 : aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY());
1419 :
1420 0 : if(rFont.GetOrientation())
1421 : {
1422 0 : aTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
1423 : }
1424 :
1425 0 : aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
1426 :
1427 : // prepare Primitive2DSequence, put text in foreground
1428 0 : drawinglayer::primitive2d::Primitive2DSequence aSequence(2);
1429 0 : aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult);
1430 :
1431 : // prepare filled polygon
1432 0 : basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aTextRange));
1433 0 : aOutline.transform(aTextTransform);
1434 :
1435 0 : aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(
1436 : new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1437 : basegfx::B2DPolyPolygon(aOutline),
1438 0 : rProperty.getTextFillColor()));
1439 :
1440 : // set as group at pResult
1441 0 : pResult = new drawinglayer::primitive2d::GroupPrimitive2D(aSequence);
1442 0 : }
1443 : }
1444 :
1445 651 : if(pResult)
1446 : {
1447 : // add created text primitive to target
1448 651 : if(rProperty.getTransformation().isIdentity())
1449 : {
1450 645 : rTarget.append(pResult);
1451 : }
1452 : else
1453 : {
1454 : // when a transformation is set, embed to it
1455 6 : const drawinglayer::primitive2d::Primitive2DReference aReference(pResult);
1456 :
1457 : rTarget.append(
1458 : new drawinglayer::primitive2d::TransformPrimitive2D(
1459 : rProperty.getTransformation(),
1460 6 : drawinglayer::primitive2d::Primitive2DSequence(&aReference, 1)));
1461 : }
1462 651 : }
1463 651 : }
1464 :
1465 : /** helper which takes complete care for creating the needed textLine primitives */
1466 0 : void proccessMetaTextLineAction(
1467 : const MetaTextLineAction& rAction,
1468 : TargetHolder& rTarget,
1469 : PropertyHolder& rProperty)
1470 : {
1471 0 : const double fLineWidth(fabs((double)rAction.GetWidth()));
1472 :
1473 0 : if(fLineWidth > 0.0)
1474 : {
1475 0 : const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetOverline()));
1476 0 : const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetUnderline()));
1477 0 : const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout()));
1478 :
1479 0 : const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode);
1480 0 : const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode);
1481 0 : const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout);
1482 :
1483 0 : if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
1484 : {
1485 0 : std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector;
1486 0 : basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
1487 0 : drawinglayer::attribute::FontAttribute aFontAttribute;
1488 0 : basegfx::B2DHomMatrix aTextTransform;
1489 :
1490 : // fill parameters derived from current font
1491 : createFontAttributeTransformAndAlignment(
1492 : aFontAttribute,
1493 : aTextTransform,
1494 : aAlignmentOffset,
1495 0 : rProperty);
1496 :
1497 : // add TextStartPosition
1498 0 : aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y());
1499 :
1500 : // prepare TextLayouter (used in most cases)
1501 0 : drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
1502 0 : aTextLayouter.setFont(rProperty.getFont());
1503 :
1504 0 : if(bOverlineUsed)
1505 : {
1506 : // create primitive geometry for overline
1507 : aTargetVector.push_back(
1508 : new drawinglayer::primitive2d::TextLinePrimitive2D(
1509 : aTextTransform,
1510 : fLineWidth,
1511 : aTextLayouter.getOverlineOffset(),
1512 : aTextLayouter.getOverlineHeight(),
1513 : aOverlineMode,
1514 0 : rProperty.getOverlineColor()));
1515 : }
1516 :
1517 0 : if(bUnderlineUsed)
1518 : {
1519 : // create primitive geometry for underline
1520 : aTargetVector.push_back(
1521 : new drawinglayer::primitive2d::TextLinePrimitive2D(
1522 : aTextTransform,
1523 : fLineWidth,
1524 : aTextLayouter.getUnderlineOffset(),
1525 : aTextLayouter.getUnderlineHeight(),
1526 : aUnderlineMode,
1527 0 : rProperty.getTextLineColor()));
1528 : }
1529 :
1530 0 : if(bStrikeoutUsed)
1531 : {
1532 : // create primitive geometry for strikeout
1533 0 : if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout
1534 0 : || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout)
1535 : {
1536 : // strikeout with character
1537 : const sal_Unicode aStrikeoutChar(
1538 0 : drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
1539 : const com::sun::star::lang::Locale aLocale(LanguageTag(
1540 0 : rProperty.getLanguageType()).getLocale());
1541 :
1542 : aTargetVector.push_back(
1543 : new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
1544 : aTextTransform,
1545 : fLineWidth,
1546 : rProperty.getTextColor(),
1547 : aStrikeoutChar,
1548 : aFontAttribute,
1549 0 : aLocale));
1550 : }
1551 : else
1552 : {
1553 : // strikeout with geometry
1554 : aTargetVector.push_back(
1555 : new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
1556 : aTextTransform,
1557 : fLineWidth,
1558 : rProperty.getTextColor(),
1559 : aTextLayouter.getUnderlineHeight(),
1560 : aTextLayouter.getStrikeoutOffset(),
1561 0 : aTextStrikeout));
1562 : }
1563 : }
1564 :
1565 0 : if(!aTargetVector.empty())
1566 : {
1567 : // add created text primitive to target
1568 0 : if(rProperty.getTransformation().isIdentity())
1569 : {
1570 0 : for(size_t a(0); a < aTargetVector.size(); a++)
1571 : {
1572 0 : rTarget.append(aTargetVector[a]);
1573 : }
1574 : }
1575 : else
1576 : {
1577 : // when a transformation is set, embed to it
1578 0 : drawinglayer::primitive2d::Primitive2DSequence xTargets(aTargetVector.size());
1579 :
1580 0 : for(size_t a(0); a < aTargetVector.size(); a++)
1581 : {
1582 0 : xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]);
1583 : }
1584 :
1585 : rTarget.append(
1586 : new drawinglayer::primitive2d::TransformPrimitive2D(
1587 : rProperty.getTransformation(),
1588 0 : xTargets));
1589 : }
1590 0 : }
1591 : }
1592 : }
1593 :
1594 0 : }
1595 :
1596 : /** This is the main interpreter method. It is designed to handle the given Metafile
1597 : completely inside the given context and target. It may use and modify the context and
1598 : target. This design allows to call itself recursively which adapted contexts and
1599 : targets as e.g. needed for the MetaActionType::FLOATTRANSPARENT where the content is expressed
1600 : as a metafile as sub-content.
1601 :
1602 : This interpreter is as free of VCL functionality as possible. It uses VCL data classes
1603 : (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
1604 : as most other MetaFile interpreters/exporters do to hold and work with the current context.
1605 : This is necessary to be able to get away from the strong internal VCL-binding.
1606 :
1607 : It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
1608 : where possible (which is not trivial with the possible line geometry definitions).
1609 :
1610 : It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
1611 : ClipRegions with (where possible) high precision by using the best possible data quality
1612 : from the Region. The vcl::Region is unavoidable as data container, but nowadays allows the transport
1613 : of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
1614 : vcl::Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
1615 :
1616 : I have marked the single MetaActions with:
1617 :
1618 : SIMPLE, DONE:
1619 : Simple, e.g nothing to do or value setting in the context
1620 :
1621 : CHECKED, WORKS WELL:
1622 : Thoroughly tested with extra written test code which created a replacement
1623 : Metafile just to test this action in various combinations
1624 :
1625 : NEEDS IMPLEMENTATION:
1626 : Not implemented and asserted, but also no usage found, neither in own Metafile
1627 : creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
1628 : bugdocs)
1629 :
1630 : For more commens, see the single action implementations.
1631 : */
1632 81 : void interpretMetafile(
1633 : const GDIMetaFile& rMetaFile,
1634 : TargetHolders& rTargetHolders,
1635 : PropertyHolders& rPropertyHolders,
1636 : const drawinglayer::geometry::ViewInformation2D& rViewInformation)
1637 : {
1638 81 : const size_t nCount(rMetaFile.GetActionSize());
1639 :
1640 5103 : for(size_t nAction(0); nAction < nCount; nAction++)
1641 : {
1642 5022 : MetaAction* pAction = rMetaFile.GetAction(nAction);
1643 :
1644 5022 : switch(pAction->GetType())
1645 : {
1646 : case MetaActionType::NONE :
1647 : {
1648 : /** SIMPLE, DONE */
1649 0 : break;
1650 : }
1651 : case MetaActionType::PIXEL :
1652 : {
1653 : /** CHECKED, WORKS WELL */
1654 0 : std::vector< basegfx::B2DPoint > aPositions;
1655 0 : Color aLastColor(COL_BLACK);
1656 :
1657 0 : while(MetaActionType::PIXEL == pAction->GetType() && nAction < nCount)
1658 : {
1659 0 : const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
1660 :
1661 0 : if(pA->GetColor() != aLastColor)
1662 : {
1663 0 : if(!aPositions.empty())
1664 : {
1665 0 : createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1666 0 : aPositions.clear();
1667 : }
1668 :
1669 0 : aLastColor = pA->GetColor();
1670 : }
1671 :
1672 0 : const Point& rPoint = pA->GetPoint();
1673 0 : aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
1674 0 : nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1675 : }
1676 :
1677 0 : nAction--;
1678 :
1679 0 : if(!aPositions.empty())
1680 : {
1681 0 : createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1682 : }
1683 :
1684 0 : break;
1685 : }
1686 : case MetaActionType::POINT :
1687 : {
1688 : /** CHECKED, WORKS WELL */
1689 0 : if(rPropertyHolders.Current().getLineColorActive())
1690 : {
1691 0 : std::vector< basegfx::B2DPoint > aPositions;
1692 :
1693 0 : while(MetaActionType::POINT == pAction->GetType() && nAction < nCount)
1694 : {
1695 0 : const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
1696 0 : const Point& rPoint = pA->GetPoint();
1697 0 : aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
1698 0 : nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1699 : }
1700 :
1701 0 : nAction--;
1702 :
1703 0 : if(!aPositions.empty())
1704 : {
1705 0 : createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
1706 0 : }
1707 : }
1708 :
1709 0 : break;
1710 : }
1711 : case MetaActionType::LINE :
1712 : {
1713 : /** CHECKED, WORKS WELL */
1714 2 : if(rPropertyHolders.Current().getLineColorActive())
1715 : {
1716 2 : basegfx::B2DPolygon aLinePolygon;
1717 4 : LineInfo aLineInfo;
1718 :
1719 7 : while(MetaActionType::LINE == pAction->GetType() && nAction < nCount)
1720 : {
1721 3 : const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
1722 3 : const Point& rStartPoint = pA->GetStartPoint();
1723 3 : const Point& rEndPoint = pA->GetEndPoint();
1724 3 : const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y());
1725 6 : const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y());
1726 :
1727 3 : if(aLinePolygon.count())
1728 : {
1729 3 : if(pA->GetLineInfo() == aLineInfo
1730 4 : && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1))
1731 : {
1732 0 : aLinePolygon.append(aEnd);
1733 : }
1734 : else
1735 : {
1736 1 : aLineInfo.SetLineJoin(basegfx::B2DLineJoin::NONE); // It were lines; force to NONE
1737 1 : createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
1738 1 : aLinePolygon.clear();
1739 1 : aLineInfo = pA->GetLineInfo();
1740 1 : aLinePolygon.append(aStart);
1741 1 : aLinePolygon.append(aEnd);
1742 : }
1743 : }
1744 : else
1745 : {
1746 2 : aLineInfo = pA->GetLineInfo();
1747 2 : aLinePolygon.append(aStart);
1748 2 : aLinePolygon.append(aEnd);
1749 : }
1750 :
1751 3 : nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1752 3 : }
1753 :
1754 2 : nAction--;
1755 :
1756 2 : if(aLinePolygon.count())
1757 : {
1758 2 : aLineInfo.SetLineJoin(basegfx::B2DLineJoin::NONE); // It were lines; force to NONE
1759 2 : createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
1760 2 : }
1761 : }
1762 :
1763 2 : break;
1764 : }
1765 : case MetaActionType::RECT :
1766 : {
1767 : /** CHECKED, WORKS WELL */
1768 40 : if(rPropertyHolders.Current().getLineOrFillActive())
1769 : {
1770 40 : const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
1771 40 : const Rectangle& rRectangle = pA->GetRect();
1772 :
1773 40 : if(!rRectangle.IsEmpty())
1774 : {
1775 40 : const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1776 :
1777 40 : if(!aRange.isEmpty())
1778 : {
1779 40 : const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
1780 40 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1781 : }
1782 : }
1783 : }
1784 :
1785 40 : break;
1786 : }
1787 : case MetaActionType::ROUNDRECT :
1788 : {
1789 : /** CHECKED, WORKS WELL */
1790 : /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
1791 : because the tools::Polygon operator creating the rounding does produce nonsense. I assume
1792 : this an error and create an unrounded rectangle in that case (implicit in
1793 : createPolygonFromRect)
1794 : */
1795 0 : if(rPropertyHolders.Current().getLineOrFillActive())
1796 : {
1797 0 : const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
1798 0 : const Rectangle& rRectangle = pA->GetRect();
1799 :
1800 0 : if(!rRectangle.IsEmpty())
1801 : {
1802 0 : const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1803 :
1804 0 : if(!aRange.isEmpty())
1805 : {
1806 0 : const sal_uInt32 nHor(pA->GetHorzRound());
1807 0 : const sal_uInt32 nVer(pA->GetVertRound());
1808 0 : basegfx::B2DPolygon aOutline;
1809 :
1810 0 : if(nHor || nVer)
1811 : {
1812 0 : double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
1813 0 : double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
1814 0 : fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
1815 0 : fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
1816 :
1817 0 : aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
1818 : }
1819 : else
1820 : {
1821 0 : aOutline = basegfx::tools::createPolygonFromRect(aRange);
1822 : }
1823 :
1824 0 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1825 : }
1826 : }
1827 : }
1828 :
1829 0 : break;
1830 : }
1831 : case MetaActionType::ELLIPSE :
1832 : {
1833 : /** CHECKED, WORKS WELL */
1834 0 : if(rPropertyHolders.Current().getLineOrFillActive())
1835 : {
1836 0 : const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
1837 0 : const Rectangle& rRectangle = pA->GetRect();
1838 :
1839 0 : if(!rRectangle.IsEmpty())
1840 : {
1841 0 : const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1842 :
1843 0 : if(!aRange.isEmpty())
1844 : {
1845 : const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromEllipse(
1846 0 : aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5));
1847 :
1848 0 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1849 : }
1850 : }
1851 : }
1852 :
1853 0 : break;
1854 : }
1855 : case MetaActionType::ARC :
1856 : {
1857 : /** CHECKED, WORKS WELL */
1858 0 : if(rPropertyHolders.Current().getLineColorActive())
1859 : {
1860 0 : const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
1861 0 : const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC);
1862 0 : const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1863 :
1864 0 : createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1865 : }
1866 :
1867 0 : break;
1868 : }
1869 : case MetaActionType::PIE :
1870 : {
1871 : /** CHECKED, WORKS WELL */
1872 0 : if(rPropertyHolders.Current().getLineOrFillActive())
1873 : {
1874 0 : const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
1875 0 : const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE);
1876 0 : const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1877 :
1878 0 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1879 : }
1880 :
1881 0 : break;
1882 : }
1883 : case MetaActionType::CHORD :
1884 : {
1885 : /** CHECKED, WORKS WELL */
1886 0 : if(rPropertyHolders.Current().getLineOrFillActive())
1887 : {
1888 0 : const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
1889 0 : const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD);
1890 0 : const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1891 :
1892 0 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1893 : }
1894 :
1895 0 : break;
1896 : }
1897 : case MetaActionType::POLYLINE :
1898 : {
1899 : /** CHECKED, WORKS WELL */
1900 31 : if(rPropertyHolders.Current().getLineColorActive())
1901 : {
1902 31 : const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
1903 31 : createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
1904 : }
1905 :
1906 31 : break;
1907 : }
1908 : case MetaActionType::POLYGON :
1909 : {
1910 : /** CHECKED, WORKS WELL */
1911 334 : if(rPropertyHolders.Current().getLineOrFillActive())
1912 : {
1913 334 : const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pAction);
1914 334 : basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon());
1915 :
1916 : // the metafile play interprets the polygons from MetaPolygonAction
1917 : // always as closed and always paints an edge from last to first point,
1918 : // so force to closed here to emulate that
1919 334 : if(aOutline.count() > 1 && !aOutline.isClosed())
1920 : {
1921 0 : aOutline.setClosed(true);
1922 : }
1923 :
1924 334 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1925 : }
1926 :
1927 334 : break;
1928 : }
1929 : case MetaActionType::POLYPOLYGON :
1930 : {
1931 : /** CHECKED, WORKS WELL */
1932 305 : if(rPropertyHolders.Current().getLineOrFillActive())
1933 : {
1934 305 : const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pAction);
1935 305 : basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
1936 :
1937 : // the metafile play interprets the single polygons from MetaPolyPolygonAction
1938 : // always as closed and always paints an edge from last to first point,
1939 : // so force to closed here to emulate that
1940 771 : for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++)
1941 : {
1942 466 : basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b));
1943 :
1944 466 : if(aPolygonOutline.count() > 1 && !aPolygonOutline.isClosed())
1945 : {
1946 24 : aPolygonOutline.setClosed(true);
1947 24 : aPolyPolygonOutline.setB2DPolygon(b, aPolygonOutline);
1948 : }
1949 466 : }
1950 :
1951 305 : createHairlineAndFillPrimitive(aPolyPolygonOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1952 : }
1953 :
1954 305 : break;
1955 : }
1956 : case MetaActionType::TEXT :
1957 : {
1958 : /** CHECKED, WORKS WELL */
1959 557 : const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
1960 557 : sal_uInt32 nTextLength(pA->GetLen());
1961 557 : const sal_uInt32 nTextIndex(pA->GetIndex());
1962 557 : const sal_uInt32 nStringLength(pA->GetText().getLength());
1963 :
1964 557 : if(nTextLength + nTextIndex > nStringLength)
1965 : {
1966 0 : nTextLength = nStringLength - nTextIndex;
1967 : }
1968 :
1969 557 : if(nTextLength && rPropertyHolders.Current().getTextColorActive())
1970 : {
1971 557 : const std::vector< double > aDXArray{};
1972 : processMetaTextAction(
1973 557 : pA->GetPoint(),
1974 557 : pA->GetText(),
1975 : nTextIndex,
1976 : nTextLength,
1977 : aDXArray,
1978 557 : rTargetHolders.Current(),
1979 1671 : rPropertyHolders.Current());
1980 : }
1981 :
1982 557 : break;
1983 : }
1984 : case MetaActionType::TEXTARRAY :
1985 : {
1986 : /** CHECKED, WORKS WELL */
1987 94 : const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
1988 94 : sal_uInt32 nTextLength(pA->GetLen());
1989 94 : const sal_uInt32 nTextIndex(pA->GetIndex());
1990 94 : const sal_uInt32 nStringLength(pA->GetText().getLength());
1991 :
1992 94 : if(nTextLength + nTextIndex > nStringLength)
1993 : {
1994 0 : nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
1995 : }
1996 :
1997 94 : if(nTextLength && rPropertyHolders.Current().getTextColorActive())
1998 : {
1999 : // preapare DXArray (if used)
2000 94 : std::vector< double > aDXArray;
2001 94 : long* pDXArray = pA->GetDXArray();
2002 :
2003 94 : if(pDXArray)
2004 : {
2005 94 : aDXArray.reserve(nTextLength);
2006 :
2007 503 : for(sal_uInt32 a(0); a < nTextLength; a++)
2008 : {
2009 409 : aDXArray.push_back((double)(*(pDXArray + a)));
2010 : }
2011 : }
2012 :
2013 : processMetaTextAction(
2014 94 : pA->GetPoint(),
2015 94 : pA->GetText(),
2016 : nTextIndex,
2017 : nTextLength,
2018 : aDXArray,
2019 94 : rTargetHolders.Current(),
2020 282 : rPropertyHolders.Current());
2021 : }
2022 :
2023 94 : break;
2024 : }
2025 : case MetaActionType::STRETCHTEXT :
2026 : {
2027 : // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
2028 : // It looks as if it pretty never really uses a width different from
2029 : // the default text-layout width, but it's not possible to be sure.
2030 : // Implemented getting the DXArray and checking for scale at all. If
2031 : // scale is more than 3.5% different, scale the DXArray before usage.
2032 : // New status:
2033 :
2034 : /** CHECKED, WORKS WELL */
2035 0 : const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
2036 0 : sal_uInt32 nTextLength(pA->GetLen());
2037 0 : const sal_uInt32 nTextIndex(pA->GetIndex());
2038 0 : const sal_uInt32 nStringLength(pA->GetText().getLength());
2039 :
2040 0 : if(nTextLength + nTextIndex > nStringLength)
2041 : {
2042 0 : nTextLength = nStringLength - nTextIndex;
2043 : }
2044 :
2045 0 : if(nTextLength && rPropertyHolders.Current().getTextColorActive())
2046 : {
2047 0 : drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
2048 0 : aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
2049 :
2050 : ::std::vector< double > aTextArray(
2051 : aTextLayouterDevice.getTextArray(
2052 0 : pA->GetText(),
2053 : nTextIndex,
2054 0 : nTextLength));
2055 :
2056 0 : if(!aTextArray.empty())
2057 : {
2058 0 : const double fTextLength(aTextArray.back());
2059 :
2060 0 : if(0.0 != fTextLength && pA->GetWidth())
2061 : {
2062 0 : const double fRelative(pA->GetWidth() / fTextLength);
2063 :
2064 0 : if(fabs(fRelative - 1.0) >= 0.035)
2065 : {
2066 : // when derivation is more than 3,5% from default text size,
2067 : // scale the DXArray
2068 0 : for(size_t a(0); a < aTextArray.size(); a++)
2069 : {
2070 0 : aTextArray[a] *= fRelative;
2071 : }
2072 : }
2073 : }
2074 : }
2075 :
2076 : processMetaTextAction(
2077 0 : pA->GetPoint(),
2078 0 : pA->GetText(),
2079 : nTextIndex,
2080 : nTextLength,
2081 : aTextArray,
2082 0 : rTargetHolders.Current(),
2083 0 : rPropertyHolders.Current());
2084 : }
2085 :
2086 0 : break;
2087 : }
2088 : case MetaActionType::TEXTRECT :
2089 : {
2090 : /** CHECKED, WORKS WELL */
2091 : // OSL_FAIL("MetaActionType::TEXTRECT requested (!)");
2092 0 : const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
2093 0 : const Rectangle& rRectangle = pA->GetRect();
2094 0 : const sal_uInt32 nStringLength(pA->GetText().getLength());
2095 :
2096 0 : if(!rRectangle.IsEmpty() && 0 != nStringLength)
2097 : {
2098 : // The problem with this action is that it describes unlayouted text
2099 : // and the layout capabilities are in EditEngine/Outliner in SVX. The
2100 : // same problem is true for VCL which internally has implementations
2101 : // to layout text in this case. There exists even a call
2102 : // OutputDevice::AddTextRectActions(...) to create the needed actions
2103 : // as 'sub-content' of a Metafile. Unfortunately i do not have an
2104 : // OutputDevice here since this interpreter tries to work without
2105 : // VCL AFAP.
2106 : // Since AddTextRectActions is the only way as long as we do not have
2107 : // a simple text layouter available, i will try to add it to the
2108 : // TextLayouterDevice isloation.
2109 0 : drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
2110 0 : aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
2111 0 : GDIMetaFile aGDIMetaFile;
2112 :
2113 : aTextLayouterDevice.addTextRectActions(
2114 0 : rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
2115 :
2116 0 : if(aGDIMetaFile.GetActionSize())
2117 : {
2118 : // create sub-content
2119 0 : drawinglayer::primitive2d::Primitive2DSequence xSubContent;
2120 : {
2121 0 : rTargetHolders.Push();
2122 :
2123 : // for sub-Mteafile contents, do start with new, default render state
2124 : // #i124686# ...but copy font, this is already set accordingly
2125 0 : vcl::Font aTargetFont = rPropertyHolders.Current().getFont();
2126 0 : rPropertyHolders.PushDefault();
2127 0 : rPropertyHolders.Current().setFont(aTargetFont);
2128 :
2129 0 : interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
2130 0 : xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
2131 0 : rPropertyHolders.Pop();
2132 0 : rTargetHolders.Pop();
2133 : }
2134 :
2135 0 : if(xSubContent.hasElements())
2136 : {
2137 : // add with transformation
2138 0 : rTargetHolders.Current().append(
2139 : new drawinglayer::primitive2d::TransformPrimitive2D(
2140 0 : rPropertyHolders.Current().getTransformation(),
2141 0 : xSubContent));
2142 0 : }
2143 0 : }
2144 : }
2145 :
2146 0 : break;
2147 : }
2148 : case MetaActionType::BMP :
2149 : {
2150 : /** CHECKED, WORKS WELL */
2151 0 : const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
2152 0 : const BitmapEx aBitmapEx(pA->GetBitmap());
2153 :
2154 0 : createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2155 :
2156 0 : break;
2157 : }
2158 : case MetaActionType::BMPSCALE :
2159 : {
2160 : /** CHECKED, WORKS WELL */
2161 39 : const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
2162 39 : const Bitmap aBitmapEx(pA->GetBitmap());
2163 :
2164 39 : createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2165 :
2166 39 : break;
2167 : }
2168 : case MetaActionType::BMPSCALEPART :
2169 : {
2170 : /** CHECKED, WORKS WELL */
2171 0 : const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
2172 0 : const Bitmap& rBitmap = pA->GetBitmap();
2173 :
2174 0 : if(!rBitmap.IsEmpty())
2175 : {
2176 0 : Bitmap aCroppedBitmap(rBitmap);
2177 0 : const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2178 :
2179 0 : if(!aCropRectangle.IsEmpty())
2180 : {
2181 0 : aCroppedBitmap.Crop(aCropRectangle);
2182 : }
2183 :
2184 0 : const BitmapEx aCroppedBitmapEx(aCroppedBitmap);
2185 0 : createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2186 : }
2187 :
2188 0 : break;
2189 : }
2190 : case MetaActionType::BMPEX :
2191 : {
2192 : /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
2193 0 : const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
2194 0 : const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2195 :
2196 0 : createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2197 :
2198 0 : break;
2199 : }
2200 : case MetaActionType::BMPEXSCALE :
2201 : {
2202 : /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
2203 5 : const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
2204 5 : const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2205 :
2206 5 : createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2207 :
2208 5 : break;
2209 : }
2210 : case MetaActionType::BMPEXSCALEPART :
2211 : {
2212 : /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
2213 0 : const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
2214 0 : const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2215 :
2216 0 : if(!rBitmapEx.IsEmpty())
2217 : {
2218 0 : BitmapEx aCroppedBitmapEx(rBitmapEx);
2219 0 : const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2220 :
2221 0 : if(!aCropRectangle.IsEmpty())
2222 : {
2223 0 : aCroppedBitmapEx.Crop(aCropRectangle);
2224 : }
2225 :
2226 0 : createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2227 : }
2228 :
2229 0 : break;
2230 : }
2231 : case MetaActionType::MASK :
2232 : {
2233 : /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
2234 : /** Huh, no it isn't!? */
2235 0 : const MetaMaskAction* pA = static_cast<const MetaMaskAction*>(pAction);
2236 0 : const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
2237 :
2238 0 : createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2239 :
2240 0 : break;
2241 : }
2242 : case MetaActionType::MASKSCALE :
2243 : {
2244 : /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
2245 0 : const MetaMaskScaleAction* pA = static_cast<const MetaMaskScaleAction*>(pAction);
2246 0 : const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
2247 :
2248 0 : createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2249 :
2250 0 : break;
2251 : }
2252 : case MetaActionType::MASKSCALEPART :
2253 : {
2254 : /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
2255 0 : const MetaMaskScalePartAction* pA = static_cast<const MetaMaskScalePartAction*>(pAction);
2256 0 : const Bitmap& rBitmap = pA->GetBitmap();
2257 :
2258 0 : if(!rBitmap.IsEmpty())
2259 : {
2260 0 : Bitmap aCroppedBitmap(rBitmap);
2261 0 : const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2262 :
2263 0 : if(!aCropRectangle.IsEmpty())
2264 : {
2265 0 : aCroppedBitmap.Crop(aCropRectangle);
2266 : }
2267 :
2268 0 : const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor()));
2269 0 : createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2270 : }
2271 :
2272 0 : break;
2273 : }
2274 : case MetaActionType::GRADIENT :
2275 : {
2276 : /** CHECKED, WORKS WELL */
2277 0 : const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
2278 0 : const Rectangle& rRectangle = pA->GetRect();
2279 :
2280 0 : if(!rRectangle.IsEmpty())
2281 : {
2282 0 : basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
2283 :
2284 0 : if(!aRange.isEmpty())
2285 : {
2286 0 : const Gradient& rGradient = pA->GetGradient();
2287 0 : const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
2288 0 : basegfx::B2DPolyPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
2289 :
2290 0 : if(aAttribute.getStartColor() == aAttribute.getEndColor())
2291 : {
2292 : // not really a gradient. Create filled rectangle
2293 : createFillPrimitive(
2294 : aOutline,
2295 0 : rTargetHolders.Current(),
2296 0 : rPropertyHolders.Current());
2297 : }
2298 : else
2299 : {
2300 : // really a gradient
2301 0 : aRange.transform(rPropertyHolders.Current().getTransformation());
2302 0 : drawinglayer::primitive2d::Primitive2DSequence xGradient(1);
2303 :
2304 0 : if(rPropertyHolders.Current().isRasterOpInvert())
2305 : {
2306 : // use a special version of FillGradientPrimitive2D which creates
2307 : // non-overlapping geometry on decomposition to makethe old XOR
2308 : // paint 'trick' work.
2309 0 : xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2310 : new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
2311 : aRange,
2312 0 : aAttribute));
2313 : }
2314 : else
2315 : {
2316 0 : xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2317 : new drawinglayer::primitive2d::FillGradientPrimitive2D(
2318 : aRange,
2319 0 : aAttribute));
2320 : }
2321 :
2322 : // #i112300# clip against polygon representing the rectangle from
2323 : // the action. This is implicitly done using a temp Clipping in VCL
2324 : // when a MetaGradientAction is executed
2325 0 : aOutline.transform(rPropertyHolders.Current().getTransformation());
2326 0 : rTargetHolders.Current().append(
2327 : new drawinglayer::primitive2d::MaskPrimitive2D(
2328 : aOutline,
2329 0 : xGradient));
2330 0 : }
2331 : }
2332 : }
2333 :
2334 0 : break;
2335 : }
2336 : case MetaActionType::HATCH :
2337 : {
2338 : /** CHECKED, WORKS WELL */
2339 0 : const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
2340 0 : basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
2341 :
2342 0 : if(aOutline.count())
2343 : {
2344 0 : const Hatch& rHatch = pA->GetHatch();
2345 0 : const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
2346 :
2347 0 : aOutline.transform(rPropertyHolders.Current().getTransformation());
2348 :
2349 0 : const basegfx::B2DRange aObjectRange(aOutline.getB2DRange());
2350 : const drawinglayer::primitive2d::Primitive2DReference aFillHatch(
2351 : new drawinglayer::primitive2d::FillHatchPrimitive2D(
2352 : aObjectRange,
2353 : basegfx::BColor(),
2354 0 : aAttribute));
2355 :
2356 0 : rTargetHolders.Current().append(
2357 : new drawinglayer::primitive2d::MaskPrimitive2D(
2358 : aOutline,
2359 0 : drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1)));
2360 : }
2361 :
2362 0 : break;
2363 : }
2364 : case MetaActionType::WALLPAPER :
2365 : {
2366 : /** CHECKED, WORKS WELL */
2367 0 : const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pAction);
2368 0 : Rectangle aWallpaperRectangle(pA->GetRect());
2369 :
2370 0 : if(!aWallpaperRectangle.IsEmpty())
2371 : {
2372 0 : const Wallpaper& rWallpaper = pA->GetWallpaper();
2373 0 : const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
2374 : basegfx::B2DRange aWallpaperRange(
2375 0 : aWallpaperRectangle.Left(), aWallpaperRectangle.Top(),
2376 0 : aWallpaperRectangle.Right(), aWallpaperRectangle.Bottom());
2377 :
2378 0 : if(WALLPAPER_NULL != eWallpaperStyle)
2379 : {
2380 0 : if(rWallpaper.IsBitmap())
2381 : {
2382 : // create bitmap background. Caution: This
2383 : // also will create gradient/color background(s)
2384 : // when the bitmap is transparent or not tiled
2385 : CreateAndAppendBitmapWallpaper(
2386 : aWallpaperRange,
2387 : rWallpaper,
2388 0 : rTargetHolders.Current(),
2389 0 : rPropertyHolders.Current());
2390 : }
2391 0 : else if(rWallpaper.IsGradient())
2392 : {
2393 : // create gradient background
2394 0 : rTargetHolders.Current().append(
2395 : CreateGradientWallpaper(
2396 : aWallpaperRange,
2397 : rWallpaper.GetGradient(),
2398 0 : rPropertyHolders.Current()));
2399 : }
2400 0 : else if(!rWallpaper.GetColor().GetTransparency())
2401 : {
2402 : // create color background
2403 0 : rTargetHolders.Current().append(
2404 : CreateColorWallpaper(
2405 : aWallpaperRange,
2406 0 : rWallpaper.GetColor().getBColor(),
2407 0 : rPropertyHolders.Current()));
2408 : }
2409 : }
2410 : }
2411 :
2412 0 : break;
2413 : }
2414 : case MetaActionType::CLIPREGION :
2415 : {
2416 : /** CHECKED, WORKS WELL */
2417 142 : const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pAction);
2418 :
2419 142 : if(pA->IsClipping())
2420 : {
2421 : // new clipping. Get tools::PolyPolygon and transform with current transformation
2422 107 : basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion()));
2423 :
2424 107 : aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
2425 107 : HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2426 : }
2427 : else
2428 : {
2429 : // end clipping
2430 35 : const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2431 :
2432 35 : HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2433 : }
2434 :
2435 142 : break;
2436 : }
2437 : case MetaActionType::ISECTRECTCLIPREGION :
2438 : {
2439 : /** CHECKED, WORKS WELL */
2440 5 : const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pAction);
2441 5 : const Rectangle& rRectangle = pA->GetRect();
2442 :
2443 5 : if(rRectangle.IsEmpty())
2444 : {
2445 : // intersect with empty rectangle will always give empty
2446 : // ClipPolyPolygon; start new clipping with empty PolyPolygon
2447 0 : const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2448 :
2449 0 : HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2450 : }
2451 : else
2452 : {
2453 : // create transformed ClipRange
2454 : basegfx::B2DRange aClipRange(
2455 10 : rRectangle.Left(), rRectangle.Top(),
2456 15 : rRectangle.Right(), rRectangle.Bottom());
2457 :
2458 5 : aClipRange.transform(rPropertyHolders.Current().getTransformation());
2459 :
2460 5 : if(rPropertyHolders.Current().getClipPolyPolygonActive())
2461 : {
2462 0 : if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2463 : {
2464 : // nothing to do, empty active clipPolyPolygon will stay
2465 : // empty when intersecting
2466 : }
2467 : else
2468 : {
2469 : // AND existing region and new ClipRange
2470 : const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
2471 0 : rPropertyHolders.Current().getClipPolyPolygon());
2472 0 : basegfx::B2DPolyPolygon aClippedPolyPolygon;
2473 :
2474 0 : if(aOriginalPolyPolygon.count())
2475 : {
2476 0 : aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnRange(
2477 : aOriginalPolyPolygon,
2478 : aClipRange,
2479 : true,
2480 0 : false);
2481 : }
2482 :
2483 0 : if(aClippedPolyPolygon != aOriginalPolyPolygon)
2484 : {
2485 : // start new clipping with intersected region
2486 : HandleNewClipRegion(
2487 : aClippedPolyPolygon,
2488 : rTargetHolders,
2489 0 : rPropertyHolders);
2490 0 : }
2491 : }
2492 : }
2493 : else
2494 : {
2495 : // start new clipping with ClipRange
2496 : const basegfx::B2DPolyPolygon aNewClipPolyPolygon(
2497 5 : basegfx::tools::createPolygonFromRect(aClipRange));
2498 :
2499 5 : HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2500 : }
2501 : }
2502 :
2503 5 : break;
2504 : }
2505 : case MetaActionType::ISECTREGIONCLIPREGION :
2506 : {
2507 : /** CHECKED, WORKS WELL */
2508 0 : const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pAction);
2509 0 : const vcl::Region& rNewRegion = pA->GetRegion();
2510 :
2511 0 : if(rNewRegion.IsEmpty())
2512 : {
2513 : // intersect with empty region will always give empty
2514 : // region; start new clipping with empty PolyPolygon
2515 0 : const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2516 :
2517 0 : HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2518 : }
2519 : else
2520 : {
2521 : // get new ClipPolyPolygon, transform it with current transformation
2522 0 : basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion));
2523 0 : aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
2524 :
2525 0 : if(rPropertyHolders.Current().getClipPolyPolygonActive())
2526 : {
2527 0 : if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2528 : {
2529 : // nothing to do, empty active clipPolyPolygon will stay empty
2530 : // when intersecting with any region
2531 : }
2532 : else
2533 : {
2534 : // AND existing and new region
2535 : const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
2536 0 : rPropertyHolders.Current().getClipPolyPolygon());
2537 0 : basegfx::B2DPolyPolygon aClippedPolyPolygon;
2538 :
2539 0 : if(aOriginalPolyPolygon.count())
2540 : {
2541 0 : aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
2542 0 : aOriginalPolyPolygon, aNewClipPolyPolygon, true, false);
2543 : }
2544 :
2545 0 : if(aClippedPolyPolygon != aOriginalPolyPolygon)
2546 : {
2547 : // start new clipping with intersected ClipPolyPolygon
2548 0 : HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders);
2549 0 : }
2550 : }
2551 : }
2552 : else
2553 : {
2554 : // start new clipping with new ClipPolyPolygon
2555 0 : HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2556 0 : }
2557 : }
2558 :
2559 0 : break;
2560 : }
2561 : case MetaActionType::MOVECLIPREGION :
2562 : {
2563 : /** CHECKED, WORKS WELL */
2564 0 : const MetaMoveClipRegionAction* pA = static_cast<const MetaMoveClipRegionAction*>(pAction);
2565 :
2566 0 : if(rPropertyHolders.Current().getClipPolyPolygonActive())
2567 : {
2568 0 : if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2569 : {
2570 : // nothing to do
2571 : }
2572 : else
2573 : {
2574 0 : const sal_Int32 nHor(pA->GetHorzMove());
2575 0 : const sal_Int32 nVer(pA->GetVertMove());
2576 :
2577 0 : if(0 != nHor || 0 != nVer)
2578 : {
2579 : // prepare translation, add current transformation
2580 0 : basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove());
2581 0 : aVector *= rPropertyHolders.Current().getTransformation();
2582 : basegfx::B2DHomMatrix aTransform(
2583 0 : basegfx::tools::createTranslateB2DHomMatrix(aVector));
2584 :
2585 : // transform existing region
2586 : basegfx::B2DPolyPolygon aClipPolyPolygon(
2587 0 : rPropertyHolders.Current().getClipPolyPolygon());
2588 :
2589 0 : aClipPolyPolygon.transform(aTransform);
2590 0 : HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders);
2591 : }
2592 : }
2593 : }
2594 :
2595 0 : break;
2596 : }
2597 : case MetaActionType::LINECOLOR :
2598 : {
2599 : /** CHECKED, WORKS WELL */
2600 552 : const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pAction);
2601 552 : const bool bActive(pA->IsSetting());
2602 :
2603 552 : rPropertyHolders.Current().setLineColorActive(bActive);
2604 552 : if(bActive)
2605 510 : rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
2606 :
2607 552 : break;
2608 : }
2609 : case MetaActionType::FILLCOLOR :
2610 : {
2611 : /** CHECKED, WORKS WELL */
2612 765 : const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pAction);
2613 765 : const bool bActive(pA->IsSetting());
2614 :
2615 765 : rPropertyHolders.Current().setFillColorActive(bActive);
2616 765 : if(bActive)
2617 741 : rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
2618 :
2619 765 : break;
2620 : }
2621 : case MetaActionType::TEXTCOLOR :
2622 : {
2623 : /** SIMPLE, DONE */
2624 115 : const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pAction);
2625 115 : const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor());
2626 :
2627 115 : rPropertyHolders.Current().setTextColorActive(bActivate);
2628 115 : rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
2629 :
2630 115 : break;
2631 : }
2632 : case MetaActionType::TEXTFILLCOLOR :
2633 : {
2634 : /** SIMPLE, DONE */
2635 355 : const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pAction);
2636 355 : const bool bWithColorArgument(pA->IsSetting());
2637 :
2638 355 : if(bWithColorArgument)
2639 : {
2640 : // emulate OutputDevice::SetTextFillColor(...) WITH argument
2641 0 : const Color& rFontFillColor = pA->GetColor();
2642 0 : rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2643 0 : rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2644 : }
2645 : else
2646 : {
2647 : // emulate SetFillColor() <- NO argument (!)
2648 355 : rPropertyHolders.Current().setTextFillColorActive(false);
2649 : }
2650 :
2651 355 : break;
2652 : }
2653 : case MetaActionType::TEXTALIGN :
2654 : {
2655 : /** SIMPLE, DONE */
2656 328 : const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pAction);
2657 328 : const TextAlign aNewTextAlign = pA->GetTextAlign();
2658 :
2659 : // TextAlign is applied to the current font (as in
2660 : // OutputDevice::SetTextAlign which would be used when
2661 : // playing the Metafile)
2662 328 : if(rPropertyHolders.Current().getFont().GetAlign() != aNewTextAlign)
2663 : {
2664 10 : vcl::Font aNewFont(rPropertyHolders.Current().getFont());
2665 10 : aNewFont.SetAlign(aNewTextAlign);
2666 10 : rPropertyHolders.Current().setFont(aNewFont);
2667 : }
2668 :
2669 328 : break;
2670 : }
2671 : case MetaActionType::MAPMODE :
2672 : {
2673 : /** CHECKED, WORKS WELL */
2674 : // the most necessary MapMode to be interpreted is MAP_RELATIVE,
2675 : // but also the others may occur. Even not yet supported ones
2676 : // may need to be added here later
2677 2 : const MetaMapModeAction* pA = static_cast<const MetaMapModeAction*>(pAction);
2678 2 : const MapMode& rMapMode = pA->GetMapMode();
2679 2 : basegfx::B2DHomMatrix aMapping;
2680 :
2681 2 : if(MAP_RELATIVE == rMapMode.GetMapUnit())
2682 : {
2683 2 : aMapping = getTransformFromMapMode(rMapMode);
2684 : }
2685 : else
2686 : {
2687 0 : switch(rMapMode.GetMapUnit())
2688 : {
2689 : case MAP_100TH_MM :
2690 : {
2691 0 : if(MAP_TWIP == rPropertyHolders.Current().getMapUnit())
2692 : {
2693 : // MAP_TWIP -> MAP_100TH_MM
2694 0 : const double fTwipTo100thMm(127.0 / 72.0);
2695 0 : aMapping.scale(fTwipTo100thMm, fTwipTo100thMm);
2696 : }
2697 0 : break;
2698 : }
2699 : case MAP_TWIP :
2700 : {
2701 0 : if(MAP_100TH_MM == rPropertyHolders.Current().getMapUnit())
2702 : {
2703 : // MAP_100TH_MM -> MAP_TWIP
2704 0 : const double f100thMmToTwip(72.0 / 127.0);
2705 0 : aMapping.scale(f100thMmToTwip, f100thMmToTwip);
2706 : }
2707 0 : break;
2708 : }
2709 : default :
2710 : {
2711 : OSL_FAIL("interpretMetafile: MetaActionType::MAPMODE with unsupported MapUnit (!)");
2712 0 : break;
2713 : }
2714 : }
2715 :
2716 0 : aMapping = getTransformFromMapMode(rMapMode) * aMapping;
2717 0 : rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit());
2718 : }
2719 :
2720 2 : if(!aMapping.isIdentity())
2721 : {
2722 2 : aMapping = aMapping * rPropertyHolders.Current().getTransformation();
2723 2 : rPropertyHolders.Current().setTransformation(aMapping);
2724 : }
2725 :
2726 2 : break;
2727 : }
2728 : case MetaActionType::FONT :
2729 : {
2730 : /** SIMPLE, DONE */
2731 322 : const MetaFontAction* pA = static_cast<const MetaFontAction*>(pAction);
2732 322 : rPropertyHolders.Current().setFont(pA->GetFont());
2733 322 : Size aFontSize(pA->GetFont().GetSize());
2734 :
2735 322 : if(0 == aFontSize.Height())
2736 : {
2737 : // this should not happen but i got Metafiles where this was the
2738 : // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
2739 0 : vcl::Font aCorrectedFont(pA->GetFont());
2740 :
2741 : // guess 16 pixel (as in VCL)
2742 0 : aFontSize = Size(0, 16);
2743 :
2744 : // convert to target MapUnit if not pixels
2745 : aFontSize = OutputDevice::LogicToLogic(
2746 0 : aFontSize, MAP_PIXEL, rPropertyHolders.Current().getMapUnit());
2747 :
2748 0 : aCorrectedFont.SetSize(aFontSize);
2749 0 : rPropertyHolders.Current().setFont(aCorrectedFont);
2750 : }
2751 :
2752 : // older Metafiles have no MetaActionType::TEXTCOLOR which defines
2753 : // the FontColor now, so use the Font's color when not transparent
2754 322 : const Color& rFontColor = pA->GetFont().GetColor();
2755 322 : const bool bActivate(COL_TRANSPARENT != rFontColor.GetColor());
2756 :
2757 322 : if(bActivate)
2758 : {
2759 316 : rPropertyHolders.Current().setTextColor(rFontColor.getBColor());
2760 : }
2761 :
2762 : // caution: do NOT decativate here on transparet, see
2763 : // OutputDevice::SetFont(..) for more info
2764 : // rPropertyHolders.Current().setTextColorActive(bActivate);
2765 :
2766 : // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
2767 : // see OutputDevice::SetFont(..) the if(mpMetaFile) case
2768 322 : if(bActivate)
2769 : {
2770 316 : const Color& rFontFillColor = pA->GetFont().GetFillColor();
2771 316 : rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2772 316 : rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2773 : }
2774 : else
2775 : {
2776 6 : rPropertyHolders.Current().setTextFillColorActive(false);
2777 : }
2778 :
2779 322 : break;
2780 : }
2781 : case MetaActionType::PUSH :
2782 : {
2783 : /** CHECKED, WORKS WELL */
2784 114 : const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction);
2785 114 : rPropertyHolders.Push(pA->GetFlags());
2786 :
2787 114 : break;
2788 : }
2789 : case MetaActionType::POP :
2790 : {
2791 : /** CHECKED, WORKS WELL */
2792 114 : const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::CLIPREGION);
2793 114 : const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::RASTEROP);
2794 :
2795 114 : if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
2796 : {
2797 : // end evtl. clipping
2798 13 : const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2799 :
2800 13 : HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2801 : }
2802 :
2803 114 : if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
2804 : {
2805 : // end evtl. RasterOp
2806 0 : HandleNewRasterOp(ROP_OVERPAINT, rTargetHolders, rPropertyHolders);
2807 : }
2808 :
2809 114 : rPropertyHolders.Pop();
2810 :
2811 114 : if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
2812 : {
2813 : // start evtl. RasterOp
2814 0 : HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders);
2815 : }
2816 :
2817 114 : if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
2818 : {
2819 : // start evtl. clipping
2820 : HandleNewClipRegion(
2821 0 : rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders);
2822 : }
2823 :
2824 114 : break;
2825 : }
2826 : case MetaActionType::RASTEROP :
2827 : {
2828 : /** CHECKED, WORKS WELL */
2829 687 : const MetaRasterOpAction* pA = static_cast<const MetaRasterOpAction*>(pAction);
2830 687 : const RasterOp aRasterOp = pA->GetRasterOp();
2831 :
2832 687 : HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
2833 :
2834 687 : break;
2835 : }
2836 : case MetaActionType::Transparent :
2837 : {
2838 : /** CHECKED, WORKS WELL */
2839 0 : const MetaTransparentAction* pA = static_cast<const MetaTransparentAction*>(pAction);
2840 0 : const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
2841 :
2842 0 : if(aOutline.count())
2843 : {
2844 0 : const sal_uInt16 nTransparence(pA->GetTransparence());
2845 :
2846 0 : if(0 == nTransparence)
2847 : {
2848 : // not transparent
2849 0 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2850 : }
2851 0 : else if(nTransparence >= 100)
2852 : {
2853 : // fully or more than transparent
2854 : }
2855 : else
2856 : {
2857 : // transparent. Create new target
2858 0 : rTargetHolders.Push();
2859 :
2860 : // create primitives there and get them
2861 0 : createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2862 : const drawinglayer::primitive2d::Primitive2DSequence aSubContent(
2863 0 : rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
2864 :
2865 : // back to old target
2866 0 : rTargetHolders.Pop();
2867 :
2868 0 : if(aSubContent.hasElements())
2869 : {
2870 0 : rTargetHolders.Current().append(
2871 : new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2872 : aSubContent,
2873 0 : nTransparence * 0.01));
2874 0 : }
2875 : }
2876 : }
2877 :
2878 0 : break;
2879 : }
2880 : case MetaActionType::EPS :
2881 : {
2882 : /** CHECKED, WORKS WELL */
2883 : // To support this action, i have added a EpsPrimitive2D which will
2884 : // by default decompose to the Metafile replacement data. To support
2885 : // this EPS on screen, the renderer visualizing this has to support
2886 : // that primitive and visualize the Eps file (e.g. printing)
2887 0 : const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
2888 0 : const Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
2889 :
2890 0 : if(!aRectangle.IsEmpty())
2891 : {
2892 : // create object transform
2893 0 : basegfx::B2DHomMatrix aObjectTransform;
2894 :
2895 0 : aObjectTransform.set(0, 0, aRectangle.GetWidth());
2896 0 : aObjectTransform.set(1, 1, aRectangle.GetHeight());
2897 0 : aObjectTransform.set(0, 2, aRectangle.Left());
2898 0 : aObjectTransform.set(1, 2, aRectangle.Top());
2899 :
2900 : // add current transformation
2901 0 : aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
2902 :
2903 : // embed using EpsPrimitive
2904 0 : rTargetHolders.Current().append(
2905 : new drawinglayer::primitive2d::EpsPrimitive2D(
2906 : aObjectTransform,
2907 : pA->GetLink(),
2908 0 : pA->GetSubstitute()));
2909 : }
2910 :
2911 0 : break;
2912 : }
2913 : case MetaActionType::REFPOINT :
2914 : {
2915 : /** SIMPLE, DONE */
2916 : // only used for hatch and line pattern offsets, pretty much no longer
2917 : // supported today
2918 : // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
2919 0 : break;
2920 : }
2921 : case MetaActionType::TEXTLINECOLOR :
2922 : {
2923 : /** SIMPLE, DONE */
2924 0 : const MetaTextLineColorAction* pA = static_cast<const MetaTextLineColorAction*>(pAction);
2925 0 : const bool bActive(pA->IsSetting());
2926 :
2927 0 : rPropertyHolders.Current().setTextLineColorActive(bActive);
2928 0 : if(bActive)
2929 0 : rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
2930 :
2931 0 : break;
2932 : }
2933 : case MetaActionType::TEXTLINE :
2934 : {
2935 : /** CHECKED, WORKS WELL */
2936 : // actually creates overline, underline and strikeouts, so
2937 : // these should be isolated from TextDecoratedPortionPrimitive2D
2938 : // to own primitives. Done, available now.
2939 : //
2940 : // This Metaaction seems not to be used (was not used in any
2941 : // checked files). It's used in combination with the current
2942 : // Font.
2943 0 : const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction);
2944 :
2945 : proccessMetaTextLineAction(
2946 : *pA,
2947 0 : rTargetHolders.Current(),
2948 0 : rPropertyHolders.Current());
2949 :
2950 0 : break;
2951 : }
2952 : case MetaActionType::FLOATTRANSPARENT :
2953 : {
2954 : /** CHECKED, WORKS WELL */
2955 0 : const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
2956 : const basegfx::B2DRange aTargetRange(
2957 0 : pA->GetPoint().X(),
2958 0 : pA->GetPoint().Y(),
2959 0 : pA->GetPoint().X() + pA->GetSize().Width(),
2960 0 : pA->GetPoint().Y() + pA->GetSize().Height());
2961 :
2962 0 : if(!aTargetRange.isEmpty())
2963 : {
2964 0 : const GDIMetaFile& rContent = pA->GetGDIMetaFile();
2965 :
2966 0 : if(rContent.GetActionSize())
2967 : {
2968 : // create the sub-content with no embedding specific to the
2969 : // sub-metafile, this seems not to be used.
2970 0 : drawinglayer::primitive2d::Primitive2DSequence xSubContent;
2971 : {
2972 0 : rTargetHolders.Push();
2973 : // #i# for sub-Mteafile contents, do start with new, default render state
2974 0 : rPropertyHolders.PushDefault();
2975 0 : interpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
2976 0 : xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
2977 0 : rPropertyHolders.Pop();
2978 0 : rTargetHolders.Pop();
2979 : }
2980 :
2981 0 : if(xSubContent.hasElements())
2982 : {
2983 : // prepare sub-content transform
2984 0 : basegfx::B2DHomMatrix aSubTransform;
2985 :
2986 : // create SourceRange
2987 : const basegfx::B2DRange aSourceRange(
2988 0 : rContent.GetPrefMapMode().GetOrigin().X(),
2989 0 : rContent.GetPrefMapMode().GetOrigin().Y(),
2990 0 : rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(),
2991 0 : rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height());
2992 :
2993 : // apply mapping if aTargetRange and aSourceRange are not equal
2994 0 : if(!aSourceRange.equal(aTargetRange))
2995 : {
2996 0 : aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
2997 : aSubTransform.scale(
2998 0 : aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()),
2999 0 : aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight()));
3000 0 : aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY());
3001 : }
3002 :
3003 : // apply general current transformation
3004 0 : aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform;
3005 :
3006 : // evtl. embed sub-content to it's transformation
3007 0 : if(!aSubTransform.isIdentity())
3008 : {
3009 : const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform(
3010 : new drawinglayer::primitive2d::TransformPrimitive2D(
3011 : aSubTransform,
3012 0 : xSubContent));
3013 :
3014 0 : xSubContent = drawinglayer::primitive2d::Primitive2DSequence(&aEmbeddedTransform, 1);
3015 : }
3016 :
3017 : // check if gradient is a real gradient
3018 0 : const Gradient& rGradient = pA->GetGradient();
3019 0 : const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
3020 :
3021 0 : if(aAttribute.getStartColor() == aAttribute.getEndColor())
3022 : {
3023 : // not really a gradient; create UnifiedTransparencePrimitive2D
3024 0 : rTargetHolders.Current().append(
3025 : new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
3026 : xSubContent,
3027 0 : aAttribute.getStartColor().luminance()));
3028 : }
3029 : else
3030 : {
3031 : // really a gradient. Create gradient sub-content (with correct scaling)
3032 0 : basegfx::B2DRange aRange(aTargetRange);
3033 0 : aRange.transform(rPropertyHolders.Current().getTransformation());
3034 :
3035 : // prepare gradient for transparent content
3036 : const drawinglayer::primitive2d::Primitive2DReference xTransparence(
3037 : new drawinglayer::primitive2d::FillGradientPrimitive2D(
3038 : aRange,
3039 0 : aAttribute));
3040 :
3041 : // create transparence primitive
3042 0 : rTargetHolders.Current().append(
3043 : new drawinglayer::primitive2d::TransparencePrimitive2D(
3044 : xSubContent,
3045 0 : drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1)));
3046 0 : }
3047 0 : }
3048 : }
3049 : }
3050 :
3051 0 : break;
3052 : }
3053 : case MetaActionType::GRADIENTEX :
3054 : {
3055 : /** SIMPLE, DONE */
3056 : // This is only a data holder which is interpreted inside comment actions,
3057 : // see MetaActionType::COMMENT for more info
3058 : // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
3059 0 : break;
3060 : }
3061 : case MetaActionType::LAYOUTMODE :
3062 : {
3063 : /** SIMPLE, DONE */
3064 0 : const MetaLayoutModeAction* pA = static_cast<const MetaLayoutModeAction*>(pAction);
3065 0 : rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
3066 0 : break;
3067 : }
3068 : case MetaActionType::TEXTLANGUAGE :
3069 : {
3070 : /** SIMPLE, DONE */
3071 2 : const MetaTextLanguageAction* pA = static_cast<const MetaTextLanguageAction*>(pAction);
3072 2 : rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
3073 2 : break;
3074 : }
3075 : case MetaActionType::OVERLINECOLOR :
3076 : {
3077 : /** SIMPLE, DONE */
3078 0 : const MetaOverlineColorAction* pA = static_cast<const MetaOverlineColorAction*>(pAction);
3079 0 : const bool bActive(pA->IsSetting());
3080 :
3081 0 : rPropertyHolders.Current().setOverlineColorActive(bActive);
3082 0 : if(bActive)
3083 0 : rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
3084 :
3085 0 : break;
3086 : }
3087 : case MetaActionType::COMMENT :
3088 : {
3089 : /** CHECKED, WORKS WELL */
3090 : // I already implemented
3091 : // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
3092 : // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
3093 : // but opted to remove these again; it works well without them
3094 : // and makes the code less dependent from those Metafile Add-Ons
3095 112 : const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
3096 :
3097 112 : if (pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
3098 : {
3099 : // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
3100 : // pure recorded paint of the gradients uses the XOR paint functionality
3101 : // ('trick'). This is (and will be) broblematic with AntAliasing, so it's
3102 : // better to use this info
3103 0 : const MetaGradientExAction* pMetaGradientExAction = 0;
3104 0 : bool bDone(false);
3105 0 : sal_uInt32 b(nAction + 1);
3106 :
3107 0 : for(; !bDone && b < nCount; b++)
3108 : {
3109 0 : pAction = rMetaFile.GetAction(b);
3110 :
3111 0 : if(MetaActionType::GRADIENTEX == pAction->GetType())
3112 : {
3113 0 : pMetaGradientExAction = static_cast<const MetaGradientExAction*>(pAction);
3114 : }
3115 0 : else if(MetaActionType::COMMENT == pAction->GetType())
3116 : {
3117 0 : if (static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END"))
3118 : {
3119 0 : bDone = true;
3120 : }
3121 : }
3122 : }
3123 :
3124 0 : if(bDone && pMetaGradientExAction)
3125 : {
3126 : // consume actions and skip forward
3127 0 : nAction = b - 1;
3128 :
3129 : // get geometry data
3130 0 : basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon());
3131 :
3132 0 : if(aPolyPolygon.count())
3133 : {
3134 : // transform geometry
3135 0 : aPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
3136 :
3137 : // get and check if gradient is a real gradient
3138 0 : const Gradient& rGradient = pMetaGradientExAction->GetGradient();
3139 0 : const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
3140 :
3141 0 : if(aAttribute.getStartColor() == aAttribute.getEndColor())
3142 : {
3143 : // not really a gradient
3144 0 : rTargetHolders.Current().append(
3145 : new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
3146 : aPolyPolygon,
3147 0 : aAttribute.getStartColor()));
3148 : }
3149 : else
3150 : {
3151 : // really a gradient
3152 0 : rTargetHolders.Current().append(
3153 : new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
3154 : aPolyPolygon,
3155 0 : aAttribute));
3156 0 : }
3157 0 : }
3158 : }
3159 : }
3160 :
3161 112 : break;
3162 : }
3163 : default:
3164 : {
3165 : OSL_FAIL("Unknown MetaFile Action (!)");
3166 0 : break;
3167 : }
3168 : }
3169 : }
3170 81 : }
3171 : } // end of anonymous namespace
3172 :
3173 :
3174 :
3175 : namespace drawinglayer
3176 : {
3177 : namespace primitive2d
3178 : {
3179 81 : Primitive2DSequence MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
3180 : {
3181 : // prepare target and porperties; each will have one default entry
3182 81 : TargetHolders aTargetHolders;
3183 162 : PropertyHolders aPropertyHolders;
3184 :
3185 : // set target MapUnit at Properties
3186 81 : aPropertyHolders.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit());
3187 :
3188 : // interpret the Metafile
3189 81 : interpretMetafile(getMetaFile(), aTargetHolders, aPropertyHolders, rViewInformation);
3190 :
3191 : // get the content. There should be only one target, as in the start condition,
3192 : // but iterating will be the right thing to do when some push/pop is not closed
3193 81 : Primitive2DSequence xRetval;
3194 :
3195 162 : while(aTargetHolders.size() > 1)
3196 : {
3197 : appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
3198 0 : aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
3199 0 : aTargetHolders.Pop();
3200 : }
3201 :
3202 : appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
3203 81 : aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
3204 :
3205 81 : if(xRetval.hasElements())
3206 : {
3207 : // get target size
3208 81 : const Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
3209 :
3210 : // create transformation
3211 81 : basegfx::B2DHomMatrix aAdaptedTransform;
3212 :
3213 81 : aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
3214 : aAdaptedTransform.scale(
3215 162 : aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0,
3216 243 : aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0);
3217 81 : aAdaptedTransform = getTransform() * aAdaptedTransform;
3218 :
3219 : // embed to target transformation
3220 : const Primitive2DReference aEmbeddedTransform(
3221 : new TransformPrimitive2D(
3222 : aAdaptedTransform,
3223 162 : xRetval));
3224 :
3225 162 : xRetval = Primitive2DSequence(&aEmbeddedTransform, 1);
3226 : }
3227 :
3228 162 : return xRetval;
3229 : }
3230 :
3231 129 : MetafilePrimitive2D::MetafilePrimitive2D(
3232 : const basegfx::B2DHomMatrix& rMetaFileTransform,
3233 : const GDIMetaFile& rMetaFile)
3234 : : BufferedDecompositionPrimitive2D(),
3235 : maMetaFileTransform(rMetaFileTransform),
3236 129 : maMetaFile(rMetaFile)
3237 : {
3238 129 : }
3239 :
3240 0 : bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
3241 : {
3242 0 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
3243 : {
3244 0 : const MetafilePrimitive2D& rCompare = static_cast<const MetafilePrimitive2D&>(rPrimitive);
3245 :
3246 0 : return (getTransform() == rCompare.getTransform()
3247 0 : && getMetaFile() == rCompare.getMetaFile());
3248 : }
3249 :
3250 0 : return false;
3251 : }
3252 :
3253 45 : basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
3254 : {
3255 : // use own implementation to quickly answer the getB2DRange question. The
3256 : // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
3257 : // this is not the case (i have already seen some wrong Metafiles) it should
3258 : // be embedded to a MaskPrimitive2D
3259 45 : basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
3260 45 : aRetval.transform(getTransform());
3261 :
3262 45 : return aRetval;
3263 : }
3264 :
3265 : // provide unique ID
3266 221 : ImplPrimitive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D)
3267 :
3268 : } // end of namespace primitive2d
3269 : } // end of namespace drawinglayer
3270 :
3271 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|