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 <mtftools.hxx> 21 22 #include <cstdlib> 23 #include <memory> 24 #include <basegfx/matrix/b2dhommatrix.hxx> 25 #include <basegfx/polygon/b2dpolypolygontools.hxx> 26 #include <vcl/metric.hxx> 27 #include <vcl/graphictools.hxx> 28 #include <vcl/BitmapTools.hxx> 29 #include <vcl/metaact.hxx> 30 #include <vcl/canvastools.hxx> 31 #include <vcl/svapp.hxx> 32 #include <tools/stream.hxx> 33 #include <rtl/tencinfo.h> 34 #include <sal/log.hxx> 35 #include <osl/diagnose.h> 36 #include <vcl/virdev.hxx> 37 #include <o3tl/safeint.hxx> 38 #include <unotools/configmgr.hxx> 39 #include <unotools/defaultencoding.hxx> 40 #include <unotools/wincodepage.hxx> 41 42 #if OSL_DEBUG_LEVEL > 1 43 #define EMFP_DEBUG(x) x 44 #else 45 #define EMFP_DEBUG(x) 46 #endif 47 48 namespace emfio 49 { operator >>(SvStream & rInStream,XForm & rXForm)50 SvStream& operator >> (SvStream& rInStream, XForm& rXForm) 51 { 52 if (sizeof(float) != 4) 53 { 54 OSL_FAIL("EmfReader::sizeof( float ) != 4"); 55 rXForm = XForm(); 56 } 57 else 58 { 59 rInStream.ReadFloat(rXForm.eM11); 60 rInStream.ReadFloat(rXForm.eM12); 61 rInStream.ReadFloat(rXForm.eM21); 62 rInStream.ReadFloat(rXForm.eM22); 63 rInStream.ReadFloat(rXForm.eDx); 64 rInStream.ReadFloat(rXForm.eDy); 65 } 66 return rInStream; 67 } 68 intersectClip(const basegfx::B2DPolyPolygon & rPolyPolygon)69 void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon& rPolyPolygon ) 70 { 71 maClip.intersectPolyPolygon(rPolyPolygon); 72 } 73 excludeClip(const basegfx::B2DPolyPolygon & rPolyPolygon)74 void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon& rPolyPolygon ) 75 { 76 maClip.subtractPolyPolygon(rPolyPolygon); 77 } 78 setClipPath(const basegfx::B2DPolyPolygon & rB2DPoly,sal_Int32 nClippingMode)79 void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon& rB2DPoly, sal_Int32 nClippingMode ) 80 { 81 switch ( nClippingMode ) 82 { 83 case RGN_OR : 84 maClip.unionPolyPolygon(rB2DPoly); 85 break; 86 case RGN_XOR : 87 maClip.xorPolyPolygon(rB2DPoly); 88 break; 89 case RGN_DIFF : 90 maClip.subtractPolyPolygon(rB2DPoly); 91 break; 92 case RGN_AND : 93 maClip.intersectPolyPolygon(rB2DPoly); 94 break; 95 case RGN_COPY : 96 maClip = basegfx::utils::B2DClipState(rB2DPoly); 97 break; 98 } 99 } 100 moveClipRegion(const Size & rSize)101 void WinMtfClipPath::moveClipRegion( const Size& rSize ) 102 { 103 basegfx::B2DHomMatrix aTranslate; 104 aTranslate.translate(rSize.Width(), rSize.Height()); 105 maClip.transform(aTranslate); 106 } 107 setDefaultClipPath()108 void WinMtfClipPath::setDefaultClipPath() 109 { 110 // Empty clip region - everything visible 111 maClip = basegfx::utils::B2DClipState(); 112 } 113 getClipPath() const114 basegfx::B2DPolyPolygon const & WinMtfClipPath::getClipPath() const 115 { 116 return maClip.getClipPoly(); 117 } 118 AddPoint(const Point & rPoint)119 void WinMtfPathObj::AddPoint( const Point& rPoint ) 120 { 121 if ( bClosed ) 122 Insert( tools::Polygon() ); 123 tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ]; 124 rPoly.Insert( rPoly.GetSize(), rPoint ); 125 bClosed = false; 126 } 127 AddPolyLine(const tools::Polygon & rPolyLine)128 void WinMtfPathObj::AddPolyLine( const tools::Polygon& rPolyLine ) 129 { 130 if ( bClosed ) 131 Insert( tools::Polygon() ); 132 tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ]; 133 rPoly.Insert( rPoly.GetSize(), rPolyLine ); 134 bClosed = false; 135 } 136 AddPolygon(const tools::Polygon & rPoly)137 void WinMtfPathObj::AddPolygon( const tools::Polygon& rPoly ) 138 { 139 Insert( rPoly ); 140 bClosed = true; 141 } 142 AddPolyPolygon(const tools::PolyPolygon & rPolyPoly)143 void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon& rPolyPoly ) 144 { 145 sal_uInt16 i, nCount = rPolyPoly.Count(); 146 for ( i = 0; i < nCount; i++ ) 147 Insert( rPolyPoly[ i ] ); 148 bClosed = true; 149 } 150 ClosePath()151 void WinMtfPathObj::ClosePath() 152 { 153 if ( Count() ) 154 { 155 tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ]; 156 if ( rPoly.GetSize() > 2 ) 157 { 158 Point aFirst( rPoly[ 0 ] ); 159 if ( aFirst != rPoly[ rPoly.GetSize() - 1 ] ) 160 rPoly.Insert( rPoly.GetSize(), aFirst ); 161 } 162 } 163 bClosed = true; 164 } 165 WinMtfFontStyle(LOGFONTW const & rFont)166 WinMtfFontStyle::WinMtfFontStyle( LOGFONTW const & rFont ) 167 { 168 rtl_TextEncoding eCharSet; 169 if ((rFont.alfFaceName == "Symbol") 170 || (rFont.alfFaceName == "MT Extra")) 171 eCharSet = RTL_TEXTENCODING_SYMBOL; 172 else if ((rFont.lfCharSet == DEFAULT_CHARSET) || (rFont.lfCharSet == OEM_CHARSET)) 173 eCharSet = utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(), 174 rFont.lfCharSet == OEM_CHARSET); 175 else 176 eCharSet = rtl_getTextEncodingFromWindowsCharset( rFont.lfCharSet ); 177 if ( eCharSet == RTL_TEXTENCODING_DONTKNOW ) 178 eCharSet = RTL_TEXTENCODING_MS_1252; 179 aFont.SetCharSet( eCharSet ); 180 aFont.SetFamilyName( rFont.alfFaceName ); 181 FontFamily eFamily; 182 switch ( rFont.lfPitchAndFamily & 0xf0 ) 183 { 184 case FF_ROMAN: 185 eFamily = FAMILY_ROMAN; 186 break; 187 188 case FF_SWISS: 189 eFamily = FAMILY_SWISS; 190 break; 191 192 case FF_MODERN: 193 eFamily = FAMILY_MODERN; 194 break; 195 196 case FF_SCRIPT: 197 eFamily = FAMILY_SCRIPT; 198 break; 199 200 case FF_DECORATIVE: 201 eFamily = FAMILY_DECORATIVE; 202 break; 203 204 default: 205 eFamily = FAMILY_DONTKNOW; 206 break; 207 } 208 aFont.SetFamily( eFamily ); 209 210 FontPitch ePitch; 211 switch ( rFont.lfPitchAndFamily & 0x0f ) 212 { 213 case FIXED_PITCH: 214 ePitch = PITCH_FIXED; 215 break; 216 217 case DEFAULT_PITCH: 218 case VARIABLE_PITCH: 219 default: 220 ePitch = PITCH_VARIABLE; 221 break; 222 } 223 aFont.SetPitch( ePitch ); 224 225 FontWeight eWeight; 226 if (rFont.lfWeight == 0) // default weight SHOULD be used 227 eWeight = WEIGHT_DONTKNOW; 228 else if (rFont.lfWeight <= FW_THIN) 229 eWeight = WEIGHT_THIN; 230 else if( rFont.lfWeight <= FW_ULTRALIGHT ) 231 eWeight = WEIGHT_ULTRALIGHT; 232 else if( rFont.lfWeight <= FW_LIGHT ) 233 eWeight = WEIGHT_LIGHT; 234 else if( rFont.lfWeight < FW_MEDIUM ) 235 eWeight = WEIGHT_NORMAL; 236 else if( rFont.lfWeight == FW_MEDIUM ) 237 eWeight = WEIGHT_MEDIUM; 238 else if( rFont.lfWeight <= FW_SEMIBOLD ) 239 eWeight = WEIGHT_SEMIBOLD; 240 else if( rFont.lfWeight <= FW_BOLD ) 241 eWeight = WEIGHT_BOLD; 242 else if( rFont.lfWeight <= FW_ULTRABOLD ) 243 eWeight = WEIGHT_ULTRABOLD; 244 else 245 eWeight = WEIGHT_BLACK; 246 aFont.SetWeight( eWeight ); 247 248 if( rFont.lfItalic ) 249 aFont.SetItalic( ITALIC_NORMAL ); 250 251 if( rFont.lfUnderline ) 252 aFont.SetUnderline( LINESTYLE_SINGLE ); 253 254 if( rFont.lfStrikeOut ) 255 aFont.SetStrikeout( STRIKEOUT_SINGLE ); 256 257 aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rFont.lfEscapement)) ); 258 259 Size aFontSize( Size( rFont.lfWidth, rFont.lfHeight ) ); 260 if ( rFont.lfHeight > 0 ) 261 { 262 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading 263 SolarMutexGuard aGuard; 264 ScopedVclPtrInstance< VirtualDevice > pVDev; 265 // converting the cell height into a font height 266 aFont.SetFontSize( aFontSize ); 267 pVDev->SetFont( aFont ); 268 FontMetric aMetric( pVDev->GetFontMetric() ); 269 tools::Long nHeight = aMetric.GetAscent() + aMetric.GetDescent(); 270 if (nHeight) 271 { 272 double fHeight = (static_cast<double>(aFontSize.Height()) * rFont.lfHeight ) / nHeight; 273 aFontSize.setHeight( static_cast<sal_Int32>( fHeight + 0.5 ) ); 274 } 275 } 276 277 // Convert height to positive 278 aFontSize.setHeight( std::abs(aFontSize.Height()) ); 279 aFont.SetFontSize(aFontSize); 280 281 // tdf#127471 adapt nFontWidth from Windows-like notation to 282 // NormedFontScaling if used for text scaling 283 #ifndef _WIN32 284 const bool bFontScaledHorizontally(aFontSize.Width() != 0 && aFontSize.Width() != aFontSize.Height()); 285 286 if(bFontScaledHorizontally) 287 { 288 // tdf#127471 nFontWidth is the Windows FontScaling, need to convert to 289 // Non-Windowslike notation relative to FontHeight. 290 const tools::Long nAverageFontWidth(aFont.GetOrCalculateAverageFontWidth()); 291 292 if(nAverageFontWidth > 0) 293 { 294 const double fScaleFactor(static_cast<double>(aFontSize.Height()) / static_cast<double>(nAverageFontWidth)); 295 aFont.SetAverageFontWidth(static_cast<tools::Long>(static_cast<double>(aFontSize.Width()) * fScaleFactor)); 296 } 297 } 298 #endif 299 }; 300 301 // tdf#127471 ScaledFontDetectCorrectHelper()302 ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper() 303 : maCurrentMetaFontAction(), 304 maAlternativeFontScales(), 305 maPositiveIdentifiedCases(), 306 maNegativeIdentifiedCases() 307 { 308 } 309 endCurrentMetaFontAction()310 void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction() 311 { 312 if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty()) 313 { 314 // create average corrected FontScale value and count 315 // positive/negative hits 316 sal_uInt32 nPositive(0); 317 sal_uInt32 nNegative(0); 318 double fAverage(0.0); 319 320 for(double fPart : maAlternativeFontScales) 321 { 322 if(fPart < 0.0) 323 { 324 nNegative++; 325 fAverage += -fPart; 326 } 327 else 328 { 329 nPositive++; 330 fAverage += fPart; 331 } 332 } 333 334 fAverage /= static_cast<double>(maAlternativeFontScales.size()); 335 336 if(nPositive >= nNegative) 337 { 338 // correction intended, it is probably an old imported file 339 maPositiveIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage)); 340 } 341 else 342 { 343 // correction not favorable in the majority of cases for this Font, still 344 // remember to have a weight in the last decision for correction 345 maNegativeIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage)); 346 } 347 } 348 349 maCurrentMetaFontAction.clear(); 350 maAlternativeFontScales.clear(); 351 } 352 newCurrentMetaFontAction(rtl::Reference<MetaFontAction> & rNewMetaFontAction)353 void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(rtl::Reference<MetaFontAction>& rNewMetaFontAction) 354 { 355 maCurrentMetaFontAction.clear(); 356 maAlternativeFontScales.clear(); 357 358 if(!rNewMetaFontAction.is()) 359 return; 360 361 // check 1st criteria for FontScale active. We usually write this, 362 // so this will already sort out most situations 363 const vcl::Font& rCandidate(rNewMetaFontAction->GetFont()); 364 365 if(0 != rCandidate.GetAverageFontWidth()) 366 { 367 const tools::Long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth()); 368 369 // check 2nd (system-dependent) criteria for FontScale 370 if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight()) 371 { 372 // FontScale is active, remember and use as current 373 maCurrentMetaFontAction = rNewMetaFontAction; 374 } 375 } 376 } 377 evaluateAlternativeFontScale(OUString const & rText,tools::Long nImportedTextLength)378 void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, tools::Long nImportedTextLength) 379 { 380 if(!maCurrentMetaFontAction.is()) 381 return; 382 383 SolarMutexGuard aGuard; // VirtualDevice is not thread-safe 384 ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice; 385 386 // calculate measured TextLength 387 const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont()); 388 pTempVirtualDevice->SetFont(rFontCandidate); 389 tools::Long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText)); 390 // on failure, use original length 391 if (!nMeasuredTextLength) 392 nMeasuredTextLength = nImportedTextLength; 393 394 // compare expected and imported TextLengths 395 if (nImportedTextLength != nMeasuredTextLength) 396 { 397 const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength)); 398 const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0); 399 400 // if we assume that loaded file was written on old linux, we have to 401 // back-convert the scale value depending on which system we run 402 #ifdef _WIN32 403 // When running on Windows the value was not adapted at font import (see WinMtfFontStyle 404 // constructor), so it is still NormedFontScaling and we need to convert to Windows-style 405 // scaling 406 #else 407 // When running on unx (non-Windows) the value was already adapted at font import (see WinMtfFontStyle 408 // constructor). It was wrongly assumed to be Windows-style FontScaling, so we need to revert that 409 // to get back to the needed unx-style FontScale 410 #endif 411 // Interestingly this leads to the *same* correction, so no need to make this 412 // system-dependent (!) 413 const tools::Long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth()); 414 const tools::Long nScaledAverageFontWidth(rFontCandidate.GetAverageFontWidth()); 415 const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight())); 416 const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor); 417 tools::Long nCorrectedTextLength(0); 418 419 { // do in own scope, only need nUnscaledAverageFontWidth 420 vcl::Font rFontCandidate2(rFontCandidate); 421 rFontCandidate2.SetAverageFontWidth(static_cast<tools::Long>(fCorrectedAverageFontWidth)); 422 pTempVirtualDevice->SetFont(rFontCandidate2); 423 nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText); 424 // on failure, use original length 425 if (!nCorrectedTextLength) 426 nCorrectedTextLength = nImportedTextLength; 427 } 428 429 const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength)); 430 const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0); 431 432 // If FactorCorrectedText fits better than FactorText this is probably 433 // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system 434 // and should be corrected. 435 // Usually in tested cases this lies inside 5% of range, so detecting this just using 436 // fFactorTextPercent inside 5% -> no old file 437 // fFactorCorrectedTextPercent inside 5% -> is old file 438 // works not too bad, but there are some strange not so often used fonts where that 439 // values do deviate, so better just compare if old corrected would fit better than 440 // the uncorrected case, that is usually safe. 441 if(fFactorCorrectedTextPercent < fFactorTextPercent) 442 { 443 maAlternativeFontScales.push_back(fCorrectedAverageFontWidth); 444 } 445 else 446 { 447 // also push, but negative to remember non-fitting case 448 maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth); 449 } 450 } 451 } 452 applyAlternativeFontScale()453 void ScaledFontDetectCorrectHelper::applyAlternativeFontScale() 454 { 455 // make sure last evtl. detected current FontAction gets added to identified cases 456 endCurrentMetaFontAction(); 457 458 // Take final decision to correct FontScaling for this imported Metafile or not. 459 // It is possible to weight positive against negative cases, so to only finally 460 // correct when more positive cases were detected. 461 // But that would be inconsequent and wrong. *If* the detected case is an old import 462 // the whole file was written with wrong FontScale values and all Font actions 463 // need to be corrected. Thus, for now, correct all when there are/is positive 464 // cases detected. 465 // On the other hand it *may* be that for some strange fonts there is a false-positive 466 // in the positive cases, so at least insist on positive cases being more than negative. 467 // Still, do then correct *all* cases. 468 if(!maPositiveIdentifiedCases.empty() 469 && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size()) 470 { 471 for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases) 472 { 473 rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second)); 474 } 475 for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases) 476 { 477 rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second)); 478 } 479 } 480 481 maPositiveIdentifiedCases.clear(); 482 maNegativeIdentifiedCases.clear(); 483 } 484 ReadColor()485 Color MtfTools::ReadColor() 486 { 487 sal_uInt32 nColor; 488 mpInputStream->ReadUInt32( nColor ); 489 Color aColor( COL_BLACK ); 490 if ( ( nColor & 0xFFFF0000 ) == 0x01000000 ) 491 { 492 size_t index = nColor & 0x0000FFFF; 493 if ( index < maPalette.aPaletteColors.size() ) 494 aColor = maPalette.aPaletteColors[ index ]; 495 else 496 SAL_INFO( "emfio", "\t\t Palette index out of range: " << index ); 497 } 498 else 499 aColor = Color( static_cast<sal_uInt8>( nColor ), static_cast<sal_uInt8>( nColor >> 8 ), static_cast<sal_uInt8>( nColor >> 16 ) ); 500 501 SAL_INFO("emfio", "\t\tColor: " << aColor); 502 return aColor; 503 }; 504 ImplScale(const Point & rPoint)505 Point MtfTools::ImplScale(const Point& rPoint) // Hack to set varying defaults for incompletely defined files. 506 { 507 if (!mbIsMapDevSet) 508 return Point(rPoint.X() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Left(), 509 rPoint.Y() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Top()); 510 else 511 return rPoint; 512 } 513 ImplMap(const Point & rPt)514 Point MtfTools::ImplMap( const Point& rPt ) 515 { 516 if ( mnWinExtX && mnWinExtY ) 517 { 518 double fX = rPt.X(); 519 double fY = rPt.Y(); 520 521 double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx; 522 double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy; 523 524 if ( mnGfxMode == GM_COMPATIBLE ) 525 { 526 switch( mnMapMode ) 527 { 528 case MM_LOENGLISH : 529 { 530 fX2 -= mnWinOrgX; 531 fY2 = mnWinOrgY-fY2; 532 fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH * 10; 533 fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH * 10; 534 double nDevOrgX = mnDevOrgX; 535 if (mnPixX) 536 nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 537 fX2 += nDevOrgX; 538 double nDevOrgY = mnDevOrgY; 539 if (mnPixY) 540 nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 541 fY2 += nDevOrgY; 542 } 543 break; 544 case MM_HIENGLISH : 545 { 546 fX2 -= mnWinOrgX; 547 fY2 = mnWinOrgY-fY2; 548 fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH; 549 fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH; 550 double nDevOrgX = mnDevOrgX; 551 if (mnPixX) 552 nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 553 fX2 += nDevOrgX; 554 double nDevOrgY = mnDevOrgY; 555 if (mnPixY) 556 nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 557 fY2 += nDevOrgY; 558 } 559 break; 560 case MM_TWIPS: 561 { 562 fX2 -= mnWinOrgX; 563 fY2 = mnWinOrgY-fY2; 564 fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH / MILLIINCH_PER_TWIPS; 565 fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH / MILLIINCH_PER_TWIPS; 566 double nDevOrgX = mnDevOrgX; 567 if (mnPixX) 568 nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 569 fX2 += nDevOrgX; 570 double nDevOrgY = mnDevOrgY; 571 if (mnPixY) 572 nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 573 fY2 += nDevOrgY; 574 } 575 break; 576 case MM_LOMETRIC : 577 { 578 fX2 -= mnWinOrgX; 579 fY2 = mnWinOrgY-fY2; 580 fX2 *= 10; 581 fY2 *= 10; 582 double nDevOrgX = mnDevOrgX; 583 if (mnPixX) 584 nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 585 fX2 += nDevOrgX; 586 double nDevOrgY = mnDevOrgY; 587 if (mnPixY) 588 nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 589 fY2 += nDevOrgY; 590 } 591 break; 592 case MM_HIMETRIC : // in hundredth of a millimeter 593 { 594 fX2 -= mnWinOrgX; 595 fY2 = mnWinOrgY-fY2; 596 double nDevOrgX = mnDevOrgX; 597 if (mnPixX) 598 nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 599 fX2 += nDevOrgX; 600 double nDevOrgY = mnDevOrgY; 601 if (mnPixY) 602 nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 603 fY2 += nDevOrgY; 604 } 605 break; 606 default : 607 { 608 if (mnPixX == 0 || mnPixY == 0) 609 { 610 SAL_WARN("emfio", "invalid scaling factor"); 611 return Point(); 612 } 613 else 614 { 615 fX2 -= mnWinOrgX; 616 fY2 -= mnWinOrgY; 617 if ( mnMapMode != MM_TEXT ) 618 { 619 fX2 /= mnWinExtX; 620 fY2 /= mnWinExtY; 621 fX2 *= mnDevWidth; 622 fY2 *= mnDevHeight; 623 } 624 fX2 += mnDevOrgX; 625 fY2 += mnDevOrgY; // fX2, fY2 now in device units 626 fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 627 fY2 *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 628 } 629 } 630 break; 631 } 632 fX2 -= mrclFrame.Left(); 633 fY2 -= mrclFrame.Top(); 634 } 635 return Point(basegfx::fround(fX2), basegfx::fround(fY2)); 636 } 637 else 638 return Point(); 639 }; 640 ImplMap(const Size & rSz,bool bDoWorldTransform)641 Size MtfTools::ImplMap(const Size& rSz, bool bDoWorldTransform) 642 { 643 if ( mnWinExtX && mnWinExtY ) 644 { 645 // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted 646 double fWidth, fHeight; 647 if (bDoWorldTransform) 648 { 649 fWidth = rSz.Width() * maXForm.eM11 + rSz.Height() * maXForm.eM21; 650 fHeight = rSz.Width() * maXForm.eM12 + rSz.Height() * maXForm.eM22; 651 } 652 else 653 { 654 //take the scale, but not the rotation 655 basegfx::B2DHomMatrix aMatrix(maXForm.eM11, maXForm.eM12, 0, 656 maXForm.eM21, maXForm.eM22, 0); 657 basegfx::B2DTuple aScale, aTranslate; 658 double fRotate, fShearX; 659 if (!aMatrix.decompose(aScale, aTranslate, fRotate, fShearX)) 660 { 661 aScale.setX(1.0); 662 aScale.setY(1.0); 663 } 664 fWidth = rSz.Width() * aScale.getX(); 665 fHeight = rSz.Height() * aScale.getY(); 666 } 667 668 if ( mnGfxMode == GM_COMPATIBLE ) 669 { 670 switch( mnMapMode ) 671 { 672 case MM_LOENGLISH : 673 { 674 fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10; 675 fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10; 676 } 677 break; 678 case MM_HIENGLISH : 679 { 680 fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH; 681 fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH; 682 } 683 break; 684 case MM_LOMETRIC : 685 { 686 fWidth *= 10; 687 fHeight*=-10; 688 } 689 break; 690 case MM_HIMETRIC : // in hundredth of millimeters 691 { 692 fHeight *= -1; 693 } 694 break; 695 case MM_TWIPS: 696 { 697 fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS; 698 fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS; 699 } 700 break; 701 default : 702 { 703 if (mnPixX == 0 || mnPixY == 0) 704 { 705 SAL_WARN("emfio", "invalid scaling factor"); 706 return Size(); 707 } 708 else 709 { 710 if ( mnMapMode != MM_TEXT ) 711 { 712 fWidth /= mnWinExtX; 713 fHeight /= mnWinExtY; 714 fWidth *= mnDevWidth; 715 fHeight *= mnDevHeight; 716 } 717 fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); 718 fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); 719 } 720 } 721 break; 722 } 723 } 724 return Size(basegfx::fround(fWidth), basegfx::fround(fHeight)); 725 } 726 else 727 return Size(); 728 } 729 ImplMap(const tools::Rectangle & rRect)730 tools::Rectangle MtfTools::ImplMap( const tools::Rectangle& rRect ) 731 { 732 tools::Rectangle aRect; 733 aRect.SetPos(ImplMap(rRect.TopLeft())); 734 aRect.SaturatingSetSize(ImplMap(rRect.GetSize())); 735 return aRect; 736 } 737 ImplMap(vcl::Font & rFont)738 void MtfTools::ImplMap( vcl::Font& rFont ) 739 { 740 // !!! HACK: we now always set the width to zero because the OS width is interpreted differently; 741 // must later be made portable in SV (KA 1996-02-08) 742 Size aFontSize = ImplMap (rFont.GetFontSize(), false); 743 744 const auto nHeight = aFontSize.Height(); 745 if (nHeight < 0) 746 aFontSize.setHeight( o3tl::saturating_toggle_sign(nHeight) ); 747 748 rFont.SetFontSize( aFontSize ); 749 750 sal_Int32 nResult; 751 const bool bFail = o3tl::checked_multiply(mnWinExtX, mnWinExtY, nResult); 752 if (!bFail && nResult < 0) 753 rFont.SetOrientation( 3600_deg10 - rFont.GetOrientation() ); 754 } 755 ImplMap(tools::Polygon & rPolygon)756 tools::Polygon& MtfTools::ImplMap( tools::Polygon& rPolygon ) 757 { 758 sal_uInt16 nPoints = rPolygon.GetSize(); 759 for ( sal_uInt16 i = 0; i < nPoints; i++ ) 760 { 761 rPolygon[ i ] = ImplMap( rPolygon[ i ] ); 762 } 763 return rPolygon; 764 } 765 ImplScale(tools::Polygon & rPolygon)766 void MtfTools::ImplScale( tools::Polygon& rPolygon ) 767 { 768 sal_uInt16 nPoints = rPolygon.GetSize(); 769 for ( sal_uInt16 i = 0; i < nPoints; i++ ) 770 { 771 rPolygon[ i ] = ImplScale( rPolygon[ i ] ); 772 } 773 } 774 ImplScale(tools::PolyPolygon & rPolyPolygon)775 tools::PolyPolygon& MtfTools::ImplScale( tools::PolyPolygon& rPolyPolygon ) 776 { 777 sal_uInt16 nPolys = rPolyPolygon.Count(); 778 for (sal_uInt16 i = 0; i < nPolys; ++i) 779 { 780 ImplScale(rPolyPolygon[i]); 781 } 782 return rPolyPolygon; 783 } 784 ImplMap(tools::PolyPolygon & rPolyPolygon)785 tools::PolyPolygon& MtfTools::ImplMap( tools::PolyPolygon& rPolyPolygon ) 786 { 787 sal_uInt16 nPolys = rPolyPolygon.Count(); 788 for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ; 789 return rPolyPolygon; 790 } 791 SelectObject(sal_uInt32 nIndex)792 void MtfTools::SelectObject( sal_uInt32 nIndex ) 793 { 794 if ( nIndex & ENHMETA_STOCK_OBJECT ) 795 { 796 SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex << nIndex ); 797 sal_uInt16 nStockId = static_cast<sal_uInt8>(nIndex); 798 switch( nStockId ) 799 { 800 case WHITE_BRUSH : 801 { 802 maFillStyle = WinMtfFillStyle( COL_WHITE ); 803 mbFillStyleSelected = true; 804 } 805 break; 806 case LTGRAY_BRUSH : 807 { 808 maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY ); 809 mbFillStyleSelected = true; 810 } 811 break; 812 case GRAY_BRUSH : 813 { 814 maFillStyle = WinMtfFillStyle( COL_GRAY ); 815 mbFillStyleSelected = true; 816 } 817 break; 818 case DKGRAY_BRUSH : 819 { 820 maFillStyle = WinMtfFillStyle( COL_GRAY7 ); 821 mbFillStyleSelected = true; 822 } 823 break; 824 case BLACK_BRUSH : 825 { 826 maFillStyle = WinMtfFillStyle( COL_BLACK ); 827 mbFillStyleSelected = true; 828 } 829 break; 830 case NULL_BRUSH : 831 { 832 maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true ); 833 mbFillStyleSelected = true; 834 } 835 break; 836 case WHITE_PEN : 837 { 838 maLineStyle = WinMtfLineStyle( COL_WHITE ); 839 } 840 break; 841 case BLACK_PEN : 842 { 843 maLineStyle = WinMtfLineStyle( COL_BLACK ); 844 } 845 break; 846 case NULL_PEN : 847 { 848 maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true ); 849 } 850 break; 851 default: 852 break; 853 } 854 } 855 else 856 { 857 nIndex &= 0xffff; // safety check: don't allow index to be > 65535 858 859 GDIObj *pGDIObj = nullptr; 860 861 if ( nIndex < mvGDIObj.size() ) 862 pGDIObj = mvGDIObj[ nIndex ].get(); 863 864 if ( pGDIObj ) 865 { 866 867 SAL_INFO ( "emfio", "\t\t Index: " << nIndex ); 868 if (const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj)) 869 { 870 maLineStyle = *pen; 871 SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex << maLineStyle.aLineColor 872 << ", Weight: " << maLineStyle.aLineInfo.GetWidth() ); 873 } 874 else if (const auto brush = dynamic_cast<WinMtfFillStyle*>( 875 pGDIObj)) 876 { 877 maFillStyle = *brush; 878 mbFillStyleSelected = true; 879 SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex << ", Color: " << maFillStyle.aFillColor); 880 } 881 else if (const auto font = dynamic_cast<WinMtfFontStyle*>( 882 pGDIObj)) 883 { 884 maFont = font->aFont; 885 SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex << ", Font: " << maFont.GetFamilyName() << " " << maFont.GetStyleName()); 886 } 887 else if (const auto palette = dynamic_cast<WinMtfPalette*>( 888 pGDIObj)) 889 { 890 maPalette = palette->aPaletteColors; 891 SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex << ", Number of colours: " << maPalette.aPaletteColors.size() ); 892 } 893 } 894 else 895 { 896 SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex); 897 } 898 } 899 } 900 SetTextLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)901 void MtfTools::SetTextLayoutMode( ComplexTextLayoutFlags nTextLayoutMode ) 902 { 903 mnTextLayoutMode = nTextLayoutMode; 904 } 905 SetBkMode(BkMode nMode)906 void MtfTools::SetBkMode( BkMode nMode ) 907 { 908 mnBkMode = nMode; 909 } 910 SetBkColor(const Color & rColor)911 void MtfTools::SetBkColor( const Color& rColor ) 912 { 913 maBkColor = rColor; 914 } 915 SetTextColor(const Color & rColor)916 void MtfTools::SetTextColor( const Color& rColor ) 917 { 918 maTextColor = rColor; 919 } 920 SetTextAlign(sal_uInt32 nAlign)921 void MtfTools::SetTextAlign( sal_uInt32 nAlign ) 922 { 923 mnTextAlign = nAlign; 924 } 925 ImplResizeObjectArry(sal_uInt32 nNewEntrys)926 void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys ) 927 { 928 mvGDIObj.resize(nNewEntrys); 929 } 930 ImplDrawClippedPolyPolygon(const tools::PolyPolygon & rPolyPoly)931 void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly ) 932 { 933 if ( !rPolyPoly.Count() ) 934 return; 935 936 ImplSetNonPersistentLineColorTransparenz(); 937 if ( rPolyPoly.Count() == 1 ) 938 { 939 if ( rPolyPoly.IsRect() ) 940 mpGDIMetaFile->AddAction( new MetaRectAction( rPolyPoly.GetBoundRect() ) ); 941 else 942 { 943 tools::Polygon aPoly( rPolyPoly[ 0 ] ); 944 sal_uInt16 nCount = aPoly.GetSize(); 945 if ( nCount ) 946 { 947 if ( aPoly[ nCount - 1 ] != aPoly[ 0 ] ) 948 { 949 Point aPoint( aPoly[ 0 ] ); 950 aPoly.Insert( nCount, aPoint ); 951 } 952 mpGDIMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); 953 } 954 } 955 } 956 else 957 mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) ); 958 } 959 CreateObject(std::unique_ptr<GDIObj> pObject)960 void MtfTools::CreateObject( std::unique_ptr<GDIObj> pObject ) 961 { 962 if ( pObject ) 963 { 964 const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get()); 965 const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get()); 966 967 if ( pFontStyle ) 968 { 969 if (pFontStyle->aFont.GetFontHeight() == 0) 970 pFontStyle->aFont.SetFontHeight(423); 971 ImplMap(pFontStyle->aFont); // defaulting to 12pt 972 } 973 else if ( pLineStyle ) 974 { 975 Size aSize(pLineStyle->aLineInfo.GetWidth(), 0); 976 aSize = ImplMap(aSize); 977 pLineStyle->aLineInfo.SetWidth(aSize.Width()); 978 } 979 } 980 std::vector<std::unique_ptr<GDIObj>>::size_type nIndex; 981 for ( nIndex = 0; nIndex < mvGDIObj.size(); nIndex++ ) 982 { 983 if ( !mvGDIObj[ nIndex ] ) 984 break; 985 } 986 if ( nIndex == mvGDIObj.size() ) 987 ImplResizeObjectArry( mvGDIObj.size() + 16 ); 988 989 mvGDIObj[ nIndex ] = std::move(pObject); 990 } 991 CreateObjectIndexed(sal_uInt32 nIndex,std::unique_ptr<GDIObj> pObject)992 void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject ) 993 { 994 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 ) 995 return; 996 997 nIndex &= 0xffff; // safety check: do not allow index to be > 65535 998 if ( pObject ) 999 { 1000 const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get()); 1001 const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get()); 1002 if ( pFontStyle ) 1003 { 1004 if (pFontStyle->aFont.GetFontHeight() == 0) 1005 pFontStyle->aFont.SetFontHeight(423); 1006 ImplMap(pFontStyle->aFont); 1007 } 1008 else if ( pLineStyle ) 1009 { 1010 Size aSize(pLineStyle->aLineInfo.GetWidth(), 0); 1011 pLineStyle->aLineInfo.SetWidth( ImplMap(aSize).Width() ); 1012 1013 if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash ) 1014 { 1015 aSize.AdjustWidth(1 ); 1016 tools::Long nDotLen = ImplMap( aSize ).Width(); 1017 pLineStyle->aLineInfo.SetDistance( nDotLen ); 1018 pLineStyle->aLineInfo.SetDotLen( nDotLen ); 1019 pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 ); 1020 } 1021 } 1022 } 1023 if ( nIndex >= mvGDIObj.size() ) 1024 ImplResizeObjectArry( nIndex + 16 ); 1025 1026 mvGDIObj[ nIndex ] = std::move(pObject); 1027 } 1028 CreateObject()1029 void MtfTools::CreateObject() 1030 { 1031 CreateObject(std::make_unique<GDIObj>()); 1032 } 1033 DeleteObject(sal_uInt32 nIndex)1034 void MtfTools::DeleteObject( sal_uInt32 nIndex ) 1035 { 1036 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 ) 1037 { 1038 if ( nIndex < mvGDIObj.size() ) 1039 { 1040 mvGDIObj[ nIndex ].reset(); 1041 } 1042 } 1043 } 1044 IntersectClipRect(const tools::Rectangle & rRect)1045 void MtfTools::IntersectClipRect( const tools::Rectangle& rRect ) 1046 { 1047 if (utl::ConfigManager::IsFuzzing()) 1048 return; 1049 mbClipNeedsUpdate=true; 1050 if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0)) 1051 { 1052 return; // empty rectangles cause trouble 1053 } 1054 tools::Polygon aPoly( rRect ); 1055 const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); 1056 maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() ); 1057 } 1058 ExcludeClipRect(const tools::Rectangle & rRect)1059 void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect ) 1060 { 1061 if (utl::ConfigManager::IsFuzzing()) 1062 return; 1063 mbClipNeedsUpdate=true; 1064 tools::Polygon aPoly( rRect ); 1065 const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); 1066 maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() ); 1067 } 1068 MoveClipRegion(const Size & rSize)1069 void MtfTools::MoveClipRegion( const Size& rSize ) 1070 { 1071 if (utl::ConfigManager::IsFuzzing()) 1072 return; 1073 mbClipNeedsUpdate=true; 1074 maClipPath.moveClipRegion( ImplMap( rSize ) ); 1075 } 1076 SetClipPath(const tools::PolyPolygon & rPolyPolygon,sal_Int32 nClippingMode,bool bIsMapped)1077 void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode, bool bIsMapped ) 1078 { 1079 if (utl::ConfigManager::IsFuzzing()) 1080 return; 1081 mbClipNeedsUpdate = true; 1082 tools::PolyPolygon aPolyPolygon(rPolyPolygon); 1083 1084 if (!bIsMapped) 1085 { 1086 if (!mbIsMapDevSet && (mnMapMode == MM_ISOTROPIC || mnMapMode == MM_ANISOTROPIC)) 1087 aPolyPolygon = ImplScale(aPolyPolygon); 1088 else 1089 aPolyPolygon = ImplMap(aPolyPolygon); 1090 } 1091 maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), nClippingMode); 1092 } 1093 SetDefaultClipPath()1094 void MtfTools::SetDefaultClipPath() 1095 { 1096 mbClipNeedsUpdate = true; 1097 maClipPath.setDefaultClipPath(); 1098 } 1099 MtfTools(GDIMetaFile & rGDIMetaFile,SvStream & rStreamWMF)1100 MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF) 1101 : maPathObj(), 1102 maClipPath(), 1103 maLatestLineStyle(), 1104 maLineStyle(), 1105 maNopLineStyle(), 1106 maLatestFillStyle(), 1107 maFillStyle(), 1108 maNopFillStyle(), 1109 maPalette(), 1110 maLatestFont(), 1111 maFont(), 1112 mnLatestTextAlign(90), 1113 mnTextAlign(TA_LEFT | TA_TOP | TA_NOUPDATECP), 1114 maLatestTextColor(), 1115 maTextColor(), 1116 maLatestBkColor(ColorTransparency, 0x12345678), 1117 maBkColor(COL_WHITE), 1118 mnLatestTextLayoutMode(ComplexTextLayoutFlags::Default), 1119 mnTextLayoutMode(ComplexTextLayoutFlags::Default), 1120 mnLatestBkMode(BkMode::NONE), 1121 mnBkMode(BkMode::OPAQUE), 1122 meLatestRasterOp(RasterOp::Invert), 1123 meRasterOp(RasterOp::OverPaint), 1124 mvGDIObj(), 1125 maActPos(), 1126 mnRop(), 1127 mvSaveStack(), 1128 mnGfxMode(GM_COMPATIBLE), 1129 mnMapMode(MM_TEXT), 1130 maXForm(), 1131 mnDevOrgX(0), 1132 mnDevOrgY(0), 1133 mnDevWidth(1), 1134 mnDevHeight(1), 1135 mnWinOrgX(0), 1136 mnWinOrgY(0), 1137 mnWinExtX(1), 1138 mnWinExtY(1), 1139 mnPixX(100), 1140 mnPixY(100), 1141 mnMillX(1), 1142 mnMillY(1), 1143 mrclFrame(), 1144 mrclBounds(), 1145 mpGDIMetaFile(&rGDIMetaFile), 1146 mpInputStream(&rStreamWMF), 1147 mnStartPos(0), 1148 mnEndPos(0), 1149 maBmpSaveList(), 1150 maScaledFontHelper(), 1151 mbNopMode(false), 1152 mbFillStyleSelected(false), 1153 mbClipNeedsUpdate(true), 1154 mbComplexClip(false), 1155 mbIsMapWinSet(false), 1156 mbIsMapDevSet(false) 1157 { 1158 SvLockBytes *pLB = mpInputStream->GetLockBytes(); 1159 1160 if (pLB) 1161 { 1162 pLB->SetSynchronMode(); 1163 } 1164 1165 mnStartPos = mpInputStream->Tell(); 1166 SetDevOrg(Point()); 1167 1168 mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) ); // The original clipregion has to be on top 1169 // of the stack so it can always be restored 1170 // this is necessary to be able to support 1171 // SetClipRgn( NULL ) and similar ClipRgn actions (SJ) 1172 1173 maFont.SetFamilyName( "Arial" ); // sj: #i57205#, we do have some scaling problems if using 1174 maFont.SetCharSet( RTL_TEXTENCODING_MS_1252 ); // the default font then most times a x11 font is used, we 1175 maFont.SetFontHeight( 423 ); // will prevent this defining a font 1176 1177 maLatestLineStyle.aLineColor = Color( 0x12, 0x34, 0x56 ); 1178 maLatestFillStyle.aFillColor = Color( 0x12, 0x34, 0x56 ); 1179 1180 mnRop = WMFRasterOp::Black; 1181 meRasterOp = RasterOp::OverPaint; 1182 mpGDIMetaFile->AddAction( new MetaRasterOpAction( RasterOp::OverPaint ) ); 1183 } 1184 ~MtfTools()1185 MtfTools::~MtfTools() COVERITY_NOEXCEPT_FALSE 1186 { 1187 mpGDIMetaFile->AddAction( new MetaPopAction() ); 1188 mpGDIMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM)); 1189 if ( mrclFrame.IsEmpty() ) 1190 mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) ); 1191 else 1192 mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() ); 1193 } 1194 UpdateClipRegion()1195 void MtfTools::UpdateClipRegion() 1196 { 1197 if (!mbClipNeedsUpdate) 1198 return; 1199 1200 mbClipNeedsUpdate = false; 1201 mbComplexClip = false; 1202 1203 mpGDIMetaFile->AddAction( new MetaPopAction() ); // taking the original clipregion 1204 mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) ); 1205 1206 // skip for 'no clipping at all' case 1207 if( maClipPath.isEmpty() ) 1208 return; 1209 1210 const basegfx::B2DPolyPolygon& rClipPoly( maClipPath.getClipPath() ); 1211 1212 mbComplexClip = rClipPoly.count() > 1 1213 || !basegfx::utils::isRectangle(rClipPoly); 1214 1215 // This makes cases like tdf#45820 work in reasonable time. 1216 if (mbComplexClip) 1217 { 1218 mpGDIMetaFile->AddAction( 1219 new MetaISectRegionClipRegionAction( 1220 vcl::Region(rClipPoly))); 1221 mbComplexClip = false; 1222 } 1223 else 1224 { 1225 mpGDIMetaFile->AddAction( 1226 new MetaISectRectClipRegionAction( 1227 vcl::unotools::rectangleFromB2DRectangle( 1228 rClipPoly.getB2DRange()))); 1229 } 1230 } 1231 ImplSetNonPersistentLineColorTransparenz()1232 void MtfTools::ImplSetNonPersistentLineColorTransparenz() 1233 { 1234 WinMtfLineStyle aTransparentLine( COL_TRANSPARENT, true ); 1235 if ( ! ( maLatestLineStyle == aTransparentLine ) ) 1236 { 1237 maLatestLineStyle = aTransparentLine; 1238 mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) ); 1239 } 1240 } 1241 UpdateLineStyle()1242 void MtfTools::UpdateLineStyle() 1243 { 1244 if (!( maLatestLineStyle == maLineStyle ) ) 1245 { 1246 maLatestLineStyle = maLineStyle; 1247 mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) ); 1248 } 1249 } 1250 UpdateFillStyle()1251 void MtfTools::UpdateFillStyle() 1252 { 1253 if ( !mbFillStyleSelected ) // SJ: #i57205# taking care of bkcolor if no brush is selected 1254 maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BkMode::Transparent ); 1255 if (!( maLatestFillStyle == maFillStyle ) ) 1256 { 1257 maLatestFillStyle = maFillStyle; 1258 if (maFillStyle.aType == WinMtfFillStyleType::Solid) 1259 mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) ); 1260 } 1261 } 1262 SetRasterOp(WMFRasterOp nRasterOp)1263 WMFRasterOp MtfTools::SetRasterOp( WMFRasterOp nRasterOp ) 1264 { 1265 WMFRasterOp nRetROP = mnRop; 1266 if ( nRasterOp != mnRop ) 1267 { 1268 mnRop = nRasterOp; 1269 1270 if ( mbNopMode && ( nRasterOp != WMFRasterOp::Nop ) ) 1271 { // changing modes from WMFRasterOp::Nop so set pen and brush 1272 maFillStyle = maNopFillStyle; 1273 maLineStyle = maNopLineStyle; 1274 mbNopMode = false; 1275 } 1276 switch( nRasterOp ) 1277 { 1278 case WMFRasterOp::Not: 1279 meRasterOp = RasterOp::Invert; 1280 break; 1281 1282 case WMFRasterOp::XorPen: 1283 meRasterOp = RasterOp::Xor; 1284 break; 1285 1286 case WMFRasterOp::Nop: 1287 { 1288 meRasterOp = RasterOp::OverPaint; 1289 if( !mbNopMode ) 1290 { 1291 maNopFillStyle = maFillStyle; 1292 maNopLineStyle = maLineStyle; 1293 maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true ); 1294 maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true ); 1295 mbNopMode = true; 1296 } 1297 } 1298 break; 1299 1300 default: 1301 meRasterOp = RasterOp::OverPaint; 1302 break; 1303 } 1304 } 1305 if ( nRetROP != nRasterOp ) 1306 mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) ); 1307 return nRetROP; 1308 }; 1309 StrokeAndFillPath(bool bStroke,bool bFill)1310 void MtfTools::StrokeAndFillPath( bool bStroke, bool bFill ) 1311 { 1312 if ( !maPathObj.Count() ) 1313 return; 1314 1315 UpdateClipRegion(); 1316 UpdateLineStyle(); 1317 UpdateFillStyle(); 1318 if ( bFill ) 1319 { 1320 if ( !bStroke ) 1321 { 1322 mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::LINECOLOR ) ); 1323 mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) ); 1324 } 1325 if ( maPathObj.Count() == 1 ) 1326 mpGDIMetaFile->AddAction( new MetaPolygonAction( maPathObj.GetObject( 0 ) ) ); 1327 else 1328 mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( maPathObj ) ); 1329 1330 if ( !bStroke ) 1331 mpGDIMetaFile->AddAction( new MetaPopAction() ); 1332 } 1333 // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction 1334 if ( bStroke ) 1335 { 1336 // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0 1337 if ( !bFill || maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1338 { 1339 sal_uInt16 i, nCount = maPathObj.Count(); 1340 for ( i = 0; i < nCount; i++ ) 1341 mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) ); 1342 } 1343 } 1344 ClearPath(); 1345 } 1346 DrawPixel(const Point & rSource,const Color & rColor)1347 void MtfTools::DrawPixel( const Point& rSource, const Color& rColor ) 1348 { 1349 mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) ); 1350 } 1351 MoveTo(const Point & rPoint,bool bRecordPath)1352 void MtfTools::MoveTo( const Point& rPoint, bool bRecordPath ) 1353 { 1354 Point aDest( ImplMap( rPoint ) ); 1355 if ( bRecordPath ) 1356 { 1357 // fdo#57353 create new subpath for subsequent moves 1358 if ( maPathObj.Count() ) 1359 if ( maPathObj[ maPathObj.Count() - 1 ].GetSize() ) 1360 maPathObj.Insert( tools::Polygon() ); 1361 maPathObj.AddPoint( aDest ); 1362 } 1363 maActPos = aDest; 1364 } 1365 LineTo(const Point & rPoint,bool bRecordPath)1366 void MtfTools::LineTo( const Point& rPoint, bool bRecordPath ) 1367 { 1368 UpdateClipRegion(); 1369 Point aDest( ImplMap( rPoint ) ); 1370 if ( bRecordPath ) 1371 maPathObj.AddPoint( aDest ); 1372 else 1373 { 1374 UpdateLineStyle(); 1375 mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) ); 1376 } 1377 maActPos = aDest; 1378 } 1379 DrawRectWithBGColor(const tools::Rectangle & rRect)1380 void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect) 1381 { 1382 WinMtfFillStyle aFillStyleBackup = maFillStyle; 1383 bool aTransparentBackup = maLineStyle.bTransparent; 1384 BkMode mnBkModeBackup = mnBkMode; 1385 1386 const tools::Polygon aPoly( rRect ); 1387 maLineStyle.bTransparent = true; 1388 maFillStyle = maBkColor; 1389 mnBkMode = BkMode::OPAQUE; 1390 ImplSetNonPersistentLineColorTransparenz(); 1391 DrawPolygon(aPoly, false); 1392 mnBkMode = mnBkModeBackup; // The rectangle needs to be always drawned even if mode is transparent 1393 maFillStyle = aFillStyleBackup; 1394 maLineStyle.bTransparent = aTransparentBackup; 1395 } 1396 DrawRect(const tools::Rectangle & rRect,bool bEdge)1397 void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge ) 1398 { 1399 UpdateClipRegion(); 1400 UpdateFillStyle(); 1401 1402 if ( mbComplexClip ) 1403 { 1404 tools::Polygon aPoly( ImplMap( rRect ) ); 1405 tools::PolyPolygon aPolyPolyRect( aPoly ); 1406 tools::PolyPolygon aDest; 1407 tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( aPolyPolyRect, aDest ); 1408 ImplDrawClippedPolyPolygon( aDest ); 1409 } 1410 else 1411 { 1412 if ( bEdge ) 1413 { 1414 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1415 { 1416 ImplSetNonPersistentLineColorTransparenz(); 1417 mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) ); 1418 UpdateLineStyle(); 1419 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( ImplMap( rRect ) ),maLineStyle.aLineInfo ) ); 1420 } 1421 else 1422 { 1423 UpdateLineStyle(); 1424 mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) ); 1425 } 1426 } 1427 else 1428 { 1429 ImplSetNonPersistentLineColorTransparenz(); 1430 mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) ); 1431 } 1432 } 1433 } 1434 DrawRoundRect(const tools::Rectangle & rRect,const Size & rSize)1435 void MtfTools::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize ) 1436 { 1437 UpdateClipRegion(); 1438 UpdateLineStyle(); 1439 UpdateFillStyle(); 1440 mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), std::abs( ImplMap( rSize ).Width() ), std::abs( ImplMap( rSize ).Height() ) ) ); 1441 // tdf#142139 Wrong line width during WMF import 1442 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1443 { 1444 tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() ); 1445 mpGDIMetaFile->AddAction( new MetaPolyLineAction( ImplMap( aRoundRectPoly ), maLineStyle.aLineInfo ) ); 1446 } 1447 } 1448 DrawEllipse(const tools::Rectangle & rRect)1449 void MtfTools::DrawEllipse( const tools::Rectangle& rRect ) 1450 { 1451 UpdateClipRegion(); 1452 UpdateFillStyle(); 1453 1454 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1455 { 1456 Point aCenter( ImplMap( rRect.Center() ) ); 1457 Size aRad( ImplMap( Size( rRect.GetWidth() / 2, rRect.GetHeight() / 2 ) ) ); 1458 1459 ImplSetNonPersistentLineColorTransparenz(); 1460 mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) ); 1461 UpdateLineStyle(); 1462 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) ); 1463 } 1464 else 1465 { 1466 UpdateLineStyle(); 1467 mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) ); 1468 } 1469 } 1470 DrawArc(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd,bool bTo)1471 void MtfTools::DrawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, bool bTo ) 1472 { 1473 UpdateClipRegion(); 1474 UpdateLineStyle(); 1475 UpdateFillStyle(); 1476 1477 tools::Rectangle aRect( ImplMap( rRect ) ); 1478 Point aStart( ImplMap( rStart ) ); 1479 Point aEnd( ImplMap( rEnd ) ); 1480 1481 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1482 { 1483 if ( aStart == aEnd ) 1484 { // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse 1485 Point aCenter( aRect.Center() ); 1486 Size aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 ); 1487 1488 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) ); 1489 } 1490 else 1491 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Arc ), maLineStyle.aLineInfo ) ); 1492 } 1493 else 1494 mpGDIMetaFile->AddAction( new MetaArcAction( aRect, aStart, aEnd ) ); 1495 1496 if ( bTo ) 1497 maActPos = aEnd; 1498 } 1499 DrawPie(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd)1500 void MtfTools::DrawPie( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd ) 1501 { 1502 UpdateClipRegion(); 1503 UpdateFillStyle(); 1504 1505 tools::Rectangle aRect( ImplMap( rRect ) ); 1506 Point aStart( ImplMap( rStart ) ); 1507 Point aEnd( ImplMap( rEnd ) ); 1508 1509 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1510 { 1511 ImplSetNonPersistentLineColorTransparenz(); 1512 mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) ); 1513 UpdateLineStyle(); 1514 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ), maLineStyle.aLineInfo ) ); 1515 } 1516 else 1517 { 1518 UpdateLineStyle(); 1519 mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) ); 1520 } 1521 } 1522 DrawChord(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd)1523 void MtfTools::DrawChord( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd ) 1524 { 1525 UpdateClipRegion(); 1526 UpdateFillStyle(); 1527 1528 tools::Rectangle aRect( ImplMap( rRect ) ); 1529 Point aStart( ImplMap( rStart ) ); 1530 Point aEnd( ImplMap( rEnd ) ); 1531 1532 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1533 { 1534 ImplSetNonPersistentLineColorTransparenz(); 1535 mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) ); 1536 UpdateLineStyle(); 1537 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ), maLineStyle.aLineInfo ) ); 1538 } 1539 else 1540 { 1541 UpdateLineStyle(); 1542 mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) ); 1543 } 1544 } 1545 DrawPolygon(tools::Polygon rPolygon,bool bRecordPath)1546 void MtfTools::DrawPolygon( tools::Polygon rPolygon, bool bRecordPath ) 1547 { 1548 UpdateClipRegion(); 1549 ImplMap( rPolygon ); 1550 if ( bRecordPath ) 1551 maPathObj.AddPolygon( rPolygon ); 1552 else 1553 { 1554 UpdateFillStyle(); 1555 1556 if ( mbComplexClip ) 1557 { 1558 tools::PolyPolygon aPolyPoly( rPolygon ); 1559 auto tmp = maClipPath.getClip(); 1560 tmp.intersectPolyPolygon(aPolyPoly.getB2DPolyPolygon()); 1561 tools::PolyPolygon aDest(tmp.getClipPoly()); 1562 ImplDrawClippedPolyPolygon( aDest ); 1563 } 1564 else 1565 { 1566 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) 1567 { 1568 sal_uInt16 nCount = rPolygon.GetSize(); 1569 if ( nCount ) 1570 { 1571 if ( rPolygon[ nCount - 1 ] != rPolygon[ 0 ] ) 1572 { 1573 Point aPoint( rPolygon[ 0 ] ); 1574 rPolygon.Insert( nCount, aPoint ); 1575 } 1576 } 1577 ImplSetNonPersistentLineColorTransparenz(); 1578 mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) ); 1579 UpdateLineStyle(); 1580 mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) ); 1581 } 1582 else 1583 { 1584 UpdateLineStyle(); 1585 1586 if (maLatestFillStyle.aType != WinMtfFillStyleType::Pattern) 1587 mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) ); 1588 else { 1589 SvtGraphicFill aFill( tools::PolyPolygon( rPolygon ), 1590 Color(), 1591 0.0, 1592 SvtGraphicFill::fillNonZero, 1593 SvtGraphicFill::fillTexture, 1594 SvtGraphicFill::Transform(), 1595 true, 1596 SvtGraphicFill::hatchSingle, 1597 Color(), 1598 SvtGraphicFill::GradientType::Linear, 1599 Color(), 1600 Color(), 1601 0, 1602 Graphic (BitmapEx(maLatestFillStyle.aBmp))); 1603 1604 SvMemoryStream aMemStm; 1605 1606 WriteSvtGraphicFill( aMemStm, aFill ); 1607 1608 mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, 1609 static_cast<const sal_uInt8*>(aMemStm.GetData()), 1610 aMemStm.TellEnd() ) ); 1611 mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END" ) ); 1612 } 1613 1614 } 1615 } 1616 } 1617 } 1618 DrawPolyPolygon(tools::PolyPolygon & rPolyPolygon,bool bRecordPath)1619 void MtfTools::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath ) 1620 { 1621 UpdateClipRegion(); 1622 1623 ImplMap( rPolyPolygon ); 1624 1625 if ( bRecordPath ) 1626 maPathObj.AddPolyPolygon( rPolyPolygon ); 1627 else 1628 { 1629 UpdateFillStyle(); 1630 1631 if ( mbComplexClip ) 1632 { 1633 tools::PolyPolygon aDest; 1634 tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( rPolyPolygon, aDest ); 1635 ImplDrawClippedPolyPolygon( aDest ); 1636 } 1637 else 1638 { 1639 UpdateLineStyle(); 1640 mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPolygon ) ); 1641 if (maLineStyle.aLineInfo.GetWidth() > 0 || maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash) 1642 { 1643 for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly) 1644 { 1645 mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo)); 1646 } 1647 } 1648 } 1649 } 1650 } 1651 DrawPolyLine(tools::Polygon rPolygon,bool bTo,bool bRecordPath)1652 void MtfTools::DrawPolyLine( tools::Polygon rPolygon, bool bTo, bool bRecordPath ) 1653 { 1654 UpdateClipRegion(); 1655 1656 sal_uInt16 nPoints = rPolygon.GetSize(); 1657 if (nPoints < 1) 1658 return; 1659 1660 ImplMap( rPolygon ); 1661 if ( bTo ) 1662 { 1663 rPolygon[ 0 ] = maActPos; 1664 maActPos = rPolygon[ rPolygon.GetSize() - 1 ]; 1665 } 1666 if ( bRecordPath ) 1667 maPathObj.AddPolyLine( rPolygon ); 1668 else 1669 { 1670 UpdateLineStyle(); 1671 mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) ); 1672 } 1673 } 1674 DrawPolyBezier(tools::Polygon rPolygon,bool bTo,bool bRecordPath)1675 void MtfTools::DrawPolyBezier( tools::Polygon rPolygon, bool bTo, bool bRecordPath ) 1676 { 1677 sal_uInt16 nPoints = rPolygon.GetSize(); 1678 if ( ( nPoints < 4 ) || ( ( ( nPoints - 4 ) % 3 ) != 0 ) ) 1679 return; 1680 1681 UpdateClipRegion(); 1682 1683 ImplMap( rPolygon ); 1684 if ( bTo ) 1685 { 1686 rPolygon[ 0 ] = maActPos; 1687 maActPos = rPolygon[ nPoints - 1 ]; 1688 } 1689 sal_uInt16 i; 1690 for ( i = 0; ( i + 2 ) < nPoints; ) 1691 { 1692 rPolygon.SetFlags( i++, PolyFlags::Normal ); 1693 rPolygon.SetFlags( i++, PolyFlags::Control ); 1694 rPolygon.SetFlags( i++, PolyFlags::Control ); 1695 } 1696 if ( bRecordPath ) 1697 maPathObj.AddPolyLine( rPolygon ); 1698 else 1699 { 1700 UpdateLineStyle(); 1701 mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) ); 1702 } 1703 } 1704 DrawText(Point & rPosition,OUString const & rText,tools::Long * pDXArry,tools::Long * pDYArry,bool bRecordPath,sal_Int32 nGfxMode)1705 void MtfTools::DrawText( Point& rPosition, OUString const & rText, tools::Long* pDXArry, tools::Long* pDYArry, bool bRecordPath, sal_Int32 nGfxMode ) 1706 { 1707 UpdateClipRegion(); 1708 rPosition = ImplMap( rPosition ); 1709 sal_Int32 nOldGfxMode = GetGfxMode(); 1710 SetGfxMode( GM_COMPATIBLE ); 1711 1712 if (pDXArry) 1713 { 1714 sal_Int32 nSumX = 0, nSumY = 0; 1715 for (sal_Int32 i = 0; i < rText.getLength(); i++ ) 1716 { 1717 nSumX += pDXArry[i]; 1718 1719 // #i121382# Map DXArray using WorldTransform 1720 const Size aSizeX(ImplMap(Size(nSumX, 0))); 1721 const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height()); 1722 pDXArry[i] = basegfx::fround(aVectorX.getLength()); 1723 pDXArry[i] *= (nSumX >= 0 ? 1 : -1); 1724 1725 if (pDYArry) 1726 { 1727 nSumY += pDYArry[i]; 1728 1729 const Size aSizeY(ImplMap(Size(0, nSumY))); 1730 const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height()); 1731 // Reverse Y 1732 pDYArry[i] = basegfx::fround(aVectorY.getLength()); 1733 pDYArry[i] *= (nSumY >= 0 ? -1 : 1); 1734 } 1735 } 1736 } 1737 if ( mnLatestTextLayoutMode != mnTextLayoutMode ) 1738 { 1739 mnLatestTextLayoutMode = mnTextLayoutMode; 1740 mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) ); 1741 } 1742 SetGfxMode( nGfxMode ); 1743 TextAlign eTextAlign; 1744 if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE ) 1745 eTextAlign = ALIGN_BASELINE; 1746 else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM ) 1747 eTextAlign = ALIGN_BOTTOM; 1748 else 1749 eTextAlign = ALIGN_TOP; 1750 bool bChangeFont = false; 1751 if ( mnLatestTextAlign != mnTextAlign ) 1752 { 1753 bChangeFont = true; 1754 mnLatestTextAlign = mnTextAlign; 1755 mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) ); 1756 } 1757 if ( maLatestTextColor != maTextColor ) 1758 { 1759 bChangeFont = true; 1760 maLatestTextColor = maTextColor; 1761 mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) ); 1762 } 1763 bool bChangeFillColor = false; 1764 if ( maLatestBkColor != maBkColor ) 1765 { 1766 bChangeFillColor = true; 1767 maLatestBkColor = maBkColor; 1768 } 1769 if ( mnLatestBkMode != mnBkMode ) 1770 { 1771 bChangeFillColor = true; 1772 mnLatestBkMode = mnBkMode; 1773 } 1774 if ( bChangeFillColor ) 1775 { 1776 bChangeFont = true; 1777 mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) ); 1778 } 1779 vcl::Font aTmp( maFont ); 1780 aTmp.SetColor( maTextColor ); 1781 aTmp.SetFillColor( maBkColor ); 1782 1783 if( mnBkMode == BkMode::Transparent ) 1784 aTmp.SetTransparent( true ); 1785 else 1786 aTmp.SetTransparent( false ); 1787 1788 aTmp.SetAlignment( eTextAlign ); 1789 1790 if ( nGfxMode == GM_ADVANCED ) 1791 { 1792 // check whether there is a font rotation applied via transformation 1793 Point aP1( ImplMap( Point() ) ); 1794 Point aP2( ImplMap( Point( 0, 100 ) ) ); 1795 aP2.AdjustX( -(aP1.X()) ); 1796 aP2.AdjustY( -(aP1.Y()) ); 1797 double fX = aP2.X(); 1798 double fY = aP2.Y(); 1799 if ( fX ) 1800 { 1801 double fOrientation = acos( fX / sqrt( fX * fX + fY * fY ) ) * 57.29577951308; 1802 if ( fY > 0 ) 1803 fOrientation = 360 - fOrientation; 1804 fOrientation += 90; 1805 fOrientation *= 10; 1806 aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) ); 1807 } 1808 } 1809 1810 if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) ) 1811 { 1812 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading 1813 SolarMutexGuard aGuard; 1814 ScopedVclPtrInstance< VirtualDevice > pVDev; 1815 sal_Int32 nTextWidth; 1816 Point aActPosDelta; 1817 pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) ); 1818 pVDev->SetFont( maFont ); 1819 const sal_uInt32 nLen = pDXArry ? rText.getLength() : 0; 1820 if (nLen) 1821 { 1822 nTextWidth = pVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) ); 1823 if( nLen > 1 ) 1824 nTextWidth += pDXArry[ nLen - 2 ]; 1825 // tdf#39894: We should consider the distance to next character cell origin 1826 aActPosDelta.setX( pDXArry[ nLen - 1 ] ); 1827 if ( pDYArry ) 1828 { 1829 aActPosDelta.setY( pDYArry[ nLen - 1 ] ); 1830 } 1831 } 1832 else 1833 { 1834 nTextWidth = pVDev->GetTextWidth( rText ); 1835 aActPosDelta.setX( nTextWidth ); 1836 } 1837 1838 if( mnTextAlign & TA_UPDATECP ) 1839 rPosition = maActPos; 1840 1841 if ( mnTextAlign & TA_RIGHT_CENTER ) 1842 { 1843 Point aDisplacement( ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1, 0 ); 1844 Point().RotateAround(aDisplacement, maFont.GetOrientation()); 1845 rPosition -= aDisplacement; 1846 } 1847 1848 if( mnTextAlign & TA_UPDATECP ) 1849 { 1850 Point().RotateAround(aActPosDelta, maFont.GetOrientation()); 1851 maActPos = rPosition + aActPosDelta; 1852 } 1853 } 1854 1855 if(bChangeFont || (maLatestFont != aTmp)) 1856 { 1857 maLatestFont = aTmp; 1858 rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp)); 1859 1860 // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector 1861 maScaledFontHelper.endCurrentMetaFontAction(); 1862 1863 // !bRecordPath: else no MetaTextArrayAction will be created 1864 // nullptr != pDXArry: detection only possible when text size is given 1865 // rText.getLength(): no useful check without text 1866 if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength()) 1867 { 1868 maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction); 1869 } 1870 1871 mpGDIMetaFile->AddAction( aNewMetaFontAction ); 1872 mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) ); 1873 mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) ); 1874 mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) ); 1875 } 1876 1877 if ( bRecordPath ) 1878 { 1879 // TODO 1880 } 1881 else 1882 { 1883 if ( pDXArry && pDYArry ) 1884 { 1885 for (sal_Int32 i = 0; i < rText.getLength(); ++i) 1886 { 1887 Point aCharDisplacement( i ? pDXArry[i-1] : 0, i ? pDYArry[i-1] : 0 ); 1888 Point().RotateAround(aCharDisplacement, maFont.GetOrientation()); 1889 mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), nullptr, 0, 1 ) ); 1890 } 1891 } 1892 else 1893 { 1894 /* because text without dx array is badly scaled, we 1895 will create such an array if necessary */ 1896 tools::Long* pDX = pDXArry; 1897 if (pDXArry) 1898 { 1899 // only useful when we have an imported DXArray 1900 if(!rText.isEmpty()) 1901 { 1902 maScaledFontHelper.evaluateAlternativeFontScale( 1903 rText, 1904 pDXArry[rText.getLength() - 1] // extract imported TextLength 1905 ); 1906 } 1907 } 1908 else 1909 { 1910 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading 1911 SolarMutexGuard aGuard; 1912 ScopedVclPtrInstance< VirtualDevice > pVDev; 1913 pDX = new tools::Long[ rText.getLength() ]; 1914 pVDev->SetMapMode(MapMode(MapUnit::Map100thMM)); 1915 pVDev->SetFont( maLatestFont ); 1916 pVDev->GetTextArray( rText, pDX, 0, rText.getLength()); 1917 } 1918 mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) ); 1919 if ( !pDXArry ) // this means we have created our own array 1920 delete[] pDX; // which must be deleted 1921 } 1922 } 1923 SetGfxMode( nOldGfxMode ); 1924 } 1925 ImplDrawBitmap(const Point & rPos,const Size & rSize,const BitmapEx & rBitmap)1926 void MtfTools::ImplDrawBitmap( const Point& rPos, const Size& rSize, const BitmapEx& rBitmap ) 1927 { 1928 BitmapEx aBmpEx( rBitmap ); 1929 if ( mbComplexClip ) 1930 { 1931 vcl::bitmap::DrawAndClipBitmap(rPos, rSize, rBitmap, aBmpEx, maClipPath.getClipPath()); 1932 } 1933 1934 if ( aBmpEx.IsAlpha() ) 1935 mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmpEx ) ); 1936 else 1937 mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmpEx.GetBitmap() ) ); 1938 } 1939 ResolveBitmapActions(std::vector<std::unique_ptr<BSaveStruct>> & rSaveList)1940 void MtfTools::ResolveBitmapActions( std::vector<std::unique_ptr<BSaveStruct>>& rSaveList ) 1941 { 1942 UpdateClipRegion(); 1943 1944 size_t nObjects = rSaveList.size(); 1945 size_t nObjectsLeft = nObjects; 1946 1947 while ( nObjectsLeft ) 1948 { 1949 size_t i; 1950 size_t nObjectsOfSameSize = 0; 1951 size_t nObjectStartIndex = nObjects - nObjectsLeft; 1952 1953 BSaveStruct* pSave = rSaveList[nObjectStartIndex].get(); 1954 tools::Rectangle aRect( pSave->aOutRect ); 1955 1956 for ( i = nObjectStartIndex; i < nObjects; ) 1957 { 1958 nObjectsOfSameSize++; 1959 if ( ++i < nObjects ) 1960 { 1961 pSave = rSaveList[i].get(); 1962 if ( pSave->aOutRect != aRect ) 1963 break; 1964 } 1965 } 1966 Point aPos( ImplMap( aRect.TopLeft() ) ); 1967 Size aSize( ImplMap( aRect.GetSize() ) ); 1968 1969 for ( i = nObjectStartIndex; i < ( nObjectStartIndex + nObjectsOfSameSize ); i++ ) 1970 { 1971 pSave = rSaveList[i].get(); 1972 1973 sal_uInt32 nWinRop = pSave->nWinRop; 1974 sal_uInt8 nRasterOperation = static_cast<sal_uInt8>( nWinRop >> 16 ); 1975 1976 sal_uInt32 nUsed = 0; 1977 if ( ( nRasterOperation & 0xf ) != ( nRasterOperation >> 4 ) ) 1978 nUsed |= 1; // pattern is used 1979 if ( ( nRasterOperation & 0x33 ) != ( ( nRasterOperation & 0xcc ) >> 2 ) ) 1980 nUsed |= 2; // source is used 1981 if ( ( nRasterOperation & 0xaa ) != ( ( nRasterOperation & 0x55 ) << 1 ) ) 1982 nUsed |= 4; // destination is used 1983 1984 if ( (nUsed & 1) && (( nUsed & 2 ) == 0) && nWinRop != PATINVERT ) 1985 { // patterns aren't well supported yet 1986 WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::NONE ); // in this case nRasterOperation is either 0 or 0xff 1987 UpdateFillStyle(); 1988 DrawRect( aRect, false ); 1989 SetRasterOp( nOldRop ); 1990 } 1991 else 1992 { 1993 bool bDrawn = false; 1994 1995 if ( i == nObjectStartIndex ) // optimizing, sometimes it is possible to create just one transparent bitmap 1996 { 1997 if ( nObjectsOfSameSize == 2 ) 1998 { 1999 BSaveStruct* pSave2 = rSaveList[i + 1].get(); 2000 if ( ( pSave->aBmpEx.GetPrefSize() == pSave2->aBmpEx.GetPrefSize() ) && 2001 ( pSave->aBmpEx.GetPrefMapMode() == pSave2->aBmpEx.GetPrefMapMode() ) ) 2002 { 2003 // TODO: Strictly speaking, we should 2004 // check whether mask is monochrome, and 2005 // whether image is black (upper branch) 2006 // or white (lower branch). Otherwise, the 2007 // effect is not the same as a masked 2008 // bitmap. 2009 if ( ( nWinRop == SRCPAINT ) && ( pSave2->nWinRop == SRCAND ) ) 2010 { 2011 Bitmap aMask( pSave->aBmpEx.GetBitmap() ); aMask.Invert(); 2012 BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), aMask ); 2013 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2014 bDrawn = true; 2015 i++; 2016 } 2017 // #i20085# This is just the other way 2018 // around as above. Only difference: mask 2019 // is inverted 2020 else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCPAINT ) ) 2021 { 2022 const Bitmap & rMask( pSave->aBmpEx.GetBitmap() ); 2023 BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), rMask ); 2024 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2025 bDrawn = true; 2026 i++; 2027 } 2028 // tdf#90539 2029 else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCINVERT ) ) 2030 { 2031 const Bitmap & rMask( pSave->aBmpEx.GetBitmap() ); 2032 BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), rMask ); 2033 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2034 bDrawn = true; 2035 i++; 2036 } 2037 } 2038 } 2039 } 2040 2041 if ( !bDrawn ) 2042 { 2043 Push(); 2044 WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::CopyPen ); 2045 Bitmap aBitmap( pSave->aBmpEx.GetBitmap() ); 2046 sal_uInt32 nOperation = ( nRasterOperation & 0xf ); 2047 switch( nOperation ) 2048 { 2049 case 0x1 : 2050 case 0xe : 2051 { 2052 if(pSave->aBmpEx.IsAlpha()) 2053 { 2054 ImplDrawBitmap( aPos, aSize, pSave->aBmpEx ); 2055 } 2056 else 2057 { 2058 SetRasterOp( WMFRasterOp::XorPen ); 2059 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) ); 2060 SetRasterOp( WMFRasterOp::CopyPen ); 2061 Bitmap aMask( aBitmap ); 2062 aMask.Invert(); 2063 BitmapEx aBmpEx( aBitmap, aMask ); 2064 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2065 if ( nOperation == 0x1 ) 2066 { 2067 SetRasterOp( WMFRasterOp::Not ); 2068 DrawRect( aRect, false ); 2069 } 2070 } 2071 } 2072 break; 2073 case 0x7 : 2074 case 0x8 : 2075 { 2076 Bitmap aMask( aBitmap ); 2077 if ( ( nUsed & 1 ) && ( nRasterOperation & 0xb0 ) == 0xb0 ) // pattern used 2078 { 2079 aBitmap.Convert( BmpConversion::N24Bit ); 2080 aBitmap.Erase( maFillStyle.aFillColor ); 2081 } 2082 BitmapEx aBmpEx( aBitmap, aMask ); 2083 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2084 if ( nOperation == 0x7 ) 2085 { 2086 SetRasterOp( WMFRasterOp::Not ); 2087 DrawRect( aRect, false ); 2088 } 2089 } 2090 break; 2091 2092 case 0x4 : 2093 case 0xb : 2094 { 2095 SetRasterOp( WMFRasterOp::Not ); 2096 DrawRect( aRect, false ); 2097 SetRasterOp( WMFRasterOp::CopyPen ); 2098 Bitmap aMask( aBitmap ); 2099 aBitmap.Invert(); 2100 BitmapEx aBmpEx( aBitmap, aMask ); 2101 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2102 SetRasterOp( WMFRasterOp::XorPen ); 2103 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) ); 2104 if ( nOperation == 0xb ) 2105 { 2106 SetRasterOp( WMFRasterOp::Not ); 2107 DrawRect( aRect, false ); 2108 } 2109 } 2110 break; 2111 2112 case 0x2 : 2113 case 0xd : 2114 { 2115 Bitmap aMask( aBitmap ); 2116 aMask.Invert(); 2117 BitmapEx aBmpEx( aBitmap, aMask ); 2118 ImplDrawBitmap( aPos, aSize, aBmpEx ); 2119 SetRasterOp( WMFRasterOp::XorPen ); 2120 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) ); 2121 if ( nOperation == 0xd ) 2122 { 2123 SetRasterOp( WMFRasterOp::Not ); 2124 DrawRect( aRect, false ); 2125 } 2126 } 2127 break; 2128 case 0x6 : 2129 case 0x9 : 2130 { 2131 SetRasterOp( WMFRasterOp::XorPen ); 2132 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) ); 2133 if ( nOperation == 0x9 ) 2134 { 2135 SetRasterOp( WMFRasterOp::Not ); 2136 DrawRect( aRect, false ); 2137 } 2138 } 2139 break; 2140 2141 case 0x0 : // WHITENESS 2142 case 0xf : // BLACKNESS 2143 { // in this case nRasterOperation is either 0 or 0xff 2144 maFillStyle = WinMtfFillStyle( Color( nRasterOperation, nRasterOperation, nRasterOperation ) ); 2145 UpdateFillStyle(); 2146 DrawRect( aRect, false ); 2147 } 2148 break; 2149 2150 case 0x3 : // only source is used 2151 case 0xc : 2152 { 2153 if ( nRasterOperation == 0x33 ) 2154 aBitmap.Invert(); 2155 if (pSave->m_bForceAlpha) 2156 { 2157 ImplDrawBitmap(aPos, aSize, pSave->aBmpEx); 2158 } 2159 else 2160 { 2161 ImplDrawBitmap(aPos, aSize, BitmapEx(aBitmap)); 2162 } 2163 } 2164 break; 2165 2166 case 0x5 : // only destination is used 2167 { 2168 SetRasterOp( WMFRasterOp::Not ); 2169 DrawRect( aRect, false ); 2170 } 2171 break; 2172 2173 case 0xa : // no operation 2174 break; 2175 } 2176 SetRasterOp( nOldRop ); 2177 Pop(); 2178 } 2179 } 2180 } 2181 nObjectsLeft -= nObjectsOfSameSize; 2182 } 2183 2184 rSaveList.clear(); 2185 } 2186 SetDevOrg(const Point & rPoint)2187 void MtfTools::SetDevOrg( const Point& rPoint ) 2188 { 2189 mnDevOrgX = rPoint.X(); 2190 mnDevOrgY = rPoint.Y(); 2191 } 2192 SetDevOrgOffset(sal_Int32 nXAdd,sal_Int32 nYAdd)2193 void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd ) 2194 { 2195 mnDevOrgX += nXAdd; 2196 mnDevOrgY += nYAdd; 2197 } 2198 SetDevExt(const Size & rSize,bool regular)2199 void MtfTools::SetDevExt( const Size& rSize ,bool regular) 2200 { 2201 if ( !(rSize.Width() && rSize.Height()) ) 2202 return; 2203 2204 switch( mnMapMode ) 2205 { 2206 case MM_ISOTROPIC : 2207 case MM_ANISOTROPIC : 2208 { 2209 mnDevWidth = rSize.Width(); 2210 mnDevHeight = rSize.Height(); 2211 } 2212 } 2213 if (regular) 2214 { 2215 mbIsMapDevSet=true; 2216 } 2217 } 2218 ScaleDevExt(double fX,double fY)2219 void MtfTools::ScaleDevExt(double fX, double fY) 2220 { 2221 mnDevWidth = basegfx::fround(mnDevWidth * fX); 2222 mnDevHeight = basegfx::fround(mnDevHeight * fY); 2223 } 2224 SetWinOrg(const Point & rPoint,bool bIsEMF)2225 void MtfTools::SetWinOrg( const Point& rPoint , bool bIsEMF) 2226 { 2227 mnWinOrgX = rPoint.X(); 2228 mnWinOrgY = rPoint.Y(); 2229 if (bIsEMF) 2230 { 2231 SetDevByWin(); 2232 } 2233 mbIsMapWinSet=true; 2234 } 2235 SetWinOrgOffset(sal_Int32 nXAdd,sal_Int32 nYAdd)2236 void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd ) 2237 { 2238 mnWinOrgX += nXAdd; 2239 mnWinOrgY += nYAdd; 2240 } 2241 SetDevByWin()2242 void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before. 2243 { 2244 if (!mbIsMapDevSet) 2245 { 2246 if ( mnMapMode == MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC??? 2247 { 2248 sal_Int32 nX, nY; 2249 if (o3tl::checked_add(mnWinExtX, mnWinOrgX, nX) || o3tl::checked_sub(mnWinExtY, mnWinOrgY, nY)) 2250 return; 2251 Size aSize(nX >> MS_FIXPOINT_BITCOUNT_28_4, -(nY >> MS_FIXPOINT_BITCOUNT_28_4)); 2252 SetDevExt(aSize, false); 2253 } 2254 } 2255 } 2256 SetWinExt(const Size & rSize,bool bIsEMF)2257 void MtfTools::SetWinExt(const Size& rSize, bool bIsEMF) 2258 { 2259 if (!(rSize.Width() && rSize.Height())) 2260 return; 2261 2262 switch( mnMapMode ) 2263 { 2264 case MM_ISOTROPIC : 2265 case MM_ANISOTROPIC : 2266 { 2267 mnWinExtX = rSize.Width(); 2268 mnWinExtY = rSize.Height(); 2269 if (bIsEMF) 2270 { 2271 SetDevByWin(); 2272 } 2273 mbIsMapWinSet = true; 2274 } 2275 } 2276 } 2277 ScaleWinExt(double fX,double fY)2278 void MtfTools::ScaleWinExt(double fX, double fY) 2279 { 2280 mnWinExtX = basegfx::fround(mnWinExtX * fX); 2281 mnWinExtY = basegfx::fround(mnWinExtY * fY); 2282 } 2283 SetrclBounds(const tools::Rectangle & rRect)2284 void MtfTools::SetrclBounds( const tools::Rectangle& rRect ) 2285 { 2286 mrclBounds = rRect; 2287 } 2288 SetrclFrame(const tools::Rectangle & rRect)2289 void MtfTools::SetrclFrame( const tools::Rectangle& rRect ) 2290 { 2291 mrclFrame = rRect; 2292 } 2293 SetRefPix(const Size & rSize)2294 void MtfTools::SetRefPix( const Size& rSize ) 2295 { 2296 mnPixX = rSize.Width(); 2297 mnPixY = rSize.Height(); 2298 } 2299 SetRefMill(const Size & rSize)2300 void MtfTools::SetRefMill( const Size& rSize ) 2301 { 2302 mnMillX = rSize.Width(); 2303 mnMillY = rSize.Height(); 2304 } 2305 SetMapMode(sal_uInt32 nMapMode)2306 void MtfTools::SetMapMode( sal_uInt32 nMapMode ) 2307 { 2308 mnMapMode = nMapMode; 2309 if ( nMapMode == MM_TEXT && !mbIsMapWinSet ) 2310 { 2311 mnWinExtX = mnDevWidth; 2312 mnWinExtY = mnDevHeight; 2313 } 2314 else if ( mnMapMode == MM_HIMETRIC ) 2315 { 2316 sal_Int32 nWinExtX, nWinExtY; 2317 if (o3tl::checked_multiply<sal_Int32>(mnMillX, 100, nWinExtX) || 2318 o3tl::checked_multiply<sal_Int32>(mnMillY, 100, nWinExtY)) 2319 { 2320 return; 2321 } 2322 mnWinExtX = nWinExtX; 2323 mnWinExtY = nWinExtY; 2324 } 2325 } 2326 SetWorldTransform(const XForm & rXForm)2327 void MtfTools::SetWorldTransform( const XForm& rXForm ) 2328 { 2329 maXForm.eM11 = rXForm.eM11; 2330 maXForm.eM12 = rXForm.eM12; 2331 maXForm.eM21 = rXForm.eM21; 2332 maXForm.eM22 = rXForm.eM22; 2333 maXForm.eDx = rXForm.eDx; 2334 maXForm.eDy = rXForm.eDy; 2335 } 2336 ModifyWorldTransform(const XForm & rXForm,sal_uInt32 nMode)2337 void MtfTools::ModifyWorldTransform( const XForm& rXForm, sal_uInt32 nMode ) 2338 { 2339 switch( nMode ) 2340 { 2341 case MWT_IDENTITY : 2342 { 2343 maXForm.eM11 = maXForm.eM22 = 1.0f; 2344 maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f; 2345 break; 2346 } 2347 2348 case MWT_RIGHTMULTIPLY : 2349 case MWT_LEFTMULTIPLY : 2350 { 2351 const XForm* pLeft; 2352 const XForm* pRight; 2353 2354 if ( nMode == MWT_LEFTMULTIPLY ) 2355 { 2356 pLeft = &rXForm; 2357 pRight = &maXForm; 2358 } 2359 else 2360 { 2361 pLeft = &maXForm; 2362 pRight = &rXForm; 2363 } 2364 2365 float aF[3][3]; 2366 float bF[3][3]; 2367 float cF[3][3]; 2368 2369 aF[0][0] = pLeft->eM11; 2370 aF[0][1] = pLeft->eM12; 2371 aF[0][2] = 0; 2372 aF[1][0] = pLeft->eM21; 2373 aF[1][1] = pLeft->eM22; 2374 aF[1][2] = 0; 2375 aF[2][0] = pLeft->eDx; 2376 aF[2][1] = pLeft->eDy; 2377 aF[2][2] = 1; 2378 2379 bF[0][0] = pRight->eM11; 2380 bF[0][1] = pRight->eM12; 2381 bF[0][2] = 0; 2382 bF[1][0] = pRight->eM21; 2383 bF[1][1] = pRight->eM22; 2384 bF[1][2] = 0; 2385 bF[2][0] = pRight->eDx; 2386 bF[2][1] = pRight->eDy; 2387 bF[2][2] = 1; 2388 2389 int i, j, k; 2390 for ( i = 0; i < 3; i++ ) 2391 { 2392 for ( j = 0; j < 3; j++ ) 2393 { 2394 cF[i][j] = 0; 2395 for ( k = 0; k < 3; k++ ) 2396 cF[i][j] += aF[i][k] * bF[k][j]; 2397 } 2398 } 2399 maXForm.eM11 = cF[0][0]; 2400 maXForm.eM12 = cF[0][1]; 2401 maXForm.eM21 = cF[1][0]; 2402 maXForm.eM22 = cF[1][1]; 2403 maXForm.eDx = cF[2][0]; 2404 maXForm.eDy = cF[2][1]; 2405 break; 2406 } 2407 case MWT_SET: 2408 { 2409 SetWorldTransform(rXForm); 2410 break; 2411 } 2412 } 2413 } 2414 Push()2415 void MtfTools::Push() // !! to be able to access the original ClipRegion it 2416 { // is not allowed to use the MetaPushAction() 2417 UpdateClipRegion(); // (the original clip region is on top of the stack) (SJ) 2418 auto pSave = std::make_shared<SaveStruct>(); 2419 2420 pSave->aLineStyle = maLineStyle; 2421 pSave->aFillStyle = maFillStyle; 2422 2423 pSave->aFont = maFont; 2424 pSave->aTextColor = maTextColor; 2425 pSave->nTextAlign = mnTextAlign; 2426 pSave->nTextLayoutMode = mnTextLayoutMode; 2427 pSave->nMapMode = mnMapMode; 2428 pSave->nGfxMode = mnGfxMode; 2429 pSave->nBkMode = mnBkMode; 2430 pSave->aBkColor = maBkColor; 2431 pSave->bFillStyleSelected = mbFillStyleSelected; 2432 2433 pSave->aActPos = maActPos; 2434 pSave->aXForm = maXForm; 2435 pSave->eRasterOp = meRasterOp; 2436 2437 pSave->nWinOrgX = mnWinOrgX; 2438 pSave->nWinOrgY = mnWinOrgY; 2439 pSave->nWinExtX = mnWinExtX; 2440 pSave->nWinExtY = mnWinExtY; 2441 pSave->nDevOrgX = mnDevOrgX; 2442 pSave->nDevOrgY = mnDevOrgY; 2443 pSave->nDevWidth = mnDevWidth; 2444 pSave->nDevHeight = mnDevHeight; 2445 2446 pSave->maPathObj = maPathObj; 2447 pSave->maClipPath = maClipPath; 2448 2449 SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode); 2450 SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode); 2451 SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY); 2452 SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY); 2453 SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY); 2454 SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight); 2455 SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor ); 2456 mvSaveStack.push_back( pSave ); 2457 } 2458 Pop(const sal_Int32 nSavedDC)2459 void MtfTools::Pop( const sal_Int32 nSavedDC ) 2460 { 2461 if ( nSavedDC == 0 ) 2462 return; 2463 2464 sal_Int32 aIndex; 2465 if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state. 2466 aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC; 2467 else 2468 aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored. 2469 if( aIndex < 0 ) 2470 { 2471 mvSaveStack.clear(); 2472 return; 2473 } 2474 if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) ) 2475 return; 2476 2477 mvSaveStack.resize( aIndex + 1 ); 2478 // Backup the current data on the stack 2479 std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() ); 2480 2481 maLineStyle = pSave->aLineStyle; 2482 maFillStyle = pSave->aFillStyle; 2483 2484 maFont = pSave->aFont; 2485 maTextColor = pSave->aTextColor; 2486 mnTextAlign = pSave->nTextAlign; 2487 mnTextLayoutMode = pSave->nTextLayoutMode; 2488 mnBkMode = pSave->nBkMode; 2489 mnGfxMode = pSave->nGfxMode; 2490 mnMapMode = pSave->nMapMode; 2491 maBkColor = pSave->aBkColor; 2492 mbFillStyleSelected = pSave->bFillStyleSelected; 2493 2494 maActPos = pSave->aActPos; 2495 maXForm = pSave->aXForm; 2496 meRasterOp = pSave->eRasterOp; 2497 2498 mnWinOrgX = pSave->nWinOrgX; 2499 mnWinOrgY = pSave->nWinOrgY; 2500 mnWinExtX = pSave->nWinExtX; 2501 mnWinExtY = pSave->nWinExtY; 2502 mnDevOrgX = pSave->nDevOrgX; 2503 mnDevOrgY = pSave->nDevOrgY; 2504 mnDevWidth = pSave->nDevWidth; 2505 mnDevHeight = pSave->nDevHeight; 2506 2507 maPathObj = pSave->maPathObj; 2508 if ( ! ( maClipPath == pSave->maClipPath ) ) 2509 { 2510 maClipPath = pSave->maClipPath; 2511 mbClipNeedsUpdate = true; 2512 } 2513 if ( meLatestRasterOp != meRasterOp ) 2514 { 2515 mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) ); 2516 meLatestRasterOp = meRasterOp; 2517 } 2518 2519 SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode); 2520 SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode); 2521 SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY); 2522 SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY); 2523 SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY); 2524 SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight); 2525 SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor ); 2526 mvSaveStack.pop_back(); 2527 } 2528 AddFromGDIMetaFile(GDIMetaFile & rGDIMetaFile)2529 void MtfTools::AddFromGDIMetaFile( GDIMetaFile& rGDIMetaFile ) 2530 { 2531 rGDIMetaFile.Play( *mpGDIMetaFile ); 2532 } 2533 PassEMFPlusHeaderInfo()2534 void MtfTools::PassEMFPlusHeaderInfo() 2535 { 2536 EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n")); 2537 2538 SvMemoryStream mem; 2539 sal_Int32 nLeft, nRight, nTop, nBottom; 2540 2541 nLeft = mrclFrame.Left(); 2542 nTop = mrclFrame.Top(); 2543 nRight = mrclFrame.Right(); 2544 nBottom = mrclFrame.Bottom(); 2545 2546 // emf header info 2547 mem.WriteInt32( nLeft ).WriteInt32( nTop ).WriteInt32( nRight ).WriteInt32( nBottom ); 2548 mem.WriteInt32( mnPixX ).WriteInt32( mnPixY ).WriteInt32( mnMillX ).WriteInt32( mnMillY ); 2549 2550 float one, zero; 2551 2552 one = 1; 2553 zero = 0; 2554 2555 // add transformation matrix to be used in vcl's metaact.cxx for 2556 // rotate and scale operations 2557 mem.WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ).WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ); 2558 2559 // need to flush the stream, otherwise GetEndOfData will return 0 2560 // on windows where the function parameters are probably resolved in reverse order 2561 mem.Flush(); 2562 2563 mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO", 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) ); 2564 mpGDIMetaFile->UseCanvas( true ); 2565 } 2566 PassEMFPlus(void const * pBuffer,sal_uInt32 nLength)2567 void MtfTools::PassEMFPlus( void const * pBuffer, sal_uInt32 nLength ) 2568 { 2569 EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength)); 2570 mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS", 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) ); 2571 } 2572 } 2573 2574 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2575