LibreOffice Module vcl (master) 1
LogicalFontInstance.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <hb-ot.h>
23#include <hb-graphite2.h>
24
27#include <impfontcache.hxx>
28
30 const vcl::font::FontSelectPattern& rFontSelData)
31 : mxFontMetric(new FontMetricData(rFontSelData))
32 , mpConversion(nullptr)
33 , mnLineHeight(0)
34 , mnOwnOrientation(0)
35 , mnOrientation(0)
36 , mbInit(false)
37 , mpFontCache(nullptr)
38 , m_aFontSelData(rFontSelData)
39 , m_pHbFont(nullptr)
40 , m_nAveWidthFactor(1.0f)
41 , m_pFontFace(&const_cast<vcl::font::PhysicalFontFace&>(rFontFace))
42{
43}
44
46{
48 mpFontCache = nullptr;
49 mxFontMetric = nullptr;
50
51 if (m_pHbFont)
52 hb_font_destroy(m_pHbFont);
53
55 hb_font_destroy(m_pHbFontUntransformed);
56
58 hb_draw_funcs_destroy(m_pHbDrawFuncs);
59}
60
62{
63 auto pFace = GetFontFace();
64 hb_face_t* pHbFace = pFace->GetHbFace();
65 assert(pHbFace);
66 auto nUPEM = pFace->UnitsPerEm();
67
68 hb_font_t* pHbFont = hb_font_create(pHbFace);
69 hb_font_set_scale(pHbFont, nUPEM, nUPEM);
70 hb_ot_font_set_funcs(pHbFont);
71
72 auto aVariations = pFace->GetVariations(*this);
73 if (!aVariations.empty())
74 hb_font_set_variations(pHbFont, aVariations.data(), aVariations.size());
75
76 // If we are applying artificial italic, instruct HarfBuzz to do the same
77 // so that mark positioning is also transformed.
79 hb_font_set_synthetic_slant(pHbFont, ARTIFICIAL_ITALIC_SKEW);
80
81 ImplInitHbFont(pHbFont);
82
83 return pHbFont;
84}
85
87{
88 auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
89
90 if (NeedsArtificialItalic()) // || NeedsArtificialBold()
91 {
93 {
94 m_pHbFontUntransformed = hb_font_create_sub_font(pHbFont);
95 // Unset slant set on parent font.
96 // Does not actually work: https://github.com/harfbuzz/harfbuzz/issues/3890
97 hb_font_set_synthetic_slant(m_pHbFontUntransformed, 0);
98 }
100 }
101
102 return pHbFont;
103}
104
106{
107 sal_GlyphId nGlyph = GetGlyphIndex(0x0640);
108 if (nGlyph)
109 return GetGlyphWidth(nGlyph);
110 return 0;
111}
112
113void LogicalFontInstance::GetScale(double* nXScale, double* nYScale) const
114{
115 double nUPEM = GetFontFace()->UnitsPerEm();
116 double nHeight(m_aFontSelData.mnHeight);
117
118 // On Windows, mnWidth is relative to average char width not font height,
119 // and we need to keep it that way for GDI to correctly scale the glyphs.
120 // Here we compensate for this so that HarfBuzz gives us the correct glyph
121 // positions.
123
124 if (nYScale)
125 *nYScale = nHeight / nUPEM;
126
127 if (nXScale)
128 *nXScale = nWidth / nUPEM;
129}
130
132 const OUString& rFontName, bool bEmbolden,
133 const ItalicMatrix& rMatrix)
134{
135 MapEntry& rEntry = maUnicodeFallbackList[std::pair<sal_UCS4, FontWeight>(cChar, eWeight)];
136 rEntry.sFontName = rFontName;
137 rEntry.bEmbolden = bEmbolden;
138 rEntry.aItalicMatrix = rMatrix;
139}
140
142 OUString* pFontName, bool* pEmbolden,
143 ItalicMatrix* pMatrix) const
144{
145 UnicodeFallbackList::const_iterator it
146 = maUnicodeFallbackList.find(std::pair<sal_UCS4, FontWeight>(cChar, eWeight));
147 if (it == maUnicodeFallbackList.end())
148 return false;
149
150 const MapEntry& rEntry = (*it).second;
151 *pFontName = rEntry.sFontName;
152 *pEmbolden = rEntry.bEmbolden;
153 *pMatrix = rEntry.aItalicMatrix;
154 return true;
155}
156
158 std::u16string_view rFontName)
159{
160 UnicodeFallbackList::iterator it
161 = maUnicodeFallbackList.find(std::pair<sal_UCS4, FontWeight>(cChar, eWeight));
162 if (it == maUnicodeFallbackList.end())
163 return;
164 const MapEntry& rEntry = (*it).second;
165 if (rEntry.sFontName == rFontName)
166 maUnicodeFallbackList.erase(it);
167}
168
170 bool bVertical) const
171{
172 if (mpFontCache && mpFontCache->GetCachedGlyphBoundRect(this, nID, rRect))
173 return true;
174
175 auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
176 hb_glyph_extents_t aExtents;
177 bool res = hb_font_get_glyph_extents(pHbFont, nID, &aExtents);
178
179 if (res)
180 {
181 double nXScale = 0, nYScale = 0;
182 GetScale(&nXScale, &nYScale);
183
184 double fMinX = aExtents.x_bearing;
185 double fMinY = aExtents.y_bearing;
186 double fMaxX = aExtents.x_bearing + aExtents.width;
187 double fMaxY = aExtents.y_bearing + aExtents.height;
188
189 tools::Rectangle aRect(std::floor(fMinX * nXScale), -std::ceil(fMinY * nYScale),
190 std::ceil(fMaxX * nXScale), -std::floor(fMaxY * nYScale));
191 if (mnOrientation && !bVertical)
192 {
193 // Apply font rotation.
194 const double fRad = toRadians(mnOrientation);
195 const double fCos = cos(fRad);
196 const double fSin = sin(fRad);
197
198 rRect.SetLeft(fCos * aRect.Left() + fSin * aRect.Top());
199 rRect.SetTop(-fSin * aRect.Left() - fCos * aRect.Top());
200 rRect.SetRight(fCos * aRect.Right() + fSin * aRect.Bottom());
201 rRect.SetBottom(-fSin * aRect.Right() - fCos * aRect.Bottom());
202 }
203 else
204 rRect = aRect;
205 }
206
207 if (mpFontCache && res)
208 mpFontCache->CacheGlyphBoundRect(this, nID, rRect);
209 return res;
210}
211
212sal_GlyphId LogicalFontInstance::GetGlyphIndex(uint32_t nUnicode, uint32_t nVariationSelector) const
213{
214 auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
215 sal_GlyphId nGlyph = 0;
216 if (hb_font_get_glyph(pHbFont, nUnicode, nVariationSelector, &nGlyph))
217 return nGlyph;
218 return 0;
219}
220
221double LogicalFontInstance::GetGlyphWidth(sal_GlyphId nGlyph, bool bVertical, bool bScale) const
222{
223 auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
224 int nWidth;
225 if (bVertical)
226 nWidth = hb_font_get_glyph_v_advance(pHbFont, nGlyph);
227 else
228 nWidth = hb_font_get_glyph_h_advance(pHbFont, nGlyph);
229
230 if (!bScale)
231 return nWidth;
232
233 double nScale = 0;
234 GetScale(&nScale, nullptr);
235 return double(nWidth * nScale);
236}
237
239{
241 {
243 = hb_graphite2_face_get_gr_face(hb_font_get_face(GetHbFont())) != nullptr;
244 }
245 return *m_xbIsGraphiteFont;
246}
247
249{
251 {
253
254 // DFKai-SB (ukai.ttf) is a built-in font under traditional Chinese
255 // Windows. It has wrong extent values in glyf table. The problem results
256 // in wrong positioning of glyphs in vertical writing.
257 // Check https://github.com/harfbuzz/harfbuzz/issues/3521 for reference.
260 }
261
262 bool bRet = true;
263
264 switch (*m_xeFontFamilyEnum)
265 {
267 // -839: optimization for one third of ukai.ttf
268 if (nYOffset == -839)
269 bRet = false;
270 break;
271 default:
272 bRet = false;
273 }
274
275 return bRet;
276}
277
279{
280 return m_aFontSelData.GetWeight() > WEIGHT_MEDIUM && m_pFontFace->GetWeight() <= WEIGHT_MEDIUM;
281}
282
284{
285 return m_aFontSelData.GetItalic() != ITALIC_NONE && m_pFontFace->GetItalic() == ITALIC_NONE;
286}
287
288namespace
289{
290void move_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
291 void* pUserData)
292{
293 auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
294 pPoly->append(basegfx::B2DPoint(to_x, -to_y));
295}
296
297void line_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
298 void* pUserData)
299{
300 auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
301 pPoly->append(basegfx::B2DPoint(to_x, -to_y));
302}
303
304void cubic_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float control1_x,
305 float control1_y, float control2_x, float control2_y, float to_x, float to_y,
306 void* pUserData)
307{
308 auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
309 pPoly->appendBezierSegment(basegfx::B2DPoint(control1_x, -control1_y),
310 basegfx::B2DPoint(control2_x, -control2_y),
311 basegfx::B2DPoint(to_x, -to_y));
312}
313
314void close_path_func(hb_draw_funcs_t*, void* pDrawData, hb_draw_state_t*, void* pUserData)
315{
316 auto pPolyPoly = static_cast<basegfx::B2DPolyPolygon*>(pDrawData);
317 auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
318 pPolyPoly->append(*pPoly);
319 pPoly->clear();
320}
321}
322
324{
325 if (!m_pHbDrawFuncs)
326 {
327 m_pHbDrawFuncs = hb_draw_funcs_create();
328 auto pUserData = const_cast<basegfx::B2DPolygon*>(&m_aDrawPolygon);
329 hb_draw_funcs_set_move_to_func(m_pHbDrawFuncs, move_to_func, pUserData, nullptr);
330 hb_draw_funcs_set_line_to_func(m_pHbDrawFuncs, line_to_func, pUserData, nullptr);
331 hb_draw_funcs_set_cubic_to_func(m_pHbDrawFuncs, cubic_to_func, pUserData, nullptr);
332 // B2DPolyPolygon does not support quadratic curves, HarfBuzz will
333 // convert them to cubic curves for us if we don’t set a callback
334 // function.
335 //hb_draw_funcs_set_quadratic_to_func(m_pHbDrawFuncs, quadratic_to_func, pUserData, nullptr);
336 hb_draw_funcs_set_close_path_func(m_pHbDrawFuncs, close_path_func, pUserData, nullptr);
337 }
338
339 basegfx::B2DPolyPolygon aPolyPoly;
340#if HB_VERSION_ATLEAST(7, 0, 0)
341 hb_font_draw_glyph(GetHbFontUntransformed(), nGlyph, m_pHbDrawFuncs, &aPolyPoly);
342#else
343 hb_font_get_glyph_shape(GetHbFontUntransformed(), nGlyph, m_pHbDrawFuncs, &aPolyPoly);
344#endif
345 return aPolyPoly;
346}
347
348/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr float ARTIFICIAL_ITALIC_SKEW
FontItalic GetItalic() const
FontWeight GetWeight() const
bool GetCachedGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:255
void CacheGlyphBoundRect(const LogicalFontInstance *, sal_GlyphId, tools::Rectangle &)
Definition: fontcache.cxx:272
std::optional< bool > m_xbIsGraphiteFont
basegfx::B2DPolyPolygon GetGlyphOutlineUntransformed(sal_GlyphId) const
void GetScale(double *nXScale, double *nYScale) const
const vcl::font::PhysicalFontFace * GetFontFace() const
LogicalFontInstance(const vcl::font::PhysicalFontFace &, const vcl::font::FontSelectPattern &)
void AddFallbackForUnicode(sal_UCS4 cChar, FontWeight eWeight, const OUString &rFontName, bool bEmbolden, const ItalicMatrix &rMatrix)
double GetGlyphWidth(sal_GlyphId, bool=false, bool=true) const
bool GetGlyphBoundRect(sal_GlyphId, tools::Rectangle &, bool) const
const vcl::font::FontSelectPattern m_aFontSelData
virtual void ImplInitHbFont(hb_font_t *)
bool GetFallbackForUnicode(sal_UCS4 cInChar, FontWeight eInWeight, OUString *pOutFontName, bool *pOutEmbolden, ItalicMatrix *pOutItalicMatrix) const
basegfx::B2DPolygon m_aDrawPolygon
bool NeedsArtificialItalic() const
FontMetricDataRef mxFontMetric
rtl::Reference< vcl::font::PhysicalFontFace > m_pFontFace
std::optional< FontFamilyEnum > m_xeFontFamilyEnum
hb_draw_funcs_t * m_pHbDrawFuncs
bool NeedOffsetCorrection(sal_Int32 nYOffset)
void IgnoreFallbackForUnicode(sal_UCS4, FontWeight eWeight, std::u16string_view rFontName)
double GetKashidaWidth() const
UnicodeFallbackList maUnicodeFallbackList
virtual ~LogicalFontInstance() override
hb_font_t * GetHbFontUntransformed() const
sal_GlyphId GetGlyphIndex(uint32_t, uint32_t=0) const
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
void appendBezierSegment(const basegfx::B2DPoint &rNextControlPoint, const basegfx::B2DPoint &rPrevControlPoint, const basegfx::B2DPoint &rPoint)
constexpr void SetLeft(tools::Long v)
constexpr void SetTop(tools::Long v)
constexpr tools::Long Top() const
constexpr void SetRight(tools::Long v)
constexpr tools::Long Right() const
constexpr void SetBottom(tools::Long v)
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
abstract base class for physical font faces
double toRadians(D x)
virtual OUString GetName() const override
ITALIC_NONE
WEIGHT_MEDIUM
uint32_t sal_GlyphId
Definition: glyphid.hxx:24
FontWeight
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:160