vcl/inc/font/LogicalFontInstance.hxx           |    5 -
 vcl/inc/impfontcache.hxx                       |    8 +-
 vcl/inc/impglyphitem.hxx                       |    6 +-
 vcl/inc/pdf/pdfbuildin_fonts.hxx               |    2 
 vcl/inc/qt5/QtFont.hxx                         |    1 
 vcl/inc/quartz/salgdi.h                        |    2 
 vcl/inc/unx/freetype_glyphcache.hxx            |    2 
 vcl/inc/unx/glyphcache.hxx                     |    1 
 vcl/inc/win/DWriteTextRenderer.hxx             |   15 -----
 vcl/inc/win/winlayout.hxx                      |    4 -
 vcl/qa/cppunit/cjktext.cxx                     |    2 
 vcl/qa/cppunit/fontmocks.hxx                   |    3 -
 vcl/qa/cppunit/logicalfontinstance.cxx         |   67 +++++++++++++++++++++----
 vcl/qt5/QtFont.cxx                             |    7 --
 vcl/quartz/ctfonts.cxx                         |   21 -------
 vcl/skia/gdiimpl.cxx                           |   15 ++---
 vcl/skia/win/gdiimpl.cxx                       |   33 +++++-------
 vcl/skia/x11/textrender.cxx                    |   10 +--
 vcl/source/font/LogicalFontInstance.cxx        |   36 +++++++++++--
 vcl/source/font/fontcache.cxx                  |    4 -
 vcl/source/gdi/CommonSalLayout.cxx             |    6 +-
 vcl/source/gdi/pdfbuildin_fonts.cxx            |    5 -
 vcl/source/gdi/pdfwriter_impl.cxx              |   29 +++++++++-
 vcl/source/gdi/sallayout.cxx                   |   37 +++++++++----
 vcl/source/outdev/font.cxx                     |    6 +-
 vcl/unx/generic/glyphs/freetype_glyphcache.cxx |   38 --------------
 vcl/unx/generic/glyphs/glyphcache.cxx          |    8 --
 vcl/win/gdi/DWriteTextRenderer.cxx             |   28 ++++++----
 vcl/win/gdi/salfont.cxx                        |   53 -------------------
 vcl/win/gdi/winlayout.cxx                      |   18 ------
 vcl/workben/listglyphs.cxx                     |    2 
 31 files changed, 204 insertions(+), 270 deletions(-)

New commits:
commit 36e8e419d022a9c43302efe5e702a704dea39e76
Author:     Mike Kaganski <[email protected]>
AuthorDate: Mon Apr 8 02:45:28 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 10:40:50 2024 +0500

    Exclude getHScale from DirectWrite font rendering
    
    ... and drop it, since it's unused now.
    Unkike with Skia, where the ratio produces a visible effect, in
    DirectWrite the effect seems cancelled by transformations. Yet,
    it produced computational instability, noticable in small vertical
    text.
    
    Change-Id: I2f3b20913075d1338dc75c5a04c9cc0ef29c75ce
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165877
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/inc/win/DWriteTextRenderer.hxx 
b/vcl/inc/win/DWriteTextRenderer.hxx
index 67094052ebb5..a655df6459ea 100644
--- a/vcl/inc/win/DWriteTextRenderer.hxx
+++ b/vcl/inc/win/DWriteTextRenderer.hxx
@@ -72,21 +72,6 @@ private:
     D2DTextAntiAliasMode meTextAntiAliasMode;
 };
 
-/**
- * Sets and unsets the needed DirectWrite transform to support the font's 
horizontal scaling and
- * rotation.
- */
-class WinFontTransformGuard
-{
-public:
-    WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, float fHScale, 
const GenericSalLayout& rLayout, const D2D1_POINT_2F& rBaseline, bool 
bIsVertical);
-    ~WinFontTransformGuard();
-
-private:
-    ID2D1RenderTarget* mpRenderTarget;
-    D2D1::Matrix3x2F maTransform;
-};
-
 #endif // INCLUDED_VCL_INC_WIN_DWRITERENDERER_HXX
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index 0f253230c1b4..ac70e42b66cf 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -36,8 +36,6 @@ class WinFontInstance : public LogicalFontInstance
 public:
     ~WinFontInstance() override;
 
-    float getHScale() const;
-
     void SetGraphics(WinSalGraphics*);
     WinSalGraphics* GetGraphics() const { return m_pGraphics; }
 
diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx 
b/vcl/win/gdi/DWriteTextRenderer.cxx
index b7d7c03e9995..321587db8137 100644
--- a/vcl/win/gdi/DWriteTextRenderer.cxx
+++ b/vcl/win/gdi/DWriteTextRenderer.cxx
@@ -97,6 +97,20 @@ HRESULT checkResult(HRESULT hr, const char* file, size_t 
line)
 #endif
 
 
+// Sets and unsets the needed DirectWrite transform to support the font's 
rotation.
+class WinFontTransformGuard
+{
+public:
+    WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget,
+                          const GenericSalLayout& rLayout, const 
D2D1_POINT_2F& rBaseline,
+                          bool bIsVertical);
+    ~WinFontTransformGuard();
+
+private:
+    ID2D1RenderTarget* mpRenderTarget;
+    D2D1::Matrix3x2F maTransform;
+};
+
 } // end anonymous namespace
 
 D2DWriteTextOutRenderer::D2DWriteTextOutRenderer(bool bRenderingModeNatural)
@@ -218,7 +232,6 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
     }
 
     const WinFontInstance& rWinFont = static_cast<const 
WinFontInstance&>(rLayout.GetFont());
-    float fHScale = rWinFont.getHScale();
 
     float lfEmHeight = 0;
     IDWriteFontFace* pFontFace = GetDWriteFace(rWinFont, &lfEmHeight);
@@ -251,11 +264,11 @@ bool 
D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, Sa
         while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
         {
             UINT16 glyphIndices[] = { static_cast<UINT16>(pGlyph->glyphId()) };
-            FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->newWidth()) / 
fHScale };
+            FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->newWidth()) };
             DWRITE_GLYPH_OFFSET glyphOffsets[] = { { 0.0f, 0.0f }, };
-            D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.getX() - 
bounds.Left()) / fHScale,
+            D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.getX() - 
bounds.Left()),
                                        static_cast<FLOAT>(aPos.getY() - 
bounds.Top()) };
-            WinFontTransformGuard aTransformGuard(mpRT, fHScale, rLayout, 
baseline, pGlyph->IsVertical());
+            WinFontTransformGuard aTransformGuard(mpRT, rLayout, baseline, 
pGlyph->IsVertical());
             DWRITE_GLYPH_RUN glyphs = {
                 pFontFace,
                 lfEmHeight,
@@ -306,7 +319,7 @@ IDWriteFontFace* 
D2DWriteTextOutRenderer::GetDWriteFace(const WinFontInstance& r
     return pFontFace;
 }
 
-WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, 
float fHScale,
+WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget,
                                              const GenericSalLayout& rLayout,
                                              const D2D1_POINT_2F& rBaseline,
                                              bool bIsVertical)
@@ -314,11 +327,6 @@ 
WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, f
 {
     pRenderTarget->GetTransform(&maTransform);
     D2D1::Matrix3x2F aTransform = maTransform;
-    if (fHScale != 1.0f)
-    {
-        aTransform
-            = aTransform * D2D1::Matrix3x2F::Scale(D2D1::Size(fHScale, 1.0f), 
D2D1::Point2F(0, 0));
-    }
 
     Degree10 angle = rLayout.GetOrientation();
 
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 3865f2ebed66..f7ac7f5ff851 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -147,14 +147,6 @@ WinFontInstance::~WinFontInstance()
         ::DeleteFont(m_hFont);
 }
 
-float WinFontInstance::getHScale() const
-{
-    const vcl::font::FontSelectPattern& rPattern = GetFontSelectPattern();
-    if (!rPattern.mnHeight || !rPattern.mnWidth)
-        return 1.0;
-    return rPattern.mnWidth * GetAverageWidthFactor() / rPattern.mnHeight;
-}
-
 void WinFontInstance::ImplInitHbFont(hb_font_t* /*pHbFont*/)
 {
     assert(m_pGraphics);
commit ee5584b926cd10ea600a3514242148db5a0a6f18
Author:     Mike Kaganski <[email protected]>
AuthorDate: Thu Mar 28 19:52:00 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 02:50:13 2024 +0500

    Simplify and drop unused method
    
    Change-Id: Ie987bce2bb25232d54a4a83631c609ad6dba7213
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165467
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index 18de0e684b1c..0f253230c1b4 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -36,7 +36,6 @@ class WinFontInstance : public LogicalFontInstance
 public:
     ~WinFontInstance() override;
 
-    bool hasHScale() const;
     float getHScale() const;
 
     void SetGraphics(WinSalGraphics*);
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 371bdeb9642e..3865f2ebed66 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -147,22 +147,12 @@ WinFontInstance::~WinFontInstance()
         ::DeleteFont(m_hFont);
 }
 
-bool WinFontInstance::hasHScale() const
-{
-    const vcl::font::FontSelectPattern& rPattern = GetFontSelectPattern();
-    int nHeight(rPattern.mnHeight);
-    int nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : 
nHeight);
-    return nWidth != nHeight;
-}
-
 float WinFontInstance::getHScale() const
 {
     const vcl::font::FontSelectPattern& rPattern = GetFontSelectPattern();
-    int nHeight(rPattern.mnHeight);
-    if (!nHeight)
+    if (!rPattern.mnHeight || !rPattern.mnWidth)
         return 1.0;
-    float nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() 
: nHeight);
-    return nWidth / nHeight;
+    return rPattern.mnWidth * GetAverageWidthFactor() / rPattern.mnHeight;
 }
 
 void WinFontInstance::ImplInitHbFont(hb_font_t* /*pHbFont*/)
commit 49e6927e3bd44021796b97d1ebb7886923f6cbcc
Author:     Mike Kaganski <[email protected]>
AuthorDate: Tue Apr 2 08:55:51 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 02:38:32 2024 +0500

    Fix UB after 8962141a12c966b2d891829925e6203bf8d51852
    
    (tdf#160430: Fix glyph bounds calculation, and use basegfx::B2DRectangle, 
2024-04-01).
    As reported by Stephan in 
https://gerrit.libreoffice.org/c/core/+/165553/6#message-fec1e45288c0e87d43c58f777ebe51b03c534d82:
    
     `CppunitTest_sw_rtfexport CPPUNIT_TEST_NAME=testMathEqarray::TestBody` now 
fails with
    
      vcl/source/gdi/sallayout.cxx:245:30: runtime error: inf is outside the 
range of representable values of type 'long'
       #0 in SalLayout::GetBoundRect(tools::Rectangle&) const at 
vcl/source/gdi/sallayout.cxx:245:30
       #1 in OutputDevice::GetTextBoundRect(tools::Rectangle&, rtl::OUString 
const&, int, int, int, unsigned long, KernArraySpan, std::span<unsigned char 
const, 18446744073709551615ul>, SalLayoutGlyphs const*) const at 
vcl/source/outdev/text.cxx:1932:28
       #2 in (anonymous namespace)::SmGetGlyphBoundRect(OutputDevice const&, 
rtl::OUString const&, tools::Rectangle&) at starmath/source/rect.cxx:80:32
       #3 in SmRect::SmRect(OutputDevice const&, SmFormat const*, rtl::OUString 
const&, unsigned short) at starmath/source/rect.cxx:224:21
       #4 in SmMathSymbolNode::AdaptToY(OutputDevice&, unsigned long) at 
starmath/source/node.cxx:2122:18
       #5 in SmBraceNode::Arrange(OutputDevice&, SmFormat const&) at 
starmath/source/node.cxx:1340:17
       #6 in SmBinHorNode::Arrange(OutputDevice&, SmFormat const&) at 
starmath/source/node.cxx:816:13
       #7 in SmLineNode::Arrange(OutputDevice&, SmFormat const&) at 
starmath/source/node.cxx:610:20
       #8 in SmTableNode::Arrange(OutputDevice&, SmFormat const&) at 
starmath/source/node.cxx:534:20
       #9 in SmDocShell::ArrangeFormula() at starmath/source/document.cxx:280:13
       #10 in SmDocShell::GetSize() at starmath/source/document.cxx:405:9
       #11 in SmDocShell::Repaint() at starmath/source/document.cxx:566:21
       #12 in SmDocShell::OnDocumentPrinterChanged(Printer*) at 
starmath/source/document.cxx:552:5
       #13 in SmDocShell::SetText(rtl::OUString const&) at 
starmath/source/document.cxx:188:9
       #14 in SmDocShell::readFormulaOoxml(oox::formulaimport::XmlStream&) at 
starmath/source/document.cxx:848:5
       #15 in SmModel::readFormulaOoxml(oox::formulaimport::XmlStream&) at 
starmath/source/unomodel.cxx:1105:22
       #16 in 
writerfilter::rtftok::RTFDocumentImpl::beforePopState(writerfilter::rtftok::RTFParserState&)
 at writerfilter/source/rtftok/rtfdocumentimpl.cxx:3010:30
       #17 in writerfilter::rtftok::RTFDocumentImpl::popState() at 
writerfilter/source/rtftok/rtfdocumentimpl.cxx:3666:23
       #18 in writerfilter::rtftok::RTFTokenizer::resolveParse() at 
writerfilter/source/rtftok/rtftokenizer.cxx:114:37
       #19 in 
writerfilter::rtftok::RTFDocumentImpl::resolve(writerfilter::Stream&) at 
writerfilter/source/rtftok/rtfdocumentimpl.cxx:856:27
       #20 in (anonymous 
namespace)::RtfFilter::filter(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>
 const&) at writerfilter/source/filter/RtfFilter.cxx:163:20
       #21 in SfxObjectShell::ImportFrom(SfxMedium&, 
com::sun::star::uno::Reference<com::sun::star::text::XTextRange> const&) at 
sfx2/source/doc/objstor.cxx:2392:34
       #22 in SfxObjectShell::DoLoad(SfxMedium*) at 
sfx2/source/doc/objstor.cxx:760:23
       #23 in 
SfxBaseModel::load(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>
 const&) at sfx2/source/doc/sfxbasemodel.cxx:1980:36
       #24 in (anonymous 
namespace)::SfxFrameLoader_Impl::load(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>
 const&, com::sun::star::uno::Reference<com::sun::star::frame::XFrame> const&) 
at sfx2/source/view/frmload.cxx:720:28
       #25 in framework::LoadEnv::impl_loadContent() at 
framework/source/loadenv/loadenv.cxx:1176:37
       #26 in framework::LoadEnv::start() at 
framework/source/loadenv/loadenv.cxx:412:20
       #27 in framework::LoadEnv::startLoading(rtl::OUString const&, 
com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&, 
com::sun::star::uno::Reference<com::sun::star::frame::XFrame> const&, 
rtl::OUString const&, int, LoadEnvFeatures) at 
framework/source/loadenv/loadenv.cxx:308:5
       #28 in 
framework::LoadEnv::loadComponentFromURL(com::sun::star::uno::Reference<com::sun::star::frame::XComponentLoader>
 const&, com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> 
const&, rtl::OUString const&, rtl::OUString const&, int, 
com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) at 
framework/source/loadenv/loadenv.cxx:168:14
       #29 in framework::Desktop::loadComponentFromURL(rtl::OUString const&, 
rtl::OUString const&, int, 
com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) at 
framework/source/services/desktop.cxx:591:16
       #30 in non-virtual thunk to 
framework::Desktop::loadComponentFromURL(rtl::OUString const&, rtl::OUString 
const&, int, 
com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) at 
framework/source/services/desktop.cxx
       #31 in unotest::MacrosTest::loadFromDesktop(rtl::OUString const&, 
rtl::OUString const&, 
com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) at 
unotest/source/cpp/macros_test.cxx:71:62
       #32 in UnoApiTest::loadWithParams(rtl::OUString const&, 
com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) at 
test/source/unoapi_test.cxx:126:19
       #33 in UnoApiTest::load(rtl::OUString const&, char const*) at 
test/source/unoapi_test.cxx:108:5
       #34 in SwModelTestBase::loadURL(rtl::OUString const&, char const*) at 
sw/qa/unit/swmodeltestbase.cxx:441:20
       #35 in SwModelTestBase::loadAndReload(char const*) at 
sw/qa/unit/swmodeltestbase.cxx:466:5
       #36 in (anonymous namespace)::testMathEqarray::TestBody() at 
sw/qa/extras/rtfexport/rtfexport.cxx:198:5
    
    Change-Id: I857861f5bc51a1e43bfbf5e0c9dbce542d673ca7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165664
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx
index 7397748ecd72..c8c2d6ca1093 100644
--- a/vcl/source/gdi/sallayout.cxx
+++ b/vcl/source/gdi/sallayout.cxx
@@ -216,7 +216,10 @@ bool SalLayout::GetOutline(basegfx::B2DPolyPolygonVector& 
rVector) const
 }
 
 // No need to expand to the next pixel, when the character only covers its 
tiny fraction
-static double trimInsignificant(double n) { return std::round(n * 1e5) / 1e5; }
+static double trimInsignificant(double n)
+{
+    return std::abs(n) >= 0x1p53 ? n : std::round(n * 1e5) / 1e5;
+}
 
 bool SalLayout::GetBoundRect(tools::Rectangle& rRect) const
 {
@@ -243,10 +246,19 @@ bool SalLayout::GetBoundRect(tools::Rectangle& rRect) 
const
             bRet = true;
         }
     }
-    rRect = 
tools::Rectangle(rtl::math::approxFloor(trimInsignificant(aUnion.getMinX())),
-                             
rtl::math::approxFloor(trimInsignificant(aUnion.getMinY())),
-                             
rtl::math::approxCeil(trimInsignificant(aUnion.getMaxX())),
-                             
rtl::math::approxCeil(trimInsignificant(aUnion.getMaxY())));
+    if (aUnion.isEmpty())
+    {
+        rRect = {};
+    }
+    else
+    {
+        double l = rtl::math::approxFloor(trimInsignificant(aUnion.getMinX())),
+               t = rtl::math::approxFloor(trimInsignificant(aUnion.getMinY())),
+               r = rtl::math::approxCeil(trimInsignificant(aUnion.getMaxX())),
+               b = rtl::math::approxCeil(trimInsignificant(aUnion.getMaxY()));
+        assert(std::isfinite(l) && std::isfinite(t) && std::isfinite(r) && 
std::isfinite(b));
+        rRect = tools::Rectangle(l, t, r, b);
+    }
 
     return bRet;
 }
