update credits
[LibreOffice.git] / cppcanvas / source / mtfrenderer / emfplus.cxx
blob36e64994699587828ddfde05fa5a244b975578ce
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>
50 #include <stdio.h>
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;
131 namespace cppcanvas
133 namespace internal
136 #if OSL_DEBUG_LEVEL > 1
137 void dumpWords (SvStream& s, int i)
139 sal_uInt32 pos = s.Tell ();
140 sal_Int16 data;
141 SAL_INFO ("cppcanvas.emf", "EMF+ dumping words");
142 for (; i > 0; i --) {
143 s >> data;
144 SAL_INFO ("cppcanvas.emf", "EMF+\tdata: " << std::hex << data << std::dec);
146 SAL_INFO ("cppcanvas.emf", "EMF+ end dumping words");
147 s.Seek (pos);
149 #endif
151 struct EMFPPath : public EMFPObject
153 ::basegfx::B2DPolyPolygon aPolygon;
154 sal_Int32 nPoints;
155 float* pPoints;
156 sal_uInt8* pPointTypes;
158 public:
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));
163 nPoints = _nPoints;
164 pPoints = new float [nPoints*2];
165 if (!bLines)
166 pPointTypes = new sal_uInt8 [_nPoints];
167 else
168 pPointTypes = NULL;
171 ~EMFPPath ()
173 delete [] pPoints;
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
183 sal_Int16 x, y;
185 s >> x >> y;
186 SAL_INFO ("cppcanvas.emf", "EMF+\tpoint [x,y]: " << x << "," << y);
187 pPoints [i*2] = x;
188 pPoints [i*2 + 1] = y;
189 } else {
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]);
196 if (pPointTypes)
197 for (int i = 0; i < nPoints; i ++) {
198 s >> pPointTypes [i];
199 SAL_INFO ("cppcanvas.emf", "EMF+\tpoint type: " << pPointTypes [i]);
202 aPolygon.clear ();
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)");
209 #else
210 (void) rR; // avoid warnings
211 #endif
214 ::basegfx::B2DPolyPolygon& GetPolygon (ImplRenderer& rR, bool bMapIt = true)
216 ::basegfx::B2DPolygon polygon;
218 aPolygon.clear ();
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);
226 last_normal = i;
227 p = 0;
228 polygon.clear ();
231 if (bMapIt)
232 mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]);
233 else
234 mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]);
235 if (pPointTypes) {
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 ());
240 continue;
241 } else if (((i - last_normal) % 3) == 2) {
242 prev = mapped;
243 hasPrev = true;
244 continue;
246 } else
247 last_normal = i;
249 polygon.append (mapped);
250 SAL_INFO ("cppcanvas.emf", "polygon append point: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ());
251 if (hasPrev) {
252 polygon.setPrevControlPoint (p, prev);
253 SAL_INFO ("cppcanvas.emf", "polygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ());
254 hasPrev = false;
256 p ++;
257 if (pPointTypes && (pPointTypes [i] & 0x80)) { // closed polygon
258 polygon.setClosed (true);
259 aPolygon.append (polygon);
260 SAL_INFO ("cppcanvas.emf", "close polygon");
261 last_normal = i + 1;
262 p = 0;
263 polygon.clear ();
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());
287 #endif
290 return aPolygon;
294 struct EMFPRegion : public EMFPObject
296 sal_Int32 parts;
297 sal_Int32 *combineMode;
298 sal_Int32 initialState;
299 EMFPPath *initialPath;
300 float ix, iy, iw, ih;
302 EMFPRegion ()
304 combineMode = NULL;
305 initialPath = NULL;
308 ~EMFPRegion ()
310 if (combineMode) {
311 delete [] combineMode;
312 combineMode = NULL;
314 if (initialPath) {
315 delete initialPath;
316 initialPath = NULL;
320 void Read (SvStream& s)
322 sal_uInt32 header;
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 );
329 if (parts) {
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);
341 s >> initialState;
342 SAL_INFO ("cppcanvas.emf", "EMF+\tinitial state: 0x" << std::hex << initialState << std::dec);
346 struct EMFPBrush : public EMFPObject
348 ::Color solidColor;
349 sal_uInt32 type;
350 sal_uInt32 additionalFlags;
352 /* linear gradient */
353 sal_Int32 wrapMode;
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;
360 float* blendFactors;
361 sal_Int32 colorblendPoints;
362 float* colorblendPositions;
363 ::Color* colorblendColors;
364 sal_Int32 surroundColorsNumber;
365 ::Color* surroundColors;
366 EMFPPath *path;
368 public:
369 EMFPBrush ()
371 blendPositions = NULL;
372 colorblendPositions = NULL;
373 colorblendColors = NULL;
374 surroundColors = NULL;
375 path = NULL;
376 hasTransformation = false;
379 ~EMFPBrush ()
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;
397 if (path) {
398 delete path;
399 path = NULL;
403 sal_uInt32 GetType() const { return type; }
404 const ::Color& GetColor() const { return solidColor; }
406 void Read (SvStream& s, ImplRenderer& rR)
408 sal_uInt32 header;
410 s >> header >> type;
412 SAL_INFO ("cppcanvas.emf", "EMF+\tbrush");
413 SAL_INFO ("cppcanvas.emf", "EMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec);
415 switch (type) {
416 case 0:
418 sal_uInt32 color;
420 s >> color;
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);
424 break;
426 // path gradient
427 case 3:
429 s >> additionalFlags >> wrapMode;
431 SAL_INFO ("cppcanvas.emf", "EMF+\tpath gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec);
433 sal_uInt32 color;
435 s >> color;
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);
439 s >> areaX >> areaY;
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++) {
450 s >> color;
451 surroundColors[i] = ::Color (0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
452 if (i == 0)
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;
460 s >> pathLength;
461 SAL_INFO("cppcanvas.emf", "EMF+\tpath length: " << pathLength);
463 sal_uInt32 pos = s.Tell ();
464 #if OSL_DEBUG_LEVEL > 1
465 dumpWords (s, 32);
466 #endif
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");
489 s >> 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) {
498 s >> blendPoints;
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 ++) {
528 s >> color;
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);
533 } else {
534 #if OSL_DEBUG_LEVEL > 1
535 dumpWords (s, 1024);
536 #endif
538 break;
540 // linear gradient
541 case 4:
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);
551 sal_uInt32 color;
553 s >> color;
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);
557 s >> color;
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
562 s >> color;
563 s >> color;
565 if (additionalFlags & 0x02) {
566 SAL_INFO("cppcanvas.emf", "EMF+\tuse transformation");
567 s >> 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) {
575 s >> blendPoints;
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 ++) {
605 s >> color;
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);
611 break;
613 default:
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)
622 switch (nEmfStroke)
624 case EmfPlusLineCapTypeSquare: return rendering::PathCapType::SQUARE;
625 case EmfPlusLineCapTypeRound: return rendering::PathCapType::ROUND;
628 // we have no mapping for EmfPlusLineCapTypeTriangle, so return
629 // BUTT always
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.
643 return 0;
646 struct EMFPCustomLineCap : public EMFPObject
648 sal_uInt32 type;
649 sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin;
650 float miterLimit;
651 basegfx::B2DPolyPolygon polygon;
652 bool mbIsFilled;
654 public:
655 EMFPCustomLineCap() : EMFPObject()
659 ~EMFPCustomLineCap()
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;
675 s >> 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);
689 mbIsFilled = bFill;
691 // transformation to convert the path to what LibreOffice
692 // expects
693 B2DHomMatrix aMatrix;
694 aMatrix.scale(1.0, -1.0);
696 polygon.transform(aMatrix);
699 void Read (SvStream& s, ImplRenderer& rR)
701 sal_uInt32 header;
703 s >> header >> type;
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;
711 float baseInset;
712 float widthScale;
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;
760 float width;
761 sal_Int32 startCap;
762 sal_Int32 endCap;
763 sal_Int32 lineJoin;
764 float mitterLimit;
765 sal_Int32 dashStyle;
766 sal_Int32 dashCap;
767 float dashOffset;
768 sal_Int32 dashPatternLen;
769 float *dashPattern;
770 sal_Int32 alignment;
771 sal_Int32 compoundArrayLen;
772 float *compoundArray;
773 sal_Int32 customStartCapLen;
774 EMFPCustomLineCap *customStartCap;
775 sal_Int32 customEndCapLen;
776 EMFPCustomLineCap *customEndCap;
778 public:
779 EMFPPen () : EMFPBrush ()
781 dashPattern = NULL;
782 compoundArray = NULL;
783 customStartCap = NULL;
784 customEndCap = NULL;
787 ~EMFPPen ()
789 delete[] dashPattern;
790 delete[] compoundArray;
791 delete customStartCap;
792 delete customEndCap;
795 void SetStrokeWidth(rendering::StrokeAttributes& rStrokeAttributes, ImplRenderer& rR, const OutDevState& rState)
797 #if OSL_DEBUG_LEVEL > 1
798 if (width == 0.0) {
799 SAL_INFO ("cppcanvas.emf", "TODO: pen with zero width - using minimal which might not be correct\n");
801 #endif
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};
816 sal_Int32 nLen = 0;
817 const float *pPattern;
818 switch (dashStyle)
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;
826 if (nLen > 0)
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;
840 int i;
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 );
848 if (penFlags & 1)
849 s >> transformation;
851 if (penFlags & 2)
853 s >> startCap;
854 SAL_INFO("cppcanvas.emf", "EMF+\t\tstartCap: 0x" << std::hex << startCap);
856 else
857 startCap = 0;
859 if (penFlags & 4)
861 s >> endCap;
862 SAL_INFO("cppcanvas.emf", "EMF+\t\tendCap: 0x" << std::hex << endCap);
864 else
865 endCap = 0;
867 if (penFlags & 8)
868 s >> lineJoin;
869 else
870 lineJoin = 0;
872 if (penFlags & 16)
873 s >> mitterLimit;
874 else
875 mitterLimit = 0;
877 if (penFlags & 32)
879 s >> dashStyle;
880 SAL_INFO("cppcanvas.emf", "EMF+\t\tdashStyle: 0x" << std::hex << dashStyle);
882 else
883 dashStyle = 0;
885 if (penFlags & 64)
886 s >> dashCap;
887 else
888 dashCap = 0;
890 if (penFlags & 128)
891 s >> dashOffset;
892 else
893 dashOffset = 0;
895 if (penFlags & 256)
897 dashStyle = EmfPlusLineStyleCustom;
899 s >> dashPatternLen;
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]);
911 else
912 dashPatternLen = 0;
914 if (penFlags & 512)
915 s >> alignment;
916 else
917 alignment = 0;
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];
926 } else
927 compoundArrayLen = 0;
929 if (penFlags & 2048)
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);
941 else
942 customStartCapLen = 0;
944 if (penFlags & 4096)
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);
956 else
957 customEndCapLen = 0;
959 EMFPBrush::Read (s, rR);
963 struct EMFPImage : public EMFPObject
965 sal_uInt32 type;
966 sal_Int32 width;
967 sal_Int32 height;
968 sal_Int32 stride;
969 sal_Int32 pixelFormat;
970 Graphic graphic;
973 void Read (SvMemoryStream &s, sal_uInt32 dataSize, sal_Bool bUseWholeStream)
975 sal_uInt32 header, unknown;
977 s >> header >> type;
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
1005 mfStream.Seek(0);
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 );
1013 mfStream >> file;
1014 file.Flush();
1015 file.Close();
1016 #endif
1021 struct EMFPFont : public EMFPObject
1023 sal_uInt32 version;
1024 float emSize;
1025 sal_uInt32 sizeUnit;
1026 sal_Int32 fontFlags;
1027 OUString family;
1029 void Read (SvMemoryStream &s)
1031 sal_uInt32 header;
1032 sal_uInt32 reserved;
1033 sal_uInt32 length;
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++ )
1047 s >> chars[ 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)
1057 if (bCompressed) {
1058 sal_Int16 ix, iy, iw, ih;
1060 s >> ix >> iy >> iw >> ih;
1062 x = ix;
1063 y = iy;
1064 width = iw;
1065 height = ih;
1066 } else
1067 s >> x >> y >> width >> height;
1070 void ImplRenderer::ReadPoint (SvStream& s, float& x, float& y, sal_uInt32 flags)
1072 if (flags & 0x4000) {
1073 sal_Int16 ix, iy;
1075 s >> ix >> iy;
1077 x = ix;
1078 y = iy;
1079 } else
1080 s >> x >> y;
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)
1092 double x, y;
1094 x = ix*aWorldTransform.eM11 + iy*aWorldTransform.eM21 + aWorldTransform.eDx;
1095 y = ix*aWorldTransform.eM12 + iy*aWorldTransform.eM22 + aWorldTransform.eDy;
1097 MapToDevice (x, y);
1099 x -= nFrameLeft;
1100 y -= nFrameTop;
1102 x *= aBaseTransform.eM11;
1103 y *= aBaseTransform.eM22;
1105 return ::basegfx::B2DPoint (x, y);
1108 ::basegfx::B2DSize ImplRenderer::MapSize (double iwidth, double iheight)
1110 double w, h;
1112 w = iwidth*aWorldTransform.eM11 + iheight*aWorldTransform.eM21;
1113 h = iwidth*aWorldTransform.eM12 + iheight*aWorldTransform.eM22;
1115 MapToDevice (w, h);
1117 w *= aBaseTransform.eM11;
1118 h *= aBaseTransform.eM22;
1120 return ::basegfx::B2DSize (w, h);
1123 #define COLOR(x) \
1124 ::vcl::unotools::colorToDoubleSequence( ::Color (0xff - (x >> 24), \
1125 (x >> 16) & 0xff, \
1126 (x >> 8) & 0xff, \
1127 x & 0xff), \
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;
1141 if (isColor) {
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 ) );
1150 } else {
1151 rState.isFillColorSet = true;
1152 // extract UseBrush
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
1157 if( !brush )
1158 return;
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);
1190 } else {
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];
1246 } else
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() );
1262 } else {
1263 aStops[0] = 0.0;
1264 aStops[1] = 1.0;
1266 if (brush->type == 4) {
1267 aColors[0] = aStartColor;
1268 aColors[1] = aEndColor;
1269 } else {
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,
1280 aBoundsRectangle,
1281 aStops.getLength(),
1283 0 );
1285 } else {
1286 aGradientService = "EllipticalGradient";
1287 basegfx::tools::createEllipticalODFGradientInfo( aGradInfo,
1288 aBoundsRectangle,
1289 ::basegfx::B2DVector( 0, 0 ),
1290 aStops.getLength(),
1292 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;
1303 args[0] <<= aProp;
1304 aProp.Name = "Stops";
1305 aProp.Value <<= aStops;
1306 args[1] <<= aProp;
1307 aProp.Name = "AspectRatio";
1308 aProp.Value <<= static_cast<sal_Int32>(1);
1309 args[2] <<= aProp;
1311 aTexture.Gradient.set(
1312 xFactory->createInstanceWithArguments( aGradientService,
1313 args ),
1314 uno::UNO_QUERY);
1317 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
1318 aTextureTransformation );
1320 if( aTexture.Gradient.is() )
1321 pPolyAction =
1322 ActionSharedPtr ( internal::PolyPolyActionFactory::createPolyPolyAction( localPolygon,
1323 rParms.mrCanvas,
1324 rState,
1325 aTexture ) );
1329 if( pPolyAction )
1331 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd poly action");
1333 maActions.push_back(
1334 MtfAction(
1335 pPolyAction,
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())
1347 return 0.0;
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
1357 // of the polygon.
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.
1370 if (bIsFilled)
1372 bool bWasFillColorSet = rState.isFillColorSet;
1373 rState.isFillColorSet = true;
1374 rState.fillColor = rState.lineColor;
1375 ActionSharedPtr pAction2(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow, rParms.mrCanvas, rState));
1376 if (pAction2)
1378 maActions.push_back(MtfAction(pAction2, rParms.mrCurrActionIndex));
1379 rParms.mrCurrActionIndex += pAction2->getActionCount()-1;
1381 rState.isFillColorSet = bWasFillColorSet;
1383 else
1385 ActionSharedPtr pAction(internal::PolyPolyActionFactory::createPolyPolyAction(aArrow, rParms.mrCanvas, rState, rAttributes));
1386 if (pAction)
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).
1398 if (bIsFilled)
1399 return fConsumed/2;
1400 else
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" );
1411 if (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
1427 // polygon
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;
1436 else
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;
1445 double fEnd = 0.0;
1446 double fPolyLength = basegfx::tools::getLength(aPolygon);
1448 // line start
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);
1459 // line end
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));
1481 if( pPolyAction )
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)
1491 sal_uInt32 index;
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:
1504 EMFPBrush *brush;
1505 aObjects [index] = brush = new EMFPBrush ();
1506 brush->Read (rObjectStream, *this);
1508 break;
1510 case EmfPlusObjectTypePen:
1512 EMFPPen *pen;
1513 aObjects [index] = pen = new EMFPPen ();
1514 pen->Read (rObjectStream, *this, nHDPI, nVDPI);
1516 break;
1518 case EmfPlusObjectTypePath:
1519 sal_uInt32 header, pathFlags;
1520 sal_Int32 points;
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);
1527 EMFPPath *path;
1528 aObjects [index] = path = new EMFPPath (points);
1529 path->Read (rObjectStream, pathFlags, *this);
1531 break;
1532 case EmfPlusObjectTypeRegion: {
1533 EMFPRegion *region;
1535 aObjects [index] = region = new EMFPRegion ();
1536 region->Read (rObjectStream);
1538 break;
1540 case EmfPlusObjectTypeImage:
1542 EMFPImage *image;
1543 aObjects [index] = image = new EMFPImage ();
1544 image->Read (rObjectStream, dataSize, bUseWholeStream);
1546 break;
1548 case EmfPlusObjectTypeFont:
1550 EMFPFont *font;
1551 aObjects [index] = font = new EMFPFont ();
1552 font->Read (rObjectStream);
1554 break;
1556 default:
1557 SAL_INFO("cppcanvas.emf", "EMF+\tObject unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
1558 break;
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() );
1574 return cellSize;
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;
1584 map.erase( iter );
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);
1623 #endif
1624 length -= 4;
1626 while (length > 0) {
1627 sal_uInt16 type, flags;
1628 sal_uInt32 size, dataSize;
1629 sal_uInt32 next;
1631 rMF >> type >> flags >> size >> dataSize;
1633 next = rMF.Tell() + ( size - 12 );
1635 if (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))) {
1642 if (!mbMultipart) {
1643 mbMultipart = true;
1644 mMFlags = flags;
1645 mMStream.Seek(0);
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);
1651 } else {
1652 if (mbMultipart) {
1653 SAL_INFO("cppcanvas.emf", "EMF+ multipart record flags: " << mMFlags);
1654 mMStream.Seek (0);
1655 processObjectRecord (mMStream, mMFlags, dataSize, sal_True);
1657 mbMultipart = false;
1660 if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
1662 switch (type) {
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));
1671 break;
1672 case EmfPlusRecordTypeEndOfFile:
1673 SAL_INFO("cppcanvas.emf", "EMF+ EndOfFile");
1674 break;
1675 case EmfPlusRecordTypeGetDC:
1676 SAL_INFO("cppcanvas.emf", "EMF+ GetDC");
1677 SAL_INFO("cppcanvas.emf", "EMF+\talready used in svtools wmf/emf filter parser");
1678 break;
1679 case EmfPlusRecordTypeObject:
1680 processObjectRecord (rMF, flags, dataSize);
1681 break;
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);
1705 if (startAngle < 0)
1706 startAngle += static_cast<float>(M_PI*2);
1707 endAngle = fmodf(endAngle, M_PI*2);
1708 if (endAngle < 0)
1709 endAngle += static_cast<float>(M_PI*2);
1711 if (sweepAngle < 0)
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);
1724 break;
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);
1736 break;
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 );
1764 else
1765 EMFPPlusDrawPolygon( polyPolygon,
1766 rFactoryParms, rState, rCanvas, flags & 0xff );
1768 break;
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);
1795 } else {
1796 /* Single's */
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);
1818 break;
1820 case EmfPlusRecordTypeFillPolygon:
1822 sal_uInt8 index = flags & 0xff;
1823 sal_uInt32 brushIndexOrColor;
1824 sal_Int32 points;
1826 rMF >> brushIndexOrColor;
1827 rMF >> points;
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);
1838 break;
1840 case EmfPlusRecordTypeDrawLines:
1842 sal_uInt32 index = flags & 0xff;
1843 sal_uInt32 points;
1845 rMF >> points;
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 ) );
1870 if( pPolyAction )
1872 maActions.push_back(
1873 MtfAction(
1874 pPolyAction,
1875 rFactoryParms.mrCurrActionIndex ) );
1877 rFactoryParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
1880 break;
1882 case EmfPlusRecordTypeDrawPath:
1884 sal_uInt32 penIndex;
1886 rMF >> 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);
1896 break;
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;
1912 sal_Int32 aCount;
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) {
1924 rMF >> aCount;
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);
1939 bValid = true;
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);
1951 bValid = true;
1954 if (bValid) {
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 (
1963 aBmp,
1964 rState.mapModeTransform * aDstPoint,
1965 rState.mapModeTransform * aDstSize,
1966 rCanvas,
1967 rState));
1969 if( pBmpAction ) {
1970 maActions.push_back( MtfAction( pBmpAction,
1971 rFactoryParms.mrCurrActionIndex ) );
1973 rFactoryParms.mrCurrActionIndex += pBmpAction->getActionCount()-1;
1975 } else {
1976 SAL_INFO("cppcanvas.emf", "EMF+ warning: empty bitmap");
1978 } else {
1979 SAL_INFO("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme)");
1981 } else {
1982 SAL_INFO("cppcanvas.emf", "EMF+ DrawImage(Points) TODO (fixme) - possibly unsupported source units for crop rectangle");
1984 break;
1986 case EmfPlusRecordTypeDrawString:
1988 SAL_INFO("cppcanvas.emf", "EMF+ DrawString");
1990 sal_uInt32 brushId;
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 ) ),
2014 ::Size(),
2015 ::Color(),
2016 ::Size(),
2017 ::Color(),
2018 text,
2020 stringLength,
2021 NULL,
2022 rFactoryParms.mrVDev,
2023 rFactoryParms.mrCanvas,
2024 rState,
2025 rFactoryParms.mrParms,
2026 false ) );
2027 if( pTextAction )
2029 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
2031 maActions.push_back(
2032 MtfAction(
2033 pTextAction,
2034 rFactoryParms.mrCurrActionIndex ) );
2036 rFactoryParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
2038 } else {
2039 SAL_INFO("cppcanvas.emf", "EMF+ DrawString TODO - drawing with brush not yet supported");
2042 break;
2043 case EmfPlusRecordTypeSetPageTransform:
2044 rMF >> fPageScale;
2046 SAL_INFO("cppcanvas.emf", "EMF+ SetPageTransform");
2047 SAL_INFO("cppcanvas.emf", "EMF+\tscale: " << fPageScale << " unit: " << flags);
2048 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2049 break;
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);
2054 break;
2055 case EmfPlusRecordTypeSetTextRenderingHint:
2056 SAL_INFO("cppcanvas.emf", "EMF+ SetTextRenderingHint");
2057 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2058 break;
2059 case EmfPlusRecordTypeSetAntiAliasMode:
2060 SAL_INFO("cppcanvas.emf", "EMF+ SetAntiAliasMode");
2061 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2062 break;
2063 case EmfPlusRecordTypeSetInterpolationMode:
2064 SAL_INFO("cppcanvas.emf", "EMF+ InterpolationMode");
2065 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2066 break;
2067 case EmfPlusRecordTypeSetPixelOffsetMode:
2068 SAL_INFO("cppcanvas.emf", "EMF+ SetPixelOffsetMode");
2069 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2070 break;
2071 case EmfPlusRecordTypeSetCompositingQuality:
2072 SAL_INFO("cppcanvas.emf", "EMF+ SetCompositingQuality");
2073 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2074 break;
2075 case EmfPlusRecordTypeSave:
2077 sal_uInt32 stackIndex;
2079 rMF >> stackIndex;
2081 SAL_INFO("cppcanvas.emf", "EMF+ Save stack index: " << stackIndex);
2083 GraphicStatePush( mGSStack, stackIndex, rState );
2085 break;
2087 case EmfPlusRecordTypeRestore:
2089 sal_uInt32 stackIndex;
2091 rMF >> stackIndex;
2093 SAL_INFO("cppcanvas.emf", "EMF+ Restore stack index: " << stackIndex);
2095 GraphicStatePop( mGSStack, stackIndex, rState );
2097 break;
2099 case EmfPlusRecordTypeBeginContainerNoParams:
2101 sal_uInt32 stackIndex;
2103 rMF >> stackIndex;
2105 SAL_INFO("cppcanvas.emf", "EMF+ Begin Container No Params stack index: " << stackIndex);
2107 GraphicStatePush( mGSContainerStack, stackIndex, rState );
2109 break;
2110 case EmfPlusRecordTypeEndContainer:
2112 sal_uInt32 stackIndex;
2114 rMF >> stackIndex;
2116 SAL_INFO("cppcanvas.emf", "EMF+ End Container stack index: " << stackIndex);
2118 GraphicStatePop( mGSContainerStack, stackIndex, rState );
2120 break;
2121 case EmfPlusRecordTypeSetWorldTransform: {
2122 SAL_INFO("cppcanvas.emf", "EMF+ SetWorldTransform");
2123 XForm transform;
2124 rMF >> transform;
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);
2130 break;
2132 case EmfPlusRecordTypeResetWorldTransform:
2133 SAL_INFO("cppcanvas.emf", "EMF+ ResetWorldTransform");
2134 aWorldTransform.SetIdentity ();
2135 break;
2136 case EmfPlusRecordTypeMultiplyWorldTransform: {
2137 SAL_INFO("cppcanvas.emf", "EMF+ MultiplyWorldTransform");
2138 XForm transform;
2139 rMF >> transform;
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);
2156 break;
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");
2167 #endif
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);
2185 break;
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);
2206 break;
2207 case EmfPlusCombineModeExclude:
2208 // Not doing anything is better then including exactly what we wanted to exclude.
2209 break;
2212 break;
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];
2221 // reset clip
2222 if (region && region->parts == 0 && region->initialState == EmfPlusRegionInitialStateInfinite) {
2223 updateClipping (::basegfx::B2DPolyPolygon (), rFactoryParms, combineMode == 1);
2224 } else {
2225 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2227 break;
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]);
2254 XForm transform;
2255 if( hasMatrix ) {
2256 rMF >> transform;
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] ) ),
2269 ::Size(),
2270 ::Color(),
2271 ::Size(),
2272 ::Color(),
2273 text,
2275 glyphsCount,
2276 NULL,
2277 rFactoryParms.mrVDev,
2278 rFactoryParms.mrCanvas,
2279 rState,
2280 rFactoryParms.mrParms,
2281 false ) );
2283 if( pTextAction )
2285 SAL_INFO("cppcanvas.emf", "EMF+\t\tadd text action");
2287 maActions.push_back(
2288 MtfAction(
2289 pTextAction,
2290 rFactoryParms.mrCurrActionIndex ) );
2292 rFactoryParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
2295 delete[] charsPosX;
2296 delete[] charsPosY;
2297 } else {
2298 SAL_INFO("cppcanvas.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2301 break;
2303 default:
2304 SAL_INFO("cppcanvas.emf", "EMF+ unhandled record type: " << type);
2305 SAL_INFO("cppcanvas.emf", "EMF+\tTODO");
2309 rMF.Seek (next);
2311 if (size <= length)
2313 length -= size;
2315 else
2317 SAL_WARN("cppcanvas.emf", "ImplRenderer::processEMFPlus: "
2318 "size " << size << " > length " << length);
2319 #if OSL_DEBUG_LEVEL > 1
2320 dumpWords(rMF, length);
2321 #endif
2322 length = 0;
2329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */