diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/source/font/LogicalFontInstance.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-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.cxx | 348 |
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: */ |