1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <tools/stream.hxx>
21 #include <vcl/metaact.hxx>
22 #include <vcl/graphicfilter.hxx>
23 #include <basegfx/tools/canvastools.hxx>
24 #include <basegfx/tools/gradienttools.hxx>
25 #include <basegfx/tools/tools.hxx>
26 #include <basegfx/numeric/ftools.hxx>
27 #include <basegfx/point/b2dpoint.hxx>
28 #include <basegfx/vector/b2dsize.hxx>
29 #include <basegfx/range/b2drange.hxx>
30 #include <basegfx/range/b2drectangle.hxx>
31 #include <basegfx/polygon/b2dlinegeometry.hxx>
32 #include <basegfx/polygon/b2dpolygon.hxx>
33 #include <basegfx/polygon/b2dpolygontools.hxx>
34 #include <basegfx/polygon/b2dpolypolygon.hxx>
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <vcl/canvastools.hxx>
37 #include <rtl/ustring.hxx>
38 #include <sal/alloca.h>
40 #include <com/sun/star/rendering/PathCapType.hpp>
41 #include <com/sun/star/rendering/PathJoinType.hpp>
42 #include <com/sun/star/rendering/TexturingMode.hpp>
43 #include <com/sun/star/rendering/XCanvas.hpp>
45 #include <bitmapaction.hxx>
46 #include <implrenderer.hxx>
47 #include <outdevstate.hxx>
48 #include <polypolyaction.hxx>
49 #include <textaction.hxx>
52 #define EmfPlusRecordTypeHeader 16385
53 #define EmfPlusRecordTypeEndOfFile 16386
54 #define EmfPlusRecordTypeGetDC 16388
55 #define EmfPlusRecordTypeObject 16392
56 #define EmfPlusRecordTypeFillRects 16394
57 #define EmfPlusRecordTypeFillPolygon 16396
58 #define EmfPlusRecordTypeDrawLines 16397
59 #define EmfPlusRecordTypeFillEllipse 16398
60 #define EmfPlusRecordTypeDrawEllipse 16399
61 #define EmfPlusRecordTypeFillPie 16400
62 #define EmfPlusRecordTypeFillPath 16404
63 #define EmfPlusRecordTypeDrawPath 16405
64 #define EmfPlusRecordTypeDrawImage 16410
65 #define EmfPlusRecordTypeDrawImagePoints 16411
66 #define EmfPlusRecordTypeDrawString 16412
67 #define EmfPlusRecordTypeSetRenderingOrigin 16413
68 #define EmfPlusRecordTypeSetAntiAliasMode 16414
69 #define EmfPlusRecordTypeSetTextRenderingHint 16415
70 #define EmfPlusRecordTypeSetInterpolationMode 16417
71 #define EmfPlusRecordTypeSetPixelOffsetMode 16418
72 #define EmfPlusRecordTypeSetCompositingQuality 16420
73 #define EmfPlusRecordTypeSave 16421
74 #define EmfPlusRecordTypeRestore 16422
75 #define EmfPlusRecordTypeBeginContainerNoParams 16424
76 #define EmfPlusRecordTypeEndContainer 16425
77 #define EmfPlusRecordTypeSetWorldTransform 16426
78 #define EmfPlusRecordTypeResetWorldTransform 16427
79 #define EmfPlusRecordTypeMultiplyWorldTransform 16428
80 #define EmfPlusRecordTypeSetPageTransform 16432
81 #define EmfPlusRecordTypeSetClipRect 16434
82 #define EmfPlusRecordTypeSetClipPath 16435
83 #define EmfPlusRecordTypeSetClipRegion 16436
84 #define EmfPlusRecordTypeDrawDriverString 16438
86 #define EmfPlusObjectTypeBrush 0x100
87 #define EmfPlusObjectTypePen 0x200
88 #define EmfPlusObjectTypePath 0x300
89 #define EmfPlusObjectTypeRegion 0x400
90 #define EmfPlusObjectTypeImage 0x500
91 #define EmfPlusObjectTypeFont 0x600
93 #define EmfPlusRegionInitialStateInfinite 0x10000003
95 const sal_Int32 EmfPlusLineStyleSolid
= 0x00000000;
96 const sal_Int32 EmfPlusLineStyleDash
= 0x00000001;
97 const sal_Int32 EmfPlusLineStyleDot
= 0x00000002;
98 const sal_Int32 EmfPlusLineStyleDashDot
= 0x00000003;
99 const sal_Int32 EmfPlusLineStyleDashDotDot
= 0x00000004;
100 const sal_Int32 EmfPlusLineStyleCustom
= 0x00000005;
102 const sal_uInt32 EmfPlusCustomLineCapDataTypeDefault
= 0x00000000;
103 const sal_uInt32 EmfPlusCustomLineCapDataTypeAdjustableArrow
= 0x00000001;
105 const sal_uInt32 EmfPlusCustomLineCapDataFillPath
= 0x00000001;
106 const sal_uInt32 EmfPlusCustomLineCapDataLinePath
= 0x00000002;
108 const sal_uInt32 EmfPlusLineCapTypeFlat
= 0x00000000;
109 const sal_uInt32 EmfPlusLineCapTypeSquare
= 0x00000001;
110 const sal_uInt32 EmfPlusLineCapTypeRound
= 0x00000002;
111 const sal_uInt32 EmfPlusLineCapTypeTriangle
= 0x00000003;
113 const sal_uInt32 EmfPlusLineJoinTypeMiter
= 0x00000000;
114 const sal_uInt32 EmfPlusLineJoinTypeBevel
= 0x00000001;
115 const sal_uInt32 EmfPlusLineJoinTypeRound
= 0x00000002;
116 const sal_uInt32 EmfPlusLineJoinTypeMiterClipped
= 0x00000003;
118 enum EmfPlusCombineMode
120 EmfPlusCombineModeReplace
= 0x00000000,
121 EmfPlusCombineModeIntersect
= 0x00000001,
122 EmfPlusCombineModeUnion
= 0x00000002,
123 EmfPlusCombineModeXOR
= 0x00000003,
124 EmfPlusCombineModeExclude
= 0x00000004,
125 EmfPlusCombineModeComplement
= 0x00000005
128 using namespace ::com::sun::star
;
129 using namespace ::basegfx
;
136 #if OSL_DEBUG_LEVEL > 1
137 void dumpWords (SvStream
& s
, int i
)
139 sal_uInt32 pos
= s
.Tell ();
141 SAL_INFO ("cppcanvas.emf", "EMF+ dumping words");
142 for (; i
> 0; i
--) {
144 SAL_INFO ("cppcanvas.emf", "EMF+\tdata: " << std::hex
<< data
<< std::dec
);
146 SAL_INFO ("cppcanvas.emf", "EMF+ end dumping words");
151 struct EMFPPath
: public EMFPObject
153 ::basegfx::B2DPolyPolygon aPolygon
;
156 sal_uInt8
* pPointTypes
;
159 EMFPPath (sal_Int32 _nPoints
, bool bLines
= false)
161 if( _nPoints
<0 || sal_uInt32(_nPoints
)>SAL_MAX_INT32
/(2*sizeof(float)) )
162 _nPoints
= SAL_MAX_INT32
/(2*sizeof(float));
164 pPoints
= new float [nPoints
*2];
166 pPointTypes
= new sal_uInt8
[_nPoints
];
174 delete [] pPointTypes
;
177 // TODO: remove rR argument when debug code is not longer needed
178 void Read (SvStream
& s
, sal_uInt32 pathFlags
, ImplRenderer
& rR
)
180 for (int i
= 0; i
< nPoints
; i
++) {
181 if (pathFlags
& 0x4000) {
182 // points are stored in signed short 16bit integer format
186 SAL_INFO ("cppcanvas.emf", "EMF+\tpoint [x,y]: " << x
<< "," << y
);
188 pPoints
[i
*2 + 1] = y
;
190 // points are stored in Single (float) format
191 s
>> pPoints
[i
*2] >> pPoints
[i
*2 + 1];
192 SAL_INFO ("cppcanvas.emf", "EMF+\tpoint [x,y]: " << pPoints
[i
*2] << "," << pPoints
[i
*2 + 1]);
197 for (int i
= 0; i
< nPoints
; i
++) {
198 s
>> pPointTypes
[i
];
199 SAL_INFO ("cppcanvas.emf", "EMF+\tpoint type: " << pPointTypes
[i
]);
204 #if OSL_DEBUG_LEVEL > 1
205 const ::basegfx::B2DRectangle
aBounds (::basegfx::tools::getRange (GetPolygon (rR
)));
207 SAL_INFO ("cppcanvas.emf",
208 "EMF+\tpolygon bounding box: " << aBounds
.getMinX () << "," << aBounds
.getMinY () << aBounds
.getWidth () << "x" << aBounds
.getHeight () << " (mapped)");
210 (void) rR
; // avoid warnings
214 ::basegfx::B2DPolyPolygon
& GetPolygon (ImplRenderer
& rR
, bool bMapIt
= true)
216 ::basegfx::B2DPolygon polygon
;
220 int last_normal
= 0, p
= 0;
221 ::basegfx::B2DPoint prev
, mapped
;
222 bool hasPrev
= false;
223 for (int i
= 0; i
< nPoints
; i
++) {
224 if (p
&& pPointTypes
&& (pPointTypes
[i
] == 0)) {
225 aPolygon
.append (polygon
);
232 mapped
= rR
.Map (pPoints
[i
*2], pPoints
[i
*2 + 1]);
234 mapped
= ::basegfx::B2DPoint (pPoints
[i
*2], pPoints
[i
*2 + 1]);
236 if ((pPointTypes
[i
] & 0x07) == 3) {
237 if (((i
- last_normal
)% 3) == 1) {
238 polygon
.setNextControlPoint (p
- 1, mapped
);
239 SAL_INFO ("cppcanvas.emf", "polygon append next: " << p
- 1 << " mapped: " << mapped
.getX () << "," << mapped
.getY ());
241 } else if (((i
- last_normal
) % 3) == 2) {
249 polygon
.append (mapped
);
250 SAL_INFO ("cppcanvas.emf", "polygon append point: " << pPoints
[i
*2] << "," << pPoints
[i
*2 + 1] << " mapped: " << mapped
.getX () << ":" << mapped
.getY ());
252 polygon
.setPrevControlPoint (p
, prev
);
253 SAL_INFO ("cppcanvas.emf", "polygon append prev: " << p
<< " mapped: " << prev
.getX () << "," << prev
.getY ());
257 if (pPointTypes
&& (pPointTypes
[i
] & 0x80)) { // closed polygon
258 polygon
.setClosed (true);
259 aPolygon
.append (polygon
);
260 SAL_INFO ("cppcanvas.emf", "close polygon");
267 if (polygon
.count ()) {
268 aPolygon
.append (polygon
);
270 #if OSL_DEBUG_LEVEL > 1
271 for (unsigned int i
=0; i
<aPolygon
.count(); i
++) {
272 polygon
= aPolygon
.getB2DPolygon(i
);
273 SAL_INFO ("cppcanvas.emf", "polygon: " << i
);
274 for (unsigned int j
=0; j
<polygon
.count(); j
++) {
275 ::basegfx::B2DPoint point
= polygon
.getB2DPoint(j
);
276 SAL_INFO ("cppcanvas.emf", "point: " << point
.getX() << "," << point
.getY());
277 if (polygon
.isPrevControlPointUsed(j
)) {
278 point
= polygon
.getPrevControlPoint(j
);
279 SAL_INFO ("cppcanvas.emf", "prev: " << point
.getX() << "," << point
.getY());
281 if (polygon
.isNextControlPointUsed(j
)) {
282 point
= polygon
.getNextControlPoint(j
);
283 SAL_INFO ("cppcanvas.emf", "next: " << point
.getX() << "," << point
.getY());
294 struct EMFPRegion
: public EMFPObject
297 sal_Int32
*combineMode
;
298 sal_Int32 initialState
;
299 EMFPPath
*initialPath
;
300 float ix
, iy
, iw
, ih
;
311 delete [] combineMode
;
320 void Read (SvStream
& s
)
324 s
>> header
>> parts
;
326 SAL_INFO ("cppcanvas.emf", "EMF+\tregion");
327 SAL_INFO ("cppcanvas.emf", "EMF+\theader: 0x" << std::hex
<< header
<< " parts: " << parts
<< std::dec
);
330 if( parts
<0 || sal_uInt32(parts
)>SAL_MAX_INT32
/sizeof(sal_Int32
) )
331 parts
= SAL_MAX_INT32
/sizeof(sal_Int32
);
333 combineMode
= new sal_Int32
[parts
];
335 for (int i
= 0; i
< parts
; i
++) {
336 s
>> combineMode
[i
];
337 SAL_INFO ("cppcanvas.emf", "EMF+\tcombine mode [" << i
<< "]: 0x" << std::hex
<< combineMode
[i
] << std::dec
);
342 SAL_INFO ("cppcanvas.emf", "EMF+\tinitial state: 0x" << std::hex
<< initialState
<< std::dec
);
346 struct EMFPBrush
: public EMFPObject
350 sal_uInt32 additionalFlags
;
352 /* linear gradient */
354 float areaX
, areaY
, areaWidth
, areaHeight
;
355 ::Color secondColor
; // first color is stored in solidColor;
356 XForm transformation
;
357 bool hasTransformation
;
358 sal_Int32 blendPoints
;
359 float* blendPositions
;
361 sal_Int32 colorblendPoints
;
362 float* colorblendPositions
;
363 ::Color
* colorblendColors
;
364 sal_Int32 surroundColorsNumber
;
365 ::Color
* surroundColors
;
371 blendPositions
= NULL
;
372 colorblendPositions
= NULL
;
373 colorblendColors
= NULL
;
374 surroundColors
= NULL
;
376 hasTransformation
= false;
381 if (blendPositions
!= NULL
) {
382 delete[] blendPositions
;
383 blendPositions
= NULL
;
385 if (colorblendPositions
!= NULL
) {
386 delete[] colorblendPositions
;
387 colorblendPositions
= NULL
;
389 if (colorblendColors
!= NULL
) {
390 delete[] colorblendColors
;
391 colorblendColors
= NULL
;
393 if (surroundColors
!= NULL
) {
394 delete[] surroundColors
;
395 surroundColors
= NULL
;
403 sal_uInt32
GetType() const { return type
; }
404 const ::Color
& GetColor() const { return solidColor
; }
406 void Read (SvStream
& s
, ImplRenderer
& rR
)
412 SAL_INFO ("cppcanvas.emf", "EMF+\tbrush");
413 SAL_INFO ("cppcanvas.emf", "EMF+\theader: 0x" << std::hex
<< header
<< " type: " << type
<< std::dec
);
421 solidColor
= ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
422 SAL_INFO ("cppcanvas.emf", "EMF+\tsolid color: 0x" << std::hex
<< color
<< std::dec
);
429 s
>> additionalFlags
>> wrapMode
;
431 SAL_INFO ("cppcanvas.emf", "EMF+\tpath gradient, additional flags: 0x" << std::hex
<< additionalFlags
<< std::dec
);
436 solidColor
= ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
437 SAL_INFO("cppcanvas.emf", "EMF+\tcenter color: 0x" << std::hex
<< color
<< std::dec
);
440 SAL_INFO("cppcanvas.emf", "EMF+\tcenter point: " << areaX
<< "," << areaY
);
442 s
>> surroundColorsNumber
;
443 SAL_INFO("cppcanvas.emf", "EMF+\tsurround colors: " << surroundColorsNumber
);
445 if( surroundColorsNumber
<0 || sal_uInt32(surroundColorsNumber
)>SAL_MAX_INT32
/sizeof(::Color
) )
446 surroundColorsNumber
= SAL_MAX_INT32
/sizeof(::Color
);
448 surroundColors
= new ::Color
[surroundColorsNumber
];
449 for (int i
= 0; i
< surroundColorsNumber
; i
++) {
451 surroundColors
[i
] = ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
453 secondColor
= surroundColors
[0];
454 SAL_INFO("cppcanvas.emf", "EMF+\tsurround color[" << i
<< "]: 0x" << std::hex
<< color
<< std::dec
);
457 if (additionalFlags
& 0x01) {
458 sal_Int32 pathLength
;
461 SAL_INFO("cppcanvas.emf", "EMF+\tpath length: " << pathLength
);
463 sal_uInt32 pos
= s
.Tell ();
464 #if OSL_DEBUG_LEVEL > 1
468 sal_uInt32 pathHeader
;
469 sal_Int32 pathPoints
, pathFlags
;
470 s
>> pathHeader
>> pathPoints
>> pathFlags
;
472 SAL_INFO("cppcanvas.emf", "EMF+\tpath (brush path gradient)");
473 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex
<< pathHeader
<< " points: " << std::dec
<< pathPoints
<< " additional flags: 0x" << std::hex
<< pathFlags
<< std::dec
);
475 path
= new EMFPPath (pathPoints
);
476 path
->Read (s
, pathFlags
, rR
);
478 s
.Seek (pos
+ pathLength
);
480 const ::basegfx::B2DRectangle
aBounds (::basegfx::tools::getRange (path
->GetPolygon (rR
, false)));
481 areaWidth
= aBounds
.getWidth ();
482 areaHeight
= aBounds
.getHeight ();
484 SAL_INFO("cppcanvas.emf", "EMF+\tpolygon bounding box: " << aBounds
.getMinX () << "," << aBounds
.getMinY () << " " << aBounds
.getWidth () << "x" << aBounds
.getHeight ());
487 if (additionalFlags
& 0x02) {
488 SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
490 hasTransformation
= true;
491 SAL_INFO("cppcanvas.emf",
492 "EMF+\tm11: " << transformation
.eM11
<< " m12: " << transformation
.eM12
<<
493 "\nEMF+\tm21: " << transformation
.eM21
<< " m22: " << transformation
.eM22
<<
494 "\nEMF+\tdx: " << transformation
.eDx
<< " dy: " << transformation
.eDy
);
497 if (additionalFlags
& 0x08) {
499 SAL_INFO("cppcanvas.emf", "EMF+\tuse blend, points: " << blendPoints
);
500 if( blendPoints
<0 || sal_uInt32(blendPoints
)>SAL_MAX_INT32
/(2*sizeof(float)) )
501 blendPoints
= SAL_MAX_INT32
/(2*sizeof(float));
502 blendPositions
= new float [2*blendPoints
];
503 blendFactors
= blendPositions
+ blendPoints
;
504 for (int i
=0; i
< blendPoints
; i
++) {
505 s
>> blendPositions
[i
];
506 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i
<< "]: " << blendPositions
[i
]);
508 for (int i
=0; i
< blendPoints
; i
++) {
509 s
>> blendFactors
[i
];
510 SAL_INFO("cppcanvas.emf", "EMF+\tfactor[" << i
<< "]: " << blendFactors
[i
]);
514 if (additionalFlags
& 0x04) {
515 s
>> colorblendPoints
;
516 SAL_INFO("cppcanvas.emf", "EMF+\tuse color blend, points: " << colorblendPoints
);
517 if( colorblendPoints
<0 || sal_uInt32(colorblendPoints
)>SAL_MAX_INT32
/sizeof(float) )
518 colorblendPoints
= SAL_MAX_INT32
/sizeof(float);
519 if( sal_uInt32(colorblendPoints
)>SAL_MAX_INT32
/sizeof(::Color
) )
520 colorblendPoints
= SAL_MAX_INT32
/sizeof(::Color
);
521 colorblendPositions
= new float [colorblendPoints
];
522 colorblendColors
= new ::Color
[colorblendPoints
];
523 for (int i
=0; i
< colorblendPoints
; i
++) {
524 s
>> colorblendPositions
[i
];
525 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i
<< "]: " << colorblendPositions
[i
]);
527 for (int i
=0; i
< colorblendPoints
; i
++) {
529 colorblendColors
[i
] = ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
530 SAL_INFO("cppcanvas.emf", "EMF+\tcolor[" << i
<< "]: 0x" << std::hex
<< color
<< std::dec
);
534 #if OSL_DEBUG_LEVEL > 1
543 s
>> additionalFlags
>> wrapMode
;
545 SAL_INFO("cppcanvas.emf", "EMF+\tlinear gradient, additional flags: 0x" << std::hex
<< additionalFlags
<< std::dec
);
547 s
>> areaX
>> areaY
>> areaWidth
>> areaHeight
;
549 SAL_INFO("cppcanvas.emf", "EMF+\tarea: " << areaX
<< "," << areaY
<< " - " << areaWidth
<< "x" << areaHeight
);
554 solidColor
= ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
555 SAL_INFO("cppcanvas.emf", "EMF+\tfirst color: 0x" << std::hex
<< color
<< std::dec
);
558 secondColor
= ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
559 SAL_INFO("cppcanvas.emf", "EMF+\tsecond color: 0x" << std::hex
<< color
<< std::dec
);
561 // repeated colors, unknown meaning, see http://www.aces.uiuc.edu/~jhtodd/Metafile/MetafileRecords/ObjectBrush.html
565 if (additionalFlags
& 0x02) {
566 SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
568 hasTransformation
= true;
569 SAL_INFO("cppcanvas.emf",
570 "EMF+\tm11: " << transformation
.eM11
<< " m12: " << transformation
.eM12
<<
571 "\nEMF+\tm21: " << transformation
.eM21
<< " m22: " << transformation
.eM22
<<
572 "\nEMF+\tdx: " << transformation
.eDx
<< " dy: " << transformation
.eDy
);
574 if (additionalFlags
& 0x08) {
576 SAL_INFO("cppcanvas.emf", "EMF+\tuse blend, points: " << blendPoints
);
577 if( blendPoints
<0 || sal_uInt32(blendPoints
)>SAL_MAX_INT32
/(2*sizeof(float)) )
578 blendPoints
= SAL_MAX_INT32
/(2*sizeof(float));
579 blendPositions
= new float [2*blendPoints
];
580 blendFactors
= blendPositions
+ blendPoints
;
581 for (int i
=0; i
< blendPoints
; i
++) {
582 s
>> blendPositions
[i
];
583 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i
<< "]: " << blendPositions
[i
]);
585 for (int i
=0; i
< blendPoints
; i
++) {
586 s
>> blendFactors
[i
];
587 SAL_INFO("cppcanvas.emf", "EMF+\tfactor[" << i
<< "]: " << blendFactors
[i
]);
591 if (additionalFlags
& 0x04) {
592 s
>> colorblendPoints
;
593 SAL_INFO("cppcanvas.emf", "EMF+\tuse color blend, points: " << colorblendPoints
);
594 if( colorblendPoints
<0 || sal_uInt32(colorblendPoints
)>SAL_MAX_INT32
/sizeof(float) )
595 colorblendPoints
= SAL_MAX_INT32
/sizeof(float);
596 if( sal_uInt32(colorblendPoints
)>SAL_MAX_INT32
/sizeof(::Color
) )
597 colorblendPoints
= sal_uInt32(SAL_MAX_INT32
)/sizeof(::Color
);
598 colorblendPositions
= new float [colorblendPoints
];
599 colorblendColors
= new ::Color
[colorblendPoints
];
600 for (int i
=0; i
< colorblendPoints
; i
++) {
601 s
>> colorblendPositions
[i
];
602 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i
<< "]: " << colorblendPositions
[i
]);
604 for (int i
=0; i
< colorblendPoints
; i
++) {
606 colorblendColors
[i
] = ::Color (0xff - (color
>> 24), (color
>> 16) & 0xff, (color
>> 8) & 0xff, color
& 0xff);
607 SAL_INFO("cppcanvas.emf", "EMF+\tcolor[" << i
<< "]: 0x" << std::hex
<< color
<< std::dec
);
614 SAL_INFO("cppcanvas.emf", "EMF+\tunhandled brush type: " << std::hex
<< type
<< std::dec
);
619 /// Convert stroke caps between EMF+ and rendering API
620 sal_Int8
lcl_convertStrokeCap(sal_uInt32 nEmfStroke
)
624 case EmfPlusLineCapTypeSquare
: return rendering::PathCapType::SQUARE
;
625 case EmfPlusLineCapTypeRound
: return rendering::PathCapType::ROUND
;
628 // we have no mapping for EmfPlusLineCapTypeTriangle, so return
630 return rendering::PathCapType::BUTT
;
633 sal_Int8
lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin
)
635 switch (nEmfLineJoin
)
637 case EmfPlusLineJoinTypeMiter
: // fall-through
638 case EmfPlusLineJoinTypeMiterClipped
: return rendering::PathJoinType::MITER
;
639 case EmfPlusLineJoinTypeBevel
: return rendering::PathJoinType::BEVEL
;
640 case EmfPlusLineJoinTypeRound
: return rendering::PathJoinType::ROUND
;
642 assert(false); // Line Join type isn't in specification.
646 struct EMFPCustomLineCap
: public EMFPObject
649 sal_uInt32 strokeStartCap
, strokeEndCap
, strokeJoin
;
651 basegfx::B2DPolyPolygon polygon
;
655 EMFPCustomLineCap() : EMFPObject()
663 void SetAttributes(rendering::StrokeAttributes
& aAttributes
)
665 aAttributes
.StartCapType
= lcl_convertStrokeCap(strokeStartCap
);
666 aAttributes
.EndCapType
= lcl_convertStrokeCap(strokeEndCap
);
667 aAttributes
.JoinType
= lcl_convertLineJoinType(strokeJoin
);
669 aAttributes
.MiterLimit
= miterLimit
;
672 void ReadPath(SvStream
& s
, ImplRenderer
& rR
, bool bFill
)
674 sal_Int32 pathLength
;
676 SAL_INFO("cppcanvas.emf", "EMF+\t\tpath length: " << pathLength
);
678 sal_uInt32 pathHeader
;
679 sal_Int32 pathPoints
, pathFlags
;
680 s
>> pathHeader
>> pathPoints
>> pathFlags
;
682 SAL_INFO("cppcanvas.emf", "EMF+\t\tpath (custom cap line path)");
683 SAL_INFO("cppcanvas.emf", "EMF+\t\theader: 0x" << std::hex
<< pathHeader
<< " points: " << std::dec
<< pathPoints
<< " additional flags: 0x" << std::hex
<< pathFlags
<< std::dec
);
685 EMFPPath
path(pathPoints
);
686 path
.Read(s
, pathFlags
, rR
);
688 polygon
= path
.GetPolygon(rR
, false);
691 // transformation to convert the path to what LibreOffice
693 B2DHomMatrix aMatrix
;
694 aMatrix
.scale(1.0, -1.0);
696 polygon
.transform(aMatrix
);
699 void Read (SvStream
& s
, ImplRenderer
& rR
)
705 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustom cap");
706 SAL_INFO("cppcanvas.emf", "EMF+\t\theader: 0x" << std::hex
<< header
<< " type: " << type
<< std::dec
);
708 if (type
== EmfPlusCustomLineCapDataTypeDefault
)
710 sal_uInt32 customLineCapDataFlags
, baseCap
;
713 float fillHotSpotX
, fillHotSpotY
, strokeHotSpotX
, strokeHotSpotY
;
715 s
>> customLineCapDataFlags
>> baseCap
>> baseInset
716 >> strokeStartCap
>> strokeEndCap
>> strokeJoin
717 >> miterLimit
>> widthScale
718 >> fillHotSpotX
>> fillHotSpotY
>> strokeHotSpotX
>> strokeHotSpotY
;
720 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex
<< customLineCapDataFlags
);
721 SAL_INFO("cppcanvas.emf", "EMF+\t\tbaseCap: 0x" << std::hex
<< baseCap
);
722 SAL_INFO("cppcanvas.emf", "EMF+\t\tbaseInset: " << baseInset
);
723 SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex
<< strokeStartCap
);
724 SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex
<< strokeEndCap
);
725 SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex
<< strokeJoin
);
726 SAL_INFO("cppcanvas.emf", "EMF+\t\tmiterLimit: " << miterLimit
);
727 SAL_INFO("cppcanvas.emf", "EMF+\t\twidthScale: " << widthScale
);
729 if (customLineCapDataFlags
& EmfPlusCustomLineCapDataFillPath
)
731 ReadPath(s
, rR
, true);
734 if (customLineCapDataFlags
& EmfPlusCustomLineCapDataLinePath
)
736 ReadPath(s
, rR
, false);
739 else if (type
== EmfPlusCustomLineCapDataTypeAdjustableArrow
)
741 // TODO only reads the data, does not use them [I've had
742 // no test document to be able to implement it]
744 sal_Int32 width
, height
, middleInset
, fillState
, lineStartCap
;
745 sal_Int32 lineEndCap
, lineJoin
, widthScale
;
746 float fillHotSpotX
, fillHotSpotY
, lineHotSpotX
, lineHotSpotY
;
748 s
>> width
>> height
>> middleInset
>> fillState
>> lineStartCap
749 >> lineEndCap
>> lineJoin
>> miterLimit
>> widthScale
750 >> fillHotSpotX
>> fillHotSpotY
>> lineHotSpotX
>> lineHotSpotY
;
752 SAL_INFO("cppcanvas.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)");
757 struct EMFPPen
: public EMFPBrush
759 XForm transformation
;
768 sal_Int32 dashPatternLen
;
771 sal_Int32 compoundArrayLen
;
772 float *compoundArray
;
773 sal_Int32 customStartCapLen
;
774 EMFPCustomLineCap
*customStartCap
;
775 sal_Int32 customEndCapLen
;
776 EMFPCustomLineCap
*customEndCap
;
779 EMFPPen () : EMFPBrush ()
782 compoundArray
= NULL
;
783 customStartCap
= NULL
;
789 delete[] dashPattern
;
790 delete[] compoundArray
;
791 delete customStartCap
;
795 void SetStrokeWidth(rendering::StrokeAttributes
& rStrokeAttributes
, ImplRenderer
& rR
, const OutDevState
& rState
)
797 #if OSL_DEBUG_LEVEL > 1
799 SAL_INFO ("cppcanvas.emf", "TODO: pen with zero width - using minimal which might not be correct\n");
802 rStrokeAttributes
.StrokeWidth
= fabs((rState
.mapModeTransform
* rR
.MapSize (width
== 0.0 ? 0.05 : width
, 0)).getLength());
805 void SetStrokeAttributes(rendering::StrokeAttributes
& rStrokeAttributes
)
807 rStrokeAttributes
.JoinType
= lcl_convertLineJoinType(lineJoin
);
809 if (dashStyle
!= EmfPlusLineStyleSolid
)
811 const float dash
[] = {3, 3};
812 const float dot
[] = {1, 3};
813 const float dashdot
[] = {3, 3, 1, 3};
814 const float dashdotdot
[] = {3, 3, 1, 3, 1, 3};
817 const float *pPattern
;
820 case EmfPlusLineStyleDash
: nLen
= SAL_N_ELEMENTS(dash
); pPattern
= dash
; break;
821 case EmfPlusLineStyleDot
: nLen
= SAL_N_ELEMENTS(dot
); pPattern
= dot
; break;
822 case EmfPlusLineStyleDashDot
: nLen
= SAL_N_ELEMENTS(dashdot
); pPattern
= dashdot
; break;
823 case EmfPlusLineStyleDashDotDot
: nLen
= SAL_N_ELEMENTS(dashdotdot
); pPattern
= dashdotdot
; break;
824 case EmfPlusLineStyleCustom
: nLen
= dashPatternLen
; pPattern
= dashPattern
; break;
828 uno::Sequence
<double> aDashArray(nLen
);
829 for (int i
= 0; i
< nLen
; ++i
)
830 aDashArray
[i
] = pPattern
[i
];
832 rStrokeAttributes
.DashArray
= aDashArray
;
837 void Read (SvStream
& s
, ImplRenderer
& rR
, sal_Int32
, sal_Int32
)
839 sal_uInt32 header
, unknown
, penFlags
, unknown2
;
842 s
>> header
>> unknown
>> penFlags
>> unknown2
>> width
;
844 SAL_INFO("cppcanvas.emf", "EMF+\tpen");
845 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex
<< header
<< " unknown: 0x" << unknown
<<
846 " additional flags: 0x" << penFlags
<< " unknown: 0x" << unknown2
<< " width: " << std::dec
<< width
);
854 SAL_INFO("cppcanvas.emf", "EMF+\t\tstartCap: 0x" << std::hex
<< startCap
);
862 SAL_INFO("cppcanvas.emf", "EMF+\t\tendCap: 0x" << std::hex
<< endCap
);
880 SAL_INFO("cppcanvas.emf", "EMF+\t\tdashStyle: 0x" << std::hex
<< dashStyle
);
897 dashStyle
= EmfPlusLineStyleCustom
;
900 SAL_INFO("cppcanvas.emf", "EMF+\t\tdashPatternLen: " << dashPatternLen
);
902 if( dashPatternLen
<0 || sal_uInt32(dashPatternLen
)>SAL_MAX_INT32
/sizeof(float) )
903 dashPatternLen
= SAL_MAX_INT32
/sizeof(float);
904 dashPattern
= new float [dashPatternLen
];
905 for (i
= 0; i
< dashPatternLen
; i
++)
907 s
>> dashPattern
[i
];
908 SAL_INFO("cppcanvas.emf", "EMF+\t\t\tdashPattern[" << i
<< "]: " << dashPattern
[i
]);
919 if (penFlags
& 1024) {
920 s
>> compoundArrayLen
;
921 if( compoundArrayLen
<0 || sal_uInt32(compoundArrayLen
)>SAL_MAX_INT32
/sizeof(float) )
922 compoundArrayLen
= SAL_MAX_INT32
/sizeof(float);
923 compoundArray
= new float [compoundArrayLen
];
924 for (i
= 0; i
< compoundArrayLen
; i
++)
925 s
>> compoundArray
[i
];
927 compoundArrayLen
= 0;
931 s
>> customStartCapLen
;
932 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomStartCapLen: " << customStartCapLen
);
933 sal_uInt32 pos
= s
.Tell();
935 customStartCap
= new EMFPCustomLineCap();
936 customStartCap
->Read(s
, rR
);
938 // maybe we don't read everything yet, play it safe ;-)
939 s
.Seek(pos
+ customStartCapLen
);
942 customStartCapLen
= 0;
946 s
>> customEndCapLen
;
947 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomEndCapLen: " << customEndCapLen
);
948 sal_uInt32 pos
= s
.Tell();
950 customEndCap
= new EMFPCustomLineCap();
951 customEndCap
->Read(s
, rR
);
953 // maybe we don't read everything yet, play it safe ;-)
954 s
.Seek(pos
+ customEndCapLen
);
959 EMFPBrush::Read (s
, rR
);
963 struct EMFPImage
: public EMFPObject
969 sal_Int32 pixelFormat
;
973 void Read (SvMemoryStream
&s
, sal_uInt32 dataSize
, sal_Bool bUseWholeStream
)
975 sal_uInt32 header
, unknown
;
979 SAL_INFO("cppcanvas.emf", "EMF+\timage\nEMF+\theader: 0x" << std::hex
<< header
<< " type: " << type
<< std::dec
);
981 if (type
== 1) { // bitmap
982 s
>> width
>> height
>> stride
>> pixelFormat
>> unknown
;
983 SAL_INFO("cppcanvas.emf", "EMF+\tbitmap width: " << width
<< " height: " << height
<< " stride: " << "pixelFormat: 0x" << std::hex
<< pixelFormat
<< std::dec
);
984 if (width
== 0) { // non native formats
985 GraphicFilter filter
;
987 filter
.ImportGraphic (graphic
, String (), s
);
988 SAL_INFO("cppcanvas.emf", "EMF+\tbitmap width: " << graphic
.GetBitmap().GetSizePixel().Width() << " height: " << graphic
.GetBitmap().GetSizePixel().Height());
991 } else if (type
== 2) {
992 sal_Int32 mfType
, mfSize
;
994 s
>> mfType
>> mfSize
;
995 SAL_INFO("cppcanvas.emf", "EMF+\tmetafile type: " << mfType
<< " dataSize: " << mfSize
<< " real size calculated from record dataSize: " << dataSize
- 16);
997 GraphicFilter filter
;
998 // workaround buggy metafiles, which have wrong mfSize set (n#705956 for example)
999 SvMemoryStream
mfStream (((char *)s
.GetData()) + s
.Tell(), bUseWholeStream
? s
.remainingSize() : dataSize
- 16, STREAM_READ
);
1001 filter
.ImportGraphic (graphic
, String (), mfStream
);
1003 // debug code - write the stream to debug file /tmp/emf-stream.emf
1004 #if OSL_DEBUG_LEVEL > 1
1006 static sal_Int32 emfp_debug_stream_number
= 0;
1007 OUString
emfp_debug_filename("/tmp/emf-embedded-stream");
1008 emfp_debug_filename
+= OUString::valueOf(emfp_debug_stream_number
++);
1009 emfp_debug_filename
+= OUString(".emf");
1011 SvFileStream
file( emfp_debug_filename
, STREAM_WRITE
| STREAM_TRUNC
);
1021 struct EMFPFont
: public EMFPObject
1025 sal_uInt32 sizeUnit
;
1026 sal_Int32 fontFlags
;
1029 void Read (SvMemoryStream
&s
)
1032 sal_uInt32 reserved
;
1035 s
>> header
>> emSize
>> sizeUnit
>> fontFlags
>> reserved
>> length
;
1037 OSL_ASSERT( ( header
>> 12 ) == 0xdbc01 );
1039 SAL_INFO("cppcanvas.emf", "EMF+\tfont\n"
1040 << "EMF+\theader: 0x" << std::hex
<< (header
>> 12) << " version: 0x" << (header
& 0x1fff) << " size: " << std::dec
<< emSize
<< " unit: 0x" << std::hex
<< sizeUnit
<< std::dec
);
1041 SAL_INFO("cppcanvas.emf", "EMF+\tflags: 0x" << std::hex
<< fontFlags
<< " reserved: 0x" << reserved
<< " length: 0x" << std::hex
<< length
<< std::dec
);
1043 if( length
> 0 && length
< 0x4000 ) {
1044 sal_Unicode
*chars
= (sal_Unicode
*) alloca( sizeof( sal_Unicode
) * length
);
1046 for( sal_uInt32 i
= 0; i
< length
; i
++ )
1049 family
= OUString( chars
, length
);
1050 SAL_INFO("cppcanvas.emf", "EMF+\tfamily: " << OUStringToOString( family
, RTL_TEXTENCODING_UTF8
).getStr()); // TODO: can we just use family?
1055 void ImplRenderer::ReadRectangle (SvStream
& s
, float& x
, float& y
, float &width
, float& height
, bool bCompressed
)
1058 sal_Int16 ix
, iy
, iw
, ih
;
1060 s
>> ix
>> iy
>> iw
>> ih
;
1067 s
>> x
>> y
>> width
>> height
;
1070 void ImplRenderer::ReadPoint (SvStream
& s
, float& x
, float& y
, sal_uInt32 flags
)
1072 if (flags
& 0x4000) {
1083 void ImplRenderer::MapToDevice (double& x
, double& y
)
1085 // TODO: other untis
1086 x
= 100*nMmX
*x
/nPixX
;
1087 y
= 100*nMmY
*y
/nPixY
;
1090 ::basegfx::B2DPoint
ImplRenderer::Map (double ix
, double iy
)
1094 x
= ix
*aWorldTransform
.eM11
+ iy
*aWorldTransform
.eM21
+ aWorldTransform
.eDx
;
1095 y
= ix
*aWorldTransform
.eM12
+ iy
*aWorldTransform
.eM22
+ aWorldTransform
.eDy
;
1102 x
*= aBaseTransform
.eM11
;
1103 y
*= aBaseTransform
.eM22
;
1105 return ::basegfx::B2DPoint (x
, y
);
1108 ::basegfx::B2DSize
ImplRenderer::MapSize (double iwidth
, double iheight
)
1112 w
= iwidth
*aWorldTransform
.eM11
+ iheight
*aWorldTransform
.eM21
;
1113 h
= iwidth
*aWorldTransform
.eM12
+ iheight
*aWorldTransform
.eM22
;
1117 w
*= aBaseTransform
.eM11
;
1118 h
*= aBaseTransform
.eM22
;
1120 return ::basegfx::B2DSize (w
, h
);
1124 ::vcl::unotools::colorToDoubleSequence( ::Color (0xff - (x >> 24), \
1128 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
1130 void ImplRenderer::EMFPPlusFillPolygon (::basegfx::B2DPolyPolygon
& polygon
, const ActionFactoryParameters
& rParms
,
1131 OutDevState
& rState
, const CanvasSharedPtr
& rCanvas
, bool isColor
, sal_uInt32 brushIndexOrColor
)
1133 ::basegfx::B2DPolyPolygon
localPolygon (polygon
);
1135 SAL_INFO("cppcanvas.emf", "EMF+\tfill polygon");
1137 localPolygon
.transform( rState
.mapModeTransform
);
1139 ActionSharedPtr pPolyAction
;
1142 SAL_INFO("cppcanvas.emf", "EMF+\t\tcolor fill:0x" << std::hex
<< brushIndexOrColor
<< std::dec
);
1143 rState
.isFillColorSet
= true;
1144 rState
.isLineColorSet
= false;
1146 rState
.fillColor
= COLOR(brushIndexOrColor
);
1148 pPolyAction
= ActionSharedPtr ( internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon
, rParms
.mrCanvas
, rState
) );
1151 rState
.isFillColorSet
= true;
1153 EMFPBrush
* brush
= (EMFPBrush
*) aObjects
[brushIndexOrColor
& 0xff];
1154 SAL_INFO("cppcanvas.emf", "EMF+\tbrush fill slot: " << brushIndexOrColor
<< " (type: " << brush
->GetType () << ")");
1156 // give up in case something wrong happened
1160 rState
.isFillColorSet
= false;
1161 rState
.isLineColorSet
= false;
1163 if (brush
->type
== 3 || brush
->type
== 4) {
1165 if (brush
->type
== 3 && !(brush
->additionalFlags
& 0x1))
1166 return; // we are unable to parse these brushes yet
1168 ::basegfx::B2DHomMatrix aTextureTransformation
;
1169 ::basegfx::B2DHomMatrix aWorldTransformation
;
1170 ::basegfx::B2DHomMatrix aBaseTransformation
;
1171 rendering::Texture aTexture
;
1173 aWorldTransformation
.set (0, 0, aWorldTransform
.eM11
);
1174 aWorldTransformation
.set (0, 1, aWorldTransform
.eM21
);
1175 aWorldTransformation
.set (0, 2, aWorldTransform
.eDx
);
1176 aWorldTransformation
.set (1, 0, aWorldTransform
.eM12
);
1177 aWorldTransformation
.set (1, 1, aWorldTransform
.eM22
);
1178 aWorldTransformation
.set (1, 2, aWorldTransform
.eDy
);
1180 aBaseTransformation
.set (0, 0, aBaseTransform
.eM11
);
1181 aBaseTransformation
.set (0, 1, aBaseTransform
.eM21
);
1182 aBaseTransformation
.set (0, 2, aBaseTransform
.eDx
);
1183 aBaseTransformation
.set (1, 0, aBaseTransform
.eM12
);
1184 aBaseTransformation
.set (1, 1, aBaseTransform
.eM22
);
1185 aBaseTransformation
.set (1, 2, aBaseTransform
.eDy
);
1187 if (brush
->type
== 4) {
1188 aTextureTransformation
.scale (brush
->areaWidth
, brush
->areaHeight
);
1189 aTextureTransformation
.translate (brush
->areaX
, brush
->areaY
);
1191 aTextureTransformation
.translate (-0.5, -0.5);
1192 aTextureTransformation
.scale (brush
->areaWidth
, brush
->areaHeight
);
1193 aTextureTransformation
.translate (brush
->areaX
,brush
->areaY
);
1196 if (brush
->hasTransformation
) {
1197 ::basegfx::B2DHomMatrix aTransformation
;
1199 aTransformation
.set (0, 0, brush
->transformation
.eM11
);
1200 aTransformation
.set (0, 1, brush
->transformation
.eM21
);
1201 aTransformation
.set (0, 2, brush
->transformation
.eDx
);
1202 aTransformation
.set (1, 0, brush
->transformation
.eM12
);
1203 aTransformation
.set (1, 1, brush
->transformation
.eM22
);
1204 aTransformation
.set (1, 2, brush
->transformation
.eDy
);
1206 aTextureTransformation
*= aTransformation
;
1209 aTextureTransformation
*= aWorldTransformation
;
1210 aTextureTransformation
.scale (100.0*nMmX
/nPixX
, 100.0*nMmY
/nPixY
);
1211 aTextureTransformation
.translate (-nFrameLeft
, -nFrameTop
);
1212 aTextureTransformation
*= rState
.mapModeTransform
;
1213 aTextureTransformation
*= aBaseTransformation
;
1215 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
1216 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
1217 aTexture
.Alpha
= 1.0;
1219 basegfx::ODFGradientInfo aGradInfo
;
1220 OUString aGradientService
;
1222 const uno::Sequence
< double > aStartColor(
1223 ::vcl::unotools::colorToDoubleSequence( brush
->solidColor
,
1224 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() ) );
1225 const uno::Sequence
< double > aEndColor(
1226 ::vcl::unotools::colorToDoubleSequence( brush
->secondColor
,
1227 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() ) );
1228 uno::Sequence
< uno::Sequence
< double > > aColors (2);
1229 uno::Sequence
< double > aStops (2);
1231 if (brush
->blendPositions
) {
1232 SAL_INFO("cppcanvas.emf", "EMF+\t\tuse blend");
1233 aColors
.realloc (brush
->blendPoints
);
1234 aStops
.realloc (brush
->blendPoints
);
1235 int length
= aStartColor
.getLength ();
1236 uno::Sequence
< double > aColor (length
);
1238 OSL_ASSERT (length
== aEndColor
.getLength());
1240 for (int i
= 0; i
< brush
->blendPoints
; i
++) {
1241 aStops
[i
] = brush
->blendPositions
[i
];
1243 for (int j
= 0; j
< length
; j
++) {
1244 if (brush
->type
== 4) {
1245 aColor
[j
] = aStartColor
[j
]*(1 - brush
->blendFactors
[i
]) + aEndColor
[j
]*brush
->blendFactors
[i
];
1247 aColor
[j
] = aStartColor
[j
]*brush
->blendFactors
[i
] + aEndColor
[j
]*(1 - brush
->blendFactors
[i
]);
1250 aColors
[i
] = aColor
;
1252 } else if (brush
->colorblendPositions
) {
1253 SAL_INFO("cppcanvas.emf", "EMF+\t\tuse color blend");
1254 aColors
.realloc (brush
->colorblendPoints
);
1255 aStops
.realloc (brush
->colorblendPoints
);
1257 for (int i
= 0; i
< brush
->colorblendPoints
; i
++) {
1258 aStops
[i
] = brush
->colorblendPositions
[i
];
1259 aColors
[(brush
->type
== 4) ? i
: brush
->colorblendPoints
- 1 - i
] = ::vcl::unotools::colorToDoubleSequence( brush
->colorblendColors
[i
],
1260 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1266 if (brush
->type
== 4) {
1267 aColors
[0] = aStartColor
;
1268 aColors
[1] = aEndColor
;
1270 aColors
[1] = aStartColor
;
1271 aColors
[0] = aEndColor
;
1275 SAL_INFO("cppcanvas.emf", "EMF+\t\tset gradient");
1276 basegfx::B2DRange
aBoundsRectangle (0, 0, 1, 1);
1277 if (brush
->type
== 4) {
1278 aGradientService
= "LinearGradient";
1279 basegfx::tools::createLinearODFGradientInfo( aGradInfo
,
1286 aGradientService
= "EllipticalGradient";
1287 basegfx::tools::createEllipticalODFGradientInfo( aGradInfo
,
1289 ::basegfx::B2DVector( 0, 0 ),
1295 uno::Reference
< lang::XMultiServiceFactory
> xFactory(
1296 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
1298 if( xFactory
.is() ) {
1299 uno::Sequence
<uno::Any
> args( 3 );
1300 beans::PropertyValue aProp
;
1301 aProp
.Name
= "Colors";
1302 aProp
.Value
<<= aColors
;
1304 aProp
.Name
= "Stops";
1305 aProp
.Value
<<= aStops
;
1307 aProp
.Name
= "AspectRatio";
1308 aProp
.Value
<<= static_cast<sal_Int32
>(1);
1311 aTexture
.Gradient
.set(
1312 xFactory
->createInstanceWithArguments( aGradientService
,
1317 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
1318 aTextureTransformation
);
1320 if( aTexture
.Gradient
.is() )
1322 ActionSharedPtr ( internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon
,
1331 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd poly action");
1333 maActions
.push_back(
1336 rParms
.mrCurrActionIndex
) );
1338 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1342 double ImplRenderer::EMFPPlusDrawLineCap(const ::basegfx::B2DPolygon
& rPolygon
, double fPolyLength
,
1343 const ::basegfx::B2DPolyPolygon
& rLineCap
, bool bIsFilled
, bool bStart
, const rendering::StrokeAttributes
& rAttributes
,
1344 const ActionFactoryParameters
& rParms
, OutDevState
& rState
)
1346 if (!rLineCap
.count())
1349 // createAreaGeometryForLineStartEnd normalises the arrows height
1350 // before scaling (i.e. scales down by rPolygon.height), hence
1351 // we pre-scale it (which means we can avoid changing the logic
1352 // that would affect arrows rendered outside of EMF+).
1353 const double fWidth
= rAttributes
.StrokeWidth
*rLineCap
.getB2DRange().getWidth();
1355 // When drawing an outline (as opposed to a filled endCap), we also
1356 // need to take account that the brush width also adds to the area
1358 const double fShift
= bIsFilled
? 0 : rAttributes
.StrokeWidth
;
1359 double fConsumed
= 0;
1360 basegfx::B2DPolyPolygon
aArrow(basegfx::tools::createAreaGeometryForLineStartEnd(
1361 rPolygon
, rLineCap
, bStart
,
1362 fWidth
, fPolyLength
, 0, &fConsumed
, fShift
));
1364 // createAreaGeometryForLineStartEnd from some reason always sets
1365 // the path as closed, correct it
1366 aArrow
.setClosed(rLineCap
.isClosed());
1368 // If the endcap is filled, we draw ONLY the filling, if it isn't
1369 // filled we draw ONLY the outline, but never both.
1372 bool bWasFillColorSet
= rState
.isFillColorSet
;
1373 rState
.isFillColorSet
= true;
1374 rState
.fillColor
= rState
.lineColor
;
1375 ActionSharedPtr
pAction2(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow
, rParms
.mrCanvas
, rState
));
1378 maActions
.push_back(MtfAction(pAction2
, rParms
.mrCurrActionIndex
));
1379 rParms
.mrCurrActionIndex
+= pAction2
->getActionCount()-1;
1381 rState
.isFillColorSet
= bWasFillColorSet
;
1385 ActionSharedPtr
pAction(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow
, rParms
.mrCanvas
, rState
, rAttributes
));
1388 maActions
.push_back(MtfAction(pAction
, rParms
.mrCurrActionIndex
));
1389 rParms
.mrCurrActionIndex
+= pAction
->getActionCount()-1;
1393 // There isn't any clear definition of how far the line should extend
1394 // for arrows, however the following values seem to give best results
1395 // (fConsumed/2 draws the line to the center-point of the endcap
1396 // for filled caps -- however it is likely this will need to be
1397 // changed once we start taking baseInset into account).
1401 return rAttributes
.StrokeWidth
;
1404 void ImplRenderer::EMFPPlusDrawPolygon (const ::basegfx::B2DPolyPolygon
& polygon
, const ActionFactoryParameters
& rParms
,
1405 OutDevState
& rState
, const CanvasSharedPtr
& rCanvas
, sal_uInt32 penIndex
)
1407 EMFPPen
* pen
= (EMFPPen
*) aObjects
[penIndex
& 0xff];
1409 SAL_WARN_IF( !pen
, "cppcanvas.emf", "emf+ missing pen" );
1413 rState
.isFillColorSet
= false;
1414 rState
.isLineColorSet
= true;
1415 rState
.lineColor
= ::vcl::unotools::colorToDoubleSequence (pen
->GetColor (),
1416 rCanvas
->getUNOCanvas ()->getDevice()->getDeviceColorSpace());
1418 basegfx::B2DPolyPolygon
aPolyPolygon(polygon
);
1419 aPolyPolygon
.transform(rState
.mapModeTransform
);
1420 rendering::StrokeAttributes aCommonAttributes
;
1422 // some attributes are common for the polygon, and the line
1423 // starts & ends - like the stroke width
1424 pen
->SetStrokeWidth(aCommonAttributes
, *this, rState
);
1426 // but eg. dashing has to be additionally set only on the
1428 rendering::StrokeAttributes
aPolygonAttributes(aCommonAttributes
);
1429 pen
->SetStrokeAttributes(aPolygonAttributes
);
1431 basegfx::B2DPolyPolygon aFinalPolyPolygon
;
1433 // render line starts & ends if present
1434 if (!pen
->customStartCap
&& !pen
->customEndCap
)
1435 aFinalPolyPolygon
= aPolyPolygon
;
1438 for (sal_uInt32 i
= 0; i
< aPolyPolygon
.count(); ++i
)
1440 basegfx::B2DPolygon
aPolygon(aPolyPolygon
.getB2DPolygon(i
));
1442 if (!aPolygon
.isClosed())
1444 double fStart
= 0.0;
1446 double fPolyLength
= basegfx::tools::getLength(aPolygon
);
1449 if (pen
->customStartCap
)
1451 rendering::StrokeAttributes
aAttributes(aCommonAttributes
);
1452 pen
->customStartCap
->SetAttributes(aAttributes
);
1454 fStart
= EMFPPlusDrawLineCap(aPolygon
, fPolyLength
, pen
->customStartCap
->polygon
,
1455 pen
->customStartCap
->mbIsFilled
,
1456 true, aAttributes
, rParms
, rState
);
1460 if (pen
->customEndCap
)
1462 rendering::StrokeAttributes
aAttributes(aCommonAttributes
);
1463 pen
->customEndCap
->SetAttributes(aAttributes
);
1465 fEnd
= EMFPPlusDrawLineCap(aPolygon
, fPolyLength
, pen
->customEndCap
->polygon
,
1466 pen
->customEndCap
->mbIsFilled
,
1467 false, aAttributes
, rParms
, rState
);
1470 // build new poly, consume something from the old poly
1471 if (fStart
!= 0.0 || fEnd
!= 0.0)
1472 aPolygon
= basegfx::tools::getSnippetAbsolute(aPolygon
, fStart
, fPolyLength
- fEnd
, fPolyLength
);
1475 aFinalPolyPolygon
.append(aPolygon
);
1479 // finally render the polygon
1480 ActionSharedPtr
pPolyAction(internal::PolyPolyActionFactory::createPolyPolyAction(aFinalPolyPolygon
, rParms
.mrCanvas
, rState
, aPolygonAttributes
));
1483 maActions
.push_back(MtfAction(pPolyAction
, rParms
.mrCurrActionIndex
));
1484 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1489 void ImplRenderer::processObjectRecord(SvMemoryStream
& rObjectStream
, sal_uInt16 flags
, sal_uInt32 dataSize
, sal_Bool bUseWholeStream
)
1493 SAL_INFO("cppcanvas.emf", "EMF+ Object slot: " << (flags
& 0xff) << " flags: " << (flags
& 0xff00));
1495 index
= flags
& 0xff;
1496 if (aObjects
[index
] != NULL
) {
1497 delete aObjects
[index
];
1498 aObjects
[index
] = NULL
;
1501 switch (flags
& 0x7f00) {
1502 case EmfPlusObjectTypeBrush
:
1505 aObjects
[index
] = brush
= new EMFPBrush ();
1506 brush
->Read (rObjectStream
, *this);
1510 case EmfPlusObjectTypePen
:
1513 aObjects
[index
] = pen
= new EMFPPen ();
1514 pen
->Read (rObjectStream
, *this, nHDPI
, nVDPI
);
1518 case EmfPlusObjectTypePath
:
1519 sal_uInt32 header
, pathFlags
;
1522 rObjectStream
>> header
>> points
>> pathFlags
;
1524 SAL_INFO("cppcanvas.emf", "EMF+\tpath");
1525 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex
<< header
<< " points: " << std::dec
<< points
<< " additional flags: 0x" << std::hex
<< pathFlags
<< std::dec
);
1528 aObjects
[index
] = path
= new EMFPPath (points
);
1529 path
->Read (rObjectStream
, pathFlags
, *this);
1532 case EmfPlusObjectTypeRegion
: {
1535 aObjects
[index
] = region
= new EMFPRegion ();
1536 region
->Read (rObjectStream
);
1540 case EmfPlusObjectTypeImage
:
1543 aObjects
[index
] = image
= new EMFPImage ();
1544 image
->Read (rObjectStream
, dataSize
, bUseWholeStream
);
1548 case EmfPlusObjectTypeFont
:
1551 aObjects
[index
] = font
= new EMFPFont ();
1552 font
->Read (rObjectStream
);
1557 SAL_INFO("cppcanvas.emf", "EMF+\tObject unhandled flags: 0x" << std::hex
<< (flags
& 0xff00) << std::dec
);
1562 double ImplRenderer::setFont (sal_uInt8 objectId
, const ActionFactoryParameters
& rParms
, OutDevState
& rState
)
1564 EMFPFont
*font
= (EMFPFont
*) aObjects
[ objectId
];
1566 rendering::FontRequest aFontRequest
;
1567 aFontRequest
.FontDescription
.FamilyName
= font
->family
;
1568 double cellSize
= font
->emSize
;
1569 aFontRequest
.CellSize
= (rState
.mapModeTransform
*MapSize( cellSize
, 0 )).getX();
1570 rState
.xFont
= rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
1571 uno::Sequence
< beans::PropertyValue
>(),
1572 geometry::Matrix2D() );
1577 void ImplRenderer::GraphicStatePush(GraphicStateMap
& map
, sal_Int32 index
, OutDevState
& rState
)
1579 GraphicStateMap::iterator iter
= map
.find( index
);
1581 if ( iter
!= map
.end() )
1583 EmfPlusGraphicState state
= iter
->second
;
1586 SAL_INFO("cppcanvas.emf", "stack index: " << index
<< " found and erased");
1589 EmfPlusGraphicState state
;
1591 state
.aWorldTransform
= aWorldTransform
;
1592 state
.aDevState
= rState
;
1594 map
[ index
] = state
;
1597 void ImplRenderer::GraphicStatePop(GraphicStateMap
& map
, sal_Int32 index
, OutDevState
& rState
)
1599 GraphicStateMap::iterator iter
= map
.find( index
);
1601 if ( iter
!= map
.end() )
1603 SAL_INFO("cppcanvas.emf", "stack index: " << index
<< " found");
1605 EmfPlusGraphicState state
= iter
->second
;
1607 aWorldTransform
= state
.aWorldTransform
;
1608 rState
.clip
= state
.aDevState
.clip
;
1609 rState
.clipRect
= state
.aDevState
.clipRect
;
1610 rState
.xClipPoly
= state
.aDevState
.xClipPoly
;
1614 void ImplRenderer::processEMFPlus( MetaCommentAction
* pAct
, const ActionFactoryParameters
& rFactoryParms
,
1615 OutDevState
& rState
, const CanvasSharedPtr
& rCanvas
)
1617 sal_uInt32 length
= pAct
->GetDataSize ();
1618 SvMemoryStream
rMF ((void*) pAct
->GetData (), length
, STREAM_READ
);
1620 #if OSL_DEBUG_LEVEL > 2
1621 SAL_INFO("cppcanvas.emf", "EMF+\tDump of EMF+ record");
1622 dumpWords(rMF
, length
);
1626 while (length
> 0) {
1627 sal_uInt16 type
, flags
;
1628 sal_uInt32 size
, dataSize
;
1631 rMF
>> type
>> flags
>> size
>> dataSize
;
1633 next
= rMF
.Tell() + ( size
- 12 );
1636 SAL_INFO("cppcanvas.emf", "Size field is less than 12 bytes");
1639 SAL_INFO("cppcanvas.emf", "EMF+ record size: " << size
<< " type: " << type
<< " flags: " << flags
<< " data size: " << dataSize
);
1641 if (type
== EmfPlusRecordTypeObject
&& ((mbMultipart
&& (flags
& 0x7fff) == (mMFlags
& 0x7fff)) || (flags
& 0x8000))) {
1648 // 1st 4 bytes are unknown
1649 mMStream
.Write (((const char *)rMF
.GetData()) + rMF
.Tell() + 4, dataSize
- 4);
1650 SAL_INFO("cppcanvas.emf", "EMF+ read next object part size: " << size
<< " type: " << type
<< " flags: " << flags
<< " data size: " << dataSize
);
1653 SAL_INFO("cppcanvas.emf", "EMF+ multipart record flags: " << mMFlags
);
1655 processObjectRecord (mMStream
, mMFlags
, dataSize
, sal_True
);
1657 mbMultipart
= false;
1660 if (type
!= EmfPlusRecordTypeObject
|| !(flags
& 0x8000))
1663 case EmfPlusRecordTypeHeader
:
1664 sal_uInt32 header
, version
;
1666 rMF
>> header
>> version
>> nHDPI
>> nVDPI
;
1668 SAL_INFO("cppcanvas.emf", "EMF+ Header");
1669 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex
<< header
<< " version: " << std::dec
<< version
<< " horizontal DPI: " << nHDPI
<< " vertical DPI: " << nVDPI
<< " dual: " << (flags
& 1));
1672 case EmfPlusRecordTypeEndOfFile
:
1673 SAL_INFO("cppcanvas.emf", "EMF+ EndOfFile");
1675 case EmfPlusRecordTypeGetDC
:
1676 SAL_INFO("cppcanvas.emf", "EMF+ GetDC");
1677 SAL_INFO("cppcanvas.emf", "EMF+\talready used in svtools wmf/emf filter parser");
1679 case EmfPlusRecordTypeObject
:
1680 processObjectRecord (rMF
, flags
, dataSize
);
1682 case EmfPlusRecordTypeFillPie
:
1684 sal_uInt32 brushIndexOrColor
;
1685 float startAngle
, sweepAngle
;
1687 rMF
>> brushIndexOrColor
>> startAngle
>> sweepAngle
;
1689 SAL_INFO("cppcanvas.emf", "EMF+ FillPie colorOrIndex: " << brushIndexOrColor
<< " startAngle: " << startAngle
<< " sweepAngle: " << sweepAngle
);
1691 float dx
, dy
, dw
, dh
;
1693 ReadRectangle (rMF
, dx
, dy
, dw
, dh
, flags
& 0x4000);
1695 SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx
<< "," << dy
<< " " << dw
<< "x" << dh
);
1697 startAngle
= 2*M_PI
*startAngle
/360;
1698 sweepAngle
= 2*M_PI
*sweepAngle
/360;
1700 B2DPoint
mappedCenter (Map (dx
+ dw
/2, dy
+ dh
/2));
1701 B2DSize
mappedSize( MapSize (dw
/2, dh
/2));
1703 float endAngle
= startAngle
+ sweepAngle
;
1704 startAngle
= fmodf(startAngle
, M_PI
*2);
1706 startAngle
+= static_cast<float>(M_PI
*2);
1707 endAngle
= fmodf(endAngle
, M_PI
*2);
1709 endAngle
+= static_cast<float>(M_PI
*2);
1712 std::swap (endAngle
, startAngle
);
1714 SAL_INFO("cppcanvas.emf", "EMF+ adjusted angles: start " <<
1715 (360.0*startAngle
/M_PI
) << ", end: " << (360.0*endAngle
/M_PI
));
1717 B2DPolygon polygon
= tools::createPolygonFromEllipseSegment (mappedCenter
, mappedSize
.getX (), mappedSize
.getY (), startAngle
, endAngle
);
1718 polygon
.append (mappedCenter
);
1719 polygon
.setClosed (true);
1721 B2DPolyPolygon
polyPolygon (polygon
);
1722 EMFPPlusFillPolygon (polyPolygon
, rFactoryParms
, rState
, rCanvas
, flags
& 0x8000, brushIndexOrColor
);
1725 case EmfPlusRecordTypeFillPath
:
1727 sal_uInt32 index
= flags
& 0xff;
1728 sal_uInt32 brushIndexOrColor
;
1730 rMF
>> brushIndexOrColor
;
1732 SAL_INFO("cppcanvas.emf", "EMF+ FillPath slot: " << index
);
1734 EMFPPlusFillPolygon (((EMFPPath
*) aObjects
[index
])->GetPolygon (*this), rFactoryParms
, rState
, rCanvas
, flags
& 0x8000, brushIndexOrColor
);
1737 case EmfPlusRecordTypeDrawEllipse
:
1738 case EmfPlusRecordTypeFillEllipse
:
1740 // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
1741 // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
1742 // when it is later used.
1743 sal_uInt32 brushIndexOrColor
= 1234567;
1745 if ( type
== EmfPlusRecordTypeFillEllipse
)
1746 rMF
>> brushIndexOrColor
;
1748 SAL_INFO("cppcanvas.emf", "EMF+ " << (type
== EmfPlusRecordTypeFillEllipse
? "Fill" : "Draw") << "Ellipse slot: " << (flags
& 0xff));
1750 float dx
, dy
, dw
, dh
;
1752 ReadRectangle (rMF
, dx
, dy
, dw
, dh
, flags
& 0x4000);
1754 SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx
<< "," << dy
<< " " << dw
<< "x" << dh
);
1756 B2DPoint
mappedCenter (Map (dx
+ dw
/2, dy
+ dh
/2));
1757 B2DSize
mappedSize( MapSize (dw
/2, dh
/2));
1759 ::basegfx::B2DPolyPolygon
polyPolygon( ::basegfx::B2DPolygon( ::basegfx::tools::createPolygonFromEllipse( mappedCenter
, mappedSize
.getX (), mappedSize
.getY () ) ) );
1761 if ( type
== EmfPlusRecordTypeFillEllipse
)
1762 EMFPPlusFillPolygon( polyPolygon
,
1763 rFactoryParms
, rState
, rCanvas
, flags
& 0x8000, brushIndexOrColor
);
1765 EMFPPlusDrawPolygon( polyPolygon
,
1766 rFactoryParms
, rState
, rCanvas
, flags
& 0xff );
1769 case EmfPlusRecordTypeFillRects
:
1771 SAL_INFO("cppcanvas.emf", "EMF+ FillRects");
1773 sal_uInt32 brushIndexOrColor
;
1774 sal_Int32 rectangles
;
1775 bool isColor
= (flags
& 0x8000);
1776 ::basegfx::B2DPolygon polygon
;
1778 rMF
>> brushIndexOrColor
>> rectangles
;
1780 SAL_INFO("cppcanvas.emf", "EMF+\t" << ((flags
& 0x8000) ? "color" : "brush index") << ": 0x" << std::hex
<< brushIndexOrColor
<< std::dec
);
1782 for (int i
=0; i
< rectangles
; i
++) {
1783 if (flags
& 0x4000) {
1784 /* 16bit integers */
1785 sal_Int16 x
, y
, width
, height
;
1787 rMF
>> x
>> y
>> width
>> height
;
1789 polygon
.append (Map (x
, y
));
1790 polygon
.append (Map (x
+ width
, y
));
1791 polygon
.append (Map (x
+ width
, y
+ height
));
1792 polygon
.append (Map (x
, y
+ height
));
1794 SAL_INFO("cppcanvas.emf", "EMF+\trectangle: " << x
<< "," << " " << width
<< "x" << height
);
1797 float x
, y
, width
, height
;
1799 rMF
>> x
>> y
>> width
>> height
;
1801 polygon
.append (Map (x
, y
));
1802 polygon
.append (Map (x
+ width
, y
));
1803 polygon
.append (Map (x
+ width
, y
+ height
));
1804 polygon
.append (Map (x
, y
+ height
));
1806 SAL_INFO("cppcanvas.emf", "EMF+\trectangle: " << x
<< "," << " " << width
<< "x" << height
);
1809 ::basegfx::B2DPolyPolygon
polyPolygon (polygon
);
1811 // n#812793: EMF+ Seems to specify transparent background with Alpha=0xFF !
1812 // Workaround for the problem.
1813 if( isColor
&& brushIndexOrColor
== 0xFFFFFFFF && rectangles
== 1 )
1814 brushIndexOrColor
= 0xFFFFFF;
1816 EMFPPlusFillPolygon (polyPolygon
, rFactoryParms
, rState
, rCanvas
, isColor
, brushIndexOrColor
);
1820 case EmfPlusRecordTypeFillPolygon
:
1822 sal_uInt8 index
= flags
& 0xff;
1823 sal_uInt32 brushIndexOrColor
;
1826 rMF
>> brushIndexOrColor
;
1829 SAL_INFO("cppcanvas.emf", "EMF+ FillPolygon in slot: " << +index
<< " points: " << points
);
1830 SAL_INFO("cppcanvas.emf", "EMF+\t: " << ((flags
& 0x8000) ? "color" : "brush index") << " 0x" << std::hex
<< brushIndexOrColor
<< std::dec
);
1832 EMFPPath
path (points
, true);
1833 path
.Read (rMF
, flags
, *this);
1836 EMFPPlusFillPolygon (path
.GetPolygon (*this), rFactoryParms
, rState
, rCanvas
, flags
& 0x8000, brushIndexOrColor
);
1840 case EmfPlusRecordTypeDrawLines
:
1842 sal_uInt32 index
= flags
& 0xff;
1847 SAL_INFO("cppcanvas.emf", "EMF+ DrawLines in slot: " << index
<< " points: " << points
);
1849 EMFPPath
path (points
, true);
1850 path
.Read (rMF
, flags
, *this);
1852 EMFPPen
* pen
= (EMFPPen
*) aObjects
[index
];
1854 rState
.isFillColorSet
= false;
1855 rState
.isLineColorSet
= true;
1856 rState
.lineColor
= ::vcl::unotools::colorToDoubleSequence (pen
->GetColor (),
1857 rCanvas
->getUNOCanvas ()->getDevice()->getDeviceColorSpace() );
1858 ::basegfx::B2DPolyPolygon
& polygon (path
.GetPolygon (*this));
1860 polygon
.transform( rState
.mapModeTransform
);
1862 rendering::StrokeAttributes aStrokeAttributes
;
1864 pen
->SetStrokeWidth (aStrokeAttributes
, *this, rState
);
1866 ActionSharedPtr
pPolyAction(
1867 internal::PolyPolyActionFactory::createPolyPolyAction(
1868 polygon
, rFactoryParms
.mrCanvas
, rState
, aStrokeAttributes
) );
1872 maActions
.push_back(
1875 rFactoryParms
.mrCurrActionIndex
) );
1877 rFactoryParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1882 case EmfPlusRecordTypeDrawPath
:
1884 sal_uInt32 penIndex
;
1888 SAL_INFO("cppcanvas.emf", "EMF+ DrawPath");
1889 SAL_INFO("cppcanvas.emf", "EMF+\tpen: " << penIndex
);
1891 EMFPPath
* path
= (EMFPPath
*) aObjects
[flags
& 0xff];
1892 SAL_WARN_IF( !path
, "cppcanvas.emf", "EmfPlusRecordTypeDrawPath missing path" );
1894 EMFPPlusDrawPolygon (path
->GetPolygon (*this), rFactoryParms
, rState
, rCanvas
, penIndex
);
1898 case EmfPlusRecordTypeDrawImage
:
1899 case EmfPlusRecordTypeDrawImagePoints
:
1901 sal_uInt32 attrIndex
;
1902 sal_Int32 sourceUnit
;
1904 rMF
>> attrIndex
>> sourceUnit
;
1906 SAL_INFO("cppcanvas.emf", "EMF+ " << (type
== EmfPlusRecordTypeDrawImagePoints
? "DrawImagePoints" : "DrawImage") << "attributes index: " << attrIndex
<< "source unit: " << sourceUnit
);
1907 SAL_INFO("cppcanvas.emf", "EMF+\tTODO: use image attributes");
1909 if (sourceUnit
== 2 && aObjects
[flags
& 0xff]) { // we handle only GraphicsUnit.Pixel now
1910 EMFPImage
& image
= *(EMFPImage
*) aObjects
[flags
& 0xff];
1911 float sx
, sy
, sw
, sh
;
1914 ReadRectangle (rMF
, sx
, sy
, sw
, sh
);
1915 Rectangle
aSource(Point(sx
, sy
), Size(sw
, sh
));
1917 SAL_INFO("cppcanvas.emf", "EMF+ " << (type
== EmfPlusRecordTypeDrawImagePoints
? "DrawImagePoints" : "DrawImage") << " source rectangle: " << sx
<< "," << sy
<< " " << sw
<< "x" << sh
);
1919 ::basegfx::B2DPoint aDstPoint
;
1920 ::basegfx::B2DSize aDstSize
;
1921 bool bValid
= false;
1923 if (type
== EmfPlusRecordTypeDrawImagePoints
) {
1926 if( aCount
== 3) { // TODO: now that we now that this value is count we should support it better
1927 float x1
, y1
, x2
, y2
, x3
, y3
;
1929 ReadPoint (rMF
, x1
, y1
, flags
);
1930 ReadPoint (rMF
, x2
, y2
, flags
);
1931 ReadPoint (rMF
, x3
, y3
, flags
);
1933 SAL_INFO("cppcanvas.emf", "EMF+ destination points: " << x1
<< "," << y1
<< " " << x2
<< "," << y2
<< " " << x3
<< "," << y3
);
1934 SAL_INFO("cppcanvas.emf", "EMF+ destination rectangle: " << x1
<< "," << y1
<< " " << x2
- x1
<< "x" << y3
- y1
);
1936 aDstPoint
= Map (x1
, y1
);
1937 aDstSize
= MapSize(x2
- x1
, y3
- y1
);
1941 } else if (type
== EmfPlusRecordTypeDrawImage
) {
1942 float dx
, dy
, dw
, dh
;
1944 ReadRectangle (rMF
, dx
, dy
, dw
, dh
, flags
& 0x4000);
1946 SAL_INFO("cppcanvas.emf", "EMF+ destination rectangle: " << dx
<< "," << dy
<< " " << dw
<< "x" << dh
);
1948 aDstPoint
= Map (dx
, dy
);
1949 aDstSize
= MapSize(dw
, dh
);
1955 BitmapEx
aBmp( image
.graphic
.GetBitmapEx () );
1956 aBmp
.Crop( aSource
);
1958 Size
aSize( aBmp
.GetSizePixel() );
1959 SAL_INFO("cppcanvas.emf", "EMF+ bitmap size: " << aSize
.Width() << "x" << aSize
.Height());
1960 if( aSize
.Width() > 0 && aSize
.Height() > 0 ) {
1961 ActionSharedPtr
pBmpAction (
1962 internal::BitmapActionFactory::createBitmapAction (
1964 rState
.mapModeTransform
* aDstPoint
,
1965 rState
.mapModeTransform
* aDstSize
,
1970 maActions
.push_back( MtfAction( pBmpAction
,
1971 rFactoryParms
.mrCurrActionIndex
) );
1973 rFactoryParms
.mrCurrActionIndex
+= pBmpAction
->getActionCount()-1;
1976 SAL_INFO("cppcanvas.emf", "EMF+ warning: empty bitmap");
1979 SAL_INFO("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme)");
1982 SAL_INFO("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme) - possibly unsupported source units for crop rectangle");
1986 case EmfPlusRecordTypeDrawString
:
1988 SAL_INFO("cppcanvas.emf", "EMF+ DrawString");
1991 sal_uInt32 formatId
;
1992 sal_uInt32 stringLength
;
1994 rMF
>> brushId
>> formatId
>> stringLength
;
1995 SAL_INFO("cppcanvas.emf", "EMF+ DrawString brushId: " << brushId
<< " formatId: " << formatId
<< " length: " << stringLength
);
1997 if (flags
& 0x8000) {
1998 float lx
, ly
, lw
, lh
;
2000 rMF
>> lx
>> ly
>> lw
>> lh
;
2002 SAL_INFO("cppcanvas.emf", "EMF+ DrawString layoutRect: " << lx
<< "," << ly
<< " - " << lw
<< "x" << lh
);
2004 OUString text
= read_uInt16s_ToOUString(rMF
, stringLength
);
2006 double cellSize
= setFont (flags
& 0xff, rFactoryParms
, rState
);
2007 rState
.textColor
= COLOR( brushId
);
2009 ActionSharedPtr
pTextAction(
2010 TextActionFactory::createTextAction(
2011 // position is just rough guess for now
2012 // we should calculate it exactly from layoutRect or font
2013 ::vcl::unotools::pointFromB2DPoint ( Map( lx
+ 0.15*cellSize
, ly
+ cellSize
) ),
2022 rFactoryParms
.mrVDev
,
2023 rFactoryParms
.mrCanvas
,
2025 rFactoryParms
.mrParms
,
2029 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
2031 maActions
.push_back(
2034 rFactoryParms
.mrCurrActionIndex
) );
2036 rFactoryParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
2039 SAL_INFO("cppcanvas.emf", "EMF+ DrawString TODO - drawing with brush not yet supported");
2043 case EmfPlusRecordTypeSetPageTransform
:
2046 SAL_INFO("cppcanvas.emf", "EMF+ SetPageTransform");
2047 SAL_INFO("cppcanvas.emf", "EMF+\tscale: " << fPageScale
<< " unit: " << flags
);
2048 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2050 case EmfPlusRecordTypeSetRenderingOrigin
:
2051 rMF
>> nOriginX
>> nOriginY
;
2052 SAL_INFO("cppcanvas.emf", "EMF+ SetRenderingOrigin");
2053 SAL_INFO("cppcanvas.emf", "EMF+\torigin [x,y]: " << nOriginX
<< "," << nOriginY
);
2055 case EmfPlusRecordTypeSetTextRenderingHint
:
2056 SAL_INFO("cppcanvas.emf", "EMF+ SetTextRenderingHint");
2057 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2059 case EmfPlusRecordTypeSetAntiAliasMode
:
2060 SAL_INFO("cppcanvas.emf", "EMF+ SetAntiAliasMode");
2061 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2063 case EmfPlusRecordTypeSetInterpolationMode
:
2064 SAL_INFO("cppcanvas.emf", "EMF+ InterpolationMode");
2065 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2067 case EmfPlusRecordTypeSetPixelOffsetMode
:
2068 SAL_INFO("cppcanvas.emf", "EMF+ SetPixelOffsetMode");
2069 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2071 case EmfPlusRecordTypeSetCompositingQuality
:
2072 SAL_INFO("cppcanvas.emf", "EMF+ SetCompositingQuality");
2073 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2075 case EmfPlusRecordTypeSave
:
2077 sal_uInt32 stackIndex
;
2081 SAL_INFO("cppcanvas.emf", "EMF+ Save stack index: " << stackIndex
);
2083 GraphicStatePush( mGSStack
, stackIndex
, rState
);
2087 case EmfPlusRecordTypeRestore
:
2089 sal_uInt32 stackIndex
;
2093 SAL_INFO("cppcanvas.emf", "EMF+ Restore stack index: " << stackIndex
);
2095 GraphicStatePop( mGSStack
, stackIndex
, rState
);
2099 case EmfPlusRecordTypeBeginContainerNoParams
:
2101 sal_uInt32 stackIndex
;
2105 SAL_INFO("cppcanvas.emf", "EMF+ Begin Container No Params stack index: " << stackIndex
);
2107 GraphicStatePush( mGSContainerStack
, stackIndex
, rState
);
2110 case EmfPlusRecordTypeEndContainer
:
2112 sal_uInt32 stackIndex
;
2116 SAL_INFO("cppcanvas.emf", "EMF+ End Container stack index: " << stackIndex
);
2118 GraphicStatePop( mGSContainerStack
, stackIndex
, rState
);
2121 case EmfPlusRecordTypeSetWorldTransform
: {
2122 SAL_INFO("cppcanvas.emf", "EMF+ SetWorldTransform");
2125 aWorldTransform
.Set (transform
);
2126 SAL_INFO("cppcanvas.emf",
2127 "EMF+\tm11: " << aWorldTransform
.eM11
<< "m12: " << aWorldTransform
.eM12
<<
2128 "EMF+\tm21: " << aWorldTransform
.eM21
<< "m22: " << aWorldTransform
.eM22
<<
2129 "EMF+\tdx: " << aWorldTransform
.eDx
<< "dy: " << aWorldTransform
.eDy
);
2132 case EmfPlusRecordTypeResetWorldTransform
:
2133 SAL_INFO("cppcanvas.emf", "EMF+ ResetWorldTransform");
2134 aWorldTransform
.SetIdentity ();
2136 case EmfPlusRecordTypeMultiplyWorldTransform
: {
2137 SAL_INFO("cppcanvas.emf", "EMF+ MultiplyWorldTransform");
2141 SAL_INFO("cppcanvas.emf",
2142 "EMF+\tmatrix m11: " << transform
.eM11
<< "m12: " << transform
.eM12
<<
2143 "EMF+\tm21: " << transform
.eM21
<< "m22: " << transform
.eM22
<<
2144 "EMF+\tdx: " << transform
.eDx
<< "dy: " << transform
.eDy
);
2146 if (flags
& 0x2000) // post multiply
2147 aWorldTransform
.Multiply (transform
);
2148 else { // pre multiply
2149 transform
.Multiply (aWorldTransform
);
2150 aWorldTransform
.Set (transform
);
2152 SAL_INFO("cppcanvas.emf",
2153 "EMF+\tm11: " << aWorldTransform
.eM11
<< "m12: " << aWorldTransform
.eM12
<<
2154 "EMF+\tm21: " << aWorldTransform
.eM21
<< "m22: " << aWorldTransform
.eM22
<<
2155 "EMF+\tdx: " << aWorldTransform
.eDx
<< "dy: " << aWorldTransform
.eDy
);
2158 case EmfPlusRecordTypeSetClipRect
:
2160 int combineMode
= (flags
>> 8) & 0xf;
2162 SAL_INFO("cppcanvas.emf", "EMF+ SetClipRect combine mode: " << combineMode
);
2163 #if OSL_DEBUG_LEVEL > 1
2164 if (combineMode
> 1) {
2165 SAL_INFO ("cppcanvas.emf", "EMF+ TODO combine mode > 1");
2169 float dx
, dy
, dw
, dh
;
2171 ReadRectangle (rMF
, dx
, dy
, dw
, dh
, false);
2173 SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx
<< "," << dy
<< " " << dw
<< "x" << dh
);
2175 B2DPoint
mappedPoint (Map (dx
, dy
));
2176 B2DSize
mappedSize( MapSize (dw
, dh
));
2178 ::basegfx::B2DPolyPolygon
polyPolygon( ::basegfx::B2DPolygon( ::basegfx::tools::createPolygonFromRect( ::basegfx::B2DRectangle( mappedPoint
.getX(), mappedPoint
.getY(),
2179 mappedPoint
.getX() + mappedSize
.getX(),
2180 mappedPoint
.getY() + mappedSize
.getY() ) ) ) );
2181 polyPolygon
.transform(rState
.mapModeTransform
);
2183 updateClipping (polyPolygon
, rFactoryParms
, combineMode
== 1);
2187 case EmfPlusRecordTypeSetClipPath
:
2189 int combineMode
= (flags
>> 8) & 0xf;
2191 SAL_INFO("cppcanvas.emf", "EMF+ SetClipPath combine mode: " << combineMode
);
2192 SAL_INFO("cppcanvas.emf", "EMF+\tpath in slot: " << (flags
& 0xff));
2194 EMFPPath
& path
= *(EMFPPath
*) aObjects
[flags
& 0xff];
2195 ::basegfx::B2DPolyPolygon
& clipPoly (path
.GetPolygon (*this));
2197 clipPoly
.transform (rState
.mapModeTransform
);
2198 switch (combineMode
)
2200 case EmfPlusCombineModeReplace
:
2201 case EmfPlusCombineModeIntersect
:
2202 case EmfPlusCombineModeUnion
: // Is this, EmfPlusCombineModeXOR and EmfPlusCombineModeComplement correct?
2203 case EmfPlusCombineModeXOR
:
2204 case EmfPlusCombineModeComplement
:
2205 updateClipping (clipPoly
, rFactoryParms
, combineMode
== 1);
2207 case EmfPlusCombineModeExclude
:
2208 // Not doing anything is better then including exactly what we wanted to exclude.
2214 case EmfPlusRecordTypeSetClipRegion
: {
2215 int combineMode
= (flags
>> 8) & 0xf;
2217 SAL_INFO("cppcanvas.emf", "EMF+ SetClipRegion");
2218 SAL_INFO("cppcanvas.emf", "EMF+\tregion in slot: " << (flags
& 0xff) << " combine mode: " << combineMode
);
2219 EMFPRegion
*region
= (EMFPRegion
*)aObjects
[flags
& 0xff];
2222 if (region
&& region
->parts
== 0 && region
->initialState
== EmfPlusRegionInitialStateInfinite
) {
2223 updateClipping (::basegfx::B2DPolyPolygon (), rFactoryParms
, combineMode
== 1);
2225 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2229 case EmfPlusRecordTypeDrawDriverString
: {
2230 SAL_INFO("cppcanvas.emf", "EMF+ DrawDriverString, flags: 0x" << std::hex
<< flags
<< std::dec
);
2231 sal_uInt32 brushIndexOrColor
;
2232 sal_uInt32 optionFlags
;
2233 sal_uInt32 hasMatrix
;
2234 sal_uInt32 glyphsCount
;
2236 rMF
>> brushIndexOrColor
>> optionFlags
>> hasMatrix
>> glyphsCount
;
2238 SAL_INFO("cppcanvas.emf", "EMF+\t: " << ((flags
& 0x8000) ? "color" : "brush index") << " 0x" << std::hex
<< brushIndexOrColor
<< std::dec
);
2239 SAL_INFO("cppcanvas.emf", "EMF+\toption flags: 0x" << std::hex
<< optionFlags
<< std::dec
);
2240 SAL_INFO("cppcanvas.emf", "EMF+\thas matrix: " << hasMatrix
);
2241 SAL_INFO("cppcanvas.emf", "EMF+\tglyphs: " << glyphsCount
);
2243 if( ( optionFlags
& 1 ) && glyphsCount
> 0 ) {
2244 float *charsPosX
= new float[glyphsCount
];
2245 float *charsPosY
= new float[glyphsCount
];
2247 OUString text
= read_uInt16s_ToOUString(rMF
, glyphsCount
);
2249 for( sal_uInt32 i
=0; i
<glyphsCount
; i
++) {
2250 rMF
>> charsPosX
[i
] >> charsPosY
[i
];
2251 SAL_INFO("cppcanvas.emf", "EMF+\tglyphPosition[" << i
<< "]: " << charsPosX
[i
] << "," << charsPosY
[i
]);
2257 SAL_INFO("cppcanvas.emf", "EMF+\tmatrix: " << transform
.eM11
<< ", " << transform
.eM12
<< ", " << transform
.eM21
<< ", " << transform
.eM22
<< ", " << transform
.eDx
<< ", " << transform
.eDy
);
2260 // add the text action
2261 setFont (flags
& 0xff, rFactoryParms
, rState
);
2263 if( flags
& 0x8000 )
2264 rState
.textColor
= COLOR( brushIndexOrColor
);
2266 ActionSharedPtr
pTextAction(
2267 TextActionFactory::createTextAction(
2268 ::vcl::unotools::pointFromB2DPoint ( Map( charsPosX
[0], charsPosY
[0] ) ),
2277 rFactoryParms
.mrVDev
,
2278 rFactoryParms
.mrCanvas
,
2280 rFactoryParms
.mrParms
,
2285 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
2287 maActions
.push_back(
2290 rFactoryParms
.mrCurrActionIndex
) );
2292 rFactoryParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
2298 SAL_INFO("cppcanvas.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2304 SAL_INFO("cppcanvas.emf", "EMF+ unhandled record type: " << type
);
2305 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2317 SAL_WARN("cppcanvas.emf", "ImplRenderer::processEMFPlus: "
2318 "size " << size
<< " > length " << length
);
2319 #if OSL_DEBUG_LEVEL > 1
2320 dumpWords(rMF
, length
);
2329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */