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