tdf#147999: Fix canvas font width for fallback fonts on Windows

For some reason we are getting the wrong font width for fallback fonts, which
results in rendering them squished. This happens only with VCL canvas, but not
with DX one, so it shows when hardware acceleration is disabled (either
explicitly or implicitly when Skia is enabled).

Change-Id: I5a45b1c1d68f4c6e6dd6b43371602af3330a7cd3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154272
Tested-by: Jenkins
Reviewed-by: خالد حسني <khaled@libreoffice.org>
diff --git a/canvas/source/vcl/canvasfont.cxx b/canvas/source/vcl/canvasfont.cxx
index e7fab04..41b7da2 100644
--- a/canvas/source/vcl/canvasfont.cxx
+++ b/canvas/source/vcl/canvasfont.cxx
@@ -47,7 +47,8 @@ namespace vclcanvas
                      Size( 0, ::basegfx::fround(rFontRequest.CellSize) ) ) ),
        maFontRequest( rFontRequest ),
        mpRefDevice( &rDevice ),
        mpOutDevProvider( rOutDevProvider )
        mpOutDevProvider( rOutDevProvider ),
        maFontMatrix( rFontMatrix )
    {
        maFont->SetAlignment( ALIGN_BASELINE );
        maFont->SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==css::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
@@ -63,27 +64,7 @@ namespace vclcanvas
        maFont->SetLanguage( LanguageTag::convertToLanguageType( rFontRequest.Locale, false));

        // adjust to stretched/shrunk font
        if( !::rtl::math::approxEqual( rFontMatrix.m00, rFontMatrix.m11) )
        {
            OutputDevice& rOutDev( rOutDevProvider->getOutDev() );

            const bool bOldMapState( rOutDev.IsMapModeEnabled() );
            rOutDev.EnableMapMode(false);

            const Size aSize = rOutDev.GetFontMetric( *maFont ).GetFontSize();

            const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
            double fStretch = rFontMatrix.m00 + rFontMatrix.m01;

            if( !::basegfx::fTools::equalZero( fDividend) )
                fStretch /= fDividend;

            const ::tools::Long nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );

            maFont->SetAverageFontWidth( nNewWidth );

            rOutDev.EnableMapMode(bOldMapState);
        }
        tools::setupFontWidth(rFontMatrix, maFont.get(), rOutDevProvider->getOutDev());

        sal_uInt32 nEmphasisMark = 0;

@@ -172,6 +153,13 @@ namespace vclcanvas
    {
        return *maFont;
    }

    const css::geometry::Matrix2D& CanvasFont::getFontMatrix() const
    {
        SolarMutexGuard aGuard;

        return maFontMatrix;
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/canvas/source/vcl/canvasfont.hxx b/canvas/source/vcl/canvasfont.hxx
index fdfa870..834df75 100644
--- a/canvas/source/vcl/canvasfont.hxx
+++ b/canvas/source/vcl/canvasfont.hxx
@@ -75,11 +75,14 @@ namespace vclcanvas

        vcl::Font const & getVCLFont() const;

        const css::geometry::Matrix2D& getFontMatrix() const;

    private:
        ::canvas::vcltools::VCLObject<vcl::Font>                          maFont;
        css::rendering::FontRequest                                       maFontRequest;
        css::uno::Reference< css::rendering::XGraphicDevice>              mpRefDevice;
        OutDevProviderSharedPtr                                           mpOutDevProvider;
        css::geometry::Matrix2D                                           maFontMatrix;
    };

}
diff --git a/canvas/source/vcl/impltools.cxx b/canvas/source/vcl/impltools.cxx
index 5539a92d..0a44f22 100644
--- a/canvas/source/vcl/impltools.cxx
+++ b/canvas/source/vcl/impltools.cxx
@@ -135,6 +135,33 @@ namespace vclcanvas::tools
            return true;
        }

        void setupFontWidth(const css::geometry::Matrix2D& rFontMatrix,
                            vcl::Font&                     rFont,
                            OutputDevice&                  rOutDev)
        {
            rFont.SetFontSize(Size(0, rFont.GetFontHeight()));

            if (!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
            {
                const bool bOldMapState(rOutDev.IsMapModeEnabled());
                rOutDev.EnableMapMode(false);

                const Size aSize = rOutDev.GetFontMetric(rFont).GetFontSize();

                const double fDividend(rFontMatrix.m10 + rFontMatrix.m11);
                double fStretch = rFontMatrix.m00 + rFontMatrix.m01;

                if (!::basegfx::fTools::equalZero(fDividend))
                    fStretch /= fDividend;

                const ::tools::Long nNewWidth = ::basegfx::fround(aSize.Width() * fStretch);

                rFont.SetAverageFontWidth(nNewWidth);

                rOutDev.EnableMapMode(bOldMapState);
            }
        }

        bool isRectangle( const ::tools::PolyPolygon& rPolyPoly )
        {
            // exclude some cheap cases first
diff --git a/canvas/source/vcl/impltools.hxx b/canvas/source/vcl/impltools.hxx
index f8a9db0..030d534 100644
--- a/canvas/source/vcl/impltools.hxx
+++ b/canvas/source/vcl/impltools.hxx
@@ -60,6 +60,7 @@ namespace com::sun::star::geometry
    struct RealPoint2D;
    struct RealSize2D;
    struct RealRectangle2D;
    struct Matrix2D;
}

namespace com::sun::star::rendering
@@ -88,6 +89,10 @@ namespace vclcanvas
                                 const css::rendering::RenderState&    renderState,
                                 ::OutputDevice const &                rOutDev );

        void setupFontWidth(const css::geometry::Matrix2D& rFontMatrix,
                            vcl::Font&                     rFont,
                            OutputDevice&                  rOutDev);

        /** Predicate, to determine whether polygon is actually an axis-aligned rectangle

            @return true, if the polygon is a rectangle.
diff --git a/canvas/source/vcl/textlayout.cxx b/canvas/source/vcl/textlayout.cxx
index 2d56d2f..12273da 100644
--- a/canvas/source/vcl/textlayout.cxx
+++ b/canvas/source/vcl/textlayout.cxx
@@ -350,6 +350,14 @@ namespace vclcanvas
    {
        SolarMutexGuard aGuard;

#ifdef _WIN32
        // tdf#147999
        // On Windows we get the wrong font width for fallback fonts unless we setup again here.
        vcl::Font aFont(rOutDev.GetFont());
        tools::setupFontWidth(mpFont->getFontMatrix(), aFont, rOutDev);
        rOutDev.SetFont(aFont);
#endif

        setupLayoutMode( rOutDev, mnTextDirection );

        if( maLogicalAdvancements.hasElements() )