commit a2c412dbc17b5c9a7b12639390b58b73cc858e49
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sun Mar 31 00:45:25 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 02:36:07 2024 +0500

    tdf#160436: fix glyph bounds calculation for vertical glyphs
    
    It is unclear if LogicalFontInstance::GetGlyphBoundRect can be called
    for both normal and rotated variants of the same glyph in the same font.
    If yes, then the normal and vertical variants must be cached separately,
    or possibly vertical variant can be not cached, but always calculated.
    This problem already existed before, so this change doesn't introduce
    a new issue.
    
    Change-Id: I9b50ef340c9e38db7bef890165519aadc96d3ffa
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165581
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/qa/cppunit/logicalfontinstance.cxx 
b/vcl/qa/cppunit/logicalfontinstance.cxx
index 1eaf0ebd28db..77de9b9b4e3f 100644
--- a/vcl/qa/cppunit/logicalfontinstance.cxx
+++ b/vcl/qa/cppunit/logicalfontinstance.cxx
@@ -46,6 +46,7 @@ void VclLogicalFontInstanceTest::testglyphboundrect()
 
     basegfx::B2DRectangle aBoundRect;
     const auto LATIN_SMALL_LETTER_B = 0x0062;
+    const auto SECTION_SIGN = 0x00A7; // UTR#50: Vertical_Orientation (vo) 
property value U
     
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(LATIN_SMALL_LETTER_B),
 aBoundRect,
                                      false);
 
@@ -54,6 +55,14 @@ void VclLogicalFontInstanceTest::testglyphboundrect()
     CPPUNIT_ASSERT_DOUBLES_EQUAL(49.5, aBoundRect.getWidth(), 0.05);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(80.8, aBoundRect.getHeight(), 0.05);
 
+    // tdf#160436: test vertically oriented glyphs
+    
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(SECTION_SIGN), 
aBoundRect, true);
+
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-79.7, aBoundRect.getMinX(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-55.0, aBoundRect.getMinY(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(88.9, aBoundRect.getWidth(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(48.8, aBoundRect.getHeight(), 0.05);
+
     font.SetOrientation(900_deg10);
     device->SetFont(font);
 
@@ -67,6 +76,14 @@ void VclLogicalFontInstanceTest::testglyphboundrect()
     CPPUNIT_ASSERT_DOUBLES_EQUAL(80.8, aBoundRect.getWidth(), 0.05);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(49.5, aBoundRect.getHeight(), 0.05);
 
+    // tdf#160436: test vertically oriented glyphs
+    
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(SECTION_SIGN), 
aBoundRect, true);
+
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-55.0, aBoundRect.getMinX(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-9.2, aBoundRect.getMinY(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(48.8, aBoundRect.getWidth(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(88.9, aBoundRect.getHeight(), 0.05);
+
     font.SetOrientation(450_deg10);
     device->SetFont(font);
 
@@ -79,6 +96,14 @@ void VclLogicalFontInstanceTest::testglyphboundrect()
     CPPUNIT_ASSERT_DOUBLES_EQUAL(-96.4, aBoundRect.getMinY(), 0.05);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(92.1, aBoundRect.getWidth(), 0.05);
     CPPUNIT_ASSERT_DOUBLES_EQUAL(92.1, aBoundRect.getHeight(), 0.05);
+
+    // tdf#160436: test vertically oriented glyphs
+    
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(SECTION_SIGN), 
aBoundRect, true);
+
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-95.3, aBoundRect.getMinX(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-45.4, aBoundRect.getMinY(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(97.4, aBoundRect.getWidth(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(97.4, aBoundRect.getHeight(), 0.05);
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(VclLogicalFontInstanceTest);
diff --git a/vcl/source/font/LogicalFontInstance.cxx 
b/vcl/source/font/LogicalFontInstance.cxx
index 6524422397f0..9d893b85aa70 100644
--- a/vcl/source/font/LogicalFontInstance.cxx
+++ b/vcl/source/font/LogicalFontInstance.cxx
@@ -171,8 +171,8 @@ void LogicalFontInstance::IgnoreFallbackForUnicode(sal_UCS4 
cChar, FontWeight eW
 bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId nID, 
basegfx::B2DRectangle& rRect,
                                             bool bVertical) const
 {
-    // TODO/FIXME: bVertical handling here is highly suspicious. When it's 
true, it may
-    // return different rectangle, depending on if this glyph was cached 
already or not.
+    // TODO: find out if it's possible for the same glyph in the same font to 
be used both
+    // normally and vertically; if yes, then these two variants must be cached 
separately
 
     if (mpFontCache && mpFontCache->GetCachedGlyphBoundRect(this, nID, rRect))
         return true;
@@ -191,10 +191,13 @@ bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId 
nID, basegfx::B2DRectang
     double fMaxY = -(aExtents.y_bearing + aExtents.height) * nYScale;
     rRect = basegfx::B2DRectangle(fMinX, fMinY, fMaxX, fMaxY);
 
-    if (mnOrientation && !bVertical)
+    auto orientation = mnOrientation;
+    if (bVertical)
+        orientation += 900_deg10;
+    if (orientation)
     {
         // Apply font rotation.
-        
rRect.transform(basegfx::utils::createRotateB2DHomMatrix(-toRadians(mnOrientation)));
+        
rRect.transform(basegfx::utils::createRotateB2DHomMatrix(-toRadians(orientation)));
     }
 
     if (mpFontCache)
commit aa5514cce0ab4e12bbf7f20a7d4dbf16b89ae58c
Author:     Mike Kaganski <[email protected]>
AuthorDate: Fri Mar 29 20:15:06 2024 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 02:35:44 2024 +0500

    tdf#160430: Fix glyph bounds calculation, and use basegfx::B2DRectangle
    
    ... instead of tools::Rectangle.
    
    Several problems were there:
    1. First, a horizontal bounding rectangle was calculated, with due
    rounding; and then the result was rotated, and after that, rounded
    again. That made the resulting rotated rectangle coordinates very
    imprecise.
    2. Also, ceil/floor was applied without normalization; and in case
    of rotated font, that meant, that sometimes the range could be not
    expanded to cover partially covered pixels, but instead collapsed.
    3. The rotation to angles other than 90 degree multiples was done
    incorrectly, resulting in cut off parts of characters.
    4. For 90 degrees, the imprecise result of sin/cos converted 0.0
    into values like 3e-16, which then could be ceil'ed up to 1.
    
    Using B2DRectangle and its transform allows to simplify and fix
    the calculations easily, and avoids premature rounding. Render of
    rotated text of small size is more stable with this change.
    
    Change-Id: Idffd74b9937feb2418ab76a8d325fdaf4ff841b7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165553
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/inc/font/LogicalFontInstance.hxx 
b/vcl/inc/font/LogicalFontInstance.hxx
index 518716b5c7df..e8124eac7eca 100644
--- a/vcl/inc/font/LogicalFontInstance.hxx
+++ b/vcl/inc/font/LogicalFontInstance.hxx
@@ -22,6 +22,7 @@
 #include <sal/config.h>
 
 #include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/range/b2drectangle.hxx>
 #include <o3tl/hash_combine.hxx>
 #include <rtl/ref.hxx>
 #include <salhelper/simplereferenceobject.hxx>
@@ -101,7 +102,7 @@ public: // TODO: make data members private
     vcl::font::PhysicalFontFace* GetFontFace() { return m_pFontFace.get(); }
     const ImplFontCache* GetFontCache() const { return mpFontCache; }
 
-    bool GetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) const;
+    bool GetGlyphBoundRect(sal_GlyphId, basegfx::B2DRectangle&, bool) const;
     virtual bool GetGlyphOutline(sal_GlyphId, basegfx::B2DPolyPolygon&, bool) 
const = 0;
     bool GetGlyphOutlineUntransformed(sal_GlyphId, basegfx::B2DPolyPolygon&) 
const;
 
diff --git a/vcl/inc/impfontcache.hxx b/vcl/inc/impfontcache.hxx
index 5ea19b05d9a5..4d197003b279 100644
--- a/vcl/inc/impfontcache.hxx
+++ b/vcl/inc/impfontcache.hxx
@@ -21,10 +21,10 @@
 
 #include <sal/config.h>
 
+#include <basegfx/range/b2drectangle.hxx>
 #include <rtl/ref.hxx>
 #include <o3tl/lru_map.hxx>
 #include <o3tl/hash_combine.hxx>
-#include <tools/gen.hxx>
 
 #include "font/FontSelectPattern.hxx"
 #include "glyphid.hxx"
@@ -59,7 +59,7 @@ struct GlyphBoundRectCacheHash
     }
 };
 
-typedef o3tl::lru_map<GlyphBoundRectCacheKey, tools::Rectangle,
+typedef o3tl::lru_map<GlyphBoundRectCacheKey, basegfx::B2DRectangle,
                       GlyphBoundRectCacheHash> GlyphBoundRectCache;
 
 class ImplFontCache
@@ -86,8 +86,8 @@ public:
                             LogicalFontInstance* pLogicalFont,
                             int nFallbackLevel, OUString& rMissingCodes );
 
-    bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, 
tools::Rectangle &);
-    void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, 
tools::Rectangle &);
+    bool GetCachedGlyphBoundRect(const LogicalFontInstance*, sal_GlyphId, 
basegfx::B2DRectangle&);
+    void CacheGlyphBoundRect(const LogicalFontInstance*, sal_GlyphId, 
basegfx::B2DRectangle&);
 
     void                Invalidate();
 };
diff --git a/vcl/inc/impglyphitem.hxx b/vcl/inc/impglyphitem.hxx
index 87f966bd1676..a3f1b8a7a7a6 100644
--- a/vcl/inc/impglyphitem.hxx
+++ b/vcl/inc/impglyphitem.hxx
@@ -20,8 +20,8 @@
 #ifndef INCLUDED_VCL_IMPGLYPHITEM_HXX
 #define INCLUDED_VCL_IMPGLYPHITEM_HXX
 
+#include <basegfx/range/b2drectangle.hxx>
 #include <o3tl/typed_flags_set.hxx>
-#include <tools/gen.hxx>
 #include <vcl/dllapi.h>
 #include <vcl/outdev.hxx>
 #include <vector>
@@ -87,7 +87,7 @@ public:
         return bool(m_nFlags & GlyphItemFlags::IS_SAFE_TO_INSERT_KASHIDA);
     }
 
-    inline bool GetGlyphBoundRect(const LogicalFontInstance*, 
tools::Rectangle&) const;
+    inline bool GetGlyphBoundRect(const LogicalFontInstance*, 
basegfx::B2DRectangle&) const;
     inline bool GetGlyphOutline(const LogicalFontInstance*, 
basegfx::B2DPolyPolygon&) const;
     inline void dropGlyph();
 
@@ -117,7 +117,7 @@ public:
 };
 
 bool GlyphItem::GetGlyphBoundRect(const LogicalFontInstance* pFontInstance,
-                                  tools::Rectangle& rRect) const
+                                  basegfx::B2DRectangle& rRect) const
 {
     return pFontInstance->GetGlyphBoundRect(m_aGlyphId, rRect, IsVertical());
 }
diff --git a/vcl/qa/cppunit/logicalfontinstance.cxx 
b/vcl/qa/cppunit/logicalfontinstance.cxx
index 12ee95762eef..1eaf0ebd28db 100644
--- a/vcl/qa/cppunit/logicalfontinstance.cxx
+++ b/vcl/qa/cppunit/logicalfontinstance.cxx
@@ -39,25 +39,46 @@ void VclLogicalFontInstanceTest::testglyphboundrect()
 {
     ScopedVclPtr<VirtualDevice> device = 
VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
     device->SetOutputSizePixel(Size(1000, 1000));
-    device->SetFont(vcl::Font("Liberation Sans", Size(0, 110)));
+    vcl::Font font("Liberation Sans", Size(0, 110));
+    device->SetFont(font);
 
     const LogicalFontInstance* pFontInstance = device->GetFontInstance();
 
-    tools::Rectangle aBoundRect;
+    basegfx::B2DRectangle aBoundRect;
     const auto LATIN_SMALL_LETTER_B = 0x0062;
     
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(LATIN_SMALL_LETTER_B),
 aBoundRect,
                                      false);
 
-    const tools::Long nExpectedX = 7;
-    const tools::Long nExpectedY = -80;
-    const tools::Long nExpectedWidth = 51;
-    const tools::Long nExpectedHeight = 83;
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(7.1, aBoundRect.getMinX(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-79.7, aBoundRect.getMinY(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(49.5, aBoundRect.getWidth(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(80.8, aBoundRect.getHeight(), 0.05);
 
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("x of glyph is wrong", nExpectedX, 
aBoundRect.getX());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("y of glyph is wrong", nExpectedY, 
aBoundRect.getY());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("height of glyph of wrong", nExpectedWidth, 
aBoundRect.GetWidth());
-    CPPUNIT_ASSERT_EQUAL_MESSAGE("width of glyph of wrong", nExpectedHeight,
-                                 aBoundRect.GetHeight());
+    font.SetOrientation(900_deg10);
+    device->SetFont(font);
+
+    pFontInstance = device->GetFontInstance();
+
+    
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(LATIN_SMALL_LETTER_B),
 aBoundRect,
+                                     false);
+
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-79.7, aBoundRect.getMinX(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-56.6, aBoundRect.getMinY(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(80.8, aBoundRect.getWidth(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(49.5, aBoundRect.getHeight(), 0.05);
+
+    font.SetOrientation(450_deg10);
+    device->SetFont(font);
+
+    pFontInstance = device->GetFontInstance();
+
+    
pFontInstance->GetGlyphBoundRect(pFontInstance->GetGlyphIndex(LATIN_SMALL_LETTER_B),
 aBoundRect,
+                                     false);
+
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-51.3, aBoundRect.getMinX(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(-96.4, aBoundRect.getMinY(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(92.1, aBoundRect.getWidth(), 0.05);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(92.1, aBoundRect.getHeight(), 0.05);
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(VclLogicalFontInstanceTest);
diff --git a/vcl/source/font/LogicalFontInstance.cxx 
b/vcl/source/font/LogicalFontInstance.cxx
index 6c6c95bbb6a1..6524422397f0 100644
--- a/vcl/source/font/LogicalFontInstance.cxx
+++ b/vcl/source/font/LogicalFontInstance.cxx
@@ -26,6 +26,8 @@
 #include <font/LogicalFontInstance.hxx>
 #include <impfontcache.hxx>
 
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
 LogicalFontInstance::LogicalFontInstance(const vcl::font::PhysicalFontFace& 
rFontFace,
                                          const vcl::font::FontSelectPattern& 
rFontSelData)
     : mxFontMetric(new ImplFontMetricData(rFontSelData))
@@ -166,47 +168,39 @@ void 
LogicalFontInstance::IgnoreFallbackForUnicode(sal_UCS4 cChar, FontWeight eW
         maUnicodeFallbackList.erase(it);
 }
 
-bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId nID, tools::Rectangle& 
rRect,
+bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId nID, 
basegfx::B2DRectangle& rRect,
                                             bool bVertical) const
 {
+    // TODO/FIXME: bVertical handling here is highly suspicious. When it's 
true, it may
+    // return different rectangle, depending on if this glyph was cached 
already or not.
+
     if (mpFontCache && mpFontCache->GetCachedGlyphBoundRect(this, nID, rRect))
         return true;
 
     auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
     hb_glyph_extents_t aExtents;
-    bool res = hb_font_get_glyph_extents(pHbFont, nID, &aExtents);
+    if (!hb_font_get_glyph_extents(pHbFont, nID, &aExtents))
+        return false;
 
-    if (res)
-    {
-        double nXScale = 0, nYScale = 0;
-        GetScale(&nXScale, &nYScale);
+    double nXScale = 0, nYScale = 0;
+    GetScale(&nXScale, &nYScale);
 
-        double fMinX = aExtents.x_bearing;
-        double fMinY = aExtents.y_bearing;
-        double fMaxX = aExtents.x_bearing + aExtents.width;
-        double fMaxY = aExtents.y_bearing + aExtents.height;
+    double fMinX = aExtents.x_bearing * nXScale;
+    double fMinY = -aExtents.y_bearing * nYScale;
+    double fMaxX = (aExtents.x_bearing + aExtents.width) * nXScale;
+    double fMaxY = -(aExtents.y_bearing + aExtents.height) * nYScale;
+    rRect = basegfx::B2DRectangle(fMinX, fMinY, fMaxX, fMaxY);
 
-        tools::Rectangle aRect(std::floor(fMinX * nXScale), -std::ceil(fMinY * 
nYScale),
-                               std::ceil(fMaxX * nXScale), -std::floor(fMaxY * 
nYScale));
-        if (mnOrientation && !bVertical)
-        {
-            // Apply font rotation.
-            const double fRad = toRadians(mnOrientation);
-            const double fCos = cos(fRad);
-            const double fSin = sin(fRad);
-
-            rRect.SetLeft(fCos * aRect.Left() + fSin * aRect.Top());
-            rRect.SetTop(-fSin * aRect.Left() - fCos * aRect.Top());
-            rRect.SetRight(fCos * aRect.Right() + fSin * aRect.Bottom());
-            rRect.SetBottom(-fSin * aRect.Right() - fCos * aRect.Bottom());
-        }
-        else
-            rRect = aRect;
+    if (mnOrientation && !bVertical)
+    {
+        // Apply font rotation.
+        
rRect.transform(basegfx::utils::createRotateB2DHomMatrix(-toRadians(mnOrientation)));
     }
 
-    if (mpFontCache && res)
+    if (mpFontCache)
         mpFontCache->CacheGlyphBoundRect(this, nID, rRect);
-    return res;
+
+    return true;
 }
 
 sal_GlyphId LogicalFontInstance::GetGlyphIndex(uint32_t nUnicode, uint32_t 
nVariationSelector) const
diff --git a/vcl/source/font/fontcache.cxx b/vcl/source/font/fontcache.cxx
index 58b68baa9fe3..a167f741de15 100644
--- a/vcl/source/font/fontcache.cxx
+++ b/vcl/source/font/fontcache.cxx
@@ -248,7 +248,7 @@ void ImplFontCache::Invalidate()
     m_aBoundRectCache.clear();
 }
 
-bool ImplFontCache::GetCachedGlyphBoundRect(const LogicalFontInstance *pFont, 
sal_GlyphId nID, tools::Rectangle &rRect)
+bool ImplFontCache::GetCachedGlyphBoundRect(const LogicalFontInstance *pFont, 
sal_GlyphId nID, basegfx::B2DRectangle &rRect)
 {
     if (!pFont->GetFontCache())
         return false;
@@ -265,7 +265,7 @@ bool ImplFontCache::GetCachedGlyphBoundRect(const 
LogicalFontInstance *pFont, sa
     return false;
 }
 
-void ImplFontCache::CacheGlyphBoundRect(const LogicalFontInstance *pFont, 
sal_GlyphId nID, tools::Rectangle &rRect)
+void ImplFontCache::CacheGlyphBoundRect(const LogicalFontInstance *pFont, 
sal_GlyphId nID, basegfx::B2DRectangle &rRect)
 {
     if (!pFont->GetFontCache())
         return;
diff --git a/vcl/source/gdi/CommonSalLayout.cxx 
b/vcl/source/gdi/CommonSalLayout.cxx
index be301edcd365..5167921bd7ed 100644
--- a/vcl/source/gdi/CommonSalLayout.cxx
+++ b/vcl/source/gdi/CommonSalLayout.cxx
@@ -561,12 +561,12 @@ bool 
GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay
                     {
                         // We need glyph's advance, top bearing, and height to
                         // correct y offset.
-                        tools::Rectangle aRect;
+                        basegfx::B2DRectangle aRect;
                         // Get cached bound rect value for the font,
                         GetFont().GetGlyphBoundRect(nGlyphIndex, aRect, true);
 
-                        nXOffset = -(aRect.Top() / nXScale  + ( 
pHbPositions[i].y_advance
-                                    + ( aRect.GetHeight() / nXScale ) ) / 2 );
+                        nXOffset = -(aRect.getMinX() / nXScale  + ( 
pHbPositions[i].y_advance
+                                    + ( aRect.getHeight() / nXScale ) ) / 2.0 
);
                     }
 
                 }
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 40b6b9456266..8dd72a469de1 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -7006,7 +7006,7 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const 
OUString& rText, bool
     else if ( eAlign == ALIGN_TOP )
         aOffset.AdjustY(GetFontInstance()->mxFontMetric->GetAscent() );
 
-    tools::Rectangle aRectangle;
+    basegfx::B2DRectangle aRectangle;
     nIndex = 0;
     while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex, &pGlyphFont))
     {
@@ -7023,8 +7023,8 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const 
OUString& rText, bool
             }
             else
             {
-                aAdjOffset = DevicePoint(aOffset.X(), aOffset.Y());
-                aAdjOffset.adjustX(aRectangle.Left() + (aRectangle.GetWidth() 
- aEmphasisMark.GetWidth()) / 2 );
+                aAdjOffset = basegfx::B2DPoint(aOffset.X(), aOffset.Y());
+                aAdjOffset.adjustX(aRectangle.getMinX() + 
(aRectangle.getWidth() - aEmphasisMark.GetWidth()) / 2 );
             }
 
             aAdjOffset = aRotScale.transform( aAdjOffset );
diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx
index ed368f04022e..7397748ecd72 100644
--- a/vcl/source/gdi/sallayout.cxx
+++ b/vcl/source/gdi/sallayout.cxx
@@ -33,6 +33,7 @@
 #include <sallayout.hxx>
 #include <basegfx/polygon/b2dpolypolygon.hxx>
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <rtl/math.hxx>
 
 #include <i18nlangtag/lang.h>
 
@@ -214,12 +215,15 @@ bool SalLayout::GetOutline(basegfx::B2DPolyPolygonVector& 
rVector) const
     return (bAllOk && bOneOk);
 }
 
+// No need to expand to the next pixel, when the character only covers its 
tiny fraction
+static double trimInsignificant(double n) { return std::round(n * 1e5) / 1e5; }
+
 bool SalLayout::GetBoundRect(tools::Rectangle& rRect) const
 {
     bool bRet = false;
-    rRect.SetEmpty();
 
-    tools::Rectangle aRectangle;
+    basegfx::B2DRectangle aUnion;
+    basegfx::B2DRectangle aRectangle;
 
     DevicePoint aPos;
     const GlyphItem* pGlyph;
@@ -230,22 +234,19 @@ bool SalLayout::GetBoundRect(tools::Rectangle& rRect) 
const
         // get bounding rectangle of individual glyph
         if (pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle))
         {
-            if (!aRectangle.IsEmpty())
+            if (!aRectangle.isEmpty())
             {
-                aRectangle.AdjustLeft(std::floor(aPos.getX()));
-                aRectangle.AdjustRight(std::ceil(aPos.getX()));
-                aRectangle.AdjustTop(std::floor(aPos.getY()));
-                aRectangle.AdjustBottom(std::ceil(aPos.getY()));
-
+                
aRectangle.transform(basegfx::utils::createTranslateB2DHomMatrix(aPos));
                 // merge rectangle
-                if (rRect.IsEmpty())
-                    rRect = aRectangle;
-                else
-                    rRect.Union(aRectangle);
+                aUnion.expand(aRectangle);
             }
             bRet = true;
         }
     }
+    rRect = 
tools::Rectangle(rtl::math::approxFloor(trimInsignificant(aUnion.getMinX())),
+                             
rtl::math::approxFloor(trimInsignificant(aUnion.getMinY())),
+                             
rtl::math::approxCeil(trimInsignificant(aUnion.getMaxX())),
+                             
rtl::math::approxCeil(trimInsignificant(aUnion.getMaxY())));
 
     return bRet;
 }
diff --git a/vcl/source/outdev/font.cxx b/vcl/source/outdev/font.cxx
index 3bfbb8f6d8ce..fee8021b43a9 100644
--- a/vcl/source/outdev/font.cxx
+++ b/vcl/source/outdev/font.cxx
@@ -950,8 +950,8 @@ void OutputDevice::ImplDrawEmphasisMarks( SalLayout& 
rSalLayout )
     tools::Long nEmphasisHeight2 = nEmphasisHeight / 2;
     aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
 
-    DevicePoint aOutPoint;
-    tools::Rectangle aRectangle;
+    basegfx::B2DPoint aOutPoint;
+    basegfx::B2DRectangle aRectangle;
     const GlyphItem* pGlyph;
     const LogicalFontInstance* pGlyphFont;
     int nStart = 0;
@@ -971,7 +971,7 @@ void OutputDevice::ImplDrawEmphasisMarks( SalLayout& 
rSalLayout )
             else
             {
                 aAdjPoint = aOffset;
-                aAdjPoint.AdjustX(aRectangle.Left() + (aRectangle.GetWidth() - 
aEmphasisMark.GetWidth()) / 2 );
+                aAdjPoint.AdjustX(aRectangle.getMinX() + 
(aRectangle.getWidth() - aEmphasisMark.GetWidth()) / 2 );
             }
 
             if ( mpFontInstance->mnOrientation )
diff --git a/vcl/workben/listglyphs.cxx b/vcl/workben/listglyphs.cxx
index def2ff818122..341006d433dd 100644
--- a/vcl/workben/listglyphs.cxx
+++ b/vcl/workben/listglyphs.cxx
@@ -120,7 +120,7 @@ int ListGlyphs::Main()
              nChar = pCharMap->GetNextChar(nChar))
         {
             auto nGlyphIndex = pFontInstance->GetGlyphIndex(nChar);
-            tools::Rectangle aGlyphBounds;
+            basegfx::B2DRectangle aGlyphBounds;
             pFontInstance->GetGlyphBoundRect(nGlyphIndex, aGlyphBounds, false);
             std::cout << "Codepoint: " << pFontFace->GetGlyphName(nGlyphIndex)
                       << "; glyph bounds: " << aGlyphBounds << "
";
commit cbb9d93f5396ee0842cf48fdefc98d5db6eb27c4
Author:     Khaled Hosny <[email protected]>
AuthorDate: Sun Jul 16 07:37:55 2023 +0300
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 02:24:05 2024 +0500

    vcl: Use HarfBuzz to get glyph bounding rectangle
    
    For consistent cross-platform results that also matches our glyph
    advances since platform functions might be using hints which we don’t
    use.
    
    Change-Id: I4aebd3e7c5f460dff584f5eba74f7a11bab0f9b1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154388
    Tested-by: Jenkins
    Reviewed-by: خالد حسني <[email protected]>

diff --git a/vcl/inc/font/LogicalFontInstance.hxx 
b/vcl/inc/font/LogicalFontInstance.hxx
index c9e837d540f1..518716b5c7df 100644
--- a/vcl/inc/font/LogicalFontInstance.hxx
+++ b/vcl/inc/font/LogicalFontInstance.hxx
@@ -120,8 +120,6 @@ protected:
     explicit LogicalFontInstance(const vcl::font::PhysicalFontFace&,
                                  const vcl::font::FontSelectPattern&);
 
-    virtual bool ImplGetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) 
const = 0;
-
     hb_font_t* InitHbFont();
     virtual void ImplInitHbFont(hb_font_t*) {}
 
diff --git a/vcl/inc/pdf/pdfbuildin_fonts.hxx b/vcl/inc/pdf/pdfbuildin_fonts.hxx
index 8cb34e74922b..a0c2fc06287d 100644
--- a/vcl/inc/pdf/pdfbuildin_fonts.hxx
+++ b/vcl/inc/pdf/pdfbuildin_fonts.hxx
@@ -49,8 +49,6 @@ struct BuildinFont
 
 class BuildinFontInstance final : public LogicalFontInstance
 {
-    bool ImplGetGlyphBoundRect(sal_GlyphId nID, tools::Rectangle& rRect, bool) 
const override;
-
 public:
     BuildinFontInstance(const vcl::font::PhysicalFontFace&, const 
vcl::font::FontSelectPattern&);
 
diff --git a/vcl/inc/qt5/QtFont.hxx b/vcl/inc/qt5/QtFont.hxx
index e19d6de21aa2..7d0b338c939d 100644
--- a/vcl/inc/qt5/QtFont.hxx
+++ b/vcl/inc/qt5/QtFont.hxx
@@ -33,7 +33,6 @@ class QtFont final : public QFont, public LogicalFontInstance
     QtFontFace::CreateFontInstance(const vcl::font::FontSelectPattern&) const;
 
     bool GetGlyphOutline(sal_GlyphId, basegfx::B2DPolyPolygon&, bool) const 
override;
-    bool ImplGetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) const 
override;
 
     explicit QtFont(const vcl::font::PhysicalFontFace&, const 
vcl::font::FontSelectPattern&);
 };
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 6d1668c1f913..a255e9dc330d 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -103,8 +103,6 @@ public:
 private:
     explicit CoreTextFont(const CoreTextFontFace&, const 
vcl::font::FontSelectPattern&);
 
-    bool ImplGetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) const 
override;
-
     CTFontRef mpCTFont;
 };
 
diff --git a/vcl/inc/unx/freetype_glyphcache.hxx 
b/vcl/inc/unx/freetype_glyphcache.hxx
index c80642b6126c..9c61e0ff60d1 100644
--- a/vcl/inc/unx/freetype_glyphcache.hxx
+++ b/vcl/inc/unx/freetype_glyphcache.hxx
@@ -107,8 +107,6 @@ class SAL_DLLPUBLIC_RTTI FreetypeFontInstance final : 
public LogicalFontInstance
 
     std::unique_ptr<FreetypeFont> mxFreetypeFont;
 
-    virtual bool ImplGetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) 
const override;
-
     explicit FreetypeFontInstance(const vcl::font::PhysicalFontFace& rPFF, 
const vcl::font::FontSelectPattern& rFSP);
 
 public:
diff --git a/vcl/inc/unx/glyphcache.hxx b/vcl/inc/unx/glyphcache.hxx
index 9de7bd59fe6c..4d21d34d05ea 100644
--- a/vcl/inc/unx/glyphcache.hxx
+++ b/vcl/inc/unx/glyphcache.hxx
@@ -123,7 +123,6 @@ public:
 
     void                    GetFontMetric(ImplFontMetricDataRef const &) const;
 
-    bool                    GetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, 
bool) const;
     bool                    GetGlyphOutline(sal_GlyphId, 
basegfx::B2DPolyPolygon&, bool) const;
     bool                    GetAntialiasAdvice() const;
 
diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index fb8e5a1c6e43..18de0e684b1c 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -60,7 +60,6 @@ private:
     explicit WinFontInstance(const WinFontFace&, const 
vcl::font::FontSelectPattern&);
 
     virtual void ImplInitHbFont(hb_font_t*) override;
-    bool ImplGetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) const 
override;
 
     WinSalGraphics *m_pGraphics;
     HFONT m_hFont;
diff --git a/vcl/qa/cppunit/cjktext.cxx b/vcl/qa/cppunit/cjktext.cxx
index af63b29959fd..cd9bd2c165fd 100644
--- a/vcl/qa/cppunit/cjktext.cxx
+++ b/vcl/qa/cppunit/cjktext.cxx
@@ -178,7 +178,7 @@ void VclCjkTextTest::testVerticalText()
     tools::Long height36Rotated = getCharacterRightSideHeight(device, 
Point(99, 35));
     CPPUNIT_ASSERT_DOUBLES_EQUAL(height36, height36Rotated, 1);
     tools::Long width36Rotated = getCharacterTopWidth(device, Point(25, 0));
-    CPPUNIT_ASSERT_DOUBLES_EQUAL(width36, width36Rotated, 1);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(width36, width36Rotated, 2);
 
     font = baseFont;
     font.SetFontSize(Size(0, 72));
diff --git a/vcl/qa/cppunit/fontmocks.hxx b/vcl/qa/cppunit/fontmocks.hxx
index 7e33ce8e7e13..8eac463c143f 100644
--- a/vcl/qa/cppunit/fontmocks.hxx
+++ b/vcl/qa/cppunit/fontmocks.hxx
@@ -30,9 +30,6 @@ public:
     {
         return true;
     }
-
-protected:
-    bool ImplGetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) const 
override { return true; }
 };
 
 class TestFontFace : public vcl::font::PhysicalFontFace
diff --git a/vcl/qa/cppunit/logicalfontinstance.cxx 
b/vcl/qa/cppunit/logicalfontinstance.cxx
index 2a7a5c83ac27..12ee95762eef 100644
--- a/vcl/qa/cppunit/logicalfontinstance.cxx
+++ b/vcl/qa/cppunit/logicalfontinstance.cxx
@@ -50,13 +50,14 @@ void VclLogicalFontInstanceTest::testglyphboundrect()
 
     const tools::Long nExpectedX = 7;
     const tools::Long nExpectedY = -80;
+    const tools::Long nExpectedWidth = 51;
+    const tools::Long nExpectedHeight = 83;
 
     CPPUNIT_ASSERT_EQUAL_MESSAGE("x of glyph is wrong", nExpectedX, 
aBoundRect.getX());
     CPPUNIT_ASSERT_EQUAL_MESSAGE("y of glyph is wrong", nExpectedY, 
aBoundRect.getY());
-    CPPUNIT_ASSERT_MESSAGE("height of glyph of wrong",
-                           aBoundRect.GetWidth() == 50 || 
aBoundRect.GetWidth() == 51);
-    CPPUNIT_ASSERT_MESSAGE("width of glyph of wrong",
-                           aBoundRect.GetHeight() == 82 || 
aBoundRect.GetHeight() == 83);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("height of glyph of wrong", nExpectedWidth, 
aBoundRect.GetWidth());
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("width of glyph of wrong", nExpectedHeight,
+                                 aBoundRect.GetHeight());
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(VclLogicalFontInstanceTest);
diff --git a/vcl/qt5/QtFont.cxx b/vcl/qt5/QtFont.cxx
index 384f56774ffd..e3a6c0b0a9d7 100644
--- a/vcl/qt5/QtFont.cxx
+++ b/vcl/qt5/QtFont.cxx
@@ -187,11 +187,4 @@ bool QtFont::GetGlyphOutline(sal_GlyphId nId, 
basegfx::B2DPolyPolygon& rB2DPolyP
     return true;
 }
 
-bool QtFont::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& rRect, 
bool) const
-{
-    QRawFont aRawFont(QRawFont::fromFont(*this));
-    rRect = toRectangle(aRawFont.boundingRect(nId).toAlignedRect());
-    return true;
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/quartz/ctfonts.cxx b/vcl/quartz/ctfonts.cxx
index 38112a7deb50..1cafd270552d 100644
--- a/vcl/quartz/ctfonts.cxx
+++ b/vcl/quartz/ctfonts.cxx
@@ -89,27 +89,6 @@ void CoreTextFont::GetFontMetric( ImplFontMetricDataRef 
const & rxFontMetric )
     rxFontMetric->SetMinKashida(GetKashidaWidth());
 }
 
-bool CoreTextFont::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& 
rRect, bool bVertical) const
-{
-    CGGlyph nCGGlyph = nId;
-
-    SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.11 kCTFontDefaultOrientation
-    const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // 
TODO: horz/vert
-    SAL_WNODEPRECATED_DECLARATIONS_POP
-    CGRect aCGRect = CTFontGetBoundingRectsForGlyphs(mpCTFont, 
aFontOrientation, &nCGGlyph, nullptr, 1);
-
-    // Apply font rotation to non-vertical glyphs.
-    if (mfFontRotation && !bVertical)
-        aCGRect = CGRectApplyAffineTransform(aCGRect, 
CGAffineTransformMakeRotation(mfFontRotation));
-
-    tools::Long xMin = floor(aCGRect.origin.x);
-    tools::Long yMin = floor(aCGRect.origin.y);
-    tools::Long xMax = ceil(aCGRect.origin.x + aCGRect.size.width);
-    tools::Long yMax = ceil(aCGRect.origin.y + aCGRect.size.height);
-    rRect = tools::Rectangle(xMin, -yMax, xMax, -yMin);
-    return true;
-}
-
 namespace {
 
 // callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline()
diff --git a/vcl/source/font/LogicalFontInstance.cxx 
b/vcl/source/font/LogicalFontInstance.cxx
index 6c7a7e2bde09..6c6c95bbb6a1 100644
--- a/vcl/source/font/LogicalFontInstance.cxx
+++ b/vcl/source/font/LogicalFontInstance.cxx
@@ -172,7 +172,38 @@ bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId 
nID, tools::Rectangle& r
     if (mpFontCache && mpFontCache->GetCachedGlyphBoundRect(this, nID, rRect))
         return true;
 
-    bool res = ImplGetGlyphBoundRect(nID, rRect, bVertical);
+    auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
+    hb_glyph_extents_t aExtents;
+    bool res = hb_font_get_glyph_extents(pHbFont, nID, &aExtents);
+
+    if (res)
+    {
+        double nXScale = 0, nYScale = 0;
+        GetScale(&nXScale, &nYScale);
+
+        double fMinX = aExtents.x_bearing;
+        double fMinY = aExtents.y_bearing;
+        double fMaxX = aExtents.x_bearing + aExtents.width;
+        double fMaxY = aExtents.y_bearing + aExtents.height;
+
+        tools::Rectangle aRect(std::floor(fMinX * nXScale), -std::ceil(fMinY * 
nYScale),
+                               std::ceil(fMaxX * nXScale), -std::floor(fMaxY * 
nYScale));
+        if (mnOrientation && !bVertical)
+        {
+            // Apply font rotation.
+            const double fRad = toRadians(mnOrientation);
+            const double fCos = cos(fRad);
+            const double fSin = sin(fRad);
+
+            rRect.SetLeft(fCos * aRect.Left() + fSin * aRect.Top());
+            rRect.SetTop(-fSin * aRect.Left() - fCos * aRect.Top());
+            rRect.SetRight(fCos * aRect.Right() + fSin * aRect.Bottom());
+            rRect.SetBottom(-fSin * aRect.Right() - fCos * aRect.Bottom());
+        }
+        else
+            rRect = aRect;
+    }
+
     if (mpFontCache && res)
         mpFontCache->CacheGlyphBoundRect(this, nID, rRect);
     return res;
diff --git a/vcl/source/gdi/pdfbuildin_fonts.cxx 
b/vcl/source/gdi/pdfbuildin_fonts.cxx
index e387f78638d3..7f80bfdd030d 100644
--- a/vcl/source/gdi/pdfbuildin_fonts.cxx
+++ b/vcl/source/gdi/pdfbuildin_fonts.cxx
@@ -738,11 +738,6 @@ BuildinFontInstance::BuildinFontInstance(const 
vcl::font::PhysicalFontFace& rFon
 {
 }
 
-bool BuildinFontInstance::ImplGetGlyphBoundRect(sal_GlyphId, 
tools::Rectangle&, bool) const
-{
-    return false;
-}
-
 bool BuildinFontInstance::GetGlyphOutline(sal_GlyphId, 
basegfx::B2DPolyPolygon&, bool) const
 {
     return false;
diff --git a/vcl/unx/generic/glyphs/freetype_glyphcache.cxx 
b/vcl/unx/generic/glyphs/freetype_glyphcache.cxx
index 64fcb6bfc2bf..bed24e081ee5 100644
--- a/vcl/unx/generic/glyphs/freetype_glyphcache.cxx
+++ b/vcl/unx/generic/glyphs/freetype_glyphcache.cxx
@@ -564,44 +564,6 @@ void FreetypeFont::ApplyGlyphTransform(bool bVertical, 
FT_Glyph pGlyphFT ) const
     }
 }
 
-bool FreetypeFont::GetGlyphBoundRect(sal_GlyphId nID, tools::Rectangle& rRect, 
bool bVertical) const
-{
-    FT_Activate_Size( maSizeFT );
-
-    FT_Error rc = FT_Load_Glyph(maFaceFT, nID, mnLoadFlags);
-
-    if (rc != FT_Err_Ok)
-        return false;
-
-    if (mrFontInstance.NeedsArtificialBold())
-        FT_GlyphSlot_Embolden(maFaceFT->glyph);
-
-    FT_Glyph pGlyphFT;
-    rc = FT_Get_Glyph(maFaceFT->glyph, &pGlyphFT);
-    if (rc != FT_Err_Ok)
-        return false;
-
-    ApplyGlyphTransform(bVertical, pGlyphFT);
-
-    FT_BBox aBbox;
-    FT_Glyph_Get_CBox( pGlyphFT, FT_GLYPH_BBOX_PIXELS, &aBbox );
-    FT_Done_Glyph( pGlyphFT );
-
-    tools::Rectangle aRect(aBbox.xMin, -aBbox.yMax, aBbox.xMax, -aBbox.yMin);
-    if (mnCos != 0x10000 && mnSin != 0)
-    {
-        const double nCos = mnCos / 65536.0;
-        const double nSin = mnSin / 65536.0;
-        rRect.SetLeft(  nCos*aRect.Left() + nSin*aRect.Top() );
-        rRect.SetTop( -nSin*aRect.Left() - nCos*aRect.Top() );
-        rRect.SetRight(  nCos*aRect.Right() + nSin*aRect.Bottom() );
-        rRect.SetBottom( -nSin*aRect.Right() - nCos*aRect.Bottom() );
-    }
-    else
-        rRect = aRect;
-    return true;
-}
-
 bool FreetypeFont::GetAntialiasAdvice() const
 {
     // TODO: also use GASP info
diff --git a/vcl/unx/generic/glyphs/glyphcache.cxx 
b/vcl/unx/generic/glyphs/glyphcache.cxx
index 39b10c78e123..ac3c5e15ab73 100644
--- a/vcl/unx/generic/glyphs/glyphcache.cxx
+++ b/vcl/unx/generic/glyphs/glyphcache.cxx
@@ -75,14 +75,6 @@ FreetypeFontInstance::~FreetypeFontInstance()
 {
 }
 
-bool FreetypeFontInstance::ImplGetGlyphBoundRect(sal_GlyphId nId, 
tools::Rectangle& rRect, bool bVertical) const
-{
-    assert(mxFreetypeFont);
-    if (!mxFreetypeFont)
-        return false;
-    return mxFreetypeFont->GetGlyphBoundRect(nId, rRect, bVertical);
-}
-
 bool FreetypeFontInstance::GetGlyphOutline(sal_GlyphId nId, 
basegfx::B2DPolyPolygon& rPoly, bool bVertical) const
 {
     assert(mxFreetypeFont);
diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx
index 196932982c28..d5f41e2e8819 100644
--- a/vcl/win/gdi/salfont.cxx
+++ b/vcl/win/gdi/salfont.cxx
@@ -1156,59 +1156,6 @@ void WinSalGraphics::ClearDevFontCache()
     ImplReleaseTempFonts(*GetSalData(), false);
 }
 
-bool WinFontInstance::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& 
rRect, bool bIsVertical) const
-{
-    assert(m_pGraphics);
-    HDC hDC = m_pGraphics->getHDC();
-    const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, 
OBJ_FONT));
-    const HFONT hFont = GetHFONT();
-    if (hFont != hOrigFont)
-        SelectObject(hDC, hFont);
-
-    const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, 
hDC]()
-        { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
-    const float fFontScale = GetScale();
-
-    // use unity matrix
-    MAT2 aMat;
-    const vcl::font::FontSelectPattern& rFSD = GetFontSelectPattern();
-
-    // Use identity matrix for fonts requested in horizontal
-    // writing (LTR or RTL), or rotated glyphs in vertical writing.
-    if (!rFSD.mbVertical || !bIsVertical)
-    {
-        aMat.eM11 = aMat.eM22 = FixedFromDouble(1.0);
-        aMat.eM12 = aMat.eM21 = FixedFromDouble(0.0);
-    }
-    else
-    {
-        constexpr double nCos = 0.0;
-        constexpr double nSin = 1.0;
-        aMat.eM11 = FixedFromDouble(nCos);
-        aMat.eM12 = FixedFromDouble(nSin);
-        aMat.eM21 = FixedFromDouble(-nSin);
-        aMat.eM22 = FixedFromDouble(nCos);
-    }
-
-    UINT nGGOFlags = GGO_METRICS;
-    nGGOFlags |= GGO_GLYPH_INDEX;
-
-    GLYPHMETRICS aGM;
-    aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
-    aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
-    DWORD nSize = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGM, 0, nullptr, 
&aMat);
-    if (nSize == GDI_ERROR)
-        return false;
-
-    rRect = tools::Rectangle( Point( +aGM.gmptGlyphOrigin.x, 
-aGM.gmptGlyphOrigin.y ),
-        Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
-    rRect.SetLeft(static_cast<int>( fFontScale * rRect.Left() ));
-    rRect.SetRight(static_cast<int>( fFontScale * rRect.Right() ) + 1);
-    rRect.SetTop(static_cast<int>( fFontScale * rRect.Top() ));
-    rRect.SetBottom(static_cast<int>( fFontScale * rRect.Bottom() ) + 1);
-    return true;
-}
-
 bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, 
basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
 {
     rB2DPolyPoly.clear();
commit 2b1e0c071dc186cd29ee2653166fdb9aa98bca2e
Author:     Khaled Hosny <[email protected]>
AuthorDate: Tue Jun 20 15:58:45 2023 +0300
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 01:55:14 2024 +0500

    tdf#115321: Fix PDF position of emphasis mark in vertical text
    
    This aligns the code in PDF writer with
    OutputDevice::ImplDrawEmphasisMarks().
    
    Change-Id: I404beda30ff0eb1d6c59d971a7daa59b559ef70f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153326
    Tested-by: Jenkins
    Reviewed-by: خالد حسني <[email protected]>

diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index 6ea5c67f12a3..40b6b9456266 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -6982,11 +6982,20 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, 
const OUString& rText, bool
     writeBuffer( aLine.getStr(), aLine.getLength() );
 
     Point aOffset(0,0);
+    Point aOffsetVert(0,0);
 
     if ( nEmphMark & FontEmphasisMark::PosBelow )
+    {
         aOffset.AdjustY(GetFontInstance()->mxFontMetric->GetDescent() + 
aEmphasisMark.GetYOffset() );
+        aOffsetVert = aOffset;
+    }
     else
+    {
         aOffset.AdjustY( -(GetFontInstance()->mxFontMetric->GetAscent() + 
aEmphasisMark.GetYOffset()) );
+        // Todo: use ideographic em-box or ideographic character face 
information.
+        aOffsetVert.AdjustY(-(GetFontInstance()->mxFontMetric->GetAscent() +
+                    GetFontInstance()->mxFontMetric->GetDescent() + 
aEmphasisMark.GetYOffset()));
+    }
 
     tools::Long nEmphWidth2 = aEmphasisMark.GetWidth() / 2;
     tools::Long nEmphHeight2 = nEmphHeight / 2;
@@ -6997,13 +7006,27 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, 
const OUString& rText, bool
     else if ( eAlign == ALIGN_TOP )
         aOffset.AdjustY(GetFontInstance()->mxFontMetric->GetAscent() );
 
+    tools::Rectangle aRectangle;
     nIndex = 0;
-    while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex))
+    while (rLayout.GetNextGlyph(&pGlyph, aPos, nIndex, &pGlyphFont))
     {
+        if (!pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle))
+            continue;
+
         if (!pGlyph->IsSpacing())
         {
-            DevicePoint aAdjOffset(aOffset.X(), aOffset.Y());
-            aAdjOffset.adjustX((pGlyph->newWidth() - aEmphasisMark.GetWidth()) 
/ 2);
+            DevicePoint aAdjOffset;
+            if (pGlyph->IsVertical())
+            {
+                aAdjOffset = DevicePoint(aOffsetVert.X(), aOffsetVert.Y());
+                aAdjOffset.adjustX((-pGlyph->origWidth() + 
aEmphasisMark.GetWidth()) / 2);
+            }
+            else
+            {
+                aAdjOffset = DevicePoint(aOffset.X(), aOffset.Y());
+                aAdjOffset.adjustX(aRectangle.Left() + (aRectangle.GetWidth() 
- aEmphasisMark.GetWidth()) / 2 );
+            }
+
             aAdjOffset = aRotScale.transform( aAdjOffset );
 
             aAdjOffset -= DevicePoint(nEmphWidth2, nEmphHeight2);
commit 401ed53c66085d2442c361c43fe0180e115d6b11
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sun Mar 31 10:57:43 2024 +0100
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 01:48:20 2024 +0500

    Simplify a bit
    
    And make font size calculations consistent between SkiaTextRender (x11)
    and WinSkiaSalGraphicsImpl (win). They already did ~the same, the win
    case just used an intermediate 'getHScale' coefficient.
    
    Change-Id: I90ad4d9c49427465ef3263843b34bd9bc0d762eb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165488
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/skia/win/gdiimpl.cxx b/vcl/skia/win/gdiimpl.cxx
index d063b440cf31..04dad0123573 100644
--- a/vcl/skia/win/gdiimpl.cxx
+++ b/vcl/skia/win/gdiimpl.cxx
@@ -216,20 +216,22 @@ catch (const sal::systools::ComError& e)
 bool WinSkiaSalGraphicsImpl::DrawTextLayout(const GenericSalLayout& rLayout)
 {
     assert(dynamic_cast<const SkiaWinFontInstance*>(&rLayout.GetFont()));
-    const SkiaWinFontInstance* pWinFont
-        = static_cast<const SkiaWinFontInstance*>(&rLayout.GetFont());
-    const HFONT hLayoutFont = pWinFont->GetHFONT();
-    double hScale = pWinFont->getHScale();
+    const SkiaWinFontInstance& rWinFont
+        = static_cast<const SkiaWinFontInstance&>(rLayout.GetFont());
+    const vcl::font::FontSelectPattern& rFSD = rWinFont.GetFontSelectPattern();
+    if (rFSD.mnHeight == 0)
+        return false;
+    const HFONT hLayoutFont = rWinFont.GetHFONT();
     LOGFONTW logFont;
     if (GetObjectW(hLayoutFont, sizeof(logFont), &logFont) == 0)
     {
         assert(false);
         return false;
     }
-    sk_sp<SkTypeface> typeface = pWinFont->GetSkiaTypeface();
+    sk_sp<SkTypeface> typeface = rWinFont.GetSkiaTypeface();
     if (!typeface)
     {
-        typeface = createDirectWriteTypeface(pWinFont);
+        typeface = createDirectWriteTypeface(&rWinFont);
         bool dwrite = true;
         if (!typeface) // fall back to GDI text rendering
         {
@@ -250,7 +252,7 @@ bool WinSkiaSalGraphicsImpl::DrawTextLayout(const 
GenericSalLayout& rLayout)
                 return false;
         }
         // Cache the typeface.
-        const_cast<SkiaWinFontInstance*>(pWinFont)->SetSkiaTypeface(typeface, 
dwrite);
+        const_cast<SkiaWinFontInstance&>(rWinFont).SetSkiaTypeface(typeface, 
dwrite);
     }
 
     SkFont font(typeface);
@@ -267,21 +269,14 @@ bool WinSkiaSalGraphicsImpl::DrawTextLayout(const 
GenericSalLayout& rLayout)
     font.setEdging(logFont.lfQuality == NONANTIALIASED_QUALITY ? 
SkFont::Edging::kAlias
                                                                : 
ePreferredAliasing);
 
-    const vcl::font::FontSelectPattern& rFSD = 
pWinFont->GetFontSelectPattern();
-    int nHeight = rFSD.mnHeight;
-    int nWidth = rFSD.mnWidth ? rFSD.mnWidth : nHeight;
-    if (nWidth == 0 || nHeight == 0)
-        return false;
+    double nHeight = rFSD.mnHeight;
+    double nWidth = rFSD.mnWidth ? rFSD.mnWidth * 
rWinFont.GetAverageWidthFactor() : nHeight;
     font.setSize(nHeight);
-    font.setScaleX(hScale);
+    font.setScaleX(nWidth / nHeight);
 
-    // Unlike with Freetype-based font handling, use height even in vertical 
mode,
-    // additionally multiply it by horizontal scale to get the proper
-    // size and then scale the width back, otherwise the height would
-    // not be correct. I don't know why this is inconsistent.
     SkFont verticalFont(font);
-    verticalFont.setSize(nHeight * hScale);
-    verticalFont.setScaleX(1.0 / hScale);
+    verticalFont.setSize(nWidth);
+    verticalFont.setScaleX(nHeight / nWidth);
 
     assert(dynamic_cast<SkiaSalGraphicsImpl*>(mWinParent.GetImpl()));
     SkiaSalGraphicsImpl* impl = 
static_cast<SkiaSalGraphicsImpl*>(mWinParent.GetImpl());
diff --git a/vcl/skia/x11/textrender.cxx b/vcl/skia/x11/textrender.cxx
index fed5daa8a117..c598146be376 100644
--- a/vcl/skia/x11/textrender.cxx
+++ b/vcl/skia/x11/textrender.cxx
@@ -35,10 +35,10 @@ void SkiaTextRender::DrawTextLayout(const GenericSalLayout& 
rLayout, const SalGr
     const FreetypeFontInstance& rInstance = 
static_cast<FreetypeFontInstance&>(rLayout.GetFont());
     const FreetypeFont& rFont = rInstance.GetFreetypeFont();
     const vcl::font::FontSelectPattern& rFSD = 
rInstance.GetFontSelectPattern();
-    int nHeight = rFSD.mnHeight;
-    int nWidth = rFSD.mnWidth ? rFSD.mnWidth : nHeight;
-    if (nWidth == 0 || nHeight == 0)
+    if (rFSD.mnHeight == 0)
         return;
+    double nHeight = rFSD.mnHeight;
+    double nWidth = rFSD.mnWidth ? rFSD.mnWidth : nHeight;
 
     if (!fontManager)
     {
@@ -49,7 +49,7 @@ void SkiaTextRender::DrawTextLayout(const GenericSalLayout& 
rLayout, const SalGr
         = SkFontMgr_createTypefaceFromFcPattern(fontManager, 
rFont.GetFontOptions()->GetPattern());
     SkFont font(typeface);
     font.setSize(nHeight);
-    font.setScaleX(1.0 * nWidth / nHeight);
+    font.setScaleX(nWidth / nHeight);
     if (rInstance.NeedsArtificialItalic())
         font.setSkewX(-1.0 * ARTIFICIAL_ITALIC_SKEW);
     if (rInstance.NeedsArtificialBold())
@@ -76,7 +76,7 @@ void SkiaTextRender::DrawTextLayout(const GenericSalLayout& 
rLayout, const SalGr
     // Vertical font, use width as "height".
     SkFont verticalFont(font);
     verticalFont.setSize(nWidth);
-    verticalFont.setScaleX(1.0 * nHeight / nWidth);
+    verticalFont.setScaleX(nHeight / nWidth);
 
     assert(dynamic_cast<SkiaSalGraphicsImpl*>(rGraphics.GetImpl()));
     SkiaSalGraphicsImpl* impl = 
static_cast<SkiaSalGraphicsImpl*>(rGraphics.GetImpl());
commit dc7d9bfbfb7b46d9ac2e3bb72669da1130259f93
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sun Mar 31 07:49:23 2024 +0100
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Apr 8 01:40:13 2024 +0500

    Optimize trigonometry a bit
    
    Change-Id: I2b06c29f6090233325c7ca24fac6d76190d502ab
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165486
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 7a240600be98..84b625115648 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -2099,8 +2099,8 @@ bool SkiaSalGraphicsImpl::implDrawGradient(const 
basegfx::B2DPolyPolygon& rPolyP
 }
 
 static double toRadian(Degree10 degree10th) { return toRadians(3600_deg10 - 
degree10th); }
-static double toCos(Degree10 degree10th) { return 
SkScalarCos(toRadian(degree10th)); }
-static double toSin(Degree10 degree10th) { return 
SkScalarSin(toRadian(degree10th)); }
+static auto toCos(Degree10 degree10th) { return 
SkScalarCos(toRadian(degree10th)); }
+static auto toSin(Degree10 degree10th) { return 
SkScalarSin(toRadian(degree10th)); }
 
 void SkiaSalGraphicsImpl::drawGenericLayout(const GenericSalLayout& layout, 
Color textColor,
                                             const SkFont& font, const SkFont& 
verticalFont)
@@ -2115,15 +2115,16 @@ void SkiaSalGraphicsImpl::drawGenericLayout(const 
GenericSalLayout& layout, Colo
     DevicePoint aPos;
     const GlyphItem* pGlyph;
     int nStart = 0;
+    auto cos = toCos(layout.GetOrientation());
+    auto sin = toSin(layout.GetOrientation());
     while (layout.GetNextGlyph(&pGlyph, aPos, nStart))
     {
         glyphIds.push_back(pGlyph->glyphId());
-        Degree10 angle = layout.GetOrientation();
-        if (pGlyph->IsVertical())
-            angle += 900_deg10;
-        SkRSXform form = SkRSXform::Make(toCos(angle), toSin(angle), 
aPos.getX(), aPos.getY());
-        glyphForms.emplace_back(std::move(form));
         verticals.emplace_back(pGlyph->IsVertical());
+        auto cos1 = pGlyph->IsVertical() ? sin : cos; // cos (x - 90) = sin (x)
+        auto sin1 = pGlyph->IsVertical() ? -cos : sin; // sin (x - 90) = -cos 
(x)
+        SkRSXform form = SkRSXform::Make(cos1, sin1, aPos.getX(), aPos.getY());
+        glyphForms.emplace_back(std::move(form));
     }
     if (glyphIds.empty())
         return;

Reply via email to