Bump version to 5.0-14
[LibreOffice.git] / cppcanvas / source / mtfrenderer / emfplus.cxx
blob0562e43f3325c2096ac40ad151491978cba345e5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
51 namespace
54 #define EmfPlusRecordTypeHeader 16385
55 #define EmfPlusRecordTypeEndOfFile 16386
56 #define EmfPlusRecordTypeGetDC 16388
57 #define EmfPlusRecordTypeObject 16392
58 #define EmfPlusRecordTypeFillRects 16394
59 #define EmfPlusRecordTypeFillPolygon 16396
60 #define EmfPlusRecordTypeDrawLines 16397
61 #define EmfPlusRecordTypeFillEllipse 16398
62 #define EmfPlusRecordTypeDrawEllipse 16399
63 #define EmfPlusRecordTypeFillPie 16400
64 #define EmfPlusRecordTypeFillPath 16404
65 #define EmfPlusRecordTypeDrawPath 16405
66 #define EmfPlusRecordTypeDrawImage 16410
67 #define EmfPlusRecordTypeDrawImagePoints 16411
68 #define EmfPlusRecordTypeDrawString 16412
69 #define EmfPlusRecordTypeSetRenderingOrigin 16413
70 #define EmfPlusRecordTypeSetAntiAliasMode 16414
71 #define EmfPlusRecordTypeSetTextRenderingHint 16415
72 #define EmfPlusRecordTypeSetInterpolationMode 16417
73 #define EmfPlusRecordTypeSetPixelOffsetMode 16418
74 #define EmfPlusRecordTypeSetCompositingQuality 16420
75 #define EmfPlusRecordTypeSave 16421
76 #define EmfPlusRecordTypeRestore 16422
77 #define EmfPlusRecordTypeBeginContainerNoParams 16424
78 #define EmfPlusRecordTypeEndContainer 16425
79 #define EmfPlusRecordTypeSetWorldTransform 16426
80 #define EmfPlusRecordTypeResetWorldTransform 16427
81 #define EmfPlusRecordTypeMultiplyWorldTransform 16428
82 #define EmfPlusRecordTypeSetPageTransform 16432
83 #define EmfPlusRecordTypeSetClipRect 16434
84 #define EmfPlusRecordTypeSetClipPath 16435
85 #define EmfPlusRecordTypeSetClipRegion 16436
86 #define EmfPlusRecordTypeDrawDriverString 16438
88 #define EmfPlusObjectTypeBrush 0x100
89 #define EmfPlusObjectTypePen 0x200
90 #define EmfPlusObjectTypePath 0x300
91 #define EmfPlusObjectTypeRegion 0x400
92 #define EmfPlusObjectTypeImage 0x500
93 #define EmfPlusObjectTypeFont 0x600
94 #define EmfPlusObjectTypeStringFormat 0x700
95 #define EmfPlusObjectTypeImageAttributes 0x800
96 #define EmfPlusObjectTypeCustomLineCap 0x900
98 #define EmfPlusRegionInitialStateInfinite 0x10000003
100 const sal_Int32 EmfPlusLineStyleSolid = 0x00000000;
101 const sal_Int32 EmfPlusLineStyleDash = 0x00000001;
102 const sal_Int32 EmfPlusLineStyleDot = 0x00000002;
103 const sal_Int32 EmfPlusLineStyleDashDot = 0x00000003;
104 const sal_Int32 EmfPlusLineStyleDashDotDot = 0x00000004;
105 const sal_Int32 EmfPlusLineStyleCustom = 0x00000005;
107 const sal_uInt32 EmfPlusCustomLineCapDataTypeDefault = 0x00000000;
108 const sal_uInt32 EmfPlusCustomLineCapDataTypeAdjustableArrow = 0x00000001;
110 const sal_uInt32 EmfPlusCustomLineCapDataFillPath = 0x00000001;
111 const sal_uInt32 EmfPlusCustomLineCapDataLinePath = 0x00000002;
113 const sal_uInt32 EmfPlusLineCapTypeSquare = 0x00000001;
114 const sal_uInt32 EmfPlusLineCapTypeRound = 0x00000002;
116 const sal_uInt32 EmfPlusLineJoinTypeMiter = 0x00000000;
117 const sal_uInt32 EmfPlusLineJoinTypeBevel = 0x00000001;
118 const sal_uInt32 EmfPlusLineJoinTypeRound = 0x00000002;
119 const sal_uInt32 EmfPlusLineJoinTypeMiterClipped = 0x00000003;
121 enum EmfPlusCombineMode
123 EmfPlusCombineModeReplace = 0x00000000,
124 EmfPlusCombineModeIntersect = 0x00000001,
125 EmfPlusCombineModeUnion = 0x00000002,
126 EmfPlusCombineModeXOR = 0x00000003,
127 EmfPlusCombineModeExclude = 0x00000004,
128 EmfPlusCombineModeComplement = 0x00000005
131 enum EmfPlusHatchStyle
133 HatchStyleHorizontal = 0x00000000,
134 HatchStyleVertical = 0x00000001,
135 HatchStyleForwardDiagonal = 0x00000002,
136 HatchStyleBackwardDiagonal = 0x00000003,
137 HatchStyleLargeGrid = 0x00000004,
138 HatchStyleDiagonalCross = 0x00000005,
139 HatchStyle05Percent = 0x00000006,
140 HatchStyle10Percent = 0x00000007,
141 HatchStyle20Percent = 0x00000008,
142 HatchStyle25Percent = 0x00000009,
143 HatchStyle30Percent = 0x0000000A,
144 HatchStyle40Percent = 0x0000000B,
145 HatchStyle50Percent = 0x0000000C,
146 HatchStyle60Percent = 0x0000000D,
147 HatchStyle70Percent = 0x0000000E,
148 HatchStyle75Percent = 0x0000000F,
149 HatchStyle80Percent = 0x00000010,
150 HatchStyle90Percent = 0x00000011,
151 HatchStyleLightDownwardDiagonal = 0x00000012,
152 HatchStyleLightUpwardDiagonal = 0x00000013,
153 HatchStyleDarkDownwardDiagonal = 0x00000014,
154 HatchStyleDarkUpwardDiagonal = 0x00000015,
155 HatchStyleWideDownwardDiagonal = 0x00000016,
156 HatchStyleWideUpwardDiagonal = 0x00000017,
157 HatchStyleLightVertical = 0x00000018,
158 HatchStyleLightHorizontal = 0x00000019,
159 HatchStyleNarrowVertical = 0x0000001A,
160 HatchStyleNarrowHorizontal = 0x0000001B,
161 HatchStyleDarkVertical = 0x0000001C,
162 HatchStyleDarkHorizontal = 0x0000001D,
163 HatchStyleDashedDownwardDiagonal = 0x0000001E,
164 HatchStyleDashedUpwardDiagonal = 0x0000001F,
165 HatchStyleDashedHorizontal = 0x00000020,
166 HatchStyleDashedVertical = 0x00000021,
167 HatchStyleSmallConfetti = 0x00000022,
168 HatchStyleLargeConfetti = 0x00000023,
169 HatchStyleZigZag = 0x00000024,
170 HatchStyleWave = 0x00000025,
171 HatchStyleDiagonalBrick = 0x00000026,
172 HatchStyleHorizontalBrick = 0x00000027,
173 HatchStyleWeave = 0x00000028,
174 HatchStylePlaid = 0x00000029,
175 HatchStyleDivot = 0x0000002A,
176 HatchStyleDottedGrid = 0x0000002B,
177 HatchStyleDottedDiamond = 0x0000002C,
178 HatchStyleShingle = 0x0000002D,
179 HatchStyleTrellis = 0x0000002E,
180 HatchStyleSphere = 0x0000002F,
181 HatchStyleSmallGrid = 0x00000030,
182 HatchStyleSmallCheckerBoard = 0x00000031,
183 HatchStyleLargeCheckerBoard = 0x00000032,
184 HatchStyleOutlinedDiamond = 0x00000033,
185 HatchStyleSolidDiamond = 0x00000034
188 const char* emfTypeToName(sal_uInt16 type)
190 switch(type)
192 case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
193 case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
194 case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
195 case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
196 case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
197 case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
198 case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
199 case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
200 case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
201 case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
202 case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
203 case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
204 case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
205 case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
206 case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
207 case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
208 case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
209 case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
210 case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
211 case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
212 case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
213 case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
214 case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
215 case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
216 case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
217 case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
218 case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
219 case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
220 case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
221 case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
222 case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
223 case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
224 case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
226 return "";
229 } // anonymous namespace
231 using namespace ::com::sun::star;
232 using namespace ::basegfx;
234 namespace cppcanvas
236 namespace internal
238 struct EMFPPath : public EMFPObject
240 ::basegfx::B2DPolyPolygon aPolygon;
241 sal_Int32 nPoints;
242 float* pPoints;
243 sal_uInt8* pPointTypes;
245 public:
246 EMFPPath (sal_Int32 _nPoints, bool bLines = false)
248 if( _nPoints<0 || sal_uInt32(_nPoints)>SAL_MAX_INT32/(2*sizeof(float)) )
249 _nPoints = SAL_MAX_INT32/(2*sizeof(float));
250 nPoints = _nPoints;
251 pPoints = new float [nPoints*2];
252 if (!bLines)
253 pPointTypes = new sal_uInt8 [_nPoints];
254 else
255 pPointTypes = NULL;
258 virtual ~EMFPPath ()
260 delete [] pPoints;
261 delete [] pPointTypes;
264 // TODO: remove rR argument when debug code is not longer needed
265 void Read (SvStream& s, sal_uInt32 pathFlags, ImplRenderer& rR)
267 for (int i = 0; i < nPoints; i ++) {
268 if (pathFlags & 0x4000) {
269 // EMFPlusPoint: stored in signed short 16bit integer format
270 sal_Int16 x, y;
272 s.ReadInt16( x ).ReadInt16( y );
273 SAL_INFO ("cppcanvas.emf", "EMF+\tEMFPlusPoint [x,y]: " << x << "," << y);
274 pPoints [i*2] = x;
275 pPoints [i*2 + 1] = y;
276 } else if (!(pathFlags & 0xC000)) {
277 // EMFPlusPointF: stored in Single (float) format
278 s.ReadFloat( pPoints [i*2] ).ReadFloat( pPoints [i*2 + 1] );
279 SAL_INFO ("cppcanvas.emf", "EMF+\tEMFPlusPointF [x,y]: " << pPoints [i*2] << "," << pPoints [i*2 + 1]);
280 } else { //if (pathFlags & 0x8000)
281 // EMFPlusPointR: points are stored in EMFPlusInteger7 or
282 // EMFPlusInteger15 objects, see section 2.2.2.21/22
283 SAL_INFO("cppcanvas.emf", "EMF+\t\tTODO - parse EMFPlusPointR object (section 2.2.1.6)");
288 if (pPointTypes)
289 for (int i = 0; i < nPoints; i ++) {
290 s.ReadUChar( pPointTypes [i] );
291 SAL_INFO ("cppcanvas.emf", "EMF+\tpoint type: " << (int)pPointTypes [i]);
294 aPolygon.clear ();
296 #if OSL_DEBUG_LEVEL > 1
297 const ::basegfx::B2DRectangle aBounds (::basegfx::tools::getRange (GetPolygon (rR)));
299 SAL_INFO ("cppcanvas.emf",
300 "EMF+\tpolygon bounding box: " << aBounds.getMinX () << "," << aBounds.getMinY () << aBounds.getWidth () << "x" << aBounds.getHeight () << " (mapped)");
301 #else
302 (void) rR; // avoid warnings
303 #endif
306 ::basegfx::B2DPolyPolygon& GetPolygon (ImplRenderer& rR, bool bMapIt = true)
308 ::basegfx::B2DPolygon polygon;
310 aPolygon.clear ();
312 int last_normal = 0, p = 0;
313 ::basegfx::B2DPoint prev, mapped;
314 bool hasPrev = false;
315 for (int i = 0; i < nPoints; i ++) {
316 if (p && pPointTypes && (pPointTypes [i] == 0)) {
317 aPolygon.append (polygon);
318 last_normal = i;
319 p = 0;
320 polygon.clear ();
323 if (bMapIt)
324 mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]);
325 else
326 mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]);
327 if (pPointTypes) {
328 if ((pPointTypes [i] & 0x07) == 3) {
329 if (((i - last_normal )% 3) == 1) {
330 polygon.setNextControlPoint (p - 1, mapped);
331 SAL_INFO ("cppcanvas.emf", "polygon append next: " << p - 1 << " mapped: " << mapped.getX () << "," << mapped.getY ());
332 continue;
333 } else if (((i - last_normal) % 3) == 2) {
334 prev = mapped;
335 hasPrev = true;
336 continue;
338 } else
339 last_normal = i;
341 polygon.append (mapped);
342 SAL_INFO ("cppcanvas.emf", "polygon append point: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
343 if (hasPrev) {
344 polygon.setPrevControlPoint (p, prev);
345 SAL_INFO ("cppcanvas.emf", "polygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ());
346 hasPrev = false;
348 p ++;
349 if (pPointTypes && (pPointTypes [i] & 0x80)) { // closed polygon
350 polygon.setClosed (true);
351 aPolygon.append (polygon);
352 SAL_INFO ("cppcanvas.emf", "close polygon");
353 last_normal = i + 1;
354 p = 0;
355 polygon.clear ();
359 if (polygon.count ()) {
360 aPolygon.append (polygon);
362 #if OSL_DEBUG_LEVEL > 1
363 for (unsigned int i=0; i<aPolygon.count(); i++) {
364 polygon = aPolygon.getB2DPolygon(i);
365 SAL_INFO ("cppcanvas.emf", "polygon: " << i);
366 for (unsigned int j=0; j<polygon.count(); j++) {
367 ::basegfx::B2DPoint point = polygon.getB2DPoint(j);
368 SAL_INFO ("cppcanvas.emf", "point: " << point.getX() << "," << point.getY());
369 if (polygon.isPrevControlPointUsed(j)) {
370 point = polygon.getPrevControlPoint(j);
371 SAL_INFO ("cppcanvas.emf", "prev: " << point.getX() << "," << point.getY());
373 if (polygon.isNextControlPointUsed(j)) {
374 point = polygon.getNextControlPoint(j);
375 SAL_INFO ("cppcanvas.emf", "next: " << point.getX() << "," << point.getY());
379 #endif
382 return aPolygon;
386 struct EMFPRegion : public EMFPObject
388 sal_Int32 parts;
389 sal_Int32 *combineMode;
390 sal_Int32 initialState;
391 EMFPPath *initialPath;
392 float ix, iy, iw, ih;
394 EMFPRegion ()
395 : parts(0)
396 , combineMode(NULL)
397 , initialState(0)
398 , initialPath(NULL)
399 , ix(0.0)
400 , iy(0.0)
401 , iw(0.0)
402 , ih(0.0)
406 virtual ~EMFPRegion ()
408 if (combineMode) {
409 delete [] combineMode;
410 combineMode = NULL;
412 if (initialPath) {
413 delete initialPath;
414 initialPath = NULL;
418 void Read (SvStream& s)
420 sal_uInt32 header;
422 s.ReadUInt32( header ).ReadInt32( parts );
424 SAL_INFO ("cppcanvas.emf", "EMF+\tregion");
425 SAL_INFO ("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " parts: " << parts << std::dec );
427 if (parts) {
428 if( parts<0 || sal_uInt32(parts)>SAL_MAX_INT32/sizeof(sal_Int32) )
429 parts = SAL_MAX_INT32/sizeof(sal_Int32);
431 combineMode = new sal_Int32 [parts];
433 for (int i = 0; i < parts; i ++) {
434 s.ReadInt32( combineMode [i] );
435 SAL_INFO ("cppcanvas.emf", "EMF+\tcombine mode [" << i << "]: 0x" << std::hex << combineMode [i] << std::dec);
439 s.ReadInt32( initialState );
440 SAL_INFO ("cppcanvas.emf", "EMF+\tinitial state: 0x" << std::hex << initialState << std::dec);
444 struct EMFPBrush : public EMFPObject
446 ::Color solidColor;
447 sal_uInt32 type;
448 sal_uInt32 additionalFlags;
450 /* linear gradient */
451 sal_Int32 wrapMode;
452 float areaX, areaY, areaWidth, areaHeight;
453 ::Color secondColor; // first color is stored in solidColor;
454 XForm transformation;
455 bool hasTransformation;
456 sal_Int32 blendPoints;
457 float* blendPositions;
458 float* blendFactors;
459 sal_Int32 colorblendPoints;
460 float* colorblendPositions;
461 ::Color* colorblendColors;
462 sal_Int32 surroundColorsNumber;
463 ::Color* surroundColors;
464 EMFPPath *path;
465 EmfPlusHatchStyle hatchStyle;
467 public:
468 EMFPBrush ()
469 : type(0)
470 , additionalFlags(0)
471 , wrapMode(0)
472 , areaX(0.0)
473 , areaY(0.0)
474 , areaWidth(0.0)
475 , areaHeight(0.0)
476 , hasTransformation(false)
477 , blendPoints(0)
478 , blendPositions(NULL)
479 , blendFactors(NULL)
480 , colorblendPoints(0)
481 , colorblendPositions(NULL)
482 , colorblendColors(NULL)
483 , surroundColorsNumber(0)
484 , surroundColors(NULL)
485 , path(NULL)
486 , hatchStyle(HatchStyleHorizontal)
490 virtual ~EMFPBrush ()
492 if (blendPositions != NULL) {
493 delete[] blendPositions;
494 blendPositions = NULL;
496 if (colorblendPositions != NULL) {
497 delete[] colorblendPositions;
498 colorblendPositions = NULL;
500 if (colorblendColors != NULL) {
501 delete[] colorblendColors;
502 colorblendColors = NULL;
504 if (surroundColors != NULL) {
505 delete[] surroundColors;
506 surroundColors = NULL;
508 if (path) {
509 delete path;
510 path = NULL;
514 sal_uInt32 GetType() const { return type; }
515 const ::Color& GetColor() const { return solidColor; }
517 void Read (SvStream& s, ImplRenderer& rR)
519 sal_uInt32 header;
521 s.ReadUInt32( header ).ReadUInt32( type );
523 SAL_INFO ("cppcanvas.emf", "EMF+\tbrush");
524 SAL_INFO ("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec);
526 switch (type) {
527 case 0:
529 sal_uInt32 color;
531 s.ReadUInt32( color );
532 solidColor = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
533 SAL_INFO ("cppcanvas.emf", "EMF+\tsolid color: 0x" << std::hex << color << std::dec);
534 break;
536 case 1:
538 sal_uInt32 style;
539 sal_uInt32 foregroundColor;
540 sal_uInt32 backgroundColor;
541 s.ReadUInt32( style );
542 s.ReadUInt32( foregroundColor );
543 s.ReadUInt32( backgroundColor );
545 hatchStyle = static_cast<EmfPlusHatchStyle>(style);
546 solidColor = ::Color(0xff - (foregroundColor >> 24), (foregroundColor >> 16) & 0xff, (foregroundColor >> 8) & 0xff, foregroundColor & 0xff);
547 secondColor = ::Color(0xff - (backgroundColor >> 24), (backgroundColor >> 16) & 0xff, (backgroundColor >> 8) & 0xff, backgroundColor & 0xff);
548 SAL_INFO ("cppcanvas.emf", "EMF+\thatch style " << style << " foregroundcolor: 0x" << solidColor.AsRGBHexString() << " background 0x" << secondColor.AsRGBHexString());
549 break;
551 // path gradient
552 case 3:
554 s.ReadUInt32( additionalFlags ).ReadInt32( wrapMode );
556 SAL_INFO ("cppcanvas.emf", "EMF+\tpath gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec);
558 sal_uInt32 color;
560 s.ReadUInt32( color );
561 solidColor = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
562 SAL_INFO("cppcanvas.emf", "EMF+\tcenter color: 0x" << std::hex << color << std::dec);
564 s.ReadFloat( areaX ).ReadFloat( areaY );
565 SAL_INFO("cppcanvas.emf", "EMF+\tcenter point: " << areaX << "," << areaY);
567 s.ReadInt32( surroundColorsNumber );
568 SAL_INFO("cppcanvas.emf", "EMF+\tsurround colors: " << surroundColorsNumber);
570 if( surroundColorsNumber<0 || sal_uInt32(surroundColorsNumber)>SAL_MAX_INT32/sizeof(::Color) )
571 surroundColorsNumber = SAL_MAX_INT32/sizeof(::Color);
573 surroundColors = new ::Color [surroundColorsNumber];
574 for (int i = 0; i < surroundColorsNumber; i++) {
575 s.ReadUInt32( color );
576 surroundColors[i] = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
577 if (i == 0)
578 secondColor = surroundColors [0];
579 SAL_INFO("cppcanvas.emf", "EMF+\tsurround color[" << i << "]: 0x" << std::hex << color << std::dec);
582 if (additionalFlags & 0x01) {
583 sal_Int32 pathLength;
585 s.ReadInt32( pathLength );
586 SAL_INFO("cppcanvas.emf", "EMF+\tpath length: " << pathLength);
588 sal_Size pos = s.Tell ();
590 sal_uInt32 pathHeader;
591 sal_Int32 pathPoints, pathFlags;
592 s.ReadUInt32( pathHeader ).ReadInt32( pathPoints ).ReadInt32( pathFlags );
594 SAL_INFO("cppcanvas.emf", "EMF+\tpath (brush path gradient)");
595 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec );
597 path = new EMFPPath (pathPoints);
598 path->Read (s, pathFlags, rR);
600 s.Seek (pos + pathLength);
602 const ::basegfx::B2DRectangle aBounds (::basegfx::tools::getRange (path->GetPolygon (rR, false)));
603 areaWidth = aBounds.getWidth ();
604 areaHeight = aBounds.getHeight ();
606 SAL_INFO("cppcanvas.emf", "EMF+\tpolygon bounding box: " << aBounds.getMinX () << "," << aBounds.getMinY () << " " << aBounds.getWidth () << "x" << aBounds.getHeight ());
609 if (additionalFlags & 0x02) {
610 SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
611 ReadXForm( s, transformation );
612 hasTransformation = true;
613 SAL_INFO("cppcanvas.emf",
614 "EMF+\tm11: " << transformation.eM11 << " m12: " << transformation.eM12 <<
615 "\nEMF+\tm21: " << transformation.eM21 << " m22: " << transformation.eM22 <<
616 "\nEMF+\tdx: " << transformation.eDx << " dy: " << transformation.eDy);
619 if (additionalFlags & 0x08) {
620 s.ReadInt32( blendPoints );
621 SAL_INFO("cppcanvas.emf", "EMF+\tuse blend, points: " << blendPoints);
622 if( blendPoints<0 || sal_uInt32(blendPoints)>SAL_MAX_INT32/(2*sizeof(float)) )
623 blendPoints = SAL_MAX_INT32/(2*sizeof(float));
624 blendPositions = new float [2*blendPoints];
625 blendFactors = blendPositions + blendPoints;
626 for (int i=0; i < blendPoints; i ++) {
627 s.ReadFloat( blendPositions [i] );
628 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << blendPositions [i]);
630 for (int i=0; i < blendPoints; i ++) {
631 s.ReadFloat( blendFactors [i] );
632 SAL_INFO("cppcanvas.emf", "EMF+\tfactor[" << i << "]: " << blendFactors [i]);
636 if (additionalFlags & 0x04) {
637 s.ReadInt32( colorblendPoints );
638 SAL_INFO("cppcanvas.emf", "EMF+\tuse color blend, points: " << colorblendPoints);
639 if( colorblendPoints<0 || sal_uInt32(colorblendPoints)>SAL_MAX_INT32/sizeof(float) )
640 colorblendPoints = SAL_MAX_INT32/sizeof(float);
641 if( sal_uInt32(colorblendPoints)>SAL_MAX_INT32/sizeof(::Color) )
642 colorblendPoints = SAL_MAX_INT32/sizeof(::Color);
643 colorblendPositions = new float [colorblendPoints];
644 colorblendColors = new ::Color [colorblendPoints];
645 for (int i=0; i < colorblendPoints; i ++) {
646 s.ReadFloat( colorblendPositions [i] );
647 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions [i]);
649 for (int i=0; i < colorblendPoints; i ++) {
650 s.ReadUInt32( color );
651 colorblendColors [i] = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
652 SAL_INFO("cppcanvas.emf", "EMF+\tcolor[" << i << "]: 0x" << std::hex << color << std::dec);
656 break;
658 // linear gradient
659 case 4:
661 s.ReadUInt32( additionalFlags ).ReadInt32( wrapMode );
663 SAL_INFO("cppcanvas.emf", "EMF+\tlinear gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec);
665 s.ReadFloat( areaX ).ReadFloat( areaY ).ReadFloat( areaWidth ).ReadFloat( areaHeight );
667 SAL_INFO("cppcanvas.emf", "EMF+\tarea: " << areaX << "," << areaY << " - " << areaWidth << "x" << areaHeight);
669 sal_uInt32 color;
671 s.ReadUInt32( color );
672 solidColor = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
673 SAL_INFO("cppcanvas.emf", "EMF+\tfirst color: 0x" << std::hex << color << std::dec);
675 s.ReadUInt32( color );
676 secondColor = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
677 SAL_INFO("cppcanvas.emf", "EMF+\tsecond color: 0x" << std::hex << color << std::dec);
679 // repeated colors, unknown meaning, see http://www.aces.uiuc.edu/~jhtodd/Metafile/MetafileRecords/ObjectBrush.html
680 s.ReadUInt32( color );
681 s.ReadUInt32( color );
683 if (additionalFlags & 0x02) {
684 SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
685 ReadXForm( s, transformation );
686 hasTransformation = true;
687 SAL_INFO("cppcanvas.emf",
688 "EMF+\tm11: " << transformation.eM11 << " m12: " << transformation.eM12 <<
689 "\nEMF+\tm21: " << transformation.eM21 << " m22: " << transformation.eM22 <<
690 "\nEMF+\tdx: " << transformation.eDx << " dy: " << transformation.eDy);
692 if (additionalFlags & 0x08) {
693 s.ReadInt32( blendPoints );
694 SAL_INFO("cppcanvas.emf", "EMF+\tuse blend, points: " << blendPoints);
695 if( blendPoints<0 || sal_uInt32(blendPoints)>SAL_MAX_INT32/(2*sizeof(float)) )
696 blendPoints = SAL_MAX_INT32/(2*sizeof(float));
697 blendPositions = new float [2*blendPoints];
698 blendFactors = blendPositions + blendPoints;
699 for (int i=0; i < blendPoints; i ++) {
700 s.ReadFloat( blendPositions [i] );
701 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << blendPositions [i]);
703 for (int i=0; i < blendPoints; i ++) {
704 s.ReadFloat( blendFactors [i] );
705 SAL_INFO("cppcanvas.emf", "EMF+\tfactor[" << i << "]: " << blendFactors [i]);
709 if (additionalFlags & 0x04) {
710 s.ReadInt32( colorblendPoints );
711 SAL_INFO("cppcanvas.emf", "EMF+\tuse color blend, points: " << colorblendPoints);
712 if( colorblendPoints<0 || sal_uInt32(colorblendPoints)>SAL_MAX_INT32/sizeof(float) )
713 colorblendPoints = SAL_MAX_INT32/sizeof(float);
714 if( sal_uInt32(colorblendPoints)>SAL_MAX_INT32/sizeof(::Color) )
715 colorblendPoints = sal_uInt32(SAL_MAX_INT32)/sizeof(::Color);
716 colorblendPositions = new float [colorblendPoints];
717 colorblendColors = new ::Color [colorblendPoints];
718 for (int i=0; i < colorblendPoints; i ++) {
719 s.ReadFloat( colorblendPositions [i] );
720 SAL_INFO("cppcanvas.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions [i]);
722 for (int i=0; i < colorblendPoints; i ++) {
723 s.ReadUInt32( color );
724 colorblendColors [i] = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
725 SAL_INFO("cppcanvas.emf", "EMF+\tcolor[" << i << "]: 0x" << std::hex << color << std::dec);
729 break;
731 default:
732 SAL_INFO("cppcanvas.emf", "EMF+\tunhandled brush type: " << std::hex << type << std::dec);
737 /// Convert stroke caps between EMF+ and rendering API
738 sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke)
740 switch (nEmfStroke)
742 case EmfPlusLineCapTypeSquare: return rendering::PathCapType::SQUARE;
743 case EmfPlusLineCapTypeRound: return rendering::PathCapType::ROUND;
746 // we have no mapping for EmfPlusLineCapTypeTriangle = 0x00000003,
747 // so return BUTT always
748 return rendering::PathCapType::BUTT;
751 sal_Int8 lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin)
753 switch (nEmfLineJoin)
755 case EmfPlusLineJoinTypeMiter: // fall-through
756 case EmfPlusLineJoinTypeMiterClipped: return rendering::PathJoinType::MITER;
757 case EmfPlusLineJoinTypeBevel: return rendering::PathJoinType::BEVEL;
758 case EmfPlusLineJoinTypeRound: return rendering::PathJoinType::ROUND;
760 assert(false); // Line Join type isn't in specification.
761 return 0;
764 struct EMFPCustomLineCap : public EMFPObject
766 sal_uInt32 type;
767 sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin;
768 float miterLimit;
769 basegfx::B2DPolyPolygon polygon;
770 bool mbIsFilled;
772 public:
773 EMFPCustomLineCap()
774 : EMFPObject()
775 , type(0)
776 , strokeStartCap(0)
777 , strokeEndCap(0)
778 , strokeJoin(0)
779 , miterLimit(0.0)
780 , mbIsFilled(false)
784 virtual ~EMFPCustomLineCap()
788 void SetAttributes(rendering::StrokeAttributes& aAttributes)
790 aAttributes.StartCapType = lcl_convertStrokeCap(strokeStartCap);
791 aAttributes.EndCapType = lcl_convertStrokeCap(strokeEndCap);
792 aAttributes.JoinType = lcl_convertLineJoinType(strokeJoin);
794 aAttributes.MiterLimit = miterLimit;
797 void ReadPath(SvStream& s, ImplRenderer& rR, bool bFill)
799 sal_Int32 pathLength;
800 s.ReadInt32( pathLength );
801 SAL_INFO("cppcanvas.emf", "EMF+\t\tpath length: " << pathLength);
803 sal_uInt32 pathHeader;
804 sal_Int32 pathPoints, pathFlags;
805 s.ReadUInt32( pathHeader ).ReadInt32( pathPoints ).ReadInt32( pathFlags );
807 SAL_INFO("cppcanvas.emf", "EMF+\t\tpath (custom cap line path)");
808 SAL_INFO("cppcanvas.emf", "EMF+\t\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec );
810 EMFPPath path(pathPoints);
811 path.Read(s, pathFlags, rR);
813 polygon = path.GetPolygon(rR, false);
814 mbIsFilled = bFill;
816 // transformation to convert the path to what LibreOffice
817 // expects
818 B2DHomMatrix aMatrix;
819 aMatrix.scale(1.0, -1.0);
821 polygon.transform(aMatrix);
824 void Read (SvStream& s, ImplRenderer& rR)
826 sal_uInt32 header;
828 s.ReadUInt32( header ).ReadUInt32( type );
830 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustom cap");
831 SAL_INFO("cppcanvas.emf", "EMF+\t\theader: 0x" << std::hex << header << " type: " << type << std::dec);
833 if (type == EmfPlusCustomLineCapDataTypeDefault)
835 sal_uInt32 customLineCapDataFlags, baseCap;
836 float baseInset;
837 float widthScale;
838 float fillHotSpotX, fillHotSpotY, strokeHotSpotX, strokeHotSpotY;
840 s.ReadUInt32( customLineCapDataFlags ).ReadUInt32( baseCap ).ReadFloat( baseInset )
841 .ReadUInt32( strokeStartCap ).ReadUInt32( strokeEndCap ).ReadUInt32( strokeJoin )
842 .ReadFloat( miterLimit ).ReadFloat( widthScale )
843 .ReadFloat( fillHotSpotX ).ReadFloat( fillHotSpotY ).ReadFloat( strokeHotSpotX ).ReadFloat( strokeHotSpotY );
845 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags);
846 SAL_INFO("cppcanvas.emf", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap);
847 SAL_INFO("cppcanvas.emf", "EMF+\t\tbaseInset: " << baseInset);
848 SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap);
849 SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap);
850 SAL_INFO("cppcanvas.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin);
851 SAL_INFO("cppcanvas.emf", "EMF+\t\tmiterLimit: " << miterLimit);
852 SAL_INFO("cppcanvas.emf", "EMF+\t\twidthScale: " << widthScale);
854 if (customLineCapDataFlags & EmfPlusCustomLineCapDataFillPath)
856 ReadPath(s, rR, true);
859 if (customLineCapDataFlags & EmfPlusCustomLineCapDataLinePath)
861 ReadPath(s, rR, false);
864 else if (type == EmfPlusCustomLineCapDataTypeAdjustableArrow)
866 // TODO only reads the data, does not use them [I've had
867 // no test document to be able to implement it]
869 sal_Int32 width, height, middleInset, fillState, lineStartCap;
870 sal_Int32 lineEndCap, lineJoin, widthScale;
871 float fillHotSpotX, fillHotSpotY, lineHotSpotX, lineHotSpotY;
873 s.ReadInt32( width ).ReadInt32( height ).ReadInt32( middleInset ).ReadInt32( fillState ).ReadInt32( lineStartCap )
874 .ReadInt32( lineEndCap ).ReadInt32( lineJoin ).ReadFloat( miterLimit ).ReadInt32( widthScale )
875 .ReadFloat( fillHotSpotX ).ReadFloat( fillHotSpotY ).ReadFloat( lineHotSpotX ).ReadFloat( lineHotSpotY );
877 SAL_INFO("cppcanvas.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)");
882 struct EMFPPen : public EMFPBrush
884 XForm transformation;
885 float width;
886 sal_Int32 startCap;
887 sal_Int32 endCap;
888 sal_Int32 lineJoin;
889 float mitterLimit;
890 sal_Int32 dashStyle;
891 sal_Int32 dashCap;
892 float dashOffset;
893 sal_Int32 dashPatternLen;
894 float *dashPattern;
895 sal_Int32 alignment;
896 sal_Int32 compoundArrayLen;
897 float *compoundArray;
898 sal_Int32 customStartCapLen;
899 EMFPCustomLineCap *customStartCap;
900 sal_Int32 customEndCapLen;
901 EMFPCustomLineCap *customEndCap;
903 public:
904 EMFPPen ()
905 : EMFPBrush()
906 , width(0.0)
907 , startCap(0)
908 , endCap(0)
909 , lineJoin(0)
910 , mitterLimit(0.0)
911 , dashStyle(0)
912 , dashCap(0)
913 , dashOffset(0.0)
914 , dashPatternLen(0)
915 , dashPattern(NULL)
916 , alignment(0)
917 , compoundArrayLen(0)
918 , compoundArray(NULL)
919 , customStartCapLen(0)
920 , customStartCap(NULL)
921 , customEndCapLen(0)
922 , customEndCap(NULL)
926 virtual ~EMFPPen()
928 delete[] dashPattern;
929 delete[] compoundArray;
930 delete customStartCap;
931 delete customEndCap;
934 void SetStrokeWidth(rendering::StrokeAttributes& rStrokeAttributes, ImplRenderer& rR, const OutDevState& rState)
936 #if OSL_DEBUG_LEVEL > 1
937 if (width == 0.0) {
938 SAL_INFO ("cppcanvas.emf", "TODO: pen with zero width - using minimal which might not be correct\n");
940 #endif
941 rStrokeAttributes.StrokeWidth = fabs((rState.mapModeTransform * rR.MapSize (width == 0.0 ? 0.05 : width, 0)).getLength());
944 void SetStrokeAttributes(rendering::StrokeAttributes& rStrokeAttributes)
946 rStrokeAttributes.JoinType = lcl_convertLineJoinType(lineJoin);
948 if (dashStyle != EmfPlusLineStyleSolid)
950 const float dash[] = {3, 3};
951 const float dot[] = {1, 3};
952 const float dashdot[] = {3, 3, 1, 3};
953 const float dashdotdot[] = {3, 3, 1, 3, 1, 3};
955 sal_Int32 nLen = 0;
956 const float *pPattern = NULL;
957 switch (dashStyle)
959 case EmfPlusLineStyleDash: nLen = SAL_N_ELEMENTS(dash); pPattern = dash; break;
960 case EmfPlusLineStyleDot: nLen = SAL_N_ELEMENTS(dot); pPattern = dot; break;
961 case EmfPlusLineStyleDashDot: nLen = SAL_N_ELEMENTS(dashdot); pPattern = dashdot; break;
962 case EmfPlusLineStyleDashDotDot: nLen = SAL_N_ELEMENTS(dashdotdot); pPattern = dashdotdot; break;
963 case EmfPlusLineStyleCustom: nLen = dashPatternLen; pPattern = dashPattern; break;
965 if (nLen > 0)
967 uno::Sequence<double> aDashArray(nLen);
968 for (int i = 0; i < nLen; ++i)
969 aDashArray[i] = pPattern[i];
971 rStrokeAttributes.DashArray = aDashArray;
976 void Read (SvStream& s, ImplRenderer& rR, sal_Int32, sal_Int32 )
978 sal_uInt32 header, unknown, penFlags, unknown2;
979 int i;
981 s.ReadUInt32( header ).ReadUInt32( unknown ).ReadUInt32( penFlags ).ReadUInt32( unknown2 ).ReadFloat( width );
983 SAL_INFO("cppcanvas.emf", "EMF+\tpen");
984 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " unknown: 0x" << unknown <<
985 " additional flags: 0x" << penFlags << " unknown: 0x" << unknown2 << " width: " << std::dec << width );
987 if (penFlags & 1)
988 ReadXForm( s, transformation );
990 if (penFlags & 2)
992 s.ReadInt32( startCap );
993 SAL_INFO("cppcanvas.emf", "EMF+\t\tstartCap: 0x" << std::hex << startCap);
995 else
996 startCap = 0;
998 if (penFlags & 4)
1000 s.ReadInt32( endCap );
1001 SAL_INFO("cppcanvas.emf", "EMF+\t\tendCap: 0x" << std::hex << endCap);
1003 else
1004 endCap = 0;
1006 if (penFlags & 8)
1007 s.ReadInt32( lineJoin );
1008 else
1009 lineJoin = 0;
1011 if (penFlags & 16)
1012 s.ReadFloat( mitterLimit );
1013 else
1014 mitterLimit = 0;
1016 if (penFlags & 32)
1018 s.ReadInt32( dashStyle );
1019 SAL_INFO("cppcanvas.emf", "EMF+\t\tdashStyle: 0x" << std::hex << dashStyle);
1021 else
1022 dashStyle = 0;
1024 if (penFlags & 64)
1025 s.ReadInt32( dashCap );
1026 else
1027 dashCap = 0;
1029 if (penFlags & 128)
1030 s.ReadFloat( dashOffset );
1031 else
1032 dashOffset = 0;
1034 if (penFlags & 256)
1036 dashStyle = EmfPlusLineStyleCustom;
1038 s.ReadInt32( dashPatternLen );
1039 SAL_INFO("cppcanvas.emf", "EMF+\t\tdashPatternLen: " << dashPatternLen);
1041 if( dashPatternLen<0 || sal_uInt32(dashPatternLen)>SAL_MAX_INT32/sizeof(float) )
1042 dashPatternLen = SAL_MAX_INT32/sizeof(float);
1043 dashPattern = new float [dashPatternLen];
1044 for (i = 0; i < dashPatternLen; i++)
1046 s.ReadFloat( dashPattern [i] );
1047 SAL_INFO("cppcanvas.emf", "EMF+\t\t\tdashPattern[" << i << "]: " << dashPattern[i]);
1050 else
1051 dashPatternLen = 0;
1053 if (penFlags & 512)
1054 s.ReadInt32( alignment );
1055 else
1056 alignment = 0;
1058 if (penFlags & 1024) {
1059 s.ReadInt32( compoundArrayLen );
1060 if( compoundArrayLen<0 || sal_uInt32(compoundArrayLen)>SAL_MAX_INT32/sizeof(float) )
1061 compoundArrayLen = SAL_MAX_INT32/sizeof(float);
1062 compoundArray = new float [compoundArrayLen];
1063 for (i = 0; i < compoundArrayLen; i++)
1064 s.ReadFloat( compoundArray [i] );
1065 } else
1066 compoundArrayLen = 0;
1068 if (penFlags & 2048)
1070 s.ReadInt32( customStartCapLen );
1071 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomStartCapLen: " << customStartCapLen);
1072 sal_Size pos = s.Tell();
1074 customStartCap = new EMFPCustomLineCap();
1075 customStartCap->Read(s, rR);
1077 // maybe we don't read everything yet, play it safe ;-)
1078 s.Seek(pos + customStartCapLen);
1080 else
1081 customStartCapLen = 0;
1083 if (penFlags & 4096)
1085 s.ReadInt32( customEndCapLen );
1086 SAL_INFO("cppcanvas.emf", "EMF+\t\tcustomEndCapLen: " << customEndCapLen);
1087 sal_Size pos = s.Tell();
1089 customEndCap = new EMFPCustomLineCap();
1090 customEndCap->Read(s, rR);
1092 // maybe we don't read everything yet, play it safe ;-)
1093 s.Seek(pos + customEndCapLen);
1095 else
1096 customEndCapLen = 0;
1098 EMFPBrush::Read (s, rR);
1102 struct EMFPImage : public EMFPObject
1104 sal_uInt32 type;
1105 sal_Int32 width;
1106 sal_Int32 height;
1107 sal_Int32 stride;
1108 sal_Int32 pixelFormat;
1109 Graphic graphic;
1112 void Read (SvMemoryStream &s, sal_uInt32 dataSize, bool bUseWholeStream)
1114 sal_uInt32 header, bitmapType;
1116 s.ReadUInt32( header ).ReadUInt32( type );
1118 SAL_INFO("cppcanvas.emf", "EMF+\timage\nEMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec );
1120 if (type == 1) { // bitmap
1121 s.ReadInt32( width ).ReadInt32( height ).ReadInt32( stride ).ReadInt32( pixelFormat ).ReadUInt32( bitmapType );
1122 SAL_INFO("cppcanvas.emf", "EMF+\tbitmap width: " << width << " height: " << height << " stride: " << stride << " pixelFormat: 0x" << std::hex << pixelFormat << std::dec);
1123 if ((bitmapType != 0) || (width == 0)) { // non native formats
1124 GraphicFilter filter;
1126 filter.ImportGraphic (graphic, OUString(), s);
1127 SAL_INFO("cppcanvas.emf", "EMF+\tbitmap width: " << graphic.GetBitmap().GetSizePixel().Width() << " height: " << graphic.GetBitmap().GetSizePixel().Height());
1130 } else if (type == 2) { // metafile
1131 sal_Int32 mfType, mfSize;
1133 s.ReadInt32( mfType ).ReadInt32( mfSize );
1134 if (bUseWholeStream)
1135 dataSize = s.remainingSize();
1136 else
1137 dataSize -= 16;
1138 SAL_INFO("cppcanvas.emf", "EMF+\tmetafile type: " << mfType << " dataSize: " << mfSize << " real size calculated from record dataSize: " << dataSize);
1140 GraphicFilter filter;
1141 // workaround buggy metafiles, which have wrong mfSize set (n#705956 for example)
1142 SvMemoryStream mfStream (const_cast<char *>(static_cast<char const *>(s.GetData()) + s.Tell()), dataSize, StreamMode::READ);
1144 filter.ImportGraphic (graphic, OUString(), mfStream);
1146 // debug code - write the stream to debug file /tmp/emf-stream.emf
1147 #if OSL_DEBUG_LEVEL > 1
1148 mfStream.Seek(0);
1149 static sal_Int32 emfp_debug_stream_number = 0;
1150 OUString emfp_debug_filename = "/tmp/emf-embedded-stream" +
1151 OUString::number(emfp_debug_stream_number++) + ".emf";
1153 SvFileStream file( emfp_debug_filename, StreamMode::WRITE | StreamMode::TRUNC );
1155 mfStream.WriteStream(file);
1156 file.Flush();
1157 file.Close();
1158 #endif
1163 struct EMFPFont : public EMFPObject
1165 sal_uInt32 version;
1166 float emSize;
1167 sal_uInt32 sizeUnit;
1168 sal_Int32 fontFlags;
1169 OUString family;
1171 void Read (SvMemoryStream &s)
1173 sal_uInt32 header;
1174 sal_uInt32 reserved;
1175 sal_uInt32 length;
1177 s.ReadUInt32( header ).ReadFloat( emSize ).ReadUInt32( sizeUnit ).ReadInt32( fontFlags ).ReadUInt32( reserved ).ReadUInt32( length );
1179 OSL_ASSERT( ( header >> 12 ) == 0xdbc01 );
1181 SAL_INFO("cppcanvas.emf", "EMF+\tfont\nEMF+\theader: 0x" << std::hex << (header >> 12) << " version: 0x" << (header & 0x1fff) << " size: " << std::dec << emSize << " unit: 0x" << std::hex << sizeUnit << std::dec);
1182 SAL_INFO("cppcanvas.emf", "EMF+\tflags: 0x" << std::hex << fontFlags << " reserved: 0x" << reserved << " length: 0x" << std::hex << length << std::dec);
1184 if( length > 0 && length < 0x4000 ) {
1185 sal_Unicode *chars = static_cast<sal_Unicode *>(alloca( sizeof( sal_Unicode ) * length ));
1187 for( sal_uInt32 i = 0; i < length; i++ )
1188 s.ReadUInt16( chars[ i ] );
1190 family = OUString( chars, length );
1191 SAL_INFO("cppcanvas.emf", "EMF+\tfamily: " << OUStringToOString( family, RTL_TEXTENCODING_UTF8).getStr()); // TODO: can we just use family?
1196 void ImplRenderer::ReadRectangle (SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
1198 if (bCompressed) {
1199 sal_Int16 ix, iy, iw, ih;
1201 s.ReadInt16( ix ).ReadInt16( iy ).ReadInt16( iw ).ReadInt16( ih );
1203 x = ix;
1204 y = iy;
1205 width = iw;
1206 height = ih;
1207 } else
1208 s.ReadFloat( x ).ReadFloat( y ).ReadFloat( width ).ReadFloat( height );
1211 void ImplRenderer::ReadPoint (SvStream& s, float& x, float& y, sal_uInt32 flags)
1213 if (flags & 0x4000) {
1214 sal_Int16 ix, iy;
1216 s.ReadInt16( ix ).ReadInt16( iy );
1218 x = ix;
1219 y = iy;
1220 } else
1221 s.ReadFloat( x ).ReadFloat( y );
1224 void ImplRenderer::MapToDevice (double& x, double& y)
1226 // TODO: other units
1227 x = 100*nMmX*x/nPixX;
1228 y = 100*nMmY*y/nPixY;
1231 ::basegfx::B2DPoint ImplRenderer::Map (double ix, double iy)
1233 double x, y;
1235 x = ix*aWorldTransform.eM11 + iy*aWorldTransform.eM21 + aWorldTransform.eDx;
1236 y = ix*aWorldTransform.eM12 + iy*aWorldTransform.eM22 + aWorldTransform.eDy;
1238 MapToDevice (x, y);
1240 x -= nFrameLeft;
1241 y -= nFrameTop;
1243 x *= aBaseTransform.eM11;
1244 y *= aBaseTransform.eM22;
1246 return ::basegfx::B2DPoint (x, y);
1249 ::basegfx::B2DSize ImplRenderer::MapSize (double iwidth, double iheight)
1251 double w, h;
1253 w = iwidth*aWorldTransform.eM11 + iheight*aWorldTransform.eM21;
1254 h = iwidth*aWorldTransform.eM12 + iheight*aWorldTransform.eM22;
1256 MapToDevice (w, h);
1258 w *= aBaseTransform.eM11;
1259 h *= aBaseTransform.eM22;
1261 return ::basegfx::B2DSize (w, h);
1264 #define COLOR(x) \
1265 vcl::unotools::colorToDoubleSequence( ::Color (0xff - (x >> 24), \
1266 (x >> 16) & 0xff, \
1267 (x >> 8) & 0xff, \
1268 x & 0xff), \
1269 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
1271 void ImplRenderer::EMFPPlusFillPolygon (::basegfx::B2DPolyPolygon& polygon, const ActionFactoryParameters& rParms,
1272 OutDevState& rState, const CanvasSharedPtr& rCanvas, bool isColor, sal_uInt32 brushIndexOrColor)
1274 ::basegfx::B2DPolyPolygon localPolygon (polygon);
1276 SAL_INFO("cppcanvas.emf", "EMF+\tfill polygon");
1278 localPolygon.transform( rState.mapModeTransform );
1280 ActionSharedPtr pPolyAction;
1282 if (isColor) {
1283 SAL_INFO("cppcanvas.emf", "EMF+\t\tcolor fill:0x" << std::hex << brushIndexOrColor << std::dec);
1284 rState.isFillColorSet = true;
1285 rState.isLineColorSet = false;
1287 rState.fillColor = COLOR(brushIndexOrColor);
1289 pPolyAction = ActionSharedPtr ( internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon, rParms.mrCanvas, rState ) );
1291 } else {
1292 rState.isFillColorSet = true;
1293 // extract UseBrush
1294 EMFPBrush* brush = static_cast<EMFPBrush*>( aObjects [brushIndexOrColor & 0xff] );
1295 SAL_INFO("cppcanvas.emf", "EMF+\tbrush fill slot: " << brushIndexOrColor << " (type: " << (brush ? brush->GetType() : -1) << ")");
1297 // give up in case something wrong happened
1298 if( !brush )
1299 return;
1301 rState.isFillColorSet = false;
1302 rState.isLineColorSet = false;
1304 if (brush->type == 1)
1306 // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
1307 // for the others the hatch "background" color (secondColor in brush) is used.
1309 bool isHatchBlend = true;
1310 double blendFactor = 0.0;
1312 switch (brush->hatchStyle)
1314 case HatchStyle05Percent: blendFactor = 0.05; break;
1315 case HatchStyle10Percent: blendFactor = 0.10; break;
1316 case HatchStyle20Percent: blendFactor = 0.20; break;
1317 case HatchStyle25Percent: blendFactor = 0.25; break;
1318 case HatchStyle30Percent: blendFactor = 0.30; break;
1319 case HatchStyle40Percent: blendFactor = 0.40; break;
1320 case HatchStyle50Percent: blendFactor = 0.50; break;
1321 case HatchStyle60Percent: blendFactor = 0.60; break;
1322 case HatchStyle70Percent: blendFactor = 0.70; break;
1323 case HatchStyle75Percent: blendFactor = 0.75; break;
1324 case HatchStyle80Percent: blendFactor = 0.80; break;
1325 case HatchStyle90Percent: blendFactor = 0.90; break;
1326 default:
1327 isHatchBlend = false;
1328 break;
1330 rState.isFillColorSet = true;
1331 rState.isLineColorSet = false;
1332 ::Color fillColor;
1333 if (isHatchBlend)
1335 fillColor = brush->solidColor;
1336 fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
1338 else
1340 fillColor = brush->secondColor;
1342 rState.fillColor = vcl::unotools::colorToDoubleSequence(fillColor, rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
1343 pPolyAction = ActionSharedPtr ( internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon, rParms.mrCanvas, rState ) );
1345 else if (brush->type == 3 || brush->type == 4)
1347 if (brush->type == 3 && !(brush->additionalFlags & 0x1))
1348 return; // we are unable to parse these brushes yet
1350 ::basegfx::B2DHomMatrix aTextureTransformation;
1351 ::basegfx::B2DHomMatrix aWorldTransformation;
1352 ::basegfx::B2DHomMatrix aBaseTransformation;
1353 rendering::Texture aTexture;
1355 aWorldTransformation.set (0, 0, aWorldTransform.eM11);
1356 aWorldTransformation.set (0, 1, aWorldTransform.eM21);
1357 aWorldTransformation.set (0, 2, aWorldTransform.eDx);
1358 aWorldTransformation.set (1, 0, aWorldTransform.eM12);
1359 aWorldTransformation.set (1, 1, aWorldTransform.eM22);
1360 aWorldTransformation.set (1, 2, aWorldTransform.eDy);
1362 aBaseTransformation.set (0, 0, aBaseTransform.eM11);
1363 aBaseTransformation.set (0, 1, aBaseTransform.eM21);
1364 aBaseTransformation.set (0, 2, aBaseTransform.eDx);
1365 aBaseTransformation.set (1, 0, aBaseTransform.eM12);
1366 aBaseTransformation.set (1, 1, aBaseTransform.eM22);
1367 aBaseTransformation.set (1, 2, aBaseTransform.eDy);
1369 if (brush->type == 4) {
1370 aTextureTransformation.scale (brush->areaWidth, brush->areaHeight);
1371 aTextureTransformation.translate (brush->areaX, brush->areaY);
1372 } else {
1373 aTextureTransformation.translate (-0.5, -0.5);
1374 aTextureTransformation.scale (brush->areaWidth, brush->areaHeight);
1375 aTextureTransformation.translate (brush->areaX,brush->areaY);
1378 if (brush->hasTransformation) {
1379 ::basegfx::B2DHomMatrix aTransformation;
1381 aTransformation.set (0, 0, brush->transformation.eM11);
1382 aTransformation.set (0, 1, brush->transformation.eM21);
1383 aTransformation.set (0, 2, brush->transformation.eDx);
1384 aTransformation.set (1, 0, brush->transformation.eM12);
1385 aTransformation.set (1, 1, brush->transformation.eM22);
1386 aTransformation.set (1, 2, brush->transformation.eDy);
1388 aTextureTransformation *= aTransformation;
1391 aTextureTransformation *= aWorldTransformation;
1392 aTextureTransformation.scale (100.0*nMmX/nPixX, 100.0*nMmY/nPixY);
1393 aTextureTransformation.translate (-nFrameLeft, -nFrameTop);
1394 aTextureTransformation *= rState.mapModeTransform;
1395 aTextureTransformation *= aBaseTransformation;
1397 aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
1398 aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
1399 aTexture.Alpha = 1.0;
1401 basegfx::ODFGradientInfo aGradInfo;
1402 OUString aGradientService;
1404 const uno::Sequence< double > aStartColor(
1405 vcl::unotools::colorToDoubleSequence( brush->solidColor,
1406 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ) );
1407 const uno::Sequence< double > aEndColor(
1408 vcl::unotools::colorToDoubleSequence( brush->secondColor,
1409 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() ) );
1410 uno::Sequence< uno::Sequence < double > > aColors (2);
1411 uno::Sequence< double > aStops (2);
1413 if (brush->blendPositions) {
1414 SAL_INFO("cppcanvas.emf", "EMF+\t\tuse blend");
1415 aColors.realloc (brush->blendPoints);
1416 aStops.realloc (brush->blendPoints);
1417 int length = aStartColor.getLength ();
1418 uno::Sequence< double > aColor (length);
1420 OSL_ASSERT (length == aEndColor.getLength());
1422 for (int i = 0; i < brush->blendPoints; i++) {
1423 aStops[i] = brush->blendPositions [i];
1425 for (int j = 0; j < length; j++) {
1426 if (brush->type == 4) {
1427 aColor [j] = aStartColor [j]*(1 - brush->blendFactors[i]) + aEndColor [j]*brush->blendFactors[i];
1428 } else
1429 aColor [j] = aStartColor [j]*brush->blendFactors[i] + aEndColor [j]*(1 - brush->blendFactors[i]);
1432 aColors[i] = aColor;
1434 } else if (brush->colorblendPositions) {
1435 SAL_INFO("cppcanvas.emf", "EMF+\t\tuse color blend");
1436 aColors.realloc (brush->colorblendPoints);
1437 aStops.realloc (brush->colorblendPoints);
1439 for (int i = 0; i < brush->colorblendPoints; i++) {
1440 aStops[i] = brush->colorblendPositions [i];
1441 aColors[(brush->type == 4) ? i : brush->colorblendPoints - 1 - i] = vcl::unotools::colorToDoubleSequence( brush->colorblendColors [i],
1442 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1444 } else {
1445 aStops[0] = 0.0;
1446 aStops[1] = 1.0;
1448 if (brush->type == 4) {
1449 aColors[0] = aStartColor;
1450 aColors[1] = aEndColor;
1451 } else {
1452 aColors[1] = aStartColor;
1453 aColors[0] = aEndColor;
1457 SAL_INFO("cppcanvas.emf", "EMF+\t\tset gradient");
1458 basegfx::B2DRange aBoundsRectangle (0, 0, 1, 1);
1459 if (brush->type == 4) {
1460 aGradientService = "LinearGradient";
1461 aGradInfo = basegfx::tools::createLinearODFGradientInfo(
1462 aBoundsRectangle,
1463 aStops.getLength(),
1467 } else {
1468 aGradientService = "EllipticalGradient";
1469 aGradInfo = basegfx::tools::createEllipticalODFGradientInfo(
1470 aBoundsRectangle,
1471 ::basegfx::B2DVector( 0, 0 ),
1472 aStops.getLength(),
1477 uno::Reference< lang::XMultiServiceFactory > xFactory(
1478 rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
1480 if( xFactory.is() ) {
1481 uno::Sequence<uno::Any> args( 3 );
1482 beans::PropertyValue aProp;
1483 aProp.Name = "Colors";
1484 aProp.Value <<= aColors;
1485 args[0] <<= aProp;
1486 aProp.Name = "Stops";
1487 aProp.Value <<= aStops;
1488 args[1] <<= aProp;
1489 aProp.Name = "AspectRatio";
1490 aProp.Value <<= static_cast<sal_Int32>(1);
1491 args[2] <<= aProp;
1493 aTexture.Gradient.set(
1494 xFactory->createInstanceWithArguments( aGradientService,
1495 args ),
1496 uno::UNO_QUERY);
1499 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
1500 aTextureTransformation );
1502 if( aTexture.Gradient.is() )
1503 pPolyAction =
1504 ActionSharedPtr ( internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon,
1505 rParms.mrCanvas,
1506 rState,
1507 aTexture ) );
1511 if( pPolyAction )
1513 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd poly action");
1515 maActions.push_back(
1516 MtfAction(
1517 pPolyAction,
1518 rParms.mrCurrActionIndex ) );
1520 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
1524 double ImplRenderer::EMFPPlusDrawLineCap(const ::basegfx::B2DPolygon& rPolygon, double fPolyLength,
1525 const ::basegfx::B2DPolyPolygon& rLineCap, bool bIsFilled, bool bStart, const rendering::StrokeAttributes& rAttributes,
1526 const ActionFactoryParameters& rParms, OutDevState& rState)
1528 if (!rLineCap.count())
1529 return 0.0;
1531 // createAreaGeometryForLineStartEnd normalises the arrows height
1532 // before scaling (i.e. scales down by rPolygon.height), hence
1533 // we pre-scale it (which means we can avoid changing the logic
1534 // that would affect arrows rendered outside of EMF+).
1535 const double fWidth = rAttributes.StrokeWidth*rLineCap.getB2DRange().getWidth();
1537 // When drawing an outline (as opposed to a filled endCap), we also
1538 // need to take account that the brush width also adds to the area
1539 // of the polygon.
1540 const double fShift = bIsFilled ? 0 : rAttributes.StrokeWidth;
1541 double fConsumed = 0;
1542 basegfx::B2DPolyPolygon aArrow(basegfx::tools::createAreaGeometryForLineStartEnd(
1543 rPolygon, rLineCap, bStart,
1544 fWidth, fPolyLength, 0, &fConsumed, fShift));
1546 // createAreaGeometryForLineStartEnd from some reason always sets
1547 // the path as closed, correct it
1548 aArrow.setClosed(rLineCap.isClosed());
1550 // If the endcap is filled, we draw ONLY the filling, if it isn't
1551 // filled we draw ONLY the outline, but never both.
1552 if (bIsFilled)
1554 bool bWasFillColorSet = rState.isFillColorSet;
1555 rState.isFillColorSet = true;
1556 rState.fillColor = rState.lineColor;
1557 ActionSharedPtr pAction2(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow, rParms.mrCanvas, rState));
1558 if (pAction2)
1560 maActions.push_back(MtfAction(pAction2, rParms.mrCurrActionIndex));
1561 rParms.mrCurrActionIndex += pAction2->getActionCount()-1;
1563 rState.isFillColorSet = bWasFillColorSet;
1565 else
1567 ActionSharedPtr pAction(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow, rParms.mrCanvas, rState, rAttributes));
1568 if (pAction)
1570 maActions.push_back(MtfAction(pAction, rParms.mrCurrActionIndex));
1571 rParms.mrCurrActionIndex += pAction->getActionCount()-1;
1575 // There isn't any clear definition of how far the line should extend
1576 // for arrows, however the following values seem to give best results
1577 // (fConsumed/2 draws the line to the center-point of the endcap
1578 // for filled caps -- however it is likely this will need to be
1579 // changed once we start taking baseInset into account).
1580 if (bIsFilled)
1581 return fConsumed/2;
1582 else
1583 return rAttributes.StrokeWidth;
1586 void ImplRenderer::EMFPPlusDrawPolygon (const ::basegfx::B2DPolyPolygon& polygon, const ActionFactoryParameters& rParms,
1587 OutDevState& rState, const CanvasSharedPtr& rCanvas, sal_uInt32 penIndex)
1589 EMFPPen* pen = static_cast<EMFPPen*>( aObjects [penIndex & 0xff] );
1591 SAL_WARN_IF( !pen, "cppcanvas.emf", "emf+ missing pen" );
1593 if (pen)
1595 rState.isFillColorSet = false;
1596 rState.isLineColorSet = true;
1597 rState.lineColor = vcl::unotools::colorToDoubleSequence (pen->GetColor (),
1598 rCanvas->getUNOCanvas ()->getDevice()->getDeviceColorSpace());
1600 basegfx::B2DPolyPolygon aPolyPolygon(polygon);
1601 aPolyPolygon.transform(rState.mapModeTransform);
1602 rendering::StrokeAttributes aCommonAttributes;
1604 // some attributes are common for the polygon, and the line
1605 // starts & ends - like the stroke width
1606 pen->SetStrokeWidth(aCommonAttributes, *this, rState);
1608 // but eg. dashing has to be additionally set only on the
1609 // polygon
1610 rendering::StrokeAttributes aPolygonAttributes(aCommonAttributes);
1611 pen->SetStrokeAttributes(aPolygonAttributes);
1613 basegfx::B2DPolyPolygon aFinalPolyPolygon;
1615 // render line starts & ends if present
1616 if (!pen->customStartCap && !pen->customEndCap)
1617 aFinalPolyPolygon = aPolyPolygon;
1618 else
1620 for (sal_uInt32 i = 0; i < aPolyPolygon.count(); ++i)
1622 basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(i));
1624 if (!aPolygon.isClosed())
1626 double fStart = 0.0;
1627 double fEnd = 0.0;
1628 double fPolyLength = basegfx::tools::getLength(aPolygon);
1630 // line start
1631 if (pen->customStartCap)
1633 rendering::StrokeAttributes aAttributes(aCommonAttributes);
1634 pen->customStartCap->SetAttributes(aAttributes);
1636 fStart = EMFPPlusDrawLineCap(aPolygon, fPolyLength, pen->customStartCap->polygon,
1637 pen->customStartCap->mbIsFilled,
1638 true, aAttributes, rParms, rState);
1641 // line end
1642 if (pen->customEndCap)
1644 rendering::StrokeAttributes aAttributes(aCommonAttributes);
1645 pen->customEndCap->SetAttributes(aAttributes);
1647 fEnd = EMFPPlusDrawLineCap(aPolygon, fPolyLength, pen->customEndCap->polygon,
1648 pen->customEndCap->mbIsFilled,
1649 false, aAttributes, rParms, rState);
1652 // build new poly, consume something from the old poly
1653 if (fStart != 0.0 || fEnd != 0.0)
1654 aPolygon = basegfx::tools::getSnippetAbsolute(aPolygon, fStart, fPolyLength - fEnd, fPolyLength);
1657 aFinalPolyPolygon.append(aPolygon);
1661 // finally render the polygon
1662 ActionSharedPtr pPolyAction(internal::PolyPolyActionFactory::createPolyPolyAction(aFinalPolyPolygon, rParms.mrCanvas, rState, aPolygonAttributes));
1663 if( pPolyAction )
1665 maActions.push_back(MtfAction(pPolyAction, rParms.mrCurrActionIndex));
1666 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
1671 void ImplRenderer::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
1673 sal_uInt32 index;
1675 SAL_INFO("cppcanvas.emf", "EMF+ Object slot: " << (flags & 0xff) << " flags: " << (flags & 0xff00));
1677 index = flags & 0xff;
1678 if (aObjects [index] != NULL) {
1679 delete aObjects [index];
1680 aObjects [index] = NULL;
1683 switch (flags & 0x7f00) {
1684 case EmfPlusObjectTypeBrush:
1686 EMFPBrush *brush;
1687 aObjects [index] = brush = new EMFPBrush ();
1688 brush->Read (rObjectStream, *this);
1690 break;
1692 case EmfPlusObjectTypePen:
1694 EMFPPen *pen;
1695 aObjects [index] = pen = new EMFPPen ();
1696 pen->Read (rObjectStream, *this, nHDPI, nVDPI);
1698 break;
1700 case EmfPlusObjectTypePath:
1701 sal_uInt32 header, pathFlags;
1702 sal_Int32 points;
1704 rObjectStream.ReadUInt32( header ).ReadInt32( points ).ReadUInt32( pathFlags );
1706 SAL_INFO("cppcanvas.emf", "EMF+\tpath");
1707 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " points: " << std::dec << points << " additional flags: 0x" << std::hex << pathFlags << std::dec);
1709 EMFPPath *path;
1710 aObjects [index] = path = new EMFPPath (points);
1711 path->Read (rObjectStream, pathFlags, *this);
1713 break;
1714 case EmfPlusObjectTypeRegion: {
1715 EMFPRegion *region;
1717 aObjects [index] = region = new EMFPRegion ();
1718 region->Read (rObjectStream);
1720 break;
1722 case EmfPlusObjectTypeImage:
1724 EMFPImage *image;
1725 aObjects [index] = image = new EMFPImage ();
1726 image->Read (rObjectStream, dataSize, bUseWholeStream);
1728 break;
1730 case EmfPlusObjectTypeFont:
1732 EMFPFont *font;
1733 aObjects [index] = font = new EMFPFont ();
1734 font->Read (rObjectStream);
1736 break;
1738 case EmfPlusObjectTypeStringFormat:
1740 SAL_INFO("cppcanvas.emf", "EMF+\t Object type 'string format' not yet implemented");
1741 break;
1743 case EmfPlusObjectTypeImageAttributes:
1745 SAL_INFO("cppcanvas.emf", "EMF+\t Object type 'image attributes' not yet implemented");
1746 break;
1748 case EmfPlusObjectTypeCustomLineCap:
1750 SAL_INFO("cppcanvas.emf", "EMF+\t Object type 'custom line cap' not yet implemented");
1751 break;
1753 default:
1754 SAL_INFO("cppcanvas.emf", "EMF+\tObject unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
1755 break;
1759 double ImplRenderer::setFont (sal_uInt8 objectId, const ActionFactoryParameters& rParms, OutDevState& rState)
1761 EMFPFont *font = static_cast<EMFPFont*>( aObjects[ objectId ] );
1763 rendering::FontRequest aFontRequest;
1764 aFontRequest.FontDescription.FamilyName = font->family;
1765 double cellSize = font->emSize;
1766 aFontRequest.CellSize = (rState.mapModeTransform*MapSize( cellSize, 0 )).getX();
1767 rState.xFont = rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
1768 uno::Sequence< beans::PropertyValue >(),
1769 geometry::Matrix2D() );
1771 return cellSize;
1774 void ImplRenderer::GraphicStatePush(GraphicStateMap& map, sal_Int32 index, OutDevState& rState)
1776 GraphicStateMap::iterator iter = map.find( index );
1778 if ( iter != map.end() )
1780 EmfPlusGraphicState state = iter->second;
1781 map.erase( iter );
1783 SAL_INFO("cppcanvas.emf", "stack index: " << index << " found and erased");
1786 EmfPlusGraphicState state;
1788 state.aWorldTransform = aWorldTransform;
1789 state.aDevState = rState;
1791 map[ index ] = state;
1794 void ImplRenderer::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, OutDevState& rState)
1796 GraphicStateMap::iterator iter = map.find( index );
1798 if ( iter != map.end() )
1800 SAL_INFO("cppcanvas.emf", "stack index: " << index << " found");
1802 EmfPlusGraphicState state = iter->second;
1804 aWorldTransform = state.aWorldTransform;
1805 rState.clip = state.aDevState.clip;
1806 rState.clipRect = state.aDevState.clipRect;
1807 rState.xClipPoly = state.aDevState.xClipPoly;
1811 void ImplRenderer::processEMFPlus( MetaCommentAction* pAct, const ActionFactoryParameters& rFactoryParms,
1812 OutDevState& rState, const CanvasSharedPtr& rCanvas )
1814 sal_uInt32 length = pAct->GetDataSize ();
1815 SvMemoryStream rMF ((void*) pAct->GetData (), length, StreamMode::READ);
1817 if (length < 12) {
1818 SAL_INFO("cppcanvas.emf", "length is less than required header size");
1821 // 12 is minimal valid EMF+ record size; remaining bytes are padding
1822 while (length >= 12) {
1823 sal_uInt16 type, flags;
1824 sal_uInt32 size, dataSize;
1825 sal_Size next;
1827 rMF.ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
1829 next = rMF.Tell() + ( size - 12 );
1831 if (size < 12) {
1832 SAL_INFO("cppcanvas.emf", "Size field is less than 12 bytes");
1833 } else if (size > length) {
1834 SAL_INFO("cppcanvas.emf", "Size field is greater than bytes left");
1836 if (dataSize > (size-12)) {
1837 SAL_INFO("cppcanvas.emf", "DataSize field is greater than Size-12");
1840 SAL_INFO("cppcanvas.emf", "EMF+ record size: " << size << " type: " << emfTypeToName(type) << " flags: " << flags << " data size: " << dataSize);
1842 if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000))) {
1843 if (!mbMultipart) {
1844 mbMultipart = true;
1845 mMFlags = flags;
1846 mMStream.Seek(0);
1849 OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
1850 // 1st 4 bytes are TotalObjectSize
1851 mMStream.Write (static_cast<const char *>(rMF.GetData()) + rMF.Tell() + 4, dataSize - 4);
1852 SAL_INFO("cppcanvas.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
1853 } else {
1854 if (mbMultipart) {
1855 SAL_INFO("cppcanvas.emf", "EMF+ multipart record flags: " << mMFlags);
1856 mMStream.Seek (0);
1857 processObjectRecord (mMStream, mMFlags, 0, true);
1859 mbMultipart = false;
1862 if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
1864 switch (type) {
1865 case EmfPlusRecordTypeHeader:
1866 sal_uInt32 header, version;
1868 rMF.ReadUInt32( header ).ReadUInt32( version ).ReadInt32( nHDPI ).ReadInt32( nVDPI );
1870 SAL_INFO("cppcanvas.emf", "EMF+ Header");
1871 SAL_INFO("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " version: " << std::dec << version << " horizontal DPI: " << nHDPI << " vertical DPI: " << nVDPI << " dual: " << (flags & 1));
1873 break;
1874 case EmfPlusRecordTypeEndOfFile:
1875 SAL_INFO("cppcanvas.emf", "EMF+ EndOfFile");
1876 break;
1877 case EmfPlusRecordTypeGetDC:
1878 SAL_INFO("cppcanvas.emf", "EMF+ GetDC");
1879 SAL_INFO("cppcanvas.emf", "EMF+\talready used in svtools wmf/emf filter parser");
1880 break;
1881 case EmfPlusRecordTypeObject:
1882 processObjectRecord (rMF, flags, dataSize);
1883 break;
1884 case EmfPlusRecordTypeFillPie:
1886 sal_uInt32 brushIndexOrColor;
1887 float startAngle, sweepAngle;
1889 rMF.ReadUInt32( brushIndexOrColor ).ReadFloat( startAngle ).ReadFloat( sweepAngle );
1891 SAL_INFO("cppcanvas.emf", "EMF+ FillPie colorOrIndex: " << brushIndexOrColor << " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
1893 float dx, dy, dw, dh;
1895 ReadRectangle (rMF, dx, dy, dw, dh, bool(flags & 0x4000));
1897 SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1899 startAngle = 2*M_PI*startAngle/360;
1900 sweepAngle = 2*M_PI*sweepAngle/360;
1902 B2DPoint mappedCenter (Map (dx + dw/2, dy + dh/2));
1903 B2DSize mappedSize( MapSize (dw/2, dh/2));
1905 float endAngle = startAngle + sweepAngle;
1906 startAngle = fmodf(startAngle, static_cast<float>(M_PI*2));
1907 if (startAngle < 0)
1908 startAngle += static_cast<float>(M_PI*2);
1909 endAngle = fmodf(endAngle, static_cast<float>(M_PI*2));
1910 if (endAngle < 0)
1911 endAngle += static_cast<float>(M_PI*2);
1913 if (sweepAngle < 0)
1914 std::swap (endAngle, startAngle);
1916 SAL_INFO("cppcanvas.emf", "EMF+ adjusted angles: start " <<
1917 (360.0*startAngle/M_PI) << ", end: " << (360.0*endAngle/M_PI));
1919 B2DPolygon polygon = basegfx::tools::createPolygonFromEllipseSegment (mappedCenter, mappedSize.getX (), mappedSize.getY (), startAngle, endAngle);
1920 polygon.append (mappedCenter);
1921 polygon.setClosed (true);
1923 B2DPolyPolygon polyPolygon (polygon);
1924 EMFPPlusFillPolygon (polyPolygon, rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
1926 break;
1927 case EmfPlusRecordTypeFillPath:
1929 sal_uInt32 index = flags & 0xff;
1930 sal_uInt32 brushIndexOrColor;
1932 rMF.ReadUInt32( brushIndexOrColor );
1934 SAL_INFO("cppcanvas.emf", "EMF+ FillPath slot: " << index);
1936 EMFPPlusFillPolygon( static_cast<EMFPPath*>( aObjects [index])->GetPolygon (*this), rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
1938 break;
1939 case EmfPlusRecordTypeDrawEllipse:
1940 case EmfPlusRecordTypeFillEllipse:
1942 // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
1943 // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
1944 // when it is later used.
1945 sal_uInt32 brushIndexOrColor = 1234567;
1947 if ( type == EmfPlusRecordTypeFillEllipse )
1948 rMF.ReadUInt32( brushIndexOrColor );
1950 SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
1952 float dx, dy, dw, dh;
1954 ReadRectangle (rMF, dx, dy, dw, dh, bool(flags & 0x4000));
1956 SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1958 B2DPoint mappedCenter (Map (dx + dw/2, dy + dh/2));
1959 B2DSize mappedSize( MapSize (dw/2, dh/2));
1961 ::basegfx::B2DPolyPolygon polyPolygon( ::basegfx::B2DPolygon( ::basegfx::tools::createPolygonFromEllipse( mappedCenter, mappedSize.getX (), mappedSize.getY () ) ) );
1963 if ( type == EmfPlusRecordTypeFillEllipse )
1964 EMFPPlusFillPolygon( polyPolygon,
1965 rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor );
1966 else
1967 EMFPPlusDrawPolygon( polyPolygon,
1968 rFactoryParms, rState, rCanvas, flags & 0xff );
1970 break;
1971 case EmfPlusRecordTypeFillRects:
1973 SAL_INFO("cppcanvas.emf", "EMF+ FillRects");
1975 sal_uInt32 brushIndexOrColor;
1976 sal_Int32 rectangles;
1977 bool isColor = (flags & 0x8000);
1978 ::basegfx::B2DPolygon polygon;
1980 rMF.ReadUInt32( brushIndexOrColor ).ReadInt32( rectangles );
1982 SAL_INFO("cppcanvas.emf", "EMF+\t" << ((flags & 0x8000) ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
1984 for (int i=0; i < rectangles; i++) {
1985 if (flags & 0x4000) {
1986 /* 16bit integers */
1987 sal_Int16 x, y, width, height;
1989 rMF.ReadInt16( x ).ReadInt16( y ).ReadInt16( width ).ReadInt16( height );
1991 polygon.append (Map (x, y));
1992 polygon.append (Map (x + width, y));
1993 polygon.append (Map (x + width, y + height));
1994 polygon.append (Map (x, y + height));
1996 SAL_INFO("cppcanvas.emf", "EMF+\trectangle: " << x << ", " << width << "x" << height);
1997 } else {
1998 /* Single's */
1999 float x, y, width, height;
2001 rMF.ReadFloat( x ).ReadFloat( y ).ReadFloat( width ).ReadFloat( height );
2003 polygon.append (Map (x, y));
2004 polygon.append (Map (x + width, y));
2005 polygon.append (Map (x + width, y + height));
2006 polygon.append (Map (x, y + height));
2008 SAL_INFO("cppcanvas.emf", "EMF+\trectangle: " << x << ", " << width << "x" << height);
2011 ::basegfx::B2DPolyPolygon polyPolygon (polygon);
2013 EMFPPlusFillPolygon (polyPolygon, rFactoryParms, rState, rCanvas, isColor, brushIndexOrColor);
2015 break;
2017 case EmfPlusRecordTypeFillPolygon:
2019 sal_uInt8 index = flags & 0xff;
2020 sal_uInt32 brushIndexOrColor;
2021 sal_Int32 points;
2023 rMF.ReadUInt32( brushIndexOrColor );
2024 rMF.ReadInt32( points );
2026 SAL_INFO("cppcanvas.emf", "EMF+ FillPolygon in slot: " << +index << " points: " << points);
2027 SAL_INFO("cppcanvas.emf", "EMF+\t: " << ((flags & 0x8000) ? "color" : "brush index") << " 0x" << std::hex << brushIndexOrColor << std::dec);
2029 EMFPPath path (points, true);
2030 path.Read (rMF, flags, *this);
2032 EMFPPlusFillPolygon (path.GetPolygon (*this), rFactoryParms, rState, rCanvas, flags & 0x8000, brushIndexOrColor);
2034 break;
2036 case EmfPlusRecordTypeDrawLines:
2038 sal_uInt32 points;
2040 rMF.ReadUInt32( points );
2042 SAL_INFO("cppcanvas.emf", "EMF+ DrawLines in slot: " << (flags & 0xff) << " points: " << points);
2044 EMFPPath path (points, true);
2045 path.Read (rMF, flags, *this);
2047 EMFPPlusDrawPolygon (path.GetPolygon (*this), rFactoryParms, rState, rCanvas, flags);
2049 break;
2051 case EmfPlusRecordTypeDrawPath:
2053 sal_uInt32 penIndex;
2055 rMF.ReadUInt32( penIndex );
2057 SAL_INFO("cppcanvas.emf", "EMF+ DrawPath");
2058 SAL_INFO("cppcanvas.emf", "EMF+\tpen: " << penIndex);
2060 EMFPPath* path = static_cast<EMFPPath*>( aObjects [flags & 0xff] );
2061 SAL_WARN_IF( !path, "cppcanvas.emf", "EmfPlusRecordTypeDrawPath missing path" );
2063 EMFPPlusDrawPolygon (path->GetPolygon (*this), rFactoryParms, rState, rCanvas, penIndex);
2065 break;
2067 case EmfPlusRecordTypeDrawImage:
2068 case EmfPlusRecordTypeDrawImagePoints:
2070 sal_uInt32 attrIndex;
2071 sal_Int32 sourceUnit;
2073 rMF.ReadUInt32( attrIndex ).ReadInt32( sourceUnit );
2075 SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << "attributes index: " << attrIndex << "source unit: " << sourceUnit);
2076 SAL_INFO("cppcanvas.emf", "EMF+\tTODO: use image attributes");
2078 if (sourceUnit == 2 && aObjects [flags & 0xff]) { // we handle only GraphicsUnit.Pixel now
2079 EMFPImage& image = *static_cast<EMFPImage *>( aObjects [flags & 0xff]);
2080 float sx, sy, sw, sh;
2081 sal_Int32 aCount;
2083 ReadRectangle (rMF, sx, sy, sw, sh);
2084 Rectangle aSource(Point(sx, sy), Size(sw, sh));
2086 SAL_INFO("cppcanvas.emf", "EMF+ " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " source rectangle: " << sx << "," << sy << " " << sw << "x" << sh);
2088 ::basegfx::B2DPoint aDstPoint;
2089 ::basegfx::B2DSize aDstSize;
2090 bool bValid = false;
2092 if (type == EmfPlusRecordTypeDrawImagePoints) {
2093 rMF.ReadInt32( aCount );
2095 if( aCount == 3) { // TODO: now that we now that this value is count we should support it better
2096 float x1, y1, x2, y2, x3, y3;
2098 ReadPoint (rMF, x1, y1, flags);
2099 ReadPoint (rMF, x2, y2, flags);
2100 ReadPoint (rMF, x3, y3, flags);
2102 SAL_INFO("cppcanvas.emf", "EMF+ destination points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3);
2103 SAL_INFO("cppcanvas.emf", "EMF+ destination rectangle: " << x1 << "," << y1 << " " << x2 - x1 << "x" << y3 - y1);
2105 aDstPoint = Map (x1, y1);
2106 aDstSize = MapSize(x2 - x1, y3 - y1);
2108 bValid = true;
2110 } else if (type == EmfPlusRecordTypeDrawImage) {
2111 float dx, dy, dw, dh;
2113 ReadRectangle (rMF, dx, dy, dw, dh, bool(flags & 0x4000));
2115 SAL_INFO("cppcanvas.emf", "EMF+ destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
2117 aDstPoint = Map (dx, dy);
2118 aDstSize = MapSize(dw, dh);
2120 bValid = true;
2123 if (bValid) {
2124 BitmapEx aBmp( image.graphic.GetBitmapEx () );
2125 aBmp.Crop( aSource );
2127 Size aSize( aBmp.GetSizePixel() );
2128 SAL_INFO("cppcanvas.emf", "EMF+ bitmap size: " << aSize.Width() << "x" << aSize.Height());
2129 if( aSize.Width() > 0 && aSize.Height() > 0 ) {
2130 ActionSharedPtr pBmpAction (
2131 internal::BitmapActionFactory::createBitmapAction (
2132 aBmp,
2133 rState.mapModeTransform * aDstPoint,
2134 rState.mapModeTransform * aDstSize,
2135 rCanvas,
2136 rState));
2138 if( pBmpAction ) {
2139 maActions.push_back( MtfAction( pBmpAction,
2140 rFactoryParms.mrCurrActionIndex ) );
2142 rFactoryParms.mrCurrActionIndex += pBmpAction->getActionCount()-1;
2144 } else {
2145 SAL_INFO("cppcanvas.emf", "EMF+ warning: empty bitmap");
2147 } else {
2148 SAL_INFO("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme)");
2150 } else {
2151 SAL_INFO("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme) - possibly unsupported source units for crop rectangle");
2153 break;
2155 case EmfPlusRecordTypeDrawString:
2157 SAL_INFO("cppcanvas.emf", "EMF+ DrawString");
2159 sal_uInt32 brushId;
2160 sal_uInt32 formatId;
2161 sal_uInt32 stringLength;
2163 rMF.ReadUInt32( brushId ).ReadUInt32( formatId ).ReadUInt32( stringLength );
2164 SAL_INFO("cppcanvas.emf", "EMF+ DrawString brushId: " << brushId << " formatId: " << formatId << " length: " << stringLength);
2166 if (flags & 0x8000) {
2167 float lx, ly, lw, lh;
2169 rMF.ReadFloat( lx ).ReadFloat( ly ).ReadFloat( lw ).ReadFloat( lh );
2171 SAL_INFO("cppcanvas.emf", "EMF+ DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
2173 OUString text = read_uInt16s_ToOUString(rMF, stringLength);
2175 double cellSize = setFont (flags & 0xff, rFactoryParms, rState);
2176 rState.textColor = COLOR( brushId );
2178 ::basegfx::B2DPoint point( Map( lx + 0.15*cellSize, ly + cellSize ) );
2180 ActionSharedPtr pTextAction(
2181 TextActionFactory::createTextAction(
2182 // position is just rough guess for now
2183 // we should calculate it exactly from layoutRect or font
2184 vcl::unotools::pointFromB2DPoint ( point ),
2185 ::Size(),
2186 ::Color(),
2187 ::Size(),
2188 ::Color(),
2189 text,
2191 stringLength,
2192 NULL,
2193 rFactoryParms.mrVDev,
2194 rFactoryParms.mrCanvas,
2195 rState,
2196 rFactoryParms.mrParms,
2197 false ) );
2198 if( pTextAction )
2200 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
2202 maActions.push_back(
2203 MtfAction(
2204 pTextAction,
2205 rFactoryParms.mrCurrActionIndex ) );
2207 rFactoryParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
2209 } else {
2210 SAL_INFO("cppcanvas.emf", "EMF+ DrawString TODO - drawing with brush not yet supported");
2213 break;
2214 case EmfPlusRecordTypeSetPageTransform:
2215 rMF.ReadFloat( fPageScale );
2217 SAL_INFO("cppcanvas.emf", "EMF+ SetPageTransform");
2218 SAL_INFO("cppcanvas.emf", "EMF+\tscale: " << fPageScale << " unit: " << flags);
2219 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2220 break;
2221 case EmfPlusRecordTypeSetRenderingOrigin:
2222 rMF.ReadInt32( nOriginX ).ReadInt32( nOriginY );
2223 SAL_INFO("cppcanvas.emf", "EMF+ SetRenderingOrigin");
2224 SAL_INFO("cppcanvas.emf", "EMF+\torigin [x,y]: " << nOriginX << "," << nOriginY);
2225 break;
2226 case EmfPlusRecordTypeSetTextRenderingHint:
2227 SAL_INFO("cppcanvas.emf", "EMF+ SetTextRenderingHint");
2228 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2229 break;
2230 case EmfPlusRecordTypeSetAntiAliasMode:
2231 SAL_INFO("cppcanvas.emf", "EMF+ SetAntiAliasMode");
2232 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2233 break;
2234 case EmfPlusRecordTypeSetInterpolationMode:
2235 SAL_INFO("cppcanvas.emf", "EMF+ InterpolationMode");
2236 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2237 break;
2238 case EmfPlusRecordTypeSetPixelOffsetMode:
2239 SAL_INFO("cppcanvas.emf", "EMF+ SetPixelOffsetMode");
2240 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2241 break;
2242 case EmfPlusRecordTypeSetCompositingQuality:
2243 SAL_INFO("cppcanvas.emf", "EMF+ SetCompositingQuality");
2244 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2245 break;
2246 case EmfPlusRecordTypeSave:
2248 sal_uInt32 stackIndex;
2250 rMF.ReadUInt32( stackIndex );
2252 SAL_INFO("cppcanvas.emf", "EMF+ Save stack index: " << stackIndex);
2254 GraphicStatePush( mGSStack, stackIndex, rState );
2256 break;
2258 case EmfPlusRecordTypeRestore:
2260 sal_uInt32 stackIndex;
2262 rMF.ReadUInt32( stackIndex );
2264 SAL_INFO("cppcanvas.emf", "EMF+ Restore stack index: " << stackIndex);
2266 GraphicStatePop( mGSStack, stackIndex, rState );
2268 break;
2270 case EmfPlusRecordTypeBeginContainerNoParams:
2272 sal_uInt32 stackIndex;
2274 rMF.ReadUInt32( stackIndex );
2276 SAL_INFO("cppcanvas.emf", "EMF+ Begin Container No Params stack index: " << stackIndex);
2278 GraphicStatePush( mGSContainerStack, stackIndex, rState );
2280 break;
2281 case EmfPlusRecordTypeEndContainer:
2283 sal_uInt32 stackIndex;
2285 rMF.ReadUInt32( stackIndex );
2287 SAL_INFO("cppcanvas.emf", "EMF+ End Container stack index: " << stackIndex);
2289 GraphicStatePop( mGSContainerStack, stackIndex, rState );
2291 break;
2292 case EmfPlusRecordTypeSetWorldTransform: {
2293 SAL_INFO("cppcanvas.emf", "EMF+ SetWorldTransform");
2294 XForm transform;
2295 ReadXForm( rMF, transform );
2296 aWorldTransform.Set (transform);
2297 SAL_INFO("cppcanvas.emf",
2298 "EMF+\tm11: " << aWorldTransform.eM11 << "\tm12: " << aWorldTransform.eM12 <<
2299 "\tm21: " << aWorldTransform.eM21 << "\tm22: " << aWorldTransform.eM22 <<
2300 "\tdx: " << aWorldTransform.eDx << "\tdy: " << aWorldTransform.eDy);
2301 break;
2303 case EmfPlusRecordTypeResetWorldTransform:
2304 SAL_INFO("cppcanvas.emf", "EMF+ ResetWorldTransform");
2305 aWorldTransform.SetIdentity ();
2306 break;
2307 case EmfPlusRecordTypeMultiplyWorldTransform: {
2308 SAL_INFO("cppcanvas.emf", "EMF+ MultiplyWorldTransform");
2309 XForm transform;
2310 ReadXForm( rMF, transform );
2312 SAL_INFO("cppcanvas.emf",
2313 "EMF+\tmatrix m11: " << transform.eM11 << "m12: " << transform.eM12 <<
2314 "EMF+\tm21: " << transform.eM21 << "m22: " << transform.eM22 <<
2315 "EMF+\tdx: " << transform.eDx << "dy: " << transform.eDy);
2317 if (flags & 0x2000) // post multiply
2318 aWorldTransform.Multiply (transform);
2319 else { // pre multiply
2320 transform.Multiply (aWorldTransform);
2321 aWorldTransform.Set (transform);
2323 SAL_INFO("cppcanvas.emf",
2324 "EMF+\tm11: " << aWorldTransform.eM11 << "m12: " << aWorldTransform.eM12 <<
2325 "EMF+\tm21: " << aWorldTransform.eM21 << "m22: " << aWorldTransform.eM22 <<
2326 "EMF+\tdx: " << aWorldTransform.eDx << "dy: " << aWorldTransform.eDy);
2327 break;
2329 case EmfPlusRecordTypeSetClipRect:
2331 int combineMode = (flags >> 8) & 0xf;
2333 SAL_INFO("cppcanvas.emf", "EMF+ SetClipRect combine mode: " << combineMode);
2334 #if OSL_DEBUG_LEVEL > 1
2335 if (combineMode > 1) {
2336 SAL_INFO ("cppcanvas.emf", "EMF+ TODO combine mode > 1");
2338 #endif
2340 float dx, dy, dw, dh;
2342 ReadRectangle (rMF, dx, dy, dw, dh, false);
2344 SAL_INFO("cppcanvas.emf", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh);
2346 B2DPoint mappedPoint (Map (dx, dy));
2347 B2DSize mappedSize( MapSize (dw, dh));
2349 ::basegfx::B2DPolyPolygon polyPolygon( ::basegfx::B2DPolygon( ::basegfx::tools::createPolygonFromRect( ::basegfx::B2DRectangle( mappedPoint.getX(), mappedPoint.getY(),
2350 mappedPoint.getX() + mappedSize.getX(),
2351 mappedPoint.getY() + mappedSize.getY() ) ) ) );
2352 polyPolygon.transform(rState.mapModeTransform);
2354 updateClipping (polyPolygon, rFactoryParms, combineMode == 1);
2356 break;
2358 case EmfPlusRecordTypeSetClipPath:
2360 int combineMode = (flags >> 8) & 0xf;
2362 SAL_INFO("cppcanvas.emf", "EMF+ SetClipPath combine mode: " << combineMode);
2363 SAL_INFO("cppcanvas.emf", "EMF+\tpath in slot: " << (flags & 0xff));
2365 EMFPPath& path = *static_cast<EMFPPath*>( aObjects [flags & 0xff] );
2366 ::basegfx::B2DPolyPolygon& clipPoly (path.GetPolygon (*this));
2368 clipPoly.transform (rState.mapModeTransform);
2369 switch (combineMode)
2371 case EmfPlusCombineModeReplace:
2372 case EmfPlusCombineModeIntersect:
2373 case EmfPlusCombineModeUnion: // Is this, EmfPlusCombineModeXOR and EmfPlusCombineModeComplement correct?
2374 case EmfPlusCombineModeXOR:
2375 case EmfPlusCombineModeComplement:
2376 updateClipping (clipPoly, rFactoryParms, combineMode == 1);
2377 break;
2378 case EmfPlusCombineModeExclude:
2379 // Not doing anything is better then including exactly what we wanted to exclude.
2380 break;
2383 break;
2385 case EmfPlusRecordTypeSetClipRegion: {
2386 int combineMode = (flags >> 8) & 0xf;
2388 SAL_INFO("cppcanvas.emf", "EMF+ SetClipRegion");
2389 SAL_INFO("cppcanvas.emf", "EMF+\tregion in slot: " << (flags & 0xff) << " combine mode: " << combineMode);
2390 EMFPRegion *region = static_cast<EMFPRegion*>(aObjects [flags & 0xff]);
2392 // reset clip
2393 if (region && region->parts == 0 && region->initialState == EmfPlusRegionInitialStateInfinite) {
2394 updateClipping (::basegfx::B2DPolyPolygon (), rFactoryParms, combineMode == 1);
2395 } else {
2396 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2398 break;
2400 case EmfPlusRecordTypeDrawDriverString: {
2401 SAL_INFO("cppcanvas.emf", "EMF+ DrawDriverString, flags: 0x" << std::hex << flags << std::dec);
2402 sal_uInt32 brushIndexOrColor;
2403 sal_uInt32 optionFlags;
2404 sal_uInt32 hasMatrix;
2405 sal_uInt32 glyphsCount;
2407 rMF.ReadUInt32( brushIndexOrColor ).ReadUInt32( optionFlags ).ReadUInt32( hasMatrix ).ReadUInt32( glyphsCount );
2409 SAL_INFO("cppcanvas.emf", "EMF+\t: " << ((flags & 0x8000) ? "color" : "brush index") << " 0x" << std::hex << brushIndexOrColor << std::dec);
2410 SAL_INFO("cppcanvas.emf", "EMF+\toption flags: 0x" << std::hex << optionFlags << std::dec);
2411 SAL_INFO("cppcanvas.emf", "EMF+\thas matrix: " << hasMatrix);
2412 SAL_INFO("cppcanvas.emf", "EMF+\tglyphs: " << glyphsCount);
2414 if( ( optionFlags & 1 ) && glyphsCount > 0 ) {
2415 float *charsPosX = new float[glyphsCount];
2416 float *charsPosY = new float[glyphsCount];
2418 OUString text = read_uInt16s_ToOUString(rMF, glyphsCount);
2420 for( sal_uInt32 i=0; i<glyphsCount; i++) {
2421 rMF.ReadFloat( charsPosX[i] ).ReadFloat( charsPosY[i] );
2422 SAL_INFO("cppcanvas.emf", "EMF+\tglyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
2425 XForm transform;
2426 if( hasMatrix ) {
2427 ReadXForm( rMF, transform );
2428 SAL_INFO("cppcanvas.emf", "EMF+\tmatrix: " << transform.eM11 << ", " << transform.eM12 << ", " << transform.eM21 << ", " << transform.eM22 << ", " << transform.eDx << ", " << transform.eDy);
2431 // add the text action
2432 setFont (flags & 0xff, rFactoryParms, rState);
2434 if( flags & 0x8000 )
2435 rState.textColor = COLOR( brushIndexOrColor );
2437 ::basegfx::B2DPoint point( Map( charsPosX[0], charsPosY[0] ) );
2439 ActionSharedPtr pTextAction(
2440 TextActionFactory::createTextAction(
2441 vcl::unotools::pointFromB2DPoint ( point ),
2442 ::Size(),
2443 ::Color(),
2444 ::Size(),
2445 ::Color(),
2446 text,
2448 glyphsCount,
2449 NULL,
2450 rFactoryParms.mrVDev,
2451 rFactoryParms.mrCanvas,
2452 rState,
2453 rFactoryParms.mrParms,
2454 false ) );
2456 if( pTextAction )
2458 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
2460 maActions.push_back(
2461 MtfAction(
2462 pTextAction,
2463 rFactoryParms.mrCurrActionIndex ) );
2465 rFactoryParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
2468 delete[] charsPosX;
2469 delete[] charsPosY;
2470 } else {
2471 SAL_INFO("cppcanvas.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2474 break;
2476 default:
2477 SAL_INFO("cppcanvas.emf", "EMF+ unhandled record type: " << type);
2478 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2482 rMF.Seek (next);
2484 if (size <= length)
2486 length -= size;
2488 else
2490 SAL_WARN("cppcanvas.emf", "ImplRenderer::processEMFPlus: "
2491 "size " << size << " > length " << length);
2492 length = 0;
2499 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */