diff options
Diffstat (limited to 'emfio/source/reader/mtftools.cxx')
-rw-r--r-- | emfio/source/reader/mtftools.cxx | 822 |
1 files changed, 544 insertions, 278 deletions
diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx index b05beeb2a9dc..5e1b36c861c2 100644 --- a/emfio/source/reader/mtftools.cxx +++ b/emfio/source/reader/mtftools.cxx @@ -35,7 +35,7 @@ #include <osl/diagnose.h> #include <vcl/virdev.hxx> #include <o3tl/safeint.hxx> -#include <unotools/configmgr.hxx> +#include <comphelper/configuration.hxx> #include <unotools/defaultencoding.hxx> #include <unotools/wincodepage.hxx> @@ -62,38 +62,47 @@ namespace emfio rInStream.ReadFloat(rXForm.eM22); rInStream.ReadFloat(rXForm.eDx); rInStream.ReadFloat(rXForm.eDy); + if (std::isnan(rXForm.eM11) || + std::isnan(rXForm.eM12) || + std::isnan(rXForm.eM21) || + std::isnan(rXForm.eM22) || + std::isnan(rXForm.eDx) || + std::isnan(rXForm.eDy)) + { + SAL_WARN("emfio", "XForm member isnan, ignoring"); + rXForm = XForm(); + } } return rInStream; } - void WinMtfClipPath::intersectClipRect( const tools::Rectangle& rRect ) + void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon& rPolyPolygon ) { - maClip.intersectRange(vcl::unotools::b2DRectangleFromRectangle(rRect)); + maClip.intersectPolyPolygon(rPolyPolygon); } - void WinMtfClipPath::excludeClipRect( const tools::Rectangle& rRect ) + void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon& rPolyPolygon ) { - maClip.subtractRange(vcl::unotools::b2DRectangleFromRectangle(rRect)); + maClip.subtractPolyPolygon(rPolyPolygon); } - void WinMtfClipPath::setClipPath( const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode ) + void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon& rB2DPoly, RegionMode nClippingMode ) { - const basegfx::B2DPolyPolygon& rB2DPoly=rPolyPolygon.getB2DPolyPolygon(); switch ( nClippingMode ) { - case RGN_OR : + case RegionMode::RGN_OR : maClip.unionPolyPolygon(rB2DPoly); break; - case RGN_XOR : + case RegionMode::RGN_XOR : maClip.xorPolyPolygon(rB2DPoly); break; - case RGN_DIFF : + case RegionMode::RGN_DIFF : maClip.subtractPolyPolygon(rB2DPoly); break; - case RGN_AND : + case RegionMode::RGN_AND : maClip.intersectPolyPolygon(rB2DPoly); break; - case RGN_COPY : + case RegionMode::RGN_COPY : maClip = basegfx::utils::B2DClipState(rB2DPoly); break; } @@ -180,25 +189,25 @@ namespace emfio aFont.SetCharSet( eCharSet ); aFont.SetFamilyName( rFont.alfFaceName ); FontFamily eFamily; - switch ( rFont.lfPitchAndFamily & 0xf0 ) + switch ( rFont.lfPitchAndFamily >> 4 & 0x0f ) { - case FF_ROMAN: + case FamilyFont::FF_ROMAN: eFamily = FAMILY_ROMAN; break; - case FF_SWISS: + case FamilyFont::FF_SWISS: eFamily = FAMILY_SWISS; break; - case FF_MODERN: + case FamilyFont::FF_MODERN: eFamily = FAMILY_MODERN; break; - case FF_SCRIPT: + case FamilyFont::FF_SCRIPT: eFamily = FAMILY_SCRIPT; break; - case FF_DECORATIVE: + case FamilyFont::FF_DECORATIVE: eFamily = FAMILY_DECORATIVE; break; @@ -257,7 +266,7 @@ namespace emfio aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rFont.lfEscapement)) ); - Size aFontSize( Size( rFont.lfWidth, rFont.lfHeight ) ); + Size aFontSize( rFont.lfWidth, rFont.lfHeight ); if ( rFont.lfHeight > 0 ) { // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading @@ -277,19 +286,225 @@ namespace emfio // Convert height to positive aFontSize.setHeight( std::abs(aFontSize.Height()) ); - aFont.SetFontSize(aFontSize); + + // tdf#127471 adapt nFontWidth from Windows-like notation to + // NormedFontScaling if used for text scaling +#ifndef _WIN32 + const bool bFontScaledHorizontally(aFontSize.Width() != 0 && aFontSize.Width() != aFontSize.Height()); + + if(bFontScaledHorizontally) + { + // tdf#127471 nFontWidth is the Windows FontScaling, need to convert to + // Non-Windowslike notation relative to FontHeight. + const tools::Long nAverageFontWidth(aFont.GetOrCalculateAverageFontWidth()); + + if(nAverageFontWidth > 0) + { + const double fScaleFactor(static_cast<double>(aFontSize.Height()) / static_cast<double>(nAverageFontWidth)); + aFont.SetAverageFontWidth(static_cast<tools::Long>(static_cast<double>(aFontSize.Width()) * fScaleFactor)); + } + } +#endif }; - Color MtfTools::ReadColor() + // tdf#127471 + ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper() + { + } + + void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction() + { + if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty()) + { + // create average corrected FontScale value and count + // positive/negative hits + sal_uInt32 nPositive(0); + sal_uInt32 nNegative(0); + double fAverage(0.0); + + for(double fPart : maAlternativeFontScales) + { + if(fPart < 0.0) + { + nNegative++; + fAverage += -fPart; + } + else + { + nPositive++; + fAverage += fPart; + } + } + + fAverage /= static_cast<double>(maAlternativeFontScales.size()); + + if(nPositive >= nNegative) + { + // correction intended, it is probably an old imported file + maPositiveIdentifiedCases.emplace_back(maCurrentMetaFontAction, fAverage); + } + else + { + // correction not favorable in the majority of cases for this Font, still + // remember to have a weight in the last decision for correction + maNegativeIdentifiedCases.emplace_back(maCurrentMetaFontAction, fAverage); + } + } + + maCurrentMetaFontAction.clear(); + maAlternativeFontScales.clear(); + } + + void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(const rtl::Reference<MetaFontAction>& rNewMetaFontAction) + { + maCurrentMetaFontAction.clear(); + maAlternativeFontScales.clear(); + + if(!rNewMetaFontAction.is()) + return; + + // check 1st criteria for FontScale active. We usually write this, + // so this will already sort out most situations + const vcl::Font& rCandidate(rNewMetaFontAction->GetFont()); + + if(0 != rCandidate.GetAverageFontWidth()) + { + const tools::Long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth()); + + // check 2nd (system-dependent) criteria for FontScale + if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight()) + { + // FontScale is active, remember and use as current + maCurrentMetaFontAction = rNewMetaFontAction; + } + } + } + + void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, tools::Long nImportedTextLength) + { + if(!maCurrentMetaFontAction.is()) + return; + + SolarMutexGuard aGuard; // VirtualDevice is not thread-safe + ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice; + + // calculate measured TextLength + const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont()); + pTempVirtualDevice->SetFont(rFontCandidate); + tools::Long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText)); + // on failure, use original length + if (!nMeasuredTextLength) + nMeasuredTextLength = nImportedTextLength; + + // compare expected and imported TextLengths + if (nImportedTextLength == nMeasuredTextLength) + return; + + const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength)); + const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0); + + // if we assume that loaded file was written on old linux, we have to + // back-convert the scale value depending on which system we run +#ifdef _WIN32 + // When running on Windows the value was not adapted at font import (see WinMtfFontStyle + // constructor), so it is still NormedFontScaling and we need to convert to Windows-style + // scaling +#else + // When running on unx (non-Windows) the value was already adapted at font import (see WinMtfFontStyle + // constructor). It was wrongly assumed to be Windows-style FontScaling, so we need to revert that + // to get back to the needed unx-style FontScale +#endif + // Interestingly this leads to the *same* correction, so no need to make this + // system-dependent (!) + const tools::Long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth()); + const tools::Long nScaledAverageFontWidth(rFontCandidate.GetAverageFontWidth()); + const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight())); + const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor); + tools::Long nCorrectedTextLength(0); + + { // do in own scope, only need nUnscaledAverageFontWidth + vcl::Font rFontCandidate2(rFontCandidate); + rFontCandidate2.SetAverageFontWidth(static_cast<tools::Long>(fCorrectedAverageFontWidth)); + pTempVirtualDevice->SetFont(rFontCandidate2); + nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText); + // on failure, use original length + if (!nCorrectedTextLength) + nCorrectedTextLength = nImportedTextLength; + } + + const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength)); + const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0); + + // If FactorCorrectedText fits better than FactorText this is probably + // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system + // and should be corrected. + // Usually in tested cases this lies inside 5% of range, so detecting this just using + // fFactorTextPercent inside 5% -> no old file + // fFactorCorrectedTextPercent inside 5% -> is old file + // works not too bad, but there are some strange not so often used fonts where that + // values do deviate, so better just compare if old corrected would fit better than + // the uncorrected case, that is usually safe. + if(fFactorCorrectedTextPercent < fFactorTextPercent) + { + maAlternativeFontScales.push_back(fCorrectedAverageFontWidth); + } + else + { + // also push, but negative to remember non-fitting case + maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth); + } + } + + void ScaledFontDetectCorrectHelper::applyAlternativeFontScale() { - sal_uInt32 nColor; + // make sure last evtl. detected current FontAction gets added to identified cases + endCurrentMetaFontAction(); + // Take final decision to correct FontScaling for this imported Metafile or not. + // It is possible to weight positive against negative cases, so to only finally + // correct when more positive cases were detected. + // But that would be inconsequent and wrong. *If* the detected case is an old import + // the whole file was written with wrong FontScale values and all Font actions + // need to be corrected. Thus, for now, correct all when there are/is positive + // cases detected. + // On the other hand it *may* be that for some strange fonts there is a false-positive + // in the positive cases, so at least insist on positive cases being more than negative. + // Still, do then correct *all* cases. + if(!maPositiveIdentifiedCases.empty() + && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size()) + { + for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases) + { + rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second)); + } + for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases) + { + rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second)); + } + } + + maPositiveIdentifiedCases.clear(); + maNegativeIdentifiedCases.clear(); + } + + Color MtfTools::ReadColor() + { + sal_uInt32 nColor(0); mpInputStream->ReadUInt32( nColor ); - Color aColor(static_cast<sal_uInt8>(nColor), static_cast<sal_uInt8>(nColor >> 8), static_cast<sal_uInt8>(nColor >> 16)); + Color aColor( COL_BLACK ); + if ( ( nColor & 0xFFFF0000 ) == 0x01000000 ) + { + size_t index = nColor & 0x0000FFFF; + if ( index < maPalette.aPaletteColors.size() ) + aColor = maPalette.aPaletteColors[ index ]; + else + SAL_INFO( "emfio", "\t\t Palette index out of range: " << index ); + } + else + aColor = Color( static_cast<sal_uInt8>( nColor ), static_cast<sal_uInt8>( nColor >> 8 ), static_cast<sal_uInt8>( nColor >> 16 ) ); SAL_INFO("emfio", "\t\tColor: " << aColor); - return aColor; }; @@ -312,86 +527,40 @@ namespace emfio double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx; double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy; - if ( mnGfxMode == GM_COMPATIBLE ) + if ( meGfxMode == GraphicsMode::GM_COMPATIBLE ) { - switch( mnMapMode ) + fX2 -= mnWinOrgX; + fY2 -= mnWinOrgY; + + switch( meMapMode ) { - case MM_LOENGLISH : + case MappingMode::MM_LOENGLISH : { - fX2 -= mnWinOrgX; - fY2 = mnWinOrgY-fY2; - fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH * 10; - fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH * 10; - double nDevOrgX = mnDevOrgX; - if (mnPixX) - nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); - fX2 += nDevOrgX; - double nDevOrgY = mnDevOrgY; - if (mnPixY) - nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); - fY2 += nDevOrgY; + fX2 = o3tl::convert(fX2, o3tl::Length::in100, o3tl::Length::mm100); + fY2 = o3tl::convert(-fY2, o3tl::Length::in100, o3tl::Length::mm100); } break; - case MM_HIENGLISH : + case MappingMode::MM_HIENGLISH : { - fX2 -= mnWinOrgX; - fY2 = mnWinOrgY-fY2; - fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH; - fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH; - double nDevOrgX = mnDevOrgX; - if (mnPixX) - nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); - fX2 += nDevOrgX; - double nDevOrgY = mnDevOrgY; - if (mnPixY) - nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); - fY2 += nDevOrgY; + fX2 = o3tl::convert(fX2, o3tl::Length::in1000, o3tl::Length::mm100); + fY2 = o3tl::convert(-fY2, o3tl::Length::in1000, o3tl::Length::mm100); } break; - case MM_TWIPS: + case MappingMode::MM_TWIPS: { - fX2 -= mnWinOrgX; - fY2 = mnWinOrgY-fY2; - fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH / MILLIINCH_PER_TWIPS; - fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH / MILLIINCH_PER_TWIPS; - double nDevOrgX = mnDevOrgX; - if (mnPixX) - nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); - fX2 += nDevOrgX; - double nDevOrgY = mnDevOrgY; - if (mnPixY) - nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); - fY2 += nDevOrgY; + fX2 = o3tl::convert(fX2, o3tl::Length::twip, o3tl::Length::mm100); + fY2 = o3tl::convert(-fY2, o3tl::Length::twip, o3tl::Length::mm100); } break; - case MM_LOMETRIC : + case MappingMode::MM_LOMETRIC : { - fX2 -= mnWinOrgX; - fY2 = mnWinOrgY-fY2; - fX2 *= 10; - fY2 *= 10; - double nDevOrgX = mnDevOrgX; - if (mnPixX) - nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); - fX2 += nDevOrgX; - double nDevOrgY = mnDevOrgY; - if (mnPixY) - nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); - fY2 += nDevOrgY; + fX2 = o3tl::convert(fX2, o3tl::Length::mm10, o3tl::Length::mm100); + fY2 = o3tl::convert(-fY2, o3tl::Length::mm10, o3tl::Length::mm100); } break; - case MM_HIMETRIC : // in hundredth of a millimeter + case MappingMode::MM_HIMETRIC : // in hundredth of a millimeter { - fX2 -= mnWinOrgX; - fY2 = mnWinOrgY-fY2; - double nDevOrgX = mnDevOrgX; - if (mnPixX) - nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); - fX2 += nDevOrgX; - double nDevOrgY = mnDevOrgY; - if (mnPixY) - nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); - fY2 += nDevOrgY; + fY2 *= -1; } break; default : @@ -403,27 +572,33 @@ namespace emfio } else { - fX2 -= mnWinOrgX; - fY2 -= mnWinOrgY; - if ( mnMapMode != MM_TEXT ) + if ( meMapMode != MappingMode::MM_TEXT ) { fX2 /= mnWinExtX; fY2 /= mnWinExtY; fX2 *= mnDevWidth; fY2 *= mnDevHeight; } - fX2 += mnDevOrgX; - fY2 += mnDevOrgY; // fX2, fY2 now in device units fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); fY2 *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); } } break; } + + double nDevOrgX = mnDevOrgX; + if (mnPixX) + nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX); + fX2 += nDevOrgX; + double nDevOrgY = mnDevOrgY; + if (mnPixY) + nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY); + fY2 += nDevOrgY; + fX2 -= mrclFrame.Left(); fY2 -= mrclFrame.Top(); } - return Point(basegfx::fround(fX2), basegfx::fround(fY2)); + return Point(basegfx::fround<tools::Long>(fX2), basegfx::fround<tools::Long>(fY2)); } else return Point(); @@ -456,37 +631,37 @@ namespace emfio fHeight = rSz.Height() * aScale.getY(); } - if ( mnGfxMode == GM_COMPATIBLE ) + if ( meGfxMode == GraphicsMode::GM_COMPATIBLE ) { - switch( mnMapMode ) + switch( meMapMode ) { - case MM_LOENGLISH : + case MappingMode::MM_LOENGLISH : { - fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10; - fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10; + fWidth = o3tl::convert(fWidth, o3tl::Length::in100, o3tl::Length::mm100); + fHeight = o3tl::convert(-fHeight, o3tl::Length::in100, o3tl::Length::mm100); } break; - case MM_HIENGLISH : + case MappingMode::MM_HIENGLISH : { - fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH; - fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH; + fWidth = o3tl::convert(fWidth, o3tl::Length::in1000, o3tl::Length::mm100); + fHeight = o3tl::convert(-fHeight, o3tl::Length::in1000, o3tl::Length::mm100); } break; - case MM_LOMETRIC : + case MappingMode::MM_LOMETRIC : { - fWidth *= 10; - fHeight*=-10; + fWidth = o3tl::convert(fWidth, o3tl::Length::mm10, o3tl::Length::mm100); + fHeight = o3tl::convert(-fHeight, o3tl::Length::mm10, o3tl::Length::mm100); } break; - case MM_HIMETRIC : // in hundredth of millimeters + case MappingMode::MM_HIMETRIC : // in hundredth of millimeters { fHeight *= -1; } break; - case MM_TWIPS: + case MappingMode::MM_TWIPS: { - fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS; - fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS; + fWidth = o3tl::convert(fWidth, o3tl::Length::twip, o3tl::Length::mm100); + fHeight = o3tl::convert(-fHeight, o3tl::Length::twip, o3tl::Length::mm100); } break; default : @@ -498,7 +673,7 @@ namespace emfio } else { - if ( mnMapMode != MM_TEXT ) + if ( meMapMode != MappingMode::MM_TEXT ) { fWidth /= mnWinExtX; fHeight /= mnWinExtY; @@ -512,7 +687,7 @@ namespace emfio break; } } - return Size(basegfx::fround(fWidth), basegfx::fround(fHeight)); + return Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight)); } else return Size(); @@ -580,55 +755,61 @@ namespace emfio return rPolyPolygon; } - void MtfTools::SelectObject( sal_Int32 nIndex ) + void MtfTools::SelectObject( sal_uInt32 nIndex ) { if ( nIndex & ENHMETA_STOCK_OBJECT ) { - sal_uInt16 nStockId = static_cast<sal_uInt8>(nIndex); + SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex << nIndex ); + StockObject nStockId = static_cast<StockObject>(nIndex & 0xFF); switch( nStockId ) { - case WHITE_BRUSH : + case StockObject::WHITE_BRUSH : { maFillStyle = WinMtfFillStyle( COL_WHITE ); mbFillStyleSelected = true; } break; - case LTGRAY_BRUSH : + case StockObject::LTGRAY_BRUSH : { maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY ); mbFillStyleSelected = true; } break; - case GRAY_BRUSH : - case DKGRAY_BRUSH : + case StockObject::GRAY_BRUSH : { maFillStyle = WinMtfFillStyle( COL_GRAY ); mbFillStyleSelected = true; } break; - case BLACK_BRUSH : + case StockObject::DKGRAY_BRUSH : + { + maFillStyle = WinMtfFillStyle( COL_GRAY7 ); + mbFillStyleSelected = true; + } + break; + case StockObject::BLACK_BRUSH : { maFillStyle = WinMtfFillStyle( COL_BLACK ); mbFillStyleSelected = true; } break; - case NULL_BRUSH : + case StockObject::NULL_BRUSH : { maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true ); mbFillStyleSelected = true; } break; - case WHITE_PEN : + case StockObject::WHITE_PEN : { - maLineStyle = WinMtfLineStyle( COL_WHITE ); + maLineStyle = WinMtfLineStyle(COL_WHITE, PS_COSMETIC, 0); } break; - case BLACK_PEN : + case StockObject::BLACK_PEN : { - maLineStyle = WinMtfLineStyle( COL_BLACK ); + maLineStyle = WinMtfLineStyle(COL_BLACK, PS_COSMETIC, 0); } break; - case NULL_PEN : + case StockObject::NULL_PEN : { maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true ); } @@ -643,34 +824,58 @@ namespace emfio GDIObj *pGDIObj = nullptr; - if ( o3tl::make_unsigned(nIndex) < mvGDIObj.size() ) + if ( nIndex < mvGDIObj.size() ) pGDIObj = mvGDIObj[ nIndex ].get(); if ( pGDIObj ) { + + SAL_INFO ( "emfio", "\t\t Index: " << nIndex ); if (const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj)) + { maLineStyle = *pen; + SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex << maLineStyle.aLineColor + << ", Weight: " << maLineStyle.aLineInfo.GetWidth() ); + } else if (const auto brush = dynamic_cast<WinMtfFillStyle*>( pGDIObj)) { maFillStyle = *brush; mbFillStyleSelected = true; + SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex << ", Color: " << maFillStyle.aFillColor); } else if (const auto font = dynamic_cast<WinMtfFontStyle*>( pGDIObj)) { maFont = font->aFont; + SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex << ", Font: " << maFont.GetFamilyName() << " " << maFont.GetStyleName()); } + else if (const auto palette = dynamic_cast<WinMtfPalette*>( + pGDIObj)) + { + maPalette = palette->aPaletteColors; + SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex << ", Number of colours: " << maPalette.aPaletteColors.size() ); + } + } + else + { + SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex); } } } - void MtfTools::SetTextLayoutMode( ComplexTextLayoutFlags nTextLayoutMode ) + void MtfTools::SetTextLayoutMode( vcl::text::ComplexTextLayoutFlags nTextLayoutMode ) { mnTextLayoutMode = nTextLayoutMode; } - void MtfTools::SetBkMode( BkMode nMode ) + void MtfTools::SetArcDirection(bool bClockWise) + { + SAL_INFO("emfio", "\t\t Arc direction: " << (bClockWise ? "ClockWise" : "CounterClockWise")); + mbClockWiseArcDirection = bClockWise; + } + + void MtfTools::SetBkMode( BackgroundMode nMode ) { mnBkMode = nMode; } @@ -716,7 +921,7 @@ namespace emfio Point aPoint( aPoly[ 0 ] ); aPoly.Insert( nCount, aPoint ); } - mpGDIMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); + mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(aPoly) ) ); } } } @@ -756,7 +961,7 @@ namespace emfio mvGDIObj[ nIndex ] = std::move(pObject); } - void MtfTools::CreateObjectIndexed( sal_Int32 nIndex, std::unique_ptr<GDIObj> pObject ) + void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject ) { if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 ) return; @@ -780,14 +985,22 @@ namespace emfio if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash ) { aSize.AdjustWidth(1 ); - tools::Long nDotLen = ImplMap( aSize ).Width(); - pLineStyle->aLineInfo.SetDistance( nDotLen ); - pLineStyle->aLineInfo.SetDotLen( nDotLen ); - pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 ); + tools::Long nDashLen, nDotLen = ImplMap( aSize ).Width(); + const bool bFail = o3tl::checked_multiply<tools::Long>(nDotLen, 3, nDashLen); + if (!bFail) + { + pLineStyle->aLineInfo.SetDistance( nDotLen ); + pLineStyle->aLineInfo.SetDotLen( nDotLen ); + pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 ); + } + else + { + SAL_WARN("emfio", "DotLen too long: " << nDotLen); + } } } } - if ( o3tl::make_unsigned(nIndex) >= mvGDIObj.size() ) + if ( nIndex >= mvGDIObj.size() ) ImplResizeObjectArry( nIndex + 16 ); mvGDIObj[ nIndex ] = std::move(pObject); @@ -798,11 +1011,11 @@ namespace emfio CreateObject(std::make_unique<GDIObj>()); } - void MtfTools::DeleteObject( sal_Int32 nIndex ) + void MtfTools::DeleteObject( sal_uInt32 nIndex ) { if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 ) { - if ( o3tl::make_unsigned(nIndex) < mvGDIObj.size() ) + if ( nIndex < mvGDIObj.size() ) { mvGDIObj[ nIndex ].reset(); } @@ -811,47 +1024,51 @@ namespace emfio void MtfTools::IntersectClipRect( const tools::Rectangle& rRect ) { - if (utl::ConfigManager::IsFuzzing()) + if (comphelper::IsFuzzing()) return; mbClipNeedsUpdate=true; if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0)) { return; // empty rectangles cause trouble } - maClipPath.intersectClipRect( ImplMap( rRect ) ); + tools::Polygon aPoly( rRect ); + const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); + maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() ); } void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect ) { - if (utl::ConfigManager::IsFuzzing()) + if (comphelper::IsFuzzing()) return; mbClipNeedsUpdate=true; - maClipPath.excludeClipRect( ImplMap( rRect ) ); + tools::Polygon aPoly( rRect ); + const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) ); + maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() ); } void MtfTools::MoveClipRegion( const Size& rSize ) { - if (utl::ConfigManager::IsFuzzing()) + if (comphelper::IsFuzzing()) return; mbClipNeedsUpdate=true; maClipPath.moveClipRegion( ImplMap( rSize ) ); } - void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode, bool bIsMapped ) + void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, RegionMode eClippingMode, bool bIsMapped ) { - if (utl::ConfigManager::IsFuzzing()) + if (comphelper::IsFuzzing()) return; mbClipNeedsUpdate = true; tools::PolyPolygon aPolyPolygon(rPolyPolygon); if (!bIsMapped) { - if (!mbIsMapDevSet && (mnMapMode == MM_ISOTROPIC || mnMapMode == MM_ANISOTROPIC)) + if (!mbIsMapDevSet && (meMapMode == MappingMode::MM_ISOTROPIC || meMapMode == MappingMode::MM_ANISOTROPIC)) aPolyPolygon = ImplScale(aPolyPolygon); else aPolyPolygon = ImplMap(aPolyPolygon); } - maClipPath.setClipPath(aPolyPolygon, nClippingMode); + maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), eClippingMode); } void MtfTools::SetDefaultClipPath() @@ -861,35 +1078,19 @@ namespace emfio } MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF) - : maPathObj(), - maClipPath(), - maLatestLineStyle(), - maLineStyle(), - maNopLineStyle(), - maLatestFillStyle(), - maFillStyle(), - maNopFillStyle(), - maLatestFont(), - maFont(), - mnLatestTextAlign(90), - mnTextAlign(TA_LEFT | TA_TOP | TA_NOUPDATECP), - maLatestTextColor(), - maTextColor(), + : mnLatestTextAlign(90), + mnTextAlign(TextAlignmentMode::TA_LEFT | TextAlignmentMode::TA_TOP | TextAlignmentMode::TA_NOUPDATECP), maLatestBkColor(ColorTransparency, 0x12345678), maBkColor(COL_WHITE), - mnLatestTextLayoutMode(ComplexTextLayoutFlags::Default), - mnTextLayoutMode(ComplexTextLayoutFlags::Default), - mnLatestBkMode(BkMode::NONE), - mnBkMode(BkMode::OPAQUE), + mnLatestTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default), + mnTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default), + mnLatestBkMode(BackgroundMode::NONE), + mnBkMode(BackgroundMode::OPAQUE), meLatestRasterOp(RasterOp::Invert), meRasterOp(RasterOp::OverPaint), - mvGDIObj(), - maActPos(), mnRop(), - mvSaveStack(), - mnGfxMode(GM_COMPATIBLE), - mnMapMode(MM_TEXT), - maXForm(), + meGfxMode(GraphicsMode::GM_COMPATIBLE), + meMapMode(MappingMode::MM_TEXT), mnDevOrgX(0), mnDevOrgY(0), mnDevWidth(1), @@ -902,14 +1103,12 @@ namespace emfio mnPixY(100), mnMillX(1), mnMillY(1), - mrclFrame(), - mrclBounds(), mpGDIMetaFile(&rGDIMetaFile), mpInputStream(&rStreamWMF), mnStartPos(0), mnEndPos(0), - maBmpSaveList(), mbNopMode(false), + mbClockWiseArcDirection(false), mbFillStyleSelected(false), mbClipNeedsUpdate(true), mbComplexClip(false), @@ -926,12 +1125,12 @@ namespace emfio mnStartPos = mpInputStream->Tell(); SetDevOrg(Point()); - mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) ); // The original clipregion has to be on top + mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) ); // The original clipregion has to be on top // of the stack so it can always be restored // this is necessary to be able to support // SetClipRgn( NULL ) and similar ClipRgn actions (SJ) - maFont.SetFamilyName( "Arial" ); // sj: #i57205#, we do have some scaling problems if using + maFont.SetFamilyName( u"Arial"_ustr ); // sj: #i57205#, we do have some scaling problems if using maFont.SetCharSet( RTL_TEXTENCODING_MS_1252 ); // the default font then most times a x11 font is used, we maFont.SetFontHeight( 423 ); // will prevent this defining a font @@ -962,7 +1161,7 @@ namespace emfio mbComplexClip = false; mpGDIMetaFile->AddAction( new MetaPopAction() ); // taking the original clipregion - mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) ); + mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) ); // skip for 'no clipping at all' case if( maClipPath.isEmpty() ) @@ -973,31 +1172,16 @@ namespace emfio mbComplexClip = rClipPoly.count() > 1 || !basegfx::utils::isRectangle(rClipPoly); - static bool bEnableComplexClipViaRegion = getenv("SAL_WMF_COMPLEXCLIP_VIA_REGION") != nullptr; - - if (bEnableComplexClipViaRegion) + // This makes cases like tdf#45820 work in reasonable time. + if (mbComplexClip) { - //this makes cases like tdf#45820 work in reasonable time, and I feel in theory should - //be just fine. In practice I see the output is different so needs work before its the - //default, but for file fuzzing it should be good enough - if (mbComplexClip) - { - mpGDIMetaFile->AddAction( - new MetaISectRegionClipRegionAction( - vcl::Region(rClipPoly))); - mbComplexClip = false; - } - else - { - mpGDIMetaFile->AddAction( - new MetaISectRectClipRegionAction( - vcl::unotools::rectangleFromB2DRectangle( - rClipPoly.getB2DRange()))); - } + mpGDIMetaFile->AddAction( + new MetaISectRegionClipRegionAction( + vcl::Region(rClipPoly))); + mbComplexClip = false; } else { - //normal case mpGDIMetaFile->AddAction( new MetaISectRectClipRegionAction( vcl::unotools::rectangleFromB2DRectangle( @@ -1027,7 +1211,7 @@ namespace emfio void MtfTools::UpdateFillStyle() { if ( !mbFillStyleSelected ) // SJ: #i57205# taking care of bkcolor if no brush is selected - maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BkMode::Transparent ); + maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BackgroundMode::Transparent ); if (!( maLatestFillStyle == maFillStyle ) ) { maLatestFillStyle = maFillStyle; @@ -1095,7 +1279,7 @@ namespace emfio { if ( !bStroke ) { - mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::LINECOLOR ) ); + mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) ); mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) ); } if ( maPathObj.Count() == 1 ) @@ -1106,11 +1290,16 @@ namespace emfio if ( !bStroke ) mpGDIMetaFile->AddAction( new MetaPopAction() ); } - else + // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction + if ( bStroke ) { - sal_uInt16 i, nCount = maPathObj.Count(); - for ( i = 0; i < nCount; i++ ) - mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) ); + // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0 + if ( !bFill || maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) + { + sal_uInt16 i, nCount = maPathObj.Count(); + for ( i = 0; i < nCount; i++ ) + mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) ); + } } ClearPath(); } @@ -1148,6 +1337,23 @@ namespace emfio maActPos = aDest; } + void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect) + { + WinMtfFillStyle aFillStyleBackup = maFillStyle; + bool bTransparentBackup = maLineStyle.bTransparent; + BackgroundMode mnBkModeBackup = mnBkMode; + + const tools::Polygon aPoly( rRect ); + maLineStyle.bTransparent = true; + maFillStyle = maBkColor; + mnBkMode = BackgroundMode::OPAQUE; + ImplSetNonPersistentLineColorTransparenz(); + DrawPolygon(aPoly, false); + mnBkMode = mnBkModeBackup; // The rectangle needs to be always drawned even if mode is transparent + maFillStyle = std::move(aFillStyleBackup); + maLineStyle.bTransparent = bTransparentBackup; + } + void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge ) { UpdateClipRegion(); @@ -1192,6 +1398,12 @@ namespace emfio UpdateLineStyle(); UpdateFillStyle(); mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), std::abs( ImplMap( rSize ).Width() ), std::abs( ImplMap( rSize ).Height() ) ) ); + // tdf#142139 Wrong line width during WMF import + if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) ) + { + tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() ); + mpGDIMetaFile->AddAction( new MetaPolyLineAction( ImplMap( aRoundRectPoly ), maLineStyle.aLineInfo ) ); + } } void MtfTools::DrawEllipse( const tools::Rectangle& rRect ) @@ -1207,7 +1419,7 @@ namespace emfio ImplSetNonPersistentLineColorTransparenz(); mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) ); UpdateLineStyle(); - mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) ); + mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( std::move(aCenter), aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) ); } else { @@ -1233,7 +1445,7 @@ namespace emfio Point aCenter( aRect.Center() ); Size aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 ); - mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) ); + mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( std::move(aCenter), aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) ); } else mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Arc ), maLineStyle.aLineInfo ) ); @@ -1353,10 +1565,10 @@ namespace emfio WriteSvtGraphicFill( aMemStm, aFill ); - mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, + mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), aMemStm.TellEnd() ) ); - mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END" ) ); + mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END"_ostr ) ); } } @@ -1416,7 +1628,7 @@ namespace emfio else { UpdateLineStyle(); - mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) ); + mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) ); } } @@ -1424,10 +1636,12 @@ namespace emfio { sal_uInt16 nPoints = rPolygon.GetSize(); if ( ( nPoints < 4 ) || ( ( ( nPoints - 4 ) % 3 ) != 0 ) ) + { + SAL_WARN("emfio", + "EMF file error: Number of Bezier points is not set of three"); return; - + } UpdateClipRegion(); - ImplMap( rPolygon ); if ( bTo ) { @@ -1446,29 +1660,28 @@ namespace emfio else { UpdateLineStyle(); - mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) ); + mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) ); } } - void MtfTools::DrawText( Point& rPosition, OUString const & rText, tools::Long* pDXArry, tools::Long* pDYArry, bool bRecordPath, sal_Int32 nGfxMode ) + void MtfTools::DrawText( Point& rPosition, OUString const & rText, KernArray* pDXArry, tools::Long* pDYArry, bool bRecordPath, GraphicsMode nGfxMode ) { UpdateClipRegion(); rPosition = ImplMap( rPosition ); - sal_Int32 nOldGfxMode = GetGfxMode(); - SetGfxMode( GM_COMPATIBLE ); + GraphicsMode nOldGfxMode = GetGfxMode(); + SetGfxMode( GraphicsMode::GM_COMPATIBLE ); if (pDXArry) { - sal_Int32 nSumX = 0, nSumY = 0; + sal_Int64 nSumX = 0, nSumY = 0; for (sal_Int32 i = 0; i < rText.getLength(); i++ ) { - nSumX += pDXArry[i]; + nSumX += (*pDXArry)[i]; // #i121382# Map DXArray using WorldTransform const Size aSizeX(ImplMap(Size(nSumX, 0))); const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height()); - pDXArry[i] = basegfx::fround(aVectorX.getLength()); - pDXArry[i] *= (nSumX >= 0 ? 1 : -1); + pDXArry->set(i, basegfx::fround(aVectorX.getLength()) * (nSumX >= 0 ? 1 : -1)); if (pDYArry) { @@ -1477,7 +1690,7 @@ namespace emfio const Size aSizeY(ImplMap(Size(0, nSumY))); const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height()); // Reverse Y - pDYArry[i] = basegfx::fround(aVectorY.getLength()); + pDYArry[i] = basegfx::fround<tools::Long>(aVectorY.getLength()); pDYArry[i] *= (nSumY >= 0 ? -1 : 1); } } @@ -1487,11 +1700,11 @@ namespace emfio mnLatestTextLayoutMode = mnTextLayoutMode; mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) ); } - SetGfxMode( nGfxMode ); + SetGfxMode(nGfxMode); TextAlign eTextAlign; - if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE ) + if (mnTextAlign & TA_BASELINE) eTextAlign = ALIGN_BASELINE; - else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM ) + else if (mnTextAlign & TA_BOTTOM) eTextAlign = ALIGN_BOTTOM; else eTextAlign = ALIGN_TOP; @@ -1528,25 +1741,25 @@ namespace emfio aTmp.SetColor( maTextColor ); aTmp.SetFillColor( maBkColor ); - if( mnBkMode == BkMode::Transparent ) + if( mnBkMode == BackgroundMode::Transparent ) aTmp.SetTransparent( true ); else aTmp.SetTransparent( false ); aTmp.SetAlignment( eTextAlign ); - if ( nGfxMode == GM_ADVANCED ) + if ( nGfxMode == GraphicsMode::GM_ADVANCED ) { // check whether there is a font rotation applied via transformation Point aP1( ImplMap( Point() ) ); Point aP2( ImplMap( Point( 0, 100 ) ) ); - aP2.AdjustX( -(aP1.X()) ); - aP2.AdjustY( -(aP1.Y()) ); + aP2.setX(o3tl::saturating_sub(aP2.X(), aP1.X())); + aP2.setY(o3tl::saturating_sub(aP2.Y(), aP1.Y())); double fX = aP2.X(); double fY = aP2.Y(); if ( fX ) { - double fOrientation = acos( fX / sqrt( fX * fX + fY * fY ) ) * 57.29577951308; + double fOrientation = basegfx::rad2deg(acos(fX / std::hypot(fX, fY))); if ( fY > 0 ) fOrientation = 360 - fOrientation; fOrientation += 90; @@ -1569,9 +1782,9 @@ namespace emfio { nTextWidth = pVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) ); if( nLen > 1 ) - nTextWidth += pDXArry[ nLen - 2 ]; + nTextWidth += (*pDXArry)[ nLen - 2 ]; // tdf#39894: We should consider the distance to next character cell origin - aActPosDelta.setX( pDXArry[ nLen - 1 ] ); + aActPosDelta.setX( (*pDXArry)[ nLen - 1 ] ); if ( pDYArry ) { aActPosDelta.setY( pDYArry[ nLen - 1 ] ); @@ -1586,9 +1799,9 @@ namespace emfio if( mnTextAlign & TA_UPDATECP ) rPosition = maActPos; - if ( mnTextAlign & TA_RIGHT_CENTER ) + if (mnTextAlign & TA_RIGHT_CENTER) { - Point aDisplacement( ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1, 0 ); + Point aDisplacement(((mnTextAlign & TA_RIGHT_CENTER) == TA_CENTER) ? nTextWidth >> 1: nTextWidth, 0); Point().RotateAround(aDisplacement, maFont.GetOrientation()); rPosition -= aDisplacement; } @@ -1599,14 +1812,29 @@ namespace emfio maActPos = rPosition + aActPosDelta; } } - if ( bChangeFont || ( maLatestFont != aTmp ) ) + + if(bChangeFont || (maLatestFont != aTmp)) { maLatestFont = aTmp; - mpGDIMetaFile->AddAction( new MetaFontAction( aTmp ) ); + rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp)); + + // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector + maScaledFontHelper.endCurrentMetaFontAction(); + + // !bRecordPath: else no MetaTextArrayAction will be created + // nullptr != pDXArry: detection only possible when text size is given + // rText.getLength(): no useful check without text + if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength()) + { + maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction); + } + + mpGDIMetaFile->AddAction( aNewMetaFontAction ); mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) ); mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) ); mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) ); } + if ( bRecordPath ) { // TODO @@ -1617,29 +1845,40 @@ namespace emfio { for (sal_Int32 i = 0; i < rText.getLength(); ++i) { - Point aCharDisplacement( i ? pDXArry[i-1] : 0, i ? pDYArry[i-1] : 0 ); + Point aCharDisplacement( i ? (*pDXArry)[i-1] : 0, i ? pDYArry[i-1] : 0 ); Point().RotateAround(aCharDisplacement, maFont.GetOrientation()); - mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), nullptr, 0, 1 ) ); + mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), KernArraySpan(), {}, 0, 1 ) ); } } else { /* because text without dx array is badly scaled, we will create such an array if necessary */ - tools::Long* pDX = pDXArry; - if (!pDXArry) + KernArraySpan pDX; + KernArray aMyDXArray; + if (pDXArry) + { + pDX = *pDXArry; + // only useful when we have an imported DXArray + if(!rText.isEmpty()) + { + maScaledFontHelper.evaluateAlternativeFontScale( + rText, + (*pDXArry)[rText.getLength() - 1] // extract imported TextLength + ); + } + } + else { // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading SolarMutexGuard aGuard; ScopedVclPtrInstance< VirtualDevice > pVDev; - pDX = new tools::Long[ rText.getLength() ]; pVDev->SetMapMode(MapMode(MapUnit::Map100thMM)); pVDev->SetFont( maLatestFont ); - pVDev->GetTextArray( rText, pDX, 0, rText.getLength()); + pVDev->GetTextArray( rText, &aMyDXArray, 0, rText.getLength()); + pDX = aMyDXArray; } - mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) ); - if ( !pDXArry ) // this means we have created our own array - delete[] pDX; // which must be deleted + mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, {}, 0, rText.getLength() ) ); } } SetGfxMode( nOldGfxMode ); @@ -1653,13 +1892,13 @@ namespace emfio vcl::bitmap::DrawAndClipBitmap(rPos, rSize, rBitmap, aBmpEx, maClipPath.getClipPath()); } - if ( aBmpEx.IsTransparent() ) + if ( aBmpEx.IsAlpha() ) mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmpEx ) ); else mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmpEx.GetBitmap() ) ); } - void MtfTools::ResolveBitmapActions( std::vector<std::unique_ptr<BSaveStruct>>& rSaveList ) + void MtfTools::ResolveBitmapActions( std::vector<BSaveStruct>& rSaveList ) { UpdateClipRegion(); @@ -1672,7 +1911,7 @@ namespace emfio size_t nObjectsOfSameSize = 0; size_t nObjectStartIndex = nObjects - nObjectsLeft; - BSaveStruct* pSave = rSaveList[nObjectStartIndex].get(); + BSaveStruct* pSave = &rSaveList[nObjectStartIndex]; tools::Rectangle aRect( pSave->aOutRect ); for ( i = nObjectStartIndex; i < nObjects; ) @@ -1680,7 +1919,7 @@ namespace emfio nObjectsOfSameSize++; if ( ++i < nObjects ) { - pSave = rSaveList[i].get(); + pSave = &rSaveList[i]; if ( pSave->aOutRect != aRect ) break; } @@ -1690,7 +1929,7 @@ namespace emfio for ( i = nObjectStartIndex; i < ( nObjectStartIndex + nObjectsOfSameSize ); i++ ) { - pSave = rSaveList[i].get(); + pSave = &rSaveList[i]; sal_uInt32 nWinRop = pSave->nWinRop; sal_uInt8 nRasterOperation = static_cast<sal_uInt8>( nWinRop >> 16 ); @@ -1718,7 +1957,7 @@ namespace emfio { if ( nObjectsOfSameSize == 2 ) { - BSaveStruct* pSave2 = rSaveList[i + 1].get(); + BSaveStruct* pSave2 = &rSaveList[i + 1]; if ( ( pSave->aBmpEx.GetPrefSize() == pSave2->aBmpEx.GetPrefSize() ) && ( pSave->aBmpEx.GetPrefMapMode() == pSave2->aBmpEx.GetPrefMapMode() ) ) { @@ -1923,14 +2162,19 @@ namespace emfio if ( !(rSize.Width() && rSize.Height()) ) return; - switch( mnMapMode ) + switch( meMapMode ) { - case MM_ISOTROPIC : - case MM_ANISOTROPIC : + case MappingMode::MM_ISOTROPIC : + case MappingMode::MM_ANISOTROPIC : { mnDevWidth = rSize.Width(); mnDevHeight = rSize.Height(); + break; } + + //do nothing + default: + break; } if (regular) { @@ -1965,7 +2209,7 @@ namespace emfio { if (!mbIsMapDevSet) { - if ( mnMapMode == MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC??? + if ( meMapMode == MappingMode::MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC??? { sal_Int32 nX, nY; if (o3tl::checked_add(mnWinExtX, mnWinOrgX, nX) || o3tl::checked_sub(mnWinExtY, mnWinOrgY, nY)) @@ -1981,10 +2225,10 @@ namespace emfio if (!(rSize.Width() && rSize.Height())) return; - switch( mnMapMode ) + switch( meMapMode ) { - case MM_ISOTROPIC : - case MM_ANISOTROPIC : + case MappingMode::MM_ISOTROPIC : + case MappingMode::MM_ANISOTROPIC : { mnWinExtX = rSize.Width(); mnWinExtY = rSize.Height(); @@ -1993,7 +2237,12 @@ namespace emfio SetDevByWin(); } mbIsMapWinSet = true; + break; } + + default: + //do nothing + break; } } @@ -2025,15 +2274,15 @@ namespace emfio mnMillY = rSize.Height(); } - void MtfTools::SetMapMode( sal_uInt32 nMapMode ) + void MtfTools::SetMapMode( MappingMode nMapMode ) { - mnMapMode = nMapMode; - if ( nMapMode == MM_TEXT && !mbIsMapWinSet ) + meMapMode = nMapMode; + if ( nMapMode == MappingMode::MM_TEXT && !mbIsMapWinSet ) { mnWinExtX = mnDevWidth; mnWinExtY = mnDevHeight; } - else if ( mnMapMode == MM_HIMETRIC ) + else if ( meMapMode == MappingMode::MM_HIMETRIC ) { sal_Int32 nWinExtX, nWinExtY; if (o3tl::checked_multiply<sal_Int32>(mnMillX, 100, nWinExtX) || @@ -2056,24 +2305,24 @@ namespace emfio maXForm.eDy = rXForm.eDy; } - void MtfTools::ModifyWorldTransform( const XForm& rXForm, sal_uInt32 nMode ) + void MtfTools::ModifyWorldTransform( const XForm& rXForm, ModifyWorldTransformMode nMode ) { switch( nMode ) { - case MWT_IDENTITY : + case ModifyWorldTransformMode::MWT_IDENTITY : { maXForm.eM11 = maXForm.eM22 = 1.0f; maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f; break; } - case MWT_RIGHTMULTIPLY : - case MWT_LEFTMULTIPLY : + case ModifyWorldTransformMode::MWT_RIGHTMULTIPLY : + case ModifyWorldTransformMode::MWT_LEFTMULTIPLY : { const XForm* pLeft; const XForm* pRight; - if ( nMode == MWT_LEFTMULTIPLY ) + if ( nMode == ModifyWorldTransformMode::MWT_LEFTMULTIPLY ) { pLeft = &rXForm; pRight = &maXForm; @@ -2126,7 +2375,7 @@ namespace emfio maXForm.eDy = cF[2][1]; break; } - case MWT_SET: + case ModifyWorldTransformMode::MWT_SET: { SetWorldTransform(rXForm); break; @@ -2146,10 +2395,11 @@ namespace emfio pSave->aTextColor = maTextColor; pSave->nTextAlign = mnTextAlign; pSave->nTextLayoutMode = mnTextLayoutMode; - pSave->nMapMode = mnMapMode; - pSave->nGfxMode = mnGfxMode; + pSave->eMapMode = meMapMode; + pSave->eGfxMode = meGfxMode; pSave->nBkMode = mnBkMode; pSave->aBkColor = maBkColor; + pSave->bClockWiseArcDirection = mbClockWiseArcDirection; pSave->bFillStyleSelected = mbFillStyleSelected; pSave->aActPos = maActPos; @@ -2168,21 +2418,35 @@ namespace emfio pSave->maPathObj = maPathObj; pSave->maClipPath = maClipPath; - SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode); - SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode); + SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode)); + SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode)); SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY); SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY); SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY); SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight); + SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor ); mvSaveStack.push_back( pSave ); } - void MtfTools::Pop() + void MtfTools::Pop( const sal_Int32 nSavedDC ) { - // Get the latest data from the stack - if( mvSaveStack.empty() ) + if ( nSavedDC == 0 ) + return; + + sal_Int32 aIndex; + if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state. + aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC; + else + aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored. + if( aIndex < 0 ) + { + mvSaveStack.clear(); + return; + } + if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) ) return; + mvSaveStack.resize( aIndex + 1 ); // Backup the current data on the stack std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() ); @@ -2194,9 +2458,10 @@ namespace emfio mnTextAlign = pSave->nTextAlign; mnTextLayoutMode = pSave->nTextLayoutMode; mnBkMode = pSave->nBkMode; - mnGfxMode = pSave->nGfxMode; - mnMapMode = pSave->nMapMode; + meGfxMode = pSave->eGfxMode; + meMapMode = pSave->eMapMode; maBkColor = pSave->aBkColor; + mbClockWiseArcDirection = pSave->bClockWiseArcDirection; mbFillStyleSelected = pSave->bFillStyleSelected; maActPos = pSave->aActPos; @@ -2224,12 +2489,13 @@ namespace emfio meLatestRasterOp = meRasterOp; } - SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode); - SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode); + SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode)); + SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode)); SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY); SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY); SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY); SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight); + SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor ); mvSaveStack.pop_back(); } @@ -2267,14 +2533,14 @@ namespace emfio // on windows where the function parameters are probably resolved in reverse order mem.Flush(); - mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO", 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) ); + mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO"_ostr, 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) ); mpGDIMetaFile->UseCanvas( true ); } void MtfTools::PassEMFPlus( void const * pBuffer, sal_uInt32 nLength ) { EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength)); - mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS", 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) ); + mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS"_ostr, 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) ); } } |