summaryrefslogtreecommitdiffstats
path: root/vcl/source/font/LogicalFontInstance.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/source/font/LogicalFontInstance.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/font/LogicalFontInstance.cxx')
-rw-r--r--vcl/source/font/LogicalFontInstance.cxx348
1 files changed, 348 insertions, 0 deletions
diff --git a/vcl/source/font/LogicalFontInstance.cxx b/vcl/source/font/LogicalFontInstance.cxx
new file mode 100644
index 0000000000..0c21cba475
--- /dev/null
+++ b/vcl/source/font/LogicalFontInstance.cxx
@@ -0,0 +1,348 @@
+/* -*- 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 <sal/config.h>
+
+#include <hb-ot.h>
+#include <hb-graphite2.h>
+
+#include <font/PhysicalFontFace.hxx>
+#include <font/LogicalFontInstance.hxx>
+#include <impfontcache.hxx>
+
+LogicalFontInstance::LogicalFontInstance(const vcl::font::PhysicalFontFace& rFontFace,
+ const vcl::font::FontSelectPattern& rFontSelData)
+ : mxFontMetric(new FontMetricData(rFontSelData))
+ , mpConversion(nullptr)
+ , mnLineHeight(0)
+ , mnOwnOrientation(0)
+ , mnOrientation(0)
+ , mbInit(false)
+ , mpFontCache(nullptr)
+ , m_aFontSelData(rFontSelData)
+ , m_pHbFont(nullptr)
+ , m_nAveWidthFactor(1.0f)
+ , m_pFontFace(&const_cast<vcl::font::PhysicalFontFace&>(rFontFace))
+{
+}
+
+LogicalFontInstance::~LogicalFontInstance()
+{
+ maUnicodeFallbackList.clear();
+ mpFontCache = nullptr;
+ mxFontMetric = nullptr;
+
+ if (m_pHbFont)
+ hb_font_destroy(m_pHbFont);
+
+ if (m_pHbFontUntransformed)
+ hb_font_destroy(m_pHbFontUntransformed);
+
+ if (m_pHbDrawFuncs)
+ hb_draw_funcs_destroy(m_pHbDrawFuncs);
+}
+
+hb_font_t* LogicalFontInstance::InitHbFont()
+{
+ auto pFace = GetFontFace();
+ hb_face_t* pHbFace = pFace->GetHbFace();
+ assert(pHbFace);
+ auto nUPEM = pFace->UnitsPerEm();
+
+ hb_font_t* pHbFont = hb_font_create(pHbFace);
+ hb_font_set_scale(pHbFont, nUPEM, nUPEM);
+ hb_ot_font_set_funcs(pHbFont);
+
+ auto aVariations = pFace->GetVariations(*this);
+ if (!aVariations.empty())
+ hb_font_set_variations(pHbFont, aVariations.data(), aVariations.size());
+
+ // If we are applying artificial italic, instruct HarfBuzz to do the same
+ // so that mark positioning is also transformed.
+ if (NeedsArtificialItalic())
+ hb_font_set_synthetic_slant(pHbFont, ARTIFICIAL_ITALIC_SKEW);
+
+ ImplInitHbFont(pHbFont);
+
+ return pHbFont;
+}
+
+hb_font_t* LogicalFontInstance::GetHbFontUntransformed() const
+{
+ auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
+
+ if (NeedsArtificialItalic()) // || NeedsArtificialBold()
+ {
+ if (!m_pHbFontUntransformed)
+ {
+ m_pHbFontUntransformed = hb_font_create_sub_font(pHbFont);
+ // Unset slant set on parent font.
+ // Does not actually work: https://github.com/harfbuzz/harfbuzz/issues/3890
+ hb_font_set_synthetic_slant(m_pHbFontUntransformed, 0);
+ }
+ return m_pHbFontUntransformed;
+ }
+
+ return pHbFont;
+}
+
+double LogicalFontInstance::GetKashidaWidth() const
+{
+ sal_GlyphId nGlyph = GetGlyphIndex(0x0640);
+ if (nGlyph)
+ return GetGlyphWidth(nGlyph);
+ return 0;
+}
+
+void LogicalFontInstance::GetScale(double* nXScale, double* nYScale) const
+{
+ double nUPEM = GetFontFace()->UnitsPerEm();
+ double nHeight(m_aFontSelData.mnHeight);
+
+ // On Windows, mnWidth is relative to average char width not font height,
+ // and we need to keep it that way for GDI to correctly scale the glyphs.
+ // Here we compensate for this so that HarfBuzz gives us the correct glyph
+ // positions.
+ double nWidth(m_aFontSelData.mnWidth ? m_aFontSelData.mnWidth * m_nAveWidthFactor : nHeight);
+
+ if (nYScale)
+ *nYScale = nHeight / nUPEM;
+
+ if (nXScale)
+ *nXScale = nWidth / nUPEM;
+}
+
+void LogicalFontInstance::AddFallbackForUnicode(sal_UCS4 cChar, FontWeight eWeight,
+ const OUString& rFontName, bool bEmbolden,
+ const ItalicMatrix& rMatrix)
+{
+ MapEntry& rEntry = maUnicodeFallbackList[std::pair<sal_UCS4, FontWeight>(cChar, eWeight)];
+ rEntry.sFontName = rFontName;
+ rEntry.bEmbolden = bEmbolden;
+ rEntry.aItalicMatrix = rMatrix;
+}
+
+bool LogicalFontInstance::GetFallbackForUnicode(sal_UCS4 cChar, FontWeight eWeight,
+ OUString* pFontName, bool* pEmbolden,
+ ItalicMatrix* pMatrix) const
+{
+ UnicodeFallbackList::const_iterator it
+ = maUnicodeFallbackList.find(std::pair<sal_UCS4, FontWeight>(cChar, eWeight));
+ if (it == maUnicodeFallbackList.end())
+ return false;
+
+ const MapEntry& rEntry = (*it).second;
+ *pFontName = rEntry.sFontName;
+ *pEmbolden = rEntry.bEmbolden;
+ *pMatrix = rEntry.aItalicMatrix;
+ return true;
+}
+
+void LogicalFontInstance::IgnoreFallbackForUnicode(sal_UCS4 cChar, FontWeight eWeight,
+ std::u16string_view rFontName)
+{
+ UnicodeFallbackList::iterator it
+ = maUnicodeFallbackList.find(std::pair<sal_UCS4, FontWeight>(cChar, eWeight));
+ if (it == maUnicodeFallbackList.end())
+ return;
+ const MapEntry& rEntry = (*it).second;
+ if (rEntry.sFontName == rFontName)
+ maUnicodeFallbackList.erase(it);
+}
+
+bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId nID, tools::Rectangle& rRect,
+ bool bVertical) const
+{
+ 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 (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;
+}
+
+sal_GlyphId LogicalFontInstance::GetGlyphIndex(uint32_t nUnicode, uint32_t nVariationSelector) const
+{
+ auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
+ sal_GlyphId nGlyph = 0;
+ if (hb_font_get_glyph(pHbFont, nUnicode, nVariationSelector, &nGlyph))
+ return nGlyph;
+ return 0;
+}
+
+double LogicalFontInstance::GetGlyphWidth(sal_GlyphId nGlyph, bool bVertical, bool bScale) const
+{
+ auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
+ int nWidth;
+ if (bVertical)
+ nWidth = hb_font_get_glyph_v_advance(pHbFont, nGlyph);
+ else
+ nWidth = hb_font_get_glyph_h_advance(pHbFont, nGlyph);
+
+ if (!bScale)
+ return nWidth;
+
+ double nScale = 0;
+ GetScale(&nScale, nullptr);
+ return double(nWidth * nScale);
+}
+
+bool LogicalFontInstance::IsGraphiteFont()
+{
+ if (!m_xbIsGraphiteFont.has_value())
+ {
+ m_xbIsGraphiteFont
+ = hb_graphite2_face_get_gr_face(hb_font_get_face(GetHbFont())) != nullptr;
+ }
+ return *m_xbIsGraphiteFont;
+}
+
+bool LogicalFontInstance::NeedOffsetCorrection(sal_Int32 nYOffset)
+{
+ if (!m_xeFontFamilyEnum)
+ {
+ m_xeFontFamilyEnum = FontFamilyEnum::Unclassified;
+
+ // DFKai-SB (ukai.ttf) is a built-in font under traditional Chinese
+ // Windows. It has wrong extent values in glyf table. The problem results
+ // in wrong positioning of glyphs in vertical writing.
+ // Check https://github.com/harfbuzz/harfbuzz/issues/3521 for reference.
+ if (GetFontFace()->GetName(vcl::font::NAME_ID_FONT_FAMILY) == "DFKai-SB")
+ m_xeFontFamilyEnum = FontFamilyEnum::DFKaiSB;
+ }
+
+ bool bRet = true;
+
+ switch (*m_xeFontFamilyEnum)
+ {
+ case FontFamilyEnum::DFKaiSB:
+ // -839: optimization for one third of ukai.ttf
+ if (nYOffset == -839)
+ bRet = false;
+ break;
+ default:
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+bool LogicalFontInstance::NeedsArtificialBold() const
+{
+ return m_aFontSelData.GetWeight() > WEIGHT_MEDIUM && m_pFontFace->GetWeight() <= WEIGHT_MEDIUM;
+}
+
+bool LogicalFontInstance::NeedsArtificialItalic() const
+{
+ return m_aFontSelData.GetItalic() != ITALIC_NONE && m_pFontFace->GetItalic() == ITALIC_NONE;
+}
+
+namespace
+{
+void move_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
+ void* pUserData)
+{
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPoly->append(basegfx::B2DPoint(to_x, -to_y));
+}
+
+void line_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
+ void* pUserData)
+{
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPoly->append(basegfx::B2DPoint(to_x, -to_y));
+}
+
+void cubic_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float control1_x,
+ float control1_y, float control2_x, float control2_y, float to_x, float to_y,
+ void* pUserData)
+{
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPoly->appendBezierSegment(basegfx::B2DPoint(control1_x, -control1_y),
+ basegfx::B2DPoint(control2_x, -control2_y),
+ basegfx::B2DPoint(to_x, -to_y));
+}
+
+void close_path_func(hb_draw_funcs_t*, void* pDrawData, hb_draw_state_t*, void* pUserData)
+{
+ auto pPolyPoly = static_cast<basegfx::B2DPolyPolygon*>(pDrawData);
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPolyPoly->append(*pPoly);
+ pPoly->clear();
+}
+}
+
+basegfx::B2DPolyPolygon LogicalFontInstance::GetGlyphOutlineUntransformed(sal_GlyphId nGlyph) const
+{
+ if (!m_pHbDrawFuncs)
+ {
+ m_pHbDrawFuncs = hb_draw_funcs_create();
+ auto pUserData = const_cast<basegfx::B2DPolygon*>(&m_aDrawPolygon);
+ hb_draw_funcs_set_move_to_func(m_pHbDrawFuncs, move_to_func, pUserData, nullptr);
+ hb_draw_funcs_set_line_to_func(m_pHbDrawFuncs, line_to_func, pUserData, nullptr);
+ hb_draw_funcs_set_cubic_to_func(m_pHbDrawFuncs, cubic_to_func, pUserData, nullptr);
+ // B2DPolyPolygon does not support quadratic curves, HarfBuzz will
+ // convert them to cubic curves for us if we don’t set a callback
+ // function.
+ //hb_draw_funcs_set_quadratic_to_func(m_pHbDrawFuncs, quadratic_to_func, pUserData, nullptr);
+ hb_draw_funcs_set_close_path_func(m_pHbDrawFuncs, close_path_func, pUserData, nullptr);
+ }
+
+ basegfx::B2DPolyPolygon aPolyPoly;
+#if HB_VERSION_ATLEAST(7, 0, 0)
+ hb_font_draw_glyph(GetHbFontUntransformed(), nGlyph, m_pHbDrawFuncs, &aPolyPoly);
+#else
+ hb_font_get_glyph_shape(GetHbFontUntransformed(), nGlyph, m_pHbDrawFuncs, &aPolyPoly);
+#endif
+ return aPolyPoly;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */