Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/emfio/source/reader/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 96 kB image not shown  

Quelle  mtftools.cxx   Sprache: C

 
/* -*- 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, RegionMode 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 &&nbsp;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






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.