1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include "emfpcustomlinecap.hxx" 21 #include "emfphelperdata.hxx" 22 #include "emfpbrush.hxx" 23 #include "emfppen.hxx" 24 #include "emfppath.hxx" 25 #include "emfpregion.hxx" 26 #include "emfpimage.hxx" 27 #include "emfpfont.hxx" 28 #include "emfpstringformat.hxx" 29 #include <basegfx/curve/b2dcubicbezier.hxx> 30 #include <wmfemfhelper.hxx> 31 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 32 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 33 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> 34 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 35 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 36 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> 37 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> 38 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 39 #include <drawinglayer/attribute/fontattribute.hxx> 40 #include <basegfx/color/bcolor.hxx> 41 #include <basegfx/matrix/b2dhommatrixtools.hxx> 42 #include <basegfx/polygon/b2dpolygonclipper.hxx> 43 #include <basegfx/polygon/b2dpolygontools.hxx> 44 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 45 #include <sal/log.hxx> 46 #include <vcl/svapp.hxx> 47 #include <vcl/settings.hxx> 48 #include <i18nlangtag/languagetag.hxx> 49 50 namespace emfplushelper 51 { 52 emfTypeToName(sal_uInt16 type)53 const char* emfTypeToName(sal_uInt16 type) 54 { 55 switch (type) 56 { 57 case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader"; 58 case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile"; 59 case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment"; 60 case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC"; 61 case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject"; 62 case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects"; 63 case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects"; 64 case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon"; 65 case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines"; 66 case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse"; 67 case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse"; 68 case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie"; 69 case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie"; 70 case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc"; 71 case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion"; 72 case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath"; 73 case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath"; 74 case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers"; 75 case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage"; 76 case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints"; 77 case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString"; 78 case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin"; 79 case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode"; 80 case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint"; 81 case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode"; 82 case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode"; 83 case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality"; 84 case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave"; 85 case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore"; 86 case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams"; 87 case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer"; 88 case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform"; 89 case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform"; 90 case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform"; 91 case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform"; 92 case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform"; 93 case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform"; 94 case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip"; 95 case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect"; 96 case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath"; 97 case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion"; 98 case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip"; 99 case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString"; 100 } 101 return ""; 102 } 103 ~EMFPObject()104 EMFPObject::~EMFPObject() 105 { 106 } 107 108 typedef enum 109 { 110 StringAlignmentNear = 0x00000000, 111 StringAlignmentCenter = 0x00000001, 112 StringAlignmentFar = 0x00000002 113 } StringAlignment; 114 getUnitToPixelMultiplier(const UnitType aUnitType)115 float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType) 116 { 117 switch (aUnitType) 118 { 119 case UnitTypePixel: 120 { 121 return 1.0f; 122 } 123 case UnitTypePoint: 124 { 125 SAL_INFO("drawinglayer", "EMF+\t Converting Points to Pixels."); 126 return 1.333333f; 127 } 128 case UnitTypeInch: 129 { 130 SAL_INFO("drawinglayer", "EMF+\t TODO Test Converting Inches to Pixels, if it is working correctly."); 131 return 96.0f; 132 } 133 case UnitTypeMillimeter: 134 { 135 SAL_INFO("drawinglayer", "EMF+\t TODO Test Converting Millimeters to Pixels, if it is working correctly."); 136 return 3.779528f; 137 } 138 case UnitTypeDocument: 139 { 140 SAL_INFO("drawinglayer", "EMF+\t TODO Test Converting Documents to Pixels, if it is working correctly."); 141 return 0.32f; 142 } 143 case UnitTypeWorld: 144 case UnitTypeDisplay: 145 default: 146 { 147 SAL_WARN("drawinglayer", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); 148 } 149 } 150 return 1.0f; 151 } 152 processObjectRecord(SvMemoryStream & rObjectStream,sal_uInt16 flags,sal_uInt32 dataSize,bool bUseWholeStream)153 void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream) 154 { 155 sal_uInt32 index; 156 SAL_INFO("drawinglayer", "EMF+ Object slot: " << (flags & 0xff) << " flags: " << (flags & 0xff00)); 157 index = flags & 0xff; 158 159 switch (flags & 0x7f00) 160 { 161 case EmfPlusObjectTypeBrush: 162 { 163 EMFPBrush *brush = new EMFPBrush(); 164 maEMFPObjects[index].reset(brush); 165 brush->Read(rObjectStream, *this); 166 break; 167 } 168 case EmfPlusObjectTypePen: 169 { 170 EMFPPen *pen = new EMFPPen(); 171 maEMFPObjects[index].reset(pen); 172 pen->Read(rObjectStream, *this); 173 break; 174 } 175 case EmfPlusObjectTypePath: 176 { 177 sal_uInt32 header, pathFlags; 178 sal_Int32 points; 179 180 rObjectStream.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags); 181 SAL_INFO("drawinglayer", "EMF+\tpath"); 182 SAL_INFO("drawinglayer", "EMF+\theader: 0x" << std::hex << header << " points: " << std::dec << points << " additional flags: 0x" << std::hex << pathFlags << std::dec); 183 EMFPPath *path = new EMFPPath(points); 184 maEMFPObjects[index].reset(path); 185 path->Read(rObjectStream, pathFlags); 186 break; 187 } 188 case EmfPlusObjectTypeRegion: 189 { 190 EMFPRegion *region = new EMFPRegion(); 191 maEMFPObjects[index].reset(region); 192 region->ReadRegion(rObjectStream, *this); 193 break; 194 } 195 case EmfPlusObjectTypeImage: 196 { 197 EMFPImage *image = new EMFPImage; 198 maEMFPObjects[index].reset(image); 199 image->type = 0; 200 image->width = 0; 201 image->height = 0; 202 image->stride = 0; 203 image->pixelFormat = 0; 204 image->Read(rObjectStream, dataSize, bUseWholeStream); 205 break; 206 } 207 case EmfPlusObjectTypeFont: 208 { 209 EMFPFont *font = new EMFPFont; 210 maEMFPObjects[index].reset(font); 211 font->emSize = 0; 212 font->sizeUnit = 0; 213 font->fontFlags = 0; 214 font->Read(rObjectStream); 215 break; 216 } 217 case EmfPlusObjectTypeStringFormat: 218 { 219 EMFPStringFormat *stringFormat = new EMFPStringFormat(); 220 maEMFPObjects[index].reset(stringFormat); 221 stringFormat->Read(rObjectStream); 222 break; 223 } 224 case EmfPlusObjectTypeImageAttributes: 225 { 226 SAL_WARN("drawinglayer", "EMF+\t TODO Object type 'image attributes' not yet implemented"); 227 break; 228 } 229 case EmfPlusObjectTypeCustomLineCap: 230 { 231 SAL_WARN("drawinglayer", "EMF+\t TODO Object type 'custom line cap' not yet implemented"); 232 break; 233 } 234 default: 235 { 236 SAL_WARN("drawinglayer", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec); 237 } 238 } 239 } 240 ReadPoint(SvStream & s,float & x,float & y,sal_uInt32 flags)241 void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags) 242 { 243 if (flags & 0x800) 244 { 245 // specifies a location in the coordinate space that is relative to 246 // the location specified by the previous element in the array. In the case of the first element in 247 // PointData, a previous location at coordinates (0,0) is assumed. 248 SAL_WARN("drawinglayer", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR"); 249 } 250 251 if (flags & 0x4000) 252 { 253 sal_Int16 ix, iy; 254 255 s.ReadInt16(ix).ReadInt16(iy); 256 257 x = ix; 258 y = iy; 259 } 260 else 261 { 262 s.ReadFloat(x).ReadFloat(y); 263 } 264 } 265 ReadRectangle(SvStream & s,float & x,float & y,float & width,float & height,bool bCompressed)266 void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed) 267 { 268 if (bCompressed) 269 { 270 sal_Int16 ix, iy, iw, ih; 271 272 s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih); 273 274 x = ix; 275 y = iy; 276 width = iw; 277 height = ih; 278 } 279 else 280 { 281 s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height); 282 } 283 } 284 readXForm(SvStream & rIn,basegfx::B2DHomMatrix & rTarget)285 bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget) 286 { 287 rTarget.identity(); 288 289 if (sizeof(float) != 4) 290 { 291 OSL_FAIL("EnhWMFReader::sizeof( float ) != 4"); 292 return false; 293 } 294 else 295 { 296 float eM11(0.0); 297 float eM12(0.0); 298 float eM21(0.0); 299 float eM22(0.0); 300 float eDx(0.0); 301 float eDy(0.0); 302 rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy); 303 rTarget = basegfx::B2DHomMatrix( 304 eM11, eM21, eDx, 305 eM12, eM22, eDy); 306 } 307 308 return true; 309 } 310 mappingChanged()311 void EmfPlusHelperData::mappingChanged() 312 { 313 if (mnPixX == 0 || mnPixY == 0) 314 { 315 SAL_WARN("drawinglayer", "dimensions in pixels is 0"); 316 return; 317 } 318 // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes. 319 // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should 320 // be used in the future, this method will need to be called. 321 // 322 // Re-calculate maMapTransform to contain the complete former transformation so that 323 // it can be applied by a single matrix multiplication or be added to an encapsulated 324 // primitive later 325 // 326 // To evtl. correct and see where this came from, please compare with the implementations 327 // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions 328 maMapTransform = maWorldTransform; 329 maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY, 330 double(-mnFrameLeft), double(-mnFrameTop)); 331 maMapTransform *= maBaseTransform; 332 } 333 Map(double ix,double iy) const334 ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const 335 { 336 // map in one step using complete MapTransform (see mappingChanged) 337 return maMapTransform * ::basegfx::B2DPoint(ix, iy); 338 } 339 EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags,const sal_uInt32 brushIndexOrColor) const340 Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const { 341 Color color; 342 if (flags & 0x8000) // we use a color 343 { 344 color = Color(0xff - (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, 345 (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); 346 } 347 else // we use a pen 348 { 349 const EMFPPen* pen = static_cast<EMFPPen*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); 350 if (pen) 351 { 352 color = pen->GetColor(); 353 } 354 } 355 return color; 356 } 357 GraphicStatePush(GraphicStateMap & map,sal_Int32 index)358 void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index) 359 { 360 GraphicStateMap::iterator iter = map.find( index ); 361 362 if ( iter != map.end() ) 363 { 364 map.erase( iter ); 365 SAL_INFO("drawinglayer", "stack index: " << index << " found and erased"); 366 } 367 368 wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current(); 369 // tdf#112500 We need to save world transform somehow, during graphic state push 370 state.setTransformation(maWorldTransform); 371 map[ index ] = state; 372 } 373 GraphicStatePop(GraphicStateMap & map,sal_Int32 index,wmfemfhelper::PropertyHolder & rState)374 void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState) 375 { 376 GraphicStateMap::iterator iter = map.find( index ); 377 378 if ( iter != map.end() ) 379 { 380 wmfemfhelper::PropertyHolder state = iter->second; 381 382 maWorldTransform = state.getTransformation(); 383 rState.setClipPolyPolygon( state.getClipPolyPolygon() ); 384 mappingChanged(); 385 SAL_INFO("drawinglayer", "stack index: " << index << " found, maWorldTransform: " << maWorldTransform); 386 } 387 } 388 EMFPPlusDrawPolygon(const::basegfx::B2DPolyPolygon & polygon,sal_uInt32 penIndex)389 void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex) 390 { 391 const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get()); 392 SAL_WARN_IF(!pen, "drawinglayer", "emf+ missing pen"); 393 394 if (pen && polygon.count()) 395 { 396 // we need a line join attribute 397 basegfx::B2DLineJoin lineJoin = basegfx::B2DLineJoin::Round; 398 if (pen->penDataFlags & 0x00000008) // additional line join information 399 { 400 lineJoin = static_cast<basegfx::B2DLineJoin>(EMFPPen::lcl_convertLineJoinType(pen->lineJoin)); 401 } 402 403 // we need a line cap attribute 404 css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT; 405 if (pen->penDataFlags & 0x00000002) // additional line cap information 406 { 407 lineCap = static_cast<css::drawing::LineCap>(EMFPPen::lcl_convertStrokeCap(pen->startCap)); 408 SAL_WARN_IF(pen->startCap != pen->endCap, "drawinglayer", "emf+ pen uses different start and end cap"); 409 } 410 411 const double transformedPenWidth = maMapTransform.get(0, 0) * pen->penWidth; 412 drawinglayer::attribute::LineAttribute lineAttribute(pen->GetColor().getBColor(), 413 transformedPenWidth, 414 lineJoin, 415 lineCap); 416 417 drawinglayer::attribute::StrokeAttribute aStrokeAttribute; 418 if (pen->penDataFlags & 0x00000020 && pen->dashStyle != EmfPlusLineStyleCustom) // pen has a predefined line style 419 { 420 // short writing 421 const double pw = maMapTransform.get(1, 1) * pen->penWidth; 422 // taken from the old cppcanvas implementation and multiplied with pen width 423 const std::vector<double> dash = { 3*pw, 3*pw }; 424 const std::vector<double> dot = { pw, 3*pw }; 425 const std::vector<double> dashdot = { 3*pw, 3*pw, pw, 3*pw }; 426 const std::vector<double> dashdotdot = { 3*pw, 3*pw, pw, 3*pw, pw, 3*pw }; 427 428 switch (pen->dashStyle) 429 { 430 case EmfPlusLineStyleSolid: // do nothing special, use default stroke attribute 431 break; 432 case EmfPlusLineStyleDash: 433 aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dash); 434 break; 435 case EmfPlusLineStyleDot: 436 aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dot); 437 break; 438 case EmfPlusLineStyleDashDot: 439 aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdot); 440 break; 441 case EmfPlusLineStyleDashDotDot: 442 aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdotdot); 443 break; 444 } 445 } 446 else if (pen->penDataFlags & 0x00000100) // pen has a custom dash line 447 { 448 // StrokeAttribute needs a double vector while the pen provides a float vector 449 std::vector<double> aPattern(pen->dashPattern.size()); 450 for (size_t i=0; i<aPattern.size(); i++) 451 { 452 // convert from float to double and multiply with the adjusted pen width 453 aPattern[i] = maMapTransform.get(1, 1) * pen->penWidth * pen->dashPattern[i]; 454 } 455 aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(aPattern); 456 } 457 458 if (pen->GetColor().GetTransparency() == 0) 459 { 460 mrTargetHolders.Current().append( 461 std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( 462 polygon, 463 lineAttribute, 464 aStrokeAttribute)); 465 } 466 else 467 { 468 const drawinglayer::primitive2d::Primitive2DReference aPrimitive( 469 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( 470 polygon, 471 lineAttribute, 472 aStrokeAttribute)); 473 474 mrTargetHolders.Current().append( 475 std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( 476 drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }, 477 pen->GetColor().GetTransparency() / 255.0)); 478 } 479 480 if ((pen->penDataFlags & 0x00000800) && (pen->customStartCap->polygon.begin()->count() > 1)) 481 { 482 SAL_WARN("drawinglayer", "EMF+\tCustom Start Line Cap"); 483 ::basegfx::B2DPolyPolygon startCapPolygon(pen->customStartCap->polygon); 484 485 // get the gradient of the first line in the polypolygon 486 double x1 = polygon.begin()->getB2DPoint(0).getX(); 487 double y1 = polygon.begin()->getB2DPoint(0).getY(); 488 double x2 = polygon.begin()->getB2DPoint(1).getX(); 489 double y2 = polygon.begin()->getB2DPoint(1).getY(); 490 491 if ((x2 - x1) != 0) 492 { 493 double gradient = (y2 - y1) / (x2 - x1); 494 495 // now we get the angle that we need to rotate the arrow by 496 double angle = (M_PI / 2) - atan(gradient); 497 498 // rotate the arrow 499 startCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle)); 500 } 501 502 startCapPolygon.transform(maMapTransform); 503 504 basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(0).getX(), 505 0.0, pen->penWidth, polygon.begin()->getB2DPoint(0).getY()); 506 startCapPolygon.transform(tran); 507 508 if (pen->customStartCap->mbIsFilled) 509 { 510 mrTargetHolders.Current().append( 511 std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( 512 startCapPolygon, 513 pen->GetColor().getBColor())); 514 } 515 else 516 { 517 mrTargetHolders.Current().append( 518 std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( 519 startCapPolygon, 520 lineAttribute, 521 aStrokeAttribute)); 522 } 523 } 524 525 if ((pen->penDataFlags & 0x00001000) && (pen->customEndCap->polygon.begin()->count() > 1)) 526 { 527 SAL_WARN("drawinglayer", "EMF+\tCustom End Line Cap"); 528 529 ::basegfx::B2DPolyPolygon endCapPolygon(pen->customEndCap->polygon); 530 531 // get the gradient of the first line in the polypolygon 532 double x1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(); 533 double y1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY(); 534 double x2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getX(); 535 double y2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getY(); 536 537 if ((x2 - x1) != 0) 538 { 539 double gradient = (y2 - y1) / (x2 - x1); 540 541 // now we get the angle that we need to rotate the arrow by 542 double angle = (M_PI / 2) - atan(gradient); 543 544 // rotate the arrow 545 endCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle)); 546 } 547 548 endCapPolygon.transform(maMapTransform); 549 basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(), 550 0.0, pen->penWidth, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY()); 551 endCapPolygon.transform(tran); 552 553 if (pen->customEndCap->mbIsFilled) 554 { 555 mrTargetHolders.Current().append( 556 std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( 557 endCapPolygon, 558 pen->GetColor().getBColor())); 559 } 560 else 561 { 562 mrTargetHolders.Current().append( 563 std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( 564 endCapPolygon, 565 lineAttribute, 566 aStrokeAttribute)); 567 } 568 } 569 570 mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor()); 571 mrPropertyHolders.Current().setLineColorActive(true); 572 mrPropertyHolders.Current().setFillColorActive(false); 573 } 574 } 575 EMFPPlusFillPolygonSolidColor(const::basegfx::B2DPolyPolygon & polygon,Color const & color)576 void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color) 577 { 578 if (color.GetTransparency() < 255) 579 { 580 if (color.GetTransparency() == 0) 581 { 582 // not transparent 583 mrTargetHolders.Current().append( 584 std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( 585 polygon, 586 color.getBColor())); 587 } 588 else 589 { 590 const drawinglayer::primitive2d::Primitive2DReference aPrimitive( 591 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 592 polygon, 593 color.getBColor())); 594 595 mrTargetHolders.Current().append( 596 std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( 597 drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }, 598 color.GetTransparency() / 255.0)); 599 } 600 } 601 } 602 EMFPPlusFillPolygon(const::basegfx::B2DPolyPolygon & polygon,const bool isColor,const sal_uInt32 brushIndexOrColor)603 void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor) 604 { 605 if (!polygon.count()) 606 return; 607 608 if (isColor) // use Color 609 { 610 SAL_INFO("drawinglayer", "EMF+\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec); 611 612 // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background, 613 // ranging from 0 for completely transparent to 0xFF for completely opaque. 614 const Color color(0xff - (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); 615 EMFPPlusFillPolygonSolidColor(polygon, color); 616 617 mrPropertyHolders.Current().setFillColor(color.getBColor()); 618 mrPropertyHolders.Current().setFillColorActive(true); 619 mrPropertyHolders.Current().setLineColorActive(false); 620 } 621 else // use Brush 622 { 623 EMFPBrush* brush = static_cast<EMFPBrush*>( maEMFPObjects[brushIndexOrColor & 0xff].get() ); 624 SAL_INFO("drawinglayer", "EMF+\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")"); 625 626 // give up in case something wrong happened 627 if( !brush ) 628 return; 629 630 mrPropertyHolders.Current().setFillColorActive(false); 631 mrPropertyHolders.Current().setLineColorActive(false); 632 633 if (brush->type == BrushTypeSolidColor) 634 { 635 Color fillColor = brush->solidColor; 636 EMFPPlusFillPolygonSolidColor(polygon, fillColor); 637 } 638 else if (brush->type == BrushTypeHatchFill) 639 { 640 // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them 641 // for the others the hatch "background" color (secondColor in brush) is used. 642 643 bool isHatchBlend = true; 644 double blendFactor = 0.0; 645 646 switch (brush->hatchStyle) 647 { 648 case HatchStyle05Percent: blendFactor = 0.05; break; 649 case HatchStyle10Percent: blendFactor = 0.10; break; 650 case HatchStyle20Percent: blendFactor = 0.20; break; 651 case HatchStyle25Percent: blendFactor = 0.25; break; 652 case HatchStyle30Percent: blendFactor = 0.30; break; 653 case HatchStyle40Percent: blendFactor = 0.40; break; 654 case HatchStyle50Percent: blendFactor = 0.50; break; 655 case HatchStyle60Percent: blendFactor = 0.60; break; 656 case HatchStyle70Percent: blendFactor = 0.70; break; 657 case HatchStyle75Percent: blendFactor = 0.75; break; 658 case HatchStyle80Percent: blendFactor = 0.80; break; 659 case HatchStyle90Percent: blendFactor = 0.90; break; 660 default: 661 isHatchBlend = false; 662 break; 663 } 664 Color fillColor; 665 if (isHatchBlend) 666 { 667 fillColor = brush->solidColor; 668 fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor)); 669 } 670 else 671 { 672 fillColor = brush->secondColor; 673 } 674 // temporal solution: create a solid colored polygon 675 // TODO create a 'real' hatching primitive 676 mrTargetHolders.Current().append( 677 std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( 678 polygon, 679 fillColor.getBColor())); 680 } 681 else if (brush->type == BrushTypeTextureFill) 682 { 683 SAL_WARN("drawinglayer", "EMF+\tTODO: implement BrushTypeTextureFill brush"); 684 } 685 else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient) 686 687 { 688 if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1)) 689 { 690 SAL_WARN("drawinglayer", "EMF+\t TODO Verify proper displaying of BrushTypePathGradient with flags: " << std::hex << brush->additionalFlags << std::dec); 691 } 692 ::basegfx::B2DHomMatrix aTextureTransformation; 693 694 if (brush->hasTransformation) { 695 aTextureTransformation = brush->brush_transformation; 696 697 // adjust aTextureTransformation for our world space: 698 // -> revert the mapping -> apply the transformation -> map back 699 basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform); 700 aInvertedMapTrasform.invert(); 701 aTextureTransformation = maMapTransform * aTextureTransformation * aInvertedMapTrasform; 702 } 703 704 // select the stored colors 705 const basegfx::BColor aStartColor = brush->solidColor.getBColor(); 706 const basegfx::BColor aEndColor = brush->secondColor.getBColor(); 707 drawinglayer::primitive2d::SvgGradientEntryVector aVector; 708 709 if (brush->blendPositions) 710 { 711 SAL_INFO("drawinglayer", "EMF+\t\tuse blend"); 712 713 // store the blendpoints in the vector 714 for (int i = 0; i < brush->blendPoints; i++) 715 { 716 double aBlendPoint; 717 basegfx::BColor aColor; 718 if (brush->type == BrushTypeLinearGradient) 719 { 720 aBlendPoint = brush->blendPositions [i]; 721 } 722 else 723 { 724 // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius 725 aBlendPoint = 2. * ( 1. - brush->blendPositions [i] ); 726 } 727 aColor.setGreen( aStartColor.getGreen() * (1. - brush->blendFactors[i]) + aEndColor.getGreen() * brush->blendFactors[i] ); 728 aColor.setBlue ( aStartColor.getBlue() * (1. - brush->blendFactors[i]) + aEndColor.getBlue() * brush->blendFactors[i] ); 729 aColor.setRed ( aStartColor.getRed() * (1. - brush->blendFactors[i]) + aEndColor.getRed() * brush->blendFactors[i] ); 730 aVector.emplace_back(aBlendPoint, aColor, 1. ); 731 } 732 } 733 else if (brush->colorblendPositions) 734 { 735 SAL_INFO("drawinglayer", "EMF+\t\tuse color blend"); 736 737 // store the colorBlends in the vector 738 for (int i = 0; i < brush->colorblendPoints; i++) 739 { 740 double aBlendPoint; 741 basegfx::BColor aColor; 742 if (brush->type == BrushTypeLinearGradient) 743 { 744 aBlendPoint = brush->colorblendPositions [i]; 745 } 746 else 747 { 748 // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius 749 aBlendPoint = 2. * ( 1. - brush->colorblendPositions [i] ); 750 } 751 aColor = brush->colorblendColors[i].getBColor(); 752 aVector.emplace_back(aBlendPoint, aColor, 1. ); 753 } 754 } 755 else // ok, no extra points: just start and end 756 { 757 if (brush->type == BrushTypeLinearGradient) 758 { 759 aVector.emplace_back(0.0, aStartColor, (255 - brush->solidColor.GetTransparency()) / 255.0); 760 aVector.emplace_back(1.0, aEndColor, (255 - brush->secondColor.GetTransparency()) / 255.0); 761 } 762 else // again, here reverse 763 { 764 aVector.emplace_back(0.0, aEndColor, (255 - brush->secondColor.GetTransparency()) / 255.0); 765 aVector.emplace_back(1.0, aStartColor, (255 - brush->solidColor.GetTransparency()) / 255.0); 766 } 767 } 768 769 // get the polygon range to be able to map the start/end/center point correctly 770 // therefore, create a mapping and invert it 771 basegfx::B2DRange aPolygonRange= polygon.getB2DRange(); 772 basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix( 773 aPolygonRange.getWidth(),aPolygonRange.getHeight(), 774 aPolygonRange.getMinX(), aPolygonRange.getMinY()); 775 aPolygonTransformation.invert(); 776 777 if (brush->type == BrushTypeLinearGradient) 778 { 779 basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, brush->firstPointY); 780 aStartPoint = aPolygonTransformation * aStartPoint; 781 basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->secondPointX, brush->firstPointY + brush->secondPointY); 782 aEndPoint = aPolygonTransformation * aEndPoint; 783 784 // create the same one used for SVG 785 mrTargetHolders.Current().append( 786 std::make_unique<drawinglayer::primitive2d::SvgLinearGradientPrimitive2D>( 787 aTextureTransformation, 788 polygon, 789 aVector, 790 aStartPoint, 791 aEndPoint, 792 false, // do not use UnitCoordinates 793 drawinglayer::primitive2d::SpreadMethod::Pad)); 794 } 795 else // BrushTypePathGradient 796 { 797 basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY); 798 aCenterPoint = aPolygonTransformation * aCenterPoint; 799 800 // create the same one used for SVG 801 mrTargetHolders.Current().append( 802 std::make_unique<drawinglayer::primitive2d::SvgRadialGradientPrimitive2D>( 803 aTextureTransformation, 804 polygon, 805 aVector, 806 aCenterPoint, 807 0.5, // relative radius 808 true, // use UnitCoordinates to stretch the gradient 809 drawinglayer::primitive2d::SpreadMethod::Repeat, 810 nullptr)); 811 } 812 } 813 } 814 } 815 EmfPlusHelperData(SvMemoryStream & rMS,wmfemfhelper::TargetHolders & rTargetHolders,wmfemfhelper::PropertyHolders & rPropertyHolders)816 EmfPlusHelperData::EmfPlusHelperData( 817 SvMemoryStream& rMS, 818 wmfemfhelper::TargetHolders& rTargetHolders, 819 wmfemfhelper::PropertyHolders& rPropertyHolders) 820 : maBaseTransform(), 821 maWorldTransform(), 822 maMapTransform(), 823 maEMFPObjects(), 824 mfPageScale(0.0), 825 mnOriginX(0), 826 mnOriginY(0), 827 mnHDPI(0), 828 mnVDPI(0), 829 mnFrameLeft(0), 830 mnFrameTop(0), 831 mnFrameRight(0), 832 mnFrameBottom(0), 833 mnPixX(0), 834 mnPixY(0), 835 mnMmX(0), 836 mnMmY(0), 837 mbMultipart(false), 838 mMFlags(0), 839 mMStream(), 840 mrTargetHolders(rTargetHolders), 841 mrPropertyHolders(rPropertyHolders), 842 bIsGetDCProcessing(false) 843 { 844 rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom); 845 SAL_INFO("drawinglayer", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom); 846 rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY); 847 SAL_INFO("drawinglayer", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY); 848 readXForm(rMS, maBaseTransform); 849 SAL_INFO("drawinglayer", "EMF+ base transform: " << maBaseTransform); 850 mappingChanged(); 851 } 852 ~EmfPlusHelperData()853 EmfPlusHelperData::~EmfPlusHelperData() 854 { 855 } 856 combineClip(::basegfx::B2DPolyPolygon const & leftPolygon,int combineMode,::basegfx::B2DPolyPolygon const & rightPolygon)857 ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon) 858 { 859 basegfx::B2DPolyPolygon aClippedPolyPolygon; 860 switch (combineMode) 861 { 862 case EmfPlusCombineModeReplace: 863 { 864 aClippedPolyPolygon = rightPolygon; 865 break; 866 } 867 case EmfPlusCombineModeIntersect: 868 { 869 if (leftPolygon.count()) 870 { 871 aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( 872 leftPolygon, 873 rightPolygon, 874 true, 875 false); 876 } 877 break; 878 } 879 case EmfPlusCombineModeUnion: 880 { 881 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon); 882 break; 883 } 884 case EmfPlusCombineModeXOR: 885 { 886 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon); 887 break; 888 } 889 case EmfPlusCombineModeExclude: 890 { 891 // Replaces the existing region with the part of itself that is not in the new region. 892 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon); 893 break; 894 } 895 case EmfPlusCombineModeComplement: 896 { 897 // Replaces the existing region with the part of the new region that is not in the existing region. 898 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon); 899 break; 900 } 901 } 902 return aClippedPolyPolygon; 903 } 904 processEmfPlusData(SvMemoryStream & rMS,const drawinglayer::geometry::ViewInformation2D &)905 void EmfPlusHelperData::processEmfPlusData( 906 SvMemoryStream& rMS, 907 const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) 908 { 909 sal_uInt64 length = rMS.GetSize(); 910 911 if (length < 12) 912 SAL_WARN("drawinglayer", "length is less than required header size"); 913 914 // 12 is minimal valid EMF+ record size; remaining bytes are padding 915 while (length >= 12) 916 { 917 sal_uInt16 type, flags; 918 sal_uInt32 size, dataSize; 919 sal_uInt64 next; 920 921 rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize); 922 923 next = rMS.Tell() + (size - 12); 924 925 if (size < 12) 926 { 927 SAL_WARN("drawinglayer", "Size field is less than 12 bytes"); 928 break; 929 } 930 else if (size > length) 931 { 932 SAL_WARN("drawinglayer", "Size field is greater than bytes left"); 933 break; 934 } 935 936 if (dataSize > (size - 12)) 937 { 938 SAL_WARN("drawinglayer", "DataSize field is greater than Size-12"); 939 break; 940 } 941 942 SAL_INFO("drawinglayer", "EMF+ record size: " << size << " type: " << emfTypeToName(type) << " flags: " << flags << " data size: " << dataSize); 943 944 if (bIsGetDCProcessing) 945 { 946 SAL_INFO("drawinglayer", "EMF+ Resets the current clipping region for the world space to infinity."); 947 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders); 948 bIsGetDCProcessing = false; 949 } 950 if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000))) 951 { 952 if (!mbMultipart) 953 { 954 mbMultipart = true; 955 mMFlags = flags; 956 mMStream.Seek(0); 957 } 958 959 OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord"); 960 961 // 1st 4 bytes are TotalObjectSize 962 mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4); 963 SAL_INFO("drawinglayer", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize); 964 } 965 else 966 { 967 if (mbMultipart) 968 { 969 SAL_INFO("drawinglayer", "EMF+ multipart record flags: " << mMFlags); 970 mMStream.Seek(0); 971 processObjectRecord(mMStream, mMFlags, 0, true); 972 } 973 974 mbMultipart = false; 975 } 976 977 if (type != EmfPlusRecordTypeObject || !(flags & 0x8000)) 978 { 979 switch (type) 980 { 981 case EmfPlusRecordTypeHeader: 982 { 983 sal_uInt32 header, version; 984 985 rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI); 986 SAL_INFO("drawinglayer", "EMF+ Header"); 987 SAL_INFO("drawinglayer", "EMF+\theader: 0x" << std::hex << header << " version: " << std::dec << version << " horizontal DPI: " << mnHDPI << " vertical DPI: " << mnVDPI << " dual: " << (flags & 1)); 988 break; 989 } 990 case EmfPlusRecordTypeEndOfFile: 991 { 992 SAL_INFO("drawinglayer", "EMF+ EndOfFile"); 993 break; 994 } 995 case EmfPlusRecordTypeComment: 996 { 997 #if OSL_DEBUG_LEVEL > 1 998 unsigned char data; 999 OUString hexdata; 1000 1001 SAL_INFO("drawinglayer", "EMF+ Comment"); 1002 SAL_INFO("drawinglayer", "\tdatasize: 0x" << std::hex << dataSize << std::dec); 1003 1004 for (sal_uInt32 i=0; i<dataSize; i++) 1005 { 1006 rMS.ReadUChar(data); 1007 1008 if (i % 16 == 0) 1009 hexdata += "\n"; 1010 1011 OUString padding; 1012 if ((data & 0xF0) == 0) 1013 padding = "0"; 1014 1015 hexdata += "0x" + padding + OUString::number(data, 16) + " "; 1016 } 1017 1018 SAL_INFO("drawinglayer", "\t" << hexdata); 1019 #endif 1020 break; 1021 } 1022 case EmfPlusRecordTypeGetDC: 1023 { 1024 bIsGetDCProcessing = true; 1025 SAL_INFO("drawinglayer", "EMF+ GetDC"); 1026 SAL_INFO("drawinglayer", "EMF+\talready used in svtools wmf/emf filter parser"); 1027 break; 1028 } 1029 case EmfPlusRecordTypeObject: 1030 { 1031 processObjectRecord(rMS, flags, dataSize); 1032 break; 1033 } 1034 case EmfPlusRecordTypeFillPie: 1035 case EmfPlusRecordTypeDrawPie: 1036 case EmfPlusRecordTypeDrawArc: 1037 { 1038 float startAngle, sweepAngle; 1039 1040 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used 1041 sal_uInt32 brushIndexOrColor = 999; 1042 1043 if (type == EmfPlusRecordTypeFillPie) 1044 { 1045 rMS.ReadUInt32(brushIndexOrColor); 1046 SAL_INFO("drawinglayer", "EMF+ FillPie colorOrIndex: " << brushIndexOrColor); 1047 } 1048 else if (type == EmfPlusRecordTypeDrawPie) 1049 { 1050 SAL_INFO("drawinglayer", "EMF+ DrawPie"); 1051 } 1052 else 1053 { 1054 SAL_INFO("drawinglayer", "EMF+ DrawArc"); 1055 } 1056 1057 rMS.ReadFloat(startAngle).ReadFloat(sweepAngle); 1058 float dx, dy, dw, dh; 1059 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); 1060 SAL_INFO("drawinglayer", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); 1061 startAngle = basegfx::deg2rad(startAngle); 1062 sweepAngle = basegfx::deg2rad(sweepAngle); 1063 float endAngle = startAngle + sweepAngle; 1064 startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2)); 1065 1066 if (startAngle < 0.0) 1067 { 1068 startAngle += static_cast<float>(M_PI * 2.0); 1069 } 1070 endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0)); 1071 1072 if (endAngle < 0.0) 1073 { 1074 endAngle += static_cast<float>(M_PI * 2.0); 1075 } 1076 if (sweepAngle < 0) 1077 { 1078 std::swap(endAngle, startAngle); 1079 } 1080 1081 SAL_INFO("drawinglayer", "EMF+\t adjusted angles: start " << 1082 basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) << 1083 " startAngle: " << startAngle << " sweepAngle: " << sweepAngle); 1084 const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh); 1085 ::basegfx::B2DPolygon polygon( 1086 ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint, 1087 0.5 * dw, 0.5 * dh, 1088 startAngle, endAngle)); 1089 if (type != EmfPlusRecordTypeDrawArc) 1090 { 1091 polygon.append(centerPoint); 1092 polygon.setClosed(true); 1093 } 1094 ::basegfx::B2DPolyPolygon polyPolygon(polygon); 1095 polyPolygon.transform(maMapTransform); 1096 if (type == EmfPlusRecordTypeFillPie) 1097 EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor); 1098 else 1099 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); 1100 } 1101 break; 1102 case EmfPlusRecordTypeFillPath: 1103 { 1104 sal_uInt32 index = flags & 0xff; 1105 sal_uInt32 brushIndexOrColor; 1106 rMS.ReadUInt32(brushIndexOrColor); 1107 SAL_INFO("drawinglayer", "EMF+ FillPath slot: " << index); 1108 1109 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get()); 1110 if (path) 1111 EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor); 1112 else 1113 SAL_WARN("drawinglayer", "EmfPlusRecordTypeFillPath missing path"); 1114 } 1115 break; 1116 case EmfPlusRecordTypeFillRegion: 1117 { 1118 sal_uInt32 index = flags & 0xff; 1119 sal_uInt32 brushIndexOrColor; 1120 rMS.ReadUInt32(brushIndexOrColor); 1121 SAL_INFO("drawinglayer", "EMF+ FillRegion slot: " << index); 1122 1123 EMFPPlusFillPolygon(static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get())->regionPolyPolygon, flags & 0x8000, brushIndexOrColor); 1124 } 1125 break; 1126 case EmfPlusRecordTypeDrawEllipse: 1127 case EmfPlusRecordTypeFillEllipse: 1128 { 1129 // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local 1130 // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case 1131 // when it is later used. 1132 sal_uInt32 brushIndexOrColor = 1234567; 1133 1134 if (type == EmfPlusRecordTypeFillEllipse) 1135 { 1136 rMS.ReadUInt32(brushIndexOrColor); 1137 } 1138 1139 SAL_INFO("drawinglayer", "EMF+ " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff)); 1140 float dx, dy, dw, dh; 1141 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); 1142 SAL_INFO("drawinglayer", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh); 1143 ::basegfx::B2DPolyPolygon polyPolygon( 1144 ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh), 1145 0.5 * dw, 0.5 * dh)); 1146 polyPolygon.transform(maMapTransform); 1147 if (type == EmfPlusRecordTypeFillEllipse) 1148 EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor); 1149 else 1150 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); 1151 } 1152 break; 1153 case EmfPlusRecordTypeFillRects: 1154 case EmfPlusRecordTypeDrawRects: 1155 { 1156 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used 1157 sal_uInt32 brushIndexOrColor = 999; 1158 sal_Int32 rectangles; 1159 const bool isColor = (flags & 0x8000); 1160 ::basegfx::B2DPolygon polygon; 1161 1162 if (EmfPlusRecordTypeFillRects == type) 1163 { 1164 SAL_INFO("drawinglayer", "EMF+ FillRects"); 1165 rMS.ReadUInt32(brushIndexOrColor); 1166 SAL_INFO("drawinglayer", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); 1167 } 1168 else 1169 { 1170 SAL_INFO("drawinglayer", "EMF+ DrawRects"); 1171 } 1172 1173 rMS.ReadInt32(rectangles); 1174 1175 for (int i = 0; i < rectangles; i++) 1176 { 1177 float x, y, width, height; 1178 ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000)); 1179 polygon.clear(); 1180 polygon.append(Map(x, y)); 1181 polygon.append(Map(x + width, y)); 1182 polygon.append(Map(x + width, y + height)); 1183 polygon.append(Map(x, y + height)); 1184 polygon.setClosed(true); 1185 1186 SAL_INFO("drawinglayer", "EMF+\t rectangle: " << x << ", "<< y << " " << width << "x" << height); 1187 1188 ::basegfx::B2DPolyPolygon polyPolygon(polygon); 1189 if (type == EmfPlusRecordTypeFillRects) 1190 EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor); 1191 else 1192 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); 1193 } 1194 break; 1195 } 1196 case EmfPlusRecordTypeFillPolygon: 1197 { 1198 const sal_uInt8 index = flags & 0xff; 1199 sal_uInt32 brushIndexOrColor; 1200 sal_Int32 points; 1201 1202 rMS.ReadUInt32(brushIndexOrColor); 1203 rMS.ReadInt32(points); 1204 SAL_INFO("drawinglayer", "EMF+ FillPolygon in slot: " << index << " points: " << points); 1205 SAL_INFO("drawinglayer", "EMF+\t: " << ((flags & 0x8000) ? "color" : "brush index") << " 0x" << std::hex << brushIndexOrColor << std::dec); 1206 1207 EMFPPath path(points, true); 1208 path.Read(rMS, flags); 1209 1210 EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor); 1211 1212 break; 1213 } 1214 case EmfPlusRecordTypeDrawLines: 1215 { 1216 sal_uInt32 points; 1217 rMS.ReadUInt32(points); 1218 SAL_INFO("drawinglayer", "EMF+ DrawLines in slot: " << (flags & 0xff) << " points: " << points); 1219 EMFPPath path(points, true); 1220 path.Read(rMS, flags); 1221 1222 // 0x2000 bit indicates whether to draw an extra line between the last point 1223 // and the first point, to close the shape. 1224 EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags); 1225 1226 break; 1227 } 1228 case EmfPlusRecordTypeDrawPath: 1229 { 1230 sal_uInt32 penIndex; 1231 rMS.ReadUInt32(penIndex); 1232 SAL_INFO("drawinglayer", "EMF+ DrawPath"); 1233 SAL_INFO("drawinglayer", "EMF+\tpen: " << penIndex); 1234 1235 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); 1236 if (path) 1237 EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex); 1238 else 1239 SAL_WARN("drawinglayer", "EmfPlusRecordTypeDrawPath missing path"); 1240 1241 break; 1242 } 1243 case EmfPlusRecordTypeDrawBeziers: 1244 { 1245 sal_uInt32 aCount; 1246 float x1, y1, x2, y2, x3, y3, x4, y4; 1247 ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint; 1248 ::basegfx::B2DPolygon aPolygon; 1249 rMS.ReadUInt32(aCount); 1250 SAL_INFO("drawinglayer", "EMF+ DrawBeziers slot: " << (flags & 0xff) << "Number of points: " << aCount); 1251 SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16..."); 1252 1253 if (aCount < 4) 1254 { 1255 SAL_WARN("drawinglayer", "EMF+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount); 1256 break; 1257 } 1258 1259 ReadPoint(rMS, x1, y1, flags); 1260 // We need to add first starting point 1261 aStartPoint = Map(x1, y1); 1262 aPolygon.append(aStartPoint); 1263 1264 for (sal_uInt32 i = 4; i <= aCount; i += 3) 1265 { 1266 ReadPoint(rMS, x2, y2, flags); 1267 ReadPoint(rMS, x3, y3, flags); 1268 ReadPoint(rMS, x4, y4, flags); 1269 1270 SAL_INFO("drawinglayer", "EMF+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4); 1271 1272 aStartPoint = Map(x1, y1); 1273 aControlPointA = Map(x2, y2); 1274 aControlPointB = Map(x3, y3); 1275 aEndPoint = Map(x4, y4); 1276 1277 ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint); 1278 cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0); 1279 1280 EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff); 1281 1282 // The ending coordinate of one Bezier curve is the starting coordinate of the next. 1283 x1 = x4; 1284 y1 = y4; 1285 } 1286 break; 1287 } 1288 case EmfPlusRecordTypeDrawImage: 1289 case EmfPlusRecordTypeDrawImagePoints: 1290 { 1291 sal_uInt32 imageAttributesId; 1292 sal_Int32 sourceUnit; 1293 rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit); 1294 SAL_INFO("drawinglayer", "EMF+ " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " image attributes Id: " << imageAttributesId << " source unit: " << sourceUnit); 1295 SAL_INFO("drawinglayer", "EMF+\tTODO: use image attributes"); 1296 1297 // For DrawImage and DrawImagePoints, source unit of measurement type must be 1 pixel 1298 if (sourceUnit == UnitTypePixel && maEMFPObjects[flags & 0xff].get()) 1299 { 1300 EMFPImage& image = *static_cast<EMFPImage *>(maEMFPObjects[flags & 0xff].get()); 1301 float sx, sy, sw, sh; 1302 ReadRectangle(rMS, sx, sy, sw, sh); 1303 ::tools::Rectangle aSource(Point(sx, sy), Size(sw, sh)); 1304 SAL_INFO("drawinglayer", "EMF+ " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " source rectangle: " << sx << "," << sy << " " << sw << "x" << sh); 1305 ::basegfx::B2DPoint aDstPoint; 1306 ::basegfx::B2DSize aDstSize; 1307 1308 double fShearX = 0.0; 1309 double fShearY = 0.0; 1310 if (type == EmfPlusRecordTypeDrawImagePoints) 1311 { 1312 sal_uInt32 aCount; 1313 rMS.ReadUInt32(aCount); 1314 1315 // Number of points used by DrawImagePoints. Exactly 3 points must be specified. 1316 if(aCount == 3) 1317 { 1318 float x1, y1, x2, y2, x3, y3; 1319 1320 ReadPoint(rMS, x1, y1, flags); // upper-left point 1321 ReadPoint(rMS, x2, y2, flags); // upper-right 1322 ReadPoint(rMS, x3, y3, flags); // lower-left 1323 1324 SAL_INFO("drawinglayer", "EMF+\t destination points: P1:" << x1 << "," << y1 << " P2:" << x2 << "," << y2 << " P3:" << x3 << "," << y3); 1325 1326 aDstPoint = ::basegfx::B2DPoint(x1, y1); 1327 aDstSize = ::basegfx::B2DSize(x2 - x1, y3 - y1); 1328 fShearX = x3 - x1; 1329 fShearY = y2 - y1; 1330 } 1331 else 1332 { 1333 SAL_WARN("drawinglayer", "EMF+\t DrawImagePoints Wrong EMF+ file. Expected 3 points, received: "<< aCount); 1334 break; 1335 } 1336 } 1337 else if (type == EmfPlusRecordTypeDrawImage) 1338 { 1339 float dx, dy, dw, dh; 1340 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); 1341 SAL_INFO("drawinglayer", "EMF+\t destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh); 1342 aDstPoint = ::basegfx::B2DPoint(dx, dy); 1343 aDstSize = ::basegfx::B2DSize(dw, dh); 1344 } 1345 1346 const basegfx::B2DHomMatrix aTransformMatrix = maMapTransform * 1347 basegfx::B2DHomMatrix( 1348 /* Row 0, Column 0 */ aDstSize.getX(), 1349 /* Row 0, Column 1 */ fShearX, 1350 /* Row 0, Column 2 */ aDstPoint.getX(), 1351 /* Row 1, Column 0 */ fShearY, 1352 /* Row 1, Column 1 */ aDstSize.getY(), 1353 /* Row 1, Column 2 */ aDstPoint.getY()); 1354 1355 if (image.type == ImageDataTypeBitmap) 1356 { 1357 BitmapEx aBmp(image.graphic.GetBitmapEx()); 1358 aBmp.Crop(aSource); 1359 Size aSize(aBmp.GetSizePixel()); 1360 SAL_INFO("drawinglayer", "EMF+\t bitmap size: " << aSize.Width() << "x" << aSize.Height()); 1361 if (aSize.Width() > 0 && aSize.Height() > 0) 1362 { 1363 mrTargetHolders.Current().append( 1364 std::make_unique<drawinglayer::primitive2d::BitmapPrimitive2D>(aBmp, aTransformMatrix)); 1365 } 1366 else 1367 { 1368 SAL_WARN("drawinglayer", "EMF+\t warning: empty bitmap"); 1369 break; 1370 } 1371 } 1372 else if (image.type == ImageDataTypeMetafile) 1373 { 1374 GDIMetaFile aGDI(image.graphic.GetGDIMetaFile()); 1375 aGDI.Clip(aSource); 1376 mrTargetHolders.Current().append( 1377 std::make_unique<drawinglayer::primitive2d::MetafilePrimitive2D>(aTransformMatrix, aGDI)); 1378 } 1379 } 1380 else 1381 { 1382 SAL_WARN("drawinglayer", "EMF+ DrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is support by EMF+ specification for DrawImage(Points)"); 1383 } 1384 break; 1385 } 1386 case EmfPlusRecordTypeDrawString: 1387 { 1388 SAL_INFO("drawinglayer", "EMF+ DrawString"); 1389 sal_uInt32 brushId; 1390 sal_uInt32 formatId; 1391 sal_uInt32 stringLength; 1392 rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength); 1393 SAL_INFO("drawinglayer", "EMF+ DrawString brushId: " << brushId << " formatId: " << formatId << " length: " << stringLength); 1394 1395 if (flags & 0x8000) 1396 { 1397 // read the layout rectangle 1398 float lx, ly, lw, lh; 1399 rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh); 1400 1401 SAL_INFO("drawinglayer", "EMF+ DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh); 1402 // parse the string 1403 const OUString text = read_uInt16s_ToOUString(rMS, stringLength); 1404 SAL_INFO("drawinglayer", "EMF+ DrawString string: " << text); 1405 // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr ) 1406 const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get()); 1407 // get the font from the flags 1408 const EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() ); 1409 if (!font) 1410 { 1411 break; 1412 } 1413 mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize))); 1414 1415 const OUString emptyString; 1416 drawinglayer::attribute::FontAttribute fontAttribute( 1417 font->family, // font family 1418 emptyString, // (no) font style 1419 font->Bold() ? 8u : 1u, // weight: 8 = bold 1420 font->family == "SYMBOL", // symbol 1421 stringFormat && stringFormat->DirectionVertical(), // vertical 1422 font->Italic(), // italic 1423 false, // monospaced 1424 false, // outline = false, no such thing in MS-EMFPLUS 1425 stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left 1426 false); // BiDiStrong 1427 1428 css::lang::Locale locale; 1429 double stringAlignmentHorizontalOffset = 0.0; 1430 if (stringFormat) 1431 { 1432 SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer", "EMF+ DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right."); 1433 if (stringFormat->stringAlignment == StringAlignmentNear) 1434 // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh) 1435 { 1436 stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize; 1437 } else if (stringFormat->stringAlignment == StringAlignmentCenter) 1438 // Alignment is centered between the origin and extent of the layout rectangle 1439 { 1440 stringAlignmentHorizontalOffset = 0.5 * lw + stringFormat->leadingMargin * font->emSize - 0.3 * font->emSize * stringLength; 1441 } else if (stringFormat->stringAlignment == StringAlignmentFar) 1442 // Alignment is to the right side of the layout rectangle 1443 { 1444 stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - 0.6 * font->emSize * stringLength; 1445 } 1446 1447 LanguageTag aLanguageTag(static_cast< LanguageType >(stringFormat->language)); 1448 locale = aLanguageTag.getLocale(); 1449 } 1450 else 1451 { 1452 // By default LeadingMargin is 1/6 inch 1453 // TODO for typographic fonts set value to 0. 1454 stringAlignmentHorizontalOffset = 16.0; 1455 1456 // use system default 1457 locale = Application::GetSettings().GetLanguageTag().getLocale(); 1458 } 1459 1460 const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( 1461 ::basegfx::B2DSize(font->emSize, font->emSize), 1462 ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, ly + font->emSize)); 1463 1464 const Color color = EMFPGetBrushColorOrARGBColor(flags, brushId); 1465 mrPropertyHolders.Current().setTextColor(color.getBColor()); 1466 mrPropertyHolders.Current().setTextColorActive(true); 1467 1468 if (color.GetTransparency() < 255) 1469 { 1470 std::vector<double> emptyVector; 1471 drawinglayer::primitive2d::BasePrimitive2D* pBaseText = nullptr; 1472 if (font->Underline() || font->Strikeout()) 1473 { 1474 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( 1475 transformMatrix, 1476 text, 1477 0, // text always starts at 0 1478 stringLength, 1479 emptyVector, // EMF-PLUS has no DX-array 1480 fontAttribute, 1481 locale, 1482 color.getBColor(), 1483 COL_TRANSPARENT, 1484 color.getBColor(), 1485 color.getBColor(), 1486 drawinglayer::primitive2d::TEXT_LINE_NONE, 1487 font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, 1488 false, 1489 font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE); 1490 } 1491 else 1492 { 1493 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 1494 transformMatrix, 1495 text, 1496 0, // text always starts at 0 1497 stringLength, 1498 emptyVector, // EMF-PLUS has no DX-array 1499 fontAttribute, 1500 locale, 1501 color.getBColor()); 1502 } 1503 drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); 1504 if (color.GetTransparency() != 0) 1505 { 1506 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 1507 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, 1508 color.GetTransparency() / 255.0); 1509 } 1510 1511 mrTargetHolders.Current().append( 1512 std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( 1513 maMapTransform, 1514 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); 1515 } 1516 } 1517 else 1518 { 1519 SAL_WARN("drawinglayer", "EMF+ DrawString TODO - drawing with brush not yet supported"); 1520 } 1521 break; 1522 } 1523 case EmfPlusRecordTypeSetPageTransform: 1524 { 1525 rMS.ReadFloat(mfPageScale); 1526 SAL_INFO("drawinglayer", "EMF+ SetPageTransform"); 1527 SAL_INFO("drawinglayer", "EMF+\tscale: " << mfPageScale << " unit: " << flags); 1528 1529 if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) 1530 { 1531 SAL_WARN("drawinglayer", "EMF+ file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification."); 1532 } 1533 else 1534 { 1535 const float aPageScaleMul = mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags)); 1536 mnMmX *= aPageScaleMul; 1537 mnMmY *= aPageScaleMul; 1538 mappingChanged(); 1539 } 1540 break; 1541 } 1542 case EmfPlusRecordTypeSetRenderingOrigin: 1543 { 1544 rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY); 1545 SAL_INFO("drawinglayer", "EMF+ SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY); 1546 break; 1547 } 1548 case EmfPlusRecordTypeSetTextRenderingHint: 1549 { 1550 SAL_INFO("drawinglayer", "TODO\t EMF+ SetTextRenderingHint"); 1551 break; 1552 } 1553 case EmfPlusRecordTypeSetAntiAliasMode: 1554 { 1555 SAL_INFO("drawinglayer", "TODO\t EMF+ SetAntiAliasMode"); 1556 break; 1557 } 1558 case EmfPlusRecordTypeSetInterpolationMode: 1559 { 1560 SAL_INFO("drawinglayer", "TODO\t EMF+ InterpolationMode"); 1561 break; 1562 } 1563 case EmfPlusRecordTypeSetPixelOffsetMode: 1564 { 1565 SAL_INFO("drawinglayer", "TODO\t EMF+ SetPixelOffsetMode"); 1566 break; 1567 } 1568 case EmfPlusRecordTypeSetCompositingQuality: 1569 { 1570 SAL_INFO("drawinglayer", "TODO\t EMF+ SetCompositingQuality"); 1571 break; 1572 } 1573 case EmfPlusRecordTypeSave: 1574 { 1575 sal_uInt32 stackIndex; 1576 rMS.ReadUInt32(stackIndex); 1577 SAL_INFO("drawinglayer", "EMF+ Save stack index: " << stackIndex); 1578 1579 GraphicStatePush(mGSStack, stackIndex); 1580 1581 break; 1582 } 1583 case EmfPlusRecordTypeRestore: 1584 { 1585 sal_uInt32 stackIndex; 1586 rMS.ReadUInt32(stackIndex); 1587 SAL_INFO("drawinglayer", "EMF+ Restore stack index: " << stackIndex); 1588 1589 GraphicStatePop(mGSStack, stackIndex, mrPropertyHolders.Current()); 1590 break; 1591 } 1592 case EmfPlusRecordTypeBeginContainerNoParams: 1593 { 1594 sal_uInt32 stackIndex; 1595 rMS.ReadUInt32(stackIndex); 1596 SAL_INFO("drawinglayer", "EMF+ Begin Container No Params stack index: " << stackIndex); 1597 1598 GraphicStatePush(mGSContainerStack, stackIndex); 1599 break; 1600 } 1601 case EmfPlusRecordTypeEndContainer: 1602 { 1603 sal_uInt32 stackIndex; 1604 rMS.ReadUInt32(stackIndex); 1605 SAL_INFO("drawinglayer", "EMF+ End Container stack index: " << stackIndex); 1606 1607 GraphicStatePop(mGSContainerStack, stackIndex, mrPropertyHolders.Current()); 1608 break; 1609 } 1610 case EmfPlusRecordTypeSetWorldTransform: 1611 { 1612 SAL_INFO("drawinglayer", "EMF+ SetWorldTransform, Post multiply: " << bool(flags & 0x2000)); 1613 readXForm(rMS, maWorldTransform); 1614 mappingChanged(); 1615 SAL_INFO("drawinglayer", "EMF+\t: " << maWorldTransform); 1616 break; 1617 } 1618 case EmfPlusRecordTypeResetWorldTransform: 1619 { 1620 SAL_INFO("drawinglayer", 1621 "EMF+ ResetWorldTransform"); 1622 maWorldTransform.identity(); 1623 SAL_INFO("drawinglayer", 1624 "EMF+\t: " << maWorldTransform); 1625 mappingChanged(); 1626 break; 1627 } 1628 case EmfPlusRecordTypeMultiplyWorldTransform: 1629 { 1630 SAL_INFO("drawinglayer", "EMF+ MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000)); 1631 basegfx::B2DHomMatrix transform; 1632 readXForm(rMS, transform); 1633 1634 SAL_INFO("drawinglayer", 1635 "EMF+\t Transform matrix: " << transform); 1636 1637 if (flags & 0x2000) 1638 { 1639 // post multiply 1640 maWorldTransform *= transform; 1641 } 1642 else 1643 { 1644 // pre multiply 1645 transform *= maWorldTransform; 1646 maWorldTransform = transform; 1647 } 1648 1649 mappingChanged(); 1650 1651 SAL_INFO("drawinglayer", 1652 "EMF+\t World transform matrix: " << maWorldTransform); 1653 break; 1654 } 1655 case EmfPlusRecordTypeTranslateWorldTransform: 1656 { 1657 SAL_INFO("drawinglayer", "EMF+ TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000)); 1658 1659 basegfx::B2DHomMatrix transform; 1660 float eDx, eDy; 1661 rMS.ReadFloat(eDx).ReadFloat(eDy); 1662 transform.set(0, 2, eDx); 1663 transform.set(1, 2, eDy); 1664 1665 SAL_INFO("drawinglayer", 1666 "EMF+\t Translate matrix: " << transform); 1667 1668 if (flags & 0x2000) 1669 { 1670 // post multiply 1671 maWorldTransform *= transform; 1672 } 1673 else 1674 { 1675 // pre multiply 1676 transform *= maWorldTransform; 1677 maWorldTransform = transform; 1678 } 1679 1680 mappingChanged(); 1681 1682 SAL_INFO("drawinglayer", 1683 "EMF+\t World transform matrix: " << maWorldTransform); 1684 break; 1685 } 1686 case EmfPlusRecordTypeScaleWorldTransform: 1687 { 1688 basegfx::B2DHomMatrix transform; 1689 float eSx, eSy; 1690 rMS.ReadFloat(eSx).ReadFloat(eSy); 1691 transform.set(0, 0, eSx); 1692 transform.set(1, 1, eSy); 1693 1694 SAL_INFO("drawinglayer", "EMF+ ScaleWorldTransform Sx: " << eSx << 1695 " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000)); 1696 SAL_INFO("drawinglayer", 1697 "EMF+\t World transform matrix: " << maWorldTransform); 1698 1699 if (flags & 0x2000) 1700 { 1701 // post multiply 1702 maWorldTransform *= transform; 1703 } 1704 else 1705 { 1706 // pre multiply 1707 transform *= maWorldTransform; 1708 maWorldTransform = transform; 1709 } 1710 1711 mappingChanged(); 1712 1713 SAL_INFO("drawinglayer", 1714 "EMF+\t World transform matrix: " << maWorldTransform); 1715 break; 1716 } 1717 case EmfPlusRecordTypeRotateWorldTransform: 1718 { 1719 // Angle of rotation in degrees 1720 float eAngle; 1721 rMS.ReadFloat(eAngle); 1722 1723 SAL_INFO("drawinglayer", "EMF+ RotateWorldTransform Angle: " << eAngle << 1724 ", post multiply: " << bool(flags & 0x2000)); 1725 // Skipping flags & 0x2000 1726 // For rotation transformation there is no difference between post and pre multiply 1727 maWorldTransform.rotate(basegfx::deg2rad(eAngle)); 1728 mappingChanged(); 1729 1730 SAL_INFO("drawinglayer", 1731 "EMF+\t " << maWorldTransform); 1732 break; 1733 } 1734 case EmfPlusRecordTypeResetClip: 1735 { 1736 SAL_INFO("drawinglayer", "EMF+ ResetClip"); 1737 // We don't need to read anything more, as Size needs to be set 0x0000000C 1738 // and DataSize must be set to 0. 1739 1740 // Resets the current clipping region for the world space to infinity. 1741 HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders); 1742 break; 1743 } 1744 case EmfPlusRecordTypeSetClipRect: 1745 { 1746 int combineMode = (flags >> 8) & 0xf; 1747 1748 SAL_INFO("drawinglayer", "EMF+ SetClipRect combine mode: " << combineMode); 1749 1750 float dx, dy, dw, dh; 1751 ReadRectangle(rMS, dx, dy, dw, dh); 1752 SAL_INFO("drawinglayer", "EMF+ RectData: " << dx << "," << dy << " " << dw << "x" << dh); 1753 ::basegfx::B2DPoint mappedPoint1(Map(dx, dy)); 1754 ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh)); 1755 1756 ::basegfx::B2DPolyPolygon polyPolygon( 1757 ::basegfx::utils::createPolygonFromRect( 1758 ::basegfx::B2DRectangle( 1759 mappedPoint1.getX(), 1760 mappedPoint1.getY(), 1761 mappedPoint2.getX(), 1762 mappedPoint2.getY()))); 1763 1764 HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), combineMode, polyPolygon), mrTargetHolders, mrPropertyHolders); 1765 break; 1766 } 1767 case EmfPlusRecordTypeSetClipPath: 1768 { 1769 int combineMode = (flags >> 8) & 0xf; 1770 SAL_INFO("drawinglayer", "EMF+ SetClipPath combine mode: " << combineMode); 1771 SAL_INFO("drawinglayer", "EMF+\tpath in slot: " << (flags & 0xff)); 1772 1773 EMFPPath *path = static_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); 1774 if (!path) 1775 { 1776 SAL_WARN("drawinglayer", "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff)); 1777 break; 1778 } 1779 1780 ::basegfx::B2DPolyPolygon& clipPoly(path->GetPolygon(*this)); 1781 1782 HandleNewClipRegion( combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), combineMode, clipPoly), mrTargetHolders, mrPropertyHolders); 1783 break; 1784 } 1785 case EmfPlusRecordTypeSetClipRegion: 1786 { 1787 int combineMode = (flags >> 8) & 0xf; 1788 SAL_INFO("drawinglayer", "EMF+ SetClipRegion"); 1789 SAL_INFO("drawinglayer", "EMF+\tregion in slot: " << (flags & 0xff) << " combine mode: " << combineMode); 1790 EMFPRegion *region = static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); 1791 if (!region) 1792 { 1793 SAL_WARN("drawinglayer", "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff)); 1794 break; 1795 } 1796 1797 HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), combineMode, region->regionPolyPolygon), mrTargetHolders, mrPropertyHolders); 1798 break; 1799 } 1800 case EmfPlusRecordTypeOffsetClip: 1801 { 1802 float dx, dy; 1803 rMS.ReadFloat(dx).ReadFloat(dy); 1804 SAL_INFO("drawinglayer", "EMF+ OffsetClip, Offset x:" << dx << ", y:" << dy); 1805 1806 basegfx::B2DPolyPolygon aPolyPolygon( 1807 mrPropertyHolders.Current().getClipPolyPolygon()); 1808 1809 SAL_INFO("drawinglayer", 1810 "EMF+\t PolyPolygon before translate: " << aPolyPolygon); 1811 1812 basegfx::B2DPoint aOffset = Map(dx, dy); 1813 basegfx::B2DHomMatrix transformMatrix; 1814 transformMatrix.set(0, 2, aOffset.getX()); 1815 transformMatrix.set(1, 2, aOffset.getY()); 1816 aPolyPolygon.transform(transformMatrix); 1817 1818 SAL_INFO("drawinglayer", 1819 "EMF+\t PolyPolygon after translate: " << aPolyPolygon << 1820 ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY()); 1821 HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders); 1822 break; 1823 } 1824 case EmfPlusRecordTypeDrawDriverString: 1825 { 1826 SAL_INFO("drawinglayer", "EMF+ DrawDriverString, flags: 0x" << std::hex << flags << std::dec); 1827 sal_uInt32 brushIndexOrColor; 1828 sal_uInt32 optionFlags; 1829 sal_uInt32 hasMatrix; 1830 sal_uInt32 glyphsCount; 1831 rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount); 1832 SAL_INFO("drawinglayer", "EMF+\t: " << ((flags & 0x8000) ? "color" : "brush index") << " 0x" << std::hex << brushIndexOrColor << std::dec); 1833 SAL_INFO("drawinglayer", "EMF+\toption flags: 0x" << std::hex << optionFlags << std::dec); 1834 SAL_INFO("drawinglayer", "EMF+\thas matrix: " << hasMatrix); 1835 SAL_INFO("drawinglayer", "EMF+\tglyphs: " << glyphsCount); 1836 1837 if ((optionFlags & 1) && glyphsCount > 0) 1838 { 1839 std::unique_ptr<float[]> charsPosX(new float[glyphsCount]); 1840 std::unique_ptr<float[]> charsPosY(new float[glyphsCount]); 1841 OUString text = read_uInt16s_ToOUString(rMS, glyphsCount); 1842 SAL_INFO("drawinglayer", "EMF+ DrawDriverString string: " << text); 1843 1844 for (sal_uInt32 i = 0; i<glyphsCount; i++) 1845 { 1846 rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]); 1847 SAL_INFO("drawinglayer", "EMF+\tglyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]); 1848 } 1849 1850 basegfx::B2DHomMatrix transform; 1851 1852 if (hasMatrix) 1853 { 1854 readXForm(rMS, transform); 1855 SAL_INFO("drawinglayer", "EMF+\tmatrix: " << transform); 1856 } 1857 1858 // get the font from the flags 1859 EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() ); 1860 if (!font) 1861 { 1862 break; 1863 } 1864 // done reading 1865 1866 const OUString emptyString; 1867 drawinglayer::attribute::FontAttribute fontAttribute( 1868 font->family, // font family 1869 emptyString, // (no) font style 1870 font->Bold() ? 8u : 1u, // weight: 8 = bold 1871 font->family == "SYMBOL", // symbol 1872 optionFlags & 0x2, // vertical 1873 font->Italic(), // italic 1874 false, // monospaced 1875 false, // outline = false, no such thing in MS-EMFPLUS 1876 false, // right-to-left 1877 false); // BiDiStrong 1878 1879 const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor); 1880 std::vector<double> aDXArray; // dummy for DX array (not used) 1881 1882 // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D 1883 // for all portions of text with the same charsPosY values 1884 sal_uInt32 pos = 0; 1885 while (pos < glyphsCount) 1886 { 1887 //determine the current length 1888 sal_uInt32 aLength = 1; 1889 while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon()) 1890 aLength++; 1891 1892 // generate the DX-Array 1893 aDXArray.clear(); 1894 for (size_t i = 0; i < aLength - 1; i++) 1895 { 1896 aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]); 1897 } 1898 // last entry 1899 aDXArray.push_back(0); 1900 1901 basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( 1902 ::basegfx::B2DSize(font->emSize, font->emSize), 1903 ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos])); 1904 if (hasMatrix) 1905 transformMatrix *= transform; 1906 if (color.GetTransparency() < 255) 1907 { 1908 drawinglayer::primitive2d::BasePrimitive2D* pBaseText = nullptr; 1909 if (font->Underline() || font->Strikeout()) 1910 { 1911 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( 1912 transformMatrix, 1913 text, 1914 pos, // take character at current pos 1915 aLength, // use determined length 1916 aDXArray, // generated DXArray 1917 fontAttribute, 1918 Application::GetSettings().GetLanguageTag().getLocale(), 1919 color.getBColor(), 1920 COL_TRANSPARENT, 1921 color.getBColor(), 1922 color.getBColor(), 1923 drawinglayer::primitive2d::TEXT_LINE_NONE, 1924 font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, 1925 false, 1926 font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE); 1927 } 1928 else 1929 { 1930 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 1931 transformMatrix, 1932 text, 1933 pos, // take character at current pos 1934 aLength, // use determined length 1935 aDXArray, // generated DXArray 1936 fontAttribute, 1937 Application::GetSettings().GetLanguageTag().getLocale(), 1938 color.getBColor()); 1939 } 1940 drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); 1941 if (color.GetTransparency() != 0) 1942 { 1943 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 1944 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, 1945 color.GetTransparency() / 255.0); 1946 } 1947 mrTargetHolders.Current().append( 1948 std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( 1949 maMapTransform, 1950 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); 1951 } 1952 1953 // update pos 1954 pos += aLength; 1955 } 1956 } 1957 else 1958 { 1959 SAL_WARN("drawinglayer", "EMF+\tTODO: fonts (non-unicode glyphs chars)"); 1960 } 1961 break; 1962 } 1963 default: 1964 { 1965 SAL_WARN("drawinglayer", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec); 1966 } 1967 } 1968 } 1969 1970 rMS.Seek(next); 1971 1972 if (size <= length) 1973 { 1974 length -= size; 1975 } 1976 else 1977 { 1978 SAL_WARN("drawinglayer", "ImplRenderer::processEMFPlus: " 1979 "size " << size << " > length " << length); 1980 length = 0; 1981 } 1982 } 1983 } 1984 } 1985 1986 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1987