/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <mtftools.hxx>
#include <cstdlib>
#include <memory>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <vcl/metric.hxx>
#include <vcl/graphictools.hxx>
#include <vcl/BitmapTools.hxx>
#include <vcl/metaact.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/svapp.hxx>
#include <tools/stream.hxx>
#include <rtl/tencinfo.h>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <vcl/virdev.hxx>
#include <o3tl/safeint.hxx>
#include <comphelper/configuration.hxx>
#include <unotools/defaultencoding.hxx>
#include <unotools/wincodepage.hxx>
#if OSL_DEBUG_LEVEL > 1
#define EMFP_DEBUG(x) x
#else
#define EMFP_DEBUG(x)
#endif
namespace emfio
{
SvStream&
operator >> (SvStream& rInStream, XForm& rXForm)
{
if (
sizeof (
float ) != 4)
{
OSL_FAIL(
"EmfReader::sizeof( float ) != 4" );
rXForm = XForm();
}
else
{
rInStream.ReadFloat(rXForm.eM11);
rInStream.ReadFloat(rXForm.eM12);
rInStream.ReadFloat(rXForm.eM21);
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::intersectClip(
const basegfx::B2DPolyPolygon& rPolyPolygon )
{
maClip.intersectPolyPolygon(rPolyPolygon);
}
void WinMtfClipPath::excludeClip(
const basegfx::B2DPolyPolygon& rPolyPolygon )
{
maClip.subtractPolyPolygon(rPolyPolygon);
}
void WinMtfClipPath::setClipPath(
const basegfx::B2DPolyPolygon& rB2DPoly, Region
Mode nClippingMode )
{
switch ( nClippingMode )
{
case RegionMode::RGN_OR :
maClip.unionPolyPolygon(rB2DPoly);
break ;
case RegionMode::RGN_XOR :
maClip.xorPolyPolygon(rB2DPoly);
break ;
case RegionMode::RGN_DIFF :
maClip.subtractPolyPolygon(rB2DPoly);
break ;
case RegionMode::RGN_AND :
maClip.intersectPolyPolygon(rB2DPoly);
break ;
case RegionMode::RGN_COPY :
maClip = basegfx::utils::B2DClipState(rB2DPoly);
break ;
}
}
void WinMtfClipPath::moveClipRegion( const Size& rSize )
{
basegfx::B2DHomMatrix aTranslate;
aTranslate.translate(rSize.Width(), rSize.Height());
maClip.transform(aTranslate);
}
void WinMtfClipPath::setDefaultClipPath()
{
// Empty clip region - everything visible
maClip = basegfx::utils::B2DClipState();
}
basegfx::B2DPolyPolygon const & WinMtfClipPath::getClipPath() const
{
return maClip.getClipPoly();
}
void WinMtfPathObj::AddPoint( const Point& rPoint )
{
if ( bClosed )
Insert( tools::Polygon() );
tools::Polygon& rPoly = static_cast <tools::PolyPolygon&>(*this )[ Count() - 1 ];
rPoly.Insert( rPoly.GetSize(), rPoint );
bClosed = false ;
}
void WinMtfPathObj::AddPolyLine( const tools::Polygon& rPolyLine )
{
if ( bClosed )
Insert( tools::Polygon() );
tools::Polygon& rPoly = static_cast <tools::PolyPolygon&>(*this )[ Count() - 1 ];
rPoly.Insert( rPoly.GetSize(), rPolyLine );
bClosed = false ;
}
void WinMtfPathObj::AddPolygon( const tools::Polygon& rPoly )
{
Insert( rPoly );
bClosed = true ;
}
void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon& rPolyPoly )
{
sal_uInt16 i, nCount = rPolyPoly.Count();
for ( i = 0; i < nCount; i++ )
Insert( rPolyPoly[ i ] );
bClosed = true ;
}
void WinMtfPathObj::ClosePath()
{
if ( Count() )
{
tools::Polygon& rPoly = static_cast <tools::PolyPolygon&>(*this )[ Count() - 1 ];
if ( rPoly.GetSize() > 2 )
{
Point aFirst( rPoly[ 0 ] );
if ( aFirst != rPoly[ rPoly.GetSize() - 1 ] )
rPoly.Insert( rPoly.GetSize(), aFirst );
}
}
bClosed = true ;
}
WinMtfFontStyle::WinMtfFontStyle( LOGFONTW const & rFont )
{
rtl_TextEncoding eCharSet;
if ((rFont.alfFaceName == "Symbol" )
|| (rFont.alfFaceName == "MT Extra" ))
eCharSet = RTL_TEXTENCODING_SYMBOL;
else if ((rFont.lfCharSet == DEFAULT_CHARSET) || (rFont.lfCharSet == OEM_CHARSET))
eCharSet = utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(),
rFont.lfCharSet == OEM_CHARSET);
else
eCharSet = rtl_getTextEncodingFromWindowsCharset( rFont.lfCharSet );
if ( eCharSet == RTL_TEXTENCODING_DONTKNOW )
eCharSet = RTL_TEXTENCODING_MS_1252;
aFont.SetCharSet( eCharSet );
aFont.SetFamilyName( rFont.alfFaceName );
FontFamily eFamily;
switch ( rFont.lfPitchAndFamily >> 4 & 0x0f )
{
case FamilyFont::FF_ROMAN:
eFamily = FAMILY_ROMAN;
break ;
case FamilyFont::FF_SWISS:
eFamily = FAMILY_SWISS;
break ;
case FamilyFont::FF_MODERN:
eFamily = FAMILY_MODERN;
break ;
case FamilyFont::FF_SCRIPT:
eFamily = FAMILY_SCRIPT;
break ;
case FamilyFont::FF_DECORATIVE:
eFamily = FAMILY_DECORATIVE;
break ;
default :
eFamily = FAMILY_DONTKNOW;
break ;
}
aFont.SetFamily( eFamily );
FontPitch ePitch;
switch ( rFont.lfPitchAndFamily & 0x0f )
{
case FIXED_PITCH:
ePitch = PITCH_FIXED;
break ;
case DEFAULT_PITCH:
case VARIABLE_PITCH:
default :
ePitch = PITCH_VARIABLE;
break ;
}
aFont.SetPitch( ePitch );
FontWeight eWeight;
if (rFont.lfWeight == 0) // default weight SHOULD be used
eWeight = WEIGHT_DONTKNOW;
else if (rFont.lfWeight <= FW_THIN)
eWeight = WEIGHT_THIN;
else if ( rFont.lfWeight <= FW_ULTRALIGHT )
eWeight = WEIGHT_ULTRALIGHT;
else if ( rFont.lfWeight <= FW_LIGHT )
eWeight = WEIGHT_LIGHT;
else if ( rFont.lfWeight < FW_MEDIUM )
eWeight = WEIGHT_NORMAL;
else if ( rFont.lfWeight == FW_MEDIUM )
eWeight = WEIGHT_MEDIUM;
else if ( rFont.lfWeight <= FW_SEMIBOLD )
eWeight = WEIGHT_SEMIBOLD;
else if ( rFont.lfWeight <= FW_BOLD )
eWeight = WEIGHT_BOLD;
else if ( rFont.lfWeight <= FW_ULTRABOLD )
eWeight = WEIGHT_ULTRABOLD;
else
eWeight = WEIGHT_BLACK;
aFont.SetWeight( eWeight );
if ( rFont.lfItalic )
aFont.SetItalic( ITALIC_NORMAL );
if ( rFont.lfUnderline )
aFont.SetUnderline( LINESTYLE_SINGLE );
if ( rFont.lfStrikeOut )
aFont.SetStrikeout( STRIKEOUT_SINGLE );
aFont.SetOrientation( Degree10(static_cast <sal_Int16>(rFont.lfEscapement)) );
Size aFontSize( rFont.lfWidth, rFont.lfHeight );
if ( rFont.lfHeight > 0 )
{
// #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
SolarMutexGuard aGuard;
ScopedVclPtrInstance< VirtualDevice > pVDev;
// converting the cell height into a font height
aFont.SetFontSize( aFontSize );
pVDev->SetFont( aFont );
FontMetric aMetric( pVDev->GetFontMetric() );
tools::Long nHeight = aMetric.GetAscent() + aMetric.GetDescent();
if (nHeight)
{
double fHeight = (static_cast <double >(aFontSize.Height()) * rFont.lfHeight ) / nHeight;
aFontSize.setHeight( static_cast <sal_Int32>( fHeight + 0.5 ) );
}
}
// 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
};
WinMtfFontStyle::~WinMtfFontStyle() = default ;
// 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()
{
// 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( 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;
};
Point MtfTools::ImplScale(const Point& rPoint) // Hack to set varying defaults for incompletely defined files.
{
if (!mbIsMapDevSet)
return Point(rPoint.X() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Left(),
rPoint.Y() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Top());
else
return rPoint;
}
Point MtfTools::ImplMap( const Point& rPt )
{
if ( mnWinExtX && mnWinExtY )
{
double fX = rPt.X();
double fY = rPt.Y();
double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx;
double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy;
if ( meGfxMode == GraphicsMode::GM_COMPATIBLE )
{
fX2 -= mnWinOrgX;
fY2 -= mnWinOrgY;
switch ( meMapMode )
{
case MappingMode::MM_LOENGLISH :
{
fX2 = o3tl::convert(fX2, o3tl::Length::in100, o3tl::Length::mm100);
fY2 = o3tl::convert(-fY2, o3tl::Length::in100, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_HIENGLISH :
{
fX2 = o3tl::convert(fX2, o3tl::Length::in1000, o3tl::Length::mm100);
fY2 = o3tl::convert(-fY2, o3tl::Length::in1000, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_TWIPS:
{
fX2 = o3tl::convert(fX2, o3tl::Length::twip, o3tl::Length::mm100);
fY2 = o3tl::convert(-fY2, o3tl::Length::twip, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_LOMETRIC :
{
fX2 = o3tl::convert(fX2, o3tl::Length::mm10, o3tl::Length::mm100);
fY2 = o3tl::convert(-fY2, o3tl::Length::mm10, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_HIMETRIC : // in hundredth of a millimeter
{
fY2 *= -1;
}
break ;
default :
{
if (mnPixX == 0 || mnPixY == 0)
{
SAL_WARN("emfio" , "invalid scaling factor" );
return Point();
}
else
{
if ( meMapMode != MappingMode::MM_TEXT )
{
fX2 /= mnWinExtX;
fY2 /= mnWinExtY;
fX2 *= mnDevWidth;
fY2 *= mnDevHeight;
}
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<tools::Long >(fX2), basegfx::fround<tools::Long >(fY2));
}
else
return Point();
};
Size MtfTools::ImplMap(const Size& rSz, bool bDoWorldTransform)
{
if ( mnWinExtX && mnWinExtY )
{
// #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted
double fWidth, fHeight;
if (bDoWorldTransform)
{
fWidth = rSz.Width() * maXForm.eM11 + rSz.Height() * maXForm.eM21;
fHeight = rSz.Width() * maXForm.eM12 + rSz.Height() * maXForm.eM22;
}
else
{
//take the scale, but not the rotation
basegfx::B2DHomMatrix aMatrix(maXForm.eM11, maXForm.eM12, 0,
maXForm.eM21, maXForm.eM22, 0);
basegfx::B2DTuple aScale, aTranslate;
double fRotate, fShearX;
if (!aMatrix.decompose(aScale, aTranslate, fRotate, fShearX))
{
aScale.setX(1.0);
aScale.setY(1.0);
}
fWidth = rSz.Width() * aScale.getX();
fHeight = rSz.Height() * aScale.getY();
}
if ( meGfxMode == GraphicsMode::GM_COMPATIBLE )
{
switch ( meMapMode )
{
case MappingMode::MM_LOENGLISH :
{
fWidth = o3tl::convert(fWidth, o3tl::Length::in100, o3tl::Length::mm100);
fHeight = o3tl::convert(-fHeight, o3tl::Length::in100, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_HIENGLISH :
{
fWidth = o3tl::convert(fWidth, o3tl::Length::in1000, o3tl::Length::mm100);
fHeight = o3tl::convert(-fHeight, o3tl::Length::in1000, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_LOMETRIC :
{
fWidth = o3tl::convert(fWidth, o3tl::Length::mm10, o3tl::Length::mm100);
fHeight = o3tl::convert(-fHeight, o3tl::Length::mm10, o3tl::Length::mm100);
}
break ;
case MappingMode::MM_HIMETRIC : // in hundredth of millimeters
{
fHeight *= -1;
}
break ;
case MappingMode::MM_TWIPS:
{
fWidth = o3tl::convert(fWidth, o3tl::Length::twip, o3tl::Length::mm100);
fHeight = o3tl::convert(-fHeight, o3tl::Length::twip, o3tl::Length::mm100);
}
break ;
default :
{
if (mnPixX == 0 || mnPixY == 0)
{
SAL_WARN("emfio" , "invalid scaling factor" );
return Size();
}
else
{
if ( meMapMode != MappingMode::MM_TEXT )
{
fWidth /= mnWinExtX;
fHeight /= mnWinExtY;
fWidth *= mnDevWidth;
fHeight *= mnDevHeight;
}
fWidth *= static_cast <double >(mnMillX) * 100.0 / static_cast <double >(mnPixX);
fHeight *= static_cast <double >(mnMillY) * 100.0 / static_cast <double >(mnPixY);
}
}
break ;
}
}
return Size(basegfx::fround<tools::Long >(fWidth), basegfx::fround<tools::Long >(fHeight));
}
else
return Size();
}
tools::Rectangle MtfTools::ImplMap( const tools::Rectangle& rRect )
{
tools::Rectangle aRect;
aRect.SetPos(ImplMap(rRect.TopLeft()));
aRect.SaturatingSetSize(ImplMap(rRect.GetSize()));
return aRect;
}
void MtfTools::ImplMap( vcl::Font& rFont )
{
// !!! HACK: we now always set the width to zero because the OS width is interpreted differently;
// must later be made portable in SV (KA 1996-02-08)
Size aFontSize = ImplMap (rFont.GetFontSize(), false );
const auto nHeight = aFontSize.Height();
if (nHeight < 0)
aFontSize.setHeight( o3tl::saturating_toggle_sign(nHeight) );
rFont.SetFontSize( aFontSize );
sal_Int32 nResult;
const bool bFail = o3tl::checked_multiply(mnWinExtX, mnWinExtY, nResult);
if (!bFail && nResult < 0)
rFont.SetOrientation( 3600_deg10 - rFont.GetOrientation() );
}
tools::Polygon& MtfTools::ImplMap( tools::Polygon& rPolygon )
{
sal_uInt16 nPoints = rPolygon.GetSize();
for ( sal_uInt16 i = 0; i < nPoints; i++ )
{
rPolygon[ i ] = ImplMap( rPolygon[ i ] );
}
return rPolygon;
}
void MtfTools::ImplScale( tools::Polygon& rPolygon )
{
sal_uInt16 nPoints = rPolygon.GetSize();
for ( sal_uInt16 i = 0; i < nPoints; i++ )
{
rPolygon[ i ] = ImplScale( rPolygon[ i ] );
}
}
tools::PolyPolygon& MtfTools::ImplScale( tools::PolyPolygon& rPolyPolygon )
{
sal_uInt16 nPolys = rPolyPolygon.Count();
for (sal_uInt16 i = 0; i < nPolys; ++i)
{
ImplScale(rPolyPolygon[i]);
}
return rPolyPolygon;
}
tools::PolyPolygon& MtfTools::ImplMap( tools::PolyPolygon& rPolyPolygon )
{
sal_uInt16 nPolys = rPolyPolygon.Count();
for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ;
return rPolyPolygon;
}
void MtfTools::SelectObject( sal_uInt32 nIndex )
{
if ( nIndex & ENHMETA_STOCK_OBJECT )
{
SAL_INFO ( "emfio" , "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex << nIndex );
StockObject nStockId = static_cast <StockObject>(nIndex & 0xFF);
switch ( nStockId )
{
case StockObject::WHITE_BRUSH :
{
maFillStyle = WinMtfFillStyle( COL_WHITE );
mbFillStyleSelected = true ;
}
break ;
case StockObject::LTGRAY_BRUSH :
{
maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY );
mbFillStyleSelected = true ;
}
break ;
case StockObject::GRAY_BRUSH :
{
maFillStyle = WinMtfFillStyle( COL_GRAY );
mbFillStyleSelected = true ;
}
break ;
case StockObject::DKGRAY_BRUSH :
{
maFillStyle = WinMtfFillStyle( COL_GRAY7 );
mbFillStyleSelected = true ;
}
break ;
case StockObject::BLACK_BRUSH :
{
maFillStyle = WinMtfFillStyle( COL_BLACK );
mbFillStyleSelected = true ;
}
break ;
case StockObject::NULL_BRUSH :
{
maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
mbFillStyleSelected = true ;
}
break ;
case StockObject::WHITE_PEN :
{
maLineStyle = WinMtfLineStyle(COL_WHITE, PS_COSMETIC, 1);
}
break ;
case StockObject::BLACK_PEN :
{
maLineStyle = WinMtfLineStyle(COL_BLACK, PS_COSMETIC, 1);
}
break ;
case StockObject::NULL_PEN :
{
maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
}
break ;
default :
break ;
}
}
else
{
nIndex &= 0xffff; // safety check: don't allow index to be > 65535
GDIObj *pGDIObj = nullptr;
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( vcl::text::ComplexTextLayoutFlags nTextLayoutMode )
{
mnTextLayoutMode = nTextLayoutMode;
}
void MtfTools::SetArcDirection(bool bClockWise)
{
SAL_INFO("emfio" , "\t\t Arc direction: " << (bClockWise ? "ClockWise" : "CounterClockWise" ));
mbClockWiseArcDirection = bClockWise;
}
void MtfTools::SetBkMode( BackgroundMode nMode )
{
mnBkMode = nMode;
}
void MtfTools::SetBkColor( const Color& rColor )
{
maBkColor = rColor;
}
void MtfTools::SetTextColor( const Color& rColor )
{
maTextColor = rColor;
}
void MtfTools::SetTextAlign( sal_uInt32 nAlign )
{
mnTextAlign = nAlign;
}
void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys )
{
mvGDIObj.resize(nNewEntrys);
}
void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly )
{
if ( !rPolyPoly.Count() )
return ;
ImplSetNonPersistentLineColorTransparenz();
if ( rPolyPoly.Count() == 1 )
{
if ( rPolyPoly.IsRect() )
mpGDIMetaFile->AddAction( new MetaRectAction( rPolyPoly.GetBoundRect() ) );
else
{
tools::Polygon aPoly( rPolyPoly[ 0 ] );
sal_uInt16 nCount = aPoly.GetSize();
if ( nCount )
{
if ( aPoly[ nCount - 1 ] != aPoly[ 0 ] )
{
Point aPoint( aPoly[ 0 ] );
aPoly.Insert( nCount, aPoint );
}
mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(aPoly) ) );
}
}
}
else
mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
}
void MtfTools::CreateObject( std::unique_ptr<GDIObj> pObject )
{
if ( pObject )
{
const auto pLineStyle = dynamic_cast <WinMtfLineStyle*>(pObject.get());
const auto pFontStyle = dynamic_cast <WinMtfFontStyle*>(pObject.get());
if ( pFontStyle )
{
if (pFontStyle->aFont.GetFontHeight() == 0)
pFontStyle->aFont.SetFontHeight(423);
ImplMap(pFontStyle->aFont); // defaulting to 12pt
}
else if ( pLineStyle )
{
Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
aSize = ImplMap(aSize);
pLineStyle->aLineInfo.SetWidth(aSize.Width());
}
}
std::vector<std::unique_ptr<GDIObj>>::size_type nIndex;
for ( nIndex = 0; nIndex < mvGDIObj.size(); nIndex++ )
{
if ( !mvGDIObj[ nIndex ] )
break ;
}
if ( nIndex == mvGDIObj.size() )
ImplResizeObjectArry( mvGDIObj.size() + 16 );
mvGDIObj[ nIndex ] = std::move(pObject);
}
void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject )
{
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 )
return ;
nIndex &= 0xffff; // safety check: do not allow index to be > 65535
if ( pObject )
{
const auto pLineStyle = dynamic_cast <WinMtfLineStyle*>(pObject.get());
const auto pFontStyle = dynamic_cast <WinMtfFontStyle*>(pObject.get());
if ( pFontStyle )
{
if (pFontStyle->aFont.GetFontHeight() == 0)
pFontStyle->aFont.SetFontHeight(423);
ImplMap(pFontStyle->aFont);
}
else if ( pLineStyle )
{
Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
pLineStyle->aLineInfo.SetWidth( ImplMap(aSize).Width() );
if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash )
{
aSize.AdjustWidth(1 );
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 ( nIndex >= mvGDIObj.size() )
ImplResizeObjectArry( nIndex + 16 );
mvGDIObj[ nIndex ] = std::move(pObject);
}
void MtfTools::CreateObject()
{
CreateObject(std::make_unique<GDIObj>());
}
void MtfTools::DeleteObject( sal_uInt32 nIndex )
{
if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
{
if ( nIndex < mvGDIObj.size() )
{
mvGDIObj[ nIndex ].reset();
}
}
}
void MtfTools::IntersectClipRect( const tools::Rectangle& rRect )
{
if (comphelper::IsFuzzing())
return ;
mbClipNeedsUpdate=true ;
if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0))
{
return ; // empty rectangles cause trouble
}
tools::Polygon aPoly( rRect );
const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() );
}
void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect )
{
if (comphelper::IsFuzzing())
return ;
mbClipNeedsUpdate=true ;
tools::Polygon aPoly( rRect );
const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() );
}
void MtfTools::MoveClipRegion( const Size& rSize )
{
if (comphelper::IsFuzzing())
return ;
mbClipNeedsUpdate=true ;
maClipPath.moveClipRegion( ImplMap( rSize ) );
}
void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, RegionMode eClippingMode, bool bIsMapped )
{
if (comphelper::IsFuzzing())
return ;
mbClipNeedsUpdate = true ;
tools::PolyPolygon aPolyPolygon(rPolyPolygon);
if (!bIsMapped)
{
if (!mbIsMapDevSet && (meMapMode == MappingMode::MM_ISOTROPIC || meMapMode == MappingMode::MM_ANISOTROPIC))
aPolyPolygon = ImplScale(aPolyPolygon);
else
aPolyPolygon = ImplMap(aPolyPolygon);
}
maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), eClippingMode);
}
void MtfTools::SetDefaultClipPath()
{
mbClipNeedsUpdate = true ;
maClipPath.setDefaultClipPath();
}
MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF)
: mnLatestTextAlign(90),
mnTextAlign(TextAlignmentMode::TA_LEFT | TextAlignmentMode::TA_TOP | TextAlignmentMode::TA_NOUPDATECP),
maLatestBkColor(ColorTransparency, 0x12345678),
maBkColor(COL_WHITE),
mnLatestTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default ),
mnTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default ),
mnLatestBkMode(BackgroundMode::NONE),
mnBkMode(BackgroundMode::OPAQUE),
meLatestRasterOp(RasterOp::Invert),
meRasterOp(RasterOp::OverPaint),
mnRop(),
meGfxMode(GraphicsMode::GM_COMPATIBLE),
meMapMode(MappingMode::MM_TEXT),
mnDevOrgX(0),
mnDevOrgY(0),
mnDevWidth(1),
mnDevHeight(1),
mnWinOrgX(0),
mnWinOrgY(0),
mnWinExtX(1),
mnWinExtY(1),
mnPixX(100),
mnPixY(100),
mnMillX(1),
mnMillY(1),
mpGDIMetaFile(&rGDIMetaFile),
mpInputStream(&rStreamWMF),
mnStartPos(0),
mnEndPos(0),
mbNopMode(false ),
mbClockWiseArcDirection(false ),
mbFillStyleSelected(false ),
mbClipNeedsUpdate(true ),
mbComplexClip(false ),
mbIsMapWinSet(false ),
mbIsMapDevSet(false )
{
SvLockBytes *pLB = mpInputStream->GetLockBytes();
if (pLB)
{
pLB->SetSynchronMode();
}
mnStartPos = mpInputStream->Tell();
SetDevOrg(Point());
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( 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
maLatestLineStyle.aLineColor = Color( 0x12, 0x34, 0x56 );
maLatestFillStyle.aFillColor = Color( 0x12, 0x34, 0x56 );
mnRop = WMFRasterOp::Black;
meRasterOp = RasterOp::OverPaint;
mpGDIMetaFile->AddAction( new MetaRasterOpAction( RasterOp::OverPaint ) );
}
MtfTools::~MtfTools() COVERITY_NOEXCEPT_FALSE
{
mpGDIMetaFile->AddAction( new MetaPopAction() );
mpGDIMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
if ( mrclFrame.IsEmpty() )
mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) );
else
mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() );
}
void MtfTools::UpdateClipRegion()
{
if (!mbClipNeedsUpdate)
return ;
mbClipNeedsUpdate = false ;
mbComplexClip = false ;
mpGDIMetaFile->AddAction( new MetaPopAction() ); // taking the original clipregion
mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) );
// skip for 'no clipping at all' case
if ( maClipPath.isEmpty() )
return ;
const basegfx::B2DPolyPolygon& rClipPoly( maClipPath.getClipPath() );
mbComplexClip = rClipPoly.count() > 1
|| !basegfx::utils::isRectangle(rClipPoly);
// This makes cases like tdf#45820 work in reasonable time.
if (mbComplexClip)
{
mpGDIMetaFile->AddAction(
new MetaISectRegionClipRegionAction(
vcl::Region(rClipPoly)));
mbComplexClip = false ;
}
else
{
mpGDIMetaFile->AddAction(
new MetaISectRectClipRegionAction(
vcl::unotools::rectangleFromB2DRectangle(
rClipPoly.getB2DRange())));
}
}
void MtfTools::ImplSetNonPersistentLineColorTransparenz()
{
WinMtfLineStyle aTransparentLine( COL_TRANSPARENT, true );
if ( ! ( maLatestLineStyle == aTransparentLine ) )
{
maLatestLineStyle = aTransparentLine;
mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) );
}
}
void MtfTools::UpdateLineStyle()
{
if (!( maLatestLineStyle == maLineStyle ) )
{
maLatestLineStyle = maLineStyle;
mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) );
}
}
void MtfTools::UpdateFillStyle()
{
if ( !mbFillStyleSelected ) // SJ: #i57205# taking care of bkcolor if no brush is selected
maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BackgroundMode::Transparent );
if (!( maLatestFillStyle == maFillStyle ) )
{
maLatestFillStyle = maFillStyle;
if (maFillStyle.aType == WinMtfFillStyleType::Solid)
mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) );
}
}
WMFRasterOp MtfTools::SetRasterOp( WMFRasterOp nRasterOp )
{
WMFRasterOp nRetROP = mnRop;
if ( nRasterOp != mnRop )
{
mnRop = nRasterOp;
if ( mbNopMode && ( nRasterOp != WMFRasterOp::Nop ) )
{ // changing modes from WMFRasterOp::Nop so set pen and brush
maFillStyle = maNopFillStyle;
maLineStyle = maNopLineStyle;
mbNopMode = false ;
}
switch ( nRasterOp )
{
case WMFRasterOp::Not :
meRasterOp = RasterOp::Invert;
break ;
case WMFRasterOp::XorPen:
meRasterOp = RasterOp::Xor ;
break ;
case WMFRasterOp::Nop:
{
meRasterOp = RasterOp::OverPaint;
if ( !mbNopMode )
{
maNopFillStyle = maFillStyle;
maNopLineStyle = maLineStyle;
maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
mbNopMode = true ;
}
}
break ;
default :
meRasterOp = RasterOp::OverPaint;
break ;
}
}
if ( nRetROP != nRasterOp )
mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
return nRetROP;
};
void MtfTools::StrokeAndFillPath( bool bStroke, bool bFill )
{
if ( !maPathObj.Count() )
return ;
UpdateClipRegion();
UpdateLineStyle();
UpdateFillStyle();
if ( bFill )
{
if ( !bStroke )
{
mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
}
if ( maPathObj.Count() == 1 )
mpGDIMetaFile->AddAction( new MetaPolygonAction( maPathObj.GetObject( 0 ) ) );
else
mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( maPathObj ) );
if ( !bStroke )
mpGDIMetaFile->AddAction( new MetaPopAction() );
}
// tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction
if ( bStroke )
{
// 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();
}
void MtfTools::DrawPixel( const Point& rSource, const Color& rColor )
{
mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) );
}
void MtfTools::MoveTo( const Point& rPoint, bool bRecordPath )
{
Point aDest( ImplMap( rPoint ) );
if ( bRecordPath )
{
// fdo#57353 create new subpath for subsequent moves
if ( maPathObj.Count() )
if ( maPathObj[ maPathObj.Count() - 1 ].GetSize() )
maPathObj.Insert( tools::Polygon() );
maPathObj.AddPoint( aDest );
}
maActPos = aDest;
}
void MtfTools::LineTo( const Point& rPoint, bool bRecordPath )
{
UpdateClipRegion();
Point aDest( ImplMap( rPoint ) );
if ( bRecordPath )
maPathObj.AddPoint( aDest );
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) );
}
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();
UpdateFillStyle();
if ( mbComplexClip )
{
tools::Polygon aPoly( ImplMap( rRect ) );
tools::PolyPolygon aPolyPolyRect( aPoly );
tools::PolyPolygon aDest;
tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( aPolyPolyRect, aDest );
ImplDrawClippedPolyPolygon( aDest );
}
else
{
if ( bEdge )
{
if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
{
ImplSetNonPersistentLineColorTransparenz();
mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( ImplMap( rRect ) ),maLineStyle.aLineInfo ) );
}
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
}
}
else
{
ImplSetNonPersistentLineColorTransparenz();
mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
}
}
}
void MtfTools::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize )
{
UpdateClipRegion();
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 )
{
UpdateClipRegion();
UpdateFillStyle();
if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
{
Point aCenter( ImplMap( rRect.Center() ) );
Size aRad( ImplMap( Size( rRect.GetWidth() / 2, rRect.GetHeight() / 2 ) ) );
ImplSetNonPersistentLineColorTransparenz();
mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( std::move(aCenter), aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) );
}
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
}
}
void MtfTools::DrawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, bool bTo )
{
UpdateClipRegion();
UpdateLineStyle();
UpdateFillStyle();
tools::Rectangle aRect( ImplMap( rRect ) );
Point aStart( ImplMap( rStart ) );
Point aEnd( ImplMap( rEnd ) );
if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
{
if ( aStart == aEnd )
{ // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse
Point aCenter( aRect.Center() );
Size aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 );
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 ) );
}
else
mpGDIMetaFile->AddAction( new MetaArcAction( aRect, aStart, aEnd ) );
if ( bTo )
maActPos = aEnd;
}
void MtfTools::DrawPie( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
{
UpdateClipRegion();
UpdateFillStyle();
tools::Rectangle aRect( ImplMap( rRect ) );
Point aStart( ImplMap( rStart ) );
Point aEnd( ImplMap( rEnd ) );
if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
{
ImplSetNonPersistentLineColorTransparenz();
mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ), maLineStyle.aLineInfo ) );
}
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
}
}
void MtfTools::DrawChord( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
{
UpdateClipRegion();
UpdateFillStyle();
tools::Rectangle aRect( ImplMap( rRect ) );
Point aStart( ImplMap( rStart ) );
Point aEnd( ImplMap( rEnd ) );
if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
{
ImplSetNonPersistentLineColorTransparenz();
mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ), maLineStyle.aLineInfo ) );
}
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
}
}
void MtfTools::DrawPolygon( tools::Polygon rPolygon, bool bRecordPath )
{
UpdateClipRegion();
ImplMap( rPolygon );
if ( bRecordPath )
maPathObj.AddPolygon( rPolygon );
else
{
UpdateFillStyle();
if ( mbComplexClip )
{
tools::PolyPolygon aPolyPoly( rPolygon );
auto tmp = maClipPath.getClip();
tmp.intersectPolyPolygon(aPolyPoly.getB2DPolyPolygon());
tools::PolyPolygon aDest(tmp.getClipPoly());
ImplDrawClippedPolyPolygon( aDest );
}
else
{
if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
{
sal_uInt16 nCount = rPolygon.GetSize();
if ( nCount )
{
if ( rPolygon[ nCount - 1 ] != rPolygon[ 0 ] )
{
Point aPoint( rPolygon[ 0 ] );
rPolygon.Insert( nCount, aPoint );
}
}
ImplSetNonPersistentLineColorTransparenz();
mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) );
}
else
{
UpdateLineStyle();
if (maLatestFillStyle.aType != WinMtfFillStyleType::Pattern)
mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(rPolygon) ) );
else {
SvtGraphicFill aFill( tools::PolyPolygon( rPolygon ),
Color(),
0.0,
SvtGraphicFill::fillNonZero,
SvtGraphicFill::fillTexture,
SvtGraphicFill::Transform(),
true ,
SvtGraphicFill::hatchSingle,
Color(),
SvtGraphicFill::GradientType::Linear,
Color(),
Color(),
0,
Graphic (BitmapEx(maLatestFillStyle.aBmp)));
SvMemoryStream aMemStm;
WriteSvtGraphicFill( aMemStm, aFill );
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" _ostr ) );
}
}
}
}
}
void MtfTools::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath )
{
UpdateClipRegion();
ImplMap( rPolyPolygon );
if ( bRecordPath )
maPathObj.AddPolyPolygon( rPolyPolygon );
else
{
UpdateFillStyle();
if ( mbComplexClip )
{
tools::PolyPolygon aDest;
tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( rPolyPolygon, aDest );
ImplDrawClippedPolyPolygon( aDest );
}
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPolygon ) );
if (maLineStyle.aLineInfo.GetWidth() > 0 || maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash)
{
for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly)
{
mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo));
}
}
}
}
}
void MtfTools::DrawPolyLine( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
{
UpdateClipRegion();
sal_uInt16 nPoints = rPolygon.GetSize();
if (nPoints < 1)
return ;
ImplMap( rPolygon );
if ( bTo )
{
rPolygon[ 0 ] = maActPos;
maActPos = rPolygon[ rPolygon.GetSize() - 1 ];
}
if ( bRecordPath )
maPathObj.AddPolyLine( rPolygon );
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) );
}
}
static bool AllowDim(tools::Long nDim)
{
static bool bFuzzing = comphelper::IsFuzzing();
if (bFuzzing)
{
if (nDim > 0x20000000 || nDim < -0x20000000)
{
SAL_WARN("vcl" , "skipping huge dimension: " << nDim);
return false ;
}
}
return true ;
}
static bool AllowPoint(const Point& rPoint)
{
return AllowDim(rPoint.X()) && AllowDim(rPoint.Y());
}
void MtfTools::DrawPolyBezier( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
{
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 )
{
rPolygon[ 0 ] = maActPos;
maActPos = rPolygon[ nPoints - 1 ];
}
sal_uInt16 i;
for ( i = 0; ( i + 2 ) < nPoints; )
{
rPolygon.SetFlags( i++, PolyFlags::Normal );
rPolygon.SetFlags( i++, PolyFlags::Control );
rPolygon.SetFlags( i++, PolyFlags::Control );
}
if ( bRecordPath )
maPathObj.AddPolyLine( rPolygon );
else
{
UpdateLineStyle();
mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) );
}
}
void MtfTools::DrawText( Point& rPosition, OUString const & rText, KernArray* pDXArry, tools::Long * pDYArry, bool bRecordPath, GraphicsMode nGfxMode )
{
UpdateClipRegion();
rPosition = ImplMap( rPosition );
GraphicsMode nOldGfxMode = GetGfxMode();
SetGfxMode( GraphicsMode::GM_COMPATIBLE );
if (pDXArry)
{
sal_Int64 nSumX = 0, nSumY = 0;
for (sal_Int32 i = 0; i < rText.getLength(); 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] = aVectorX.getLength() * (nSumX >= 0 ? 1 : -1);
if (pDYArry)
{
nSumY += pDYArry[i];
const Size aSizeY(ImplMap(Size(0, nSumY)));
const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height());
// Reverse Y
pDYArry[i] = basegfx::fround<tools::Long >(aVectorY.getLength());
pDYArry[i] *= (nSumY >= 0 ? -1 : 1);
}
}
}
if ( mnLatestTextLayoutMode != mnTextLayoutMode )
{
mnLatestTextLayoutMode = mnTextLayoutMode;
mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
}
SetGfxMode(nGfxMode);
TextAlign eTextAlign;
if (mnTextAlign & TA_BASELINE)
eTextAlign = ALIGN_BASELINE;
else if (mnTextAlign & TA_BOTTOM)
eTextAlign = ALIGN_BOTTOM;
else
eTextAlign = ALIGN_TOP;
bool bChangeFont = false ;
if ( mnLatestTextAlign != mnTextAlign )
{
bChangeFont = true ;
if ((mnLatestTextAlign & TA_RTLREADING) != (mnTextAlign & TA_RTLREADING))
{
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=93 H=94 G=93
¤ Dauer der Verarbeitung: 0.36 Sekunden
¤
*© Formatika GbR, Deutschland