1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
23 #include <hb-graphite2.h>
25 #include <font/PhysicalFontFace.hxx>
26 #include <font/LogicalFontInstance.hxx>
27 #include <impfontcache.hxx>
29 LogicalFontInstance::LogicalFontInstance(const vcl::font::PhysicalFontFace
& rFontFace
,
30 const vcl::font::FontSelectPattern
& rFontSelData
)
31 : mxFontMetric(new ImplFontMetricData(rFontSelData
))
32 , mpConversion(nullptr)
37 , mpFontCache(nullptr)
38 , m_aFontSelData(rFontSelData
)
40 , m_nAveWidthFactor(1.0f
)
41 , m_pFontFace(&const_cast<vcl::font::PhysicalFontFace
&>(rFontFace
))
45 LogicalFontInstance::~LogicalFontInstance()
47 maUnicodeFallbackList
.clear();
48 mpFontCache
= nullptr;
49 mxFontMetric
= nullptr;
52 hb_font_destroy(m_pHbFont
);
54 if (m_pHbFontUntransformed
)
55 hb_font_destroy(m_pHbFontUntransformed
);
58 hb_draw_funcs_destroy(m_pHbDrawFuncs
);
61 hb_font_t
* LogicalFontInstance::InitHbFont()
63 auto pFace
= GetFontFace();
64 hb_face_t
* pHbFace
= pFace
->GetHbFace();
66 auto nUPEM
= pFace
->UnitsPerEm();
68 hb_font_t
* pHbFont
= hb_font_create(pHbFace
);
69 hb_font_set_scale(pHbFont
, nUPEM
, nUPEM
);
70 hb_ot_font_set_funcs(pHbFont
);
72 auto aVariations
= pFace
->GetVariations(*this);
73 if (!aVariations
.empty())
74 hb_font_set_variations(pHbFont
, aVariations
.data(), aVariations
.size());
76 // If we are applying artificial italic, instruct HarfBuzz to do the same
77 // so that mark positioning is also transformed.
78 if (NeedsArtificialItalic())
79 hb_font_set_synthetic_slant(pHbFont
, ARTIFICIAL_ITALIC_SKEW
);
81 ImplInitHbFont(pHbFont
);
86 hb_font_t
* LogicalFontInstance::GetHbFontUntransformed() const
88 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
90 if (NeedsArtificialItalic()) // || NeedsArtificialBold()
92 if (!m_pHbFontUntransformed
)
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);
99 return m_pHbFontUntransformed
;
105 int LogicalFontInstance::GetKashidaWidth() const
107 sal_GlyphId nGlyph
= GetGlyphIndex(0x0640);
109 return std::ceil(GetGlyphWidth(nGlyph
));
113 void LogicalFontInstance::GetScale(double* nXScale
, double* nYScale
) const
115 double nUPEM
= GetFontFace()->UnitsPerEm();
116 double nHeight(m_aFontSelData
.mnHeight
);
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
122 double nWidth(m_aFontSelData
.mnWidth
? m_aFontSelData
.mnWidth
* m_nAveWidthFactor
: nHeight
);
125 *nYScale
= nHeight
/ nUPEM
;
128 *nXScale
= nWidth
/ nUPEM
;
131 void LogicalFontInstance::AddFallbackForUnicode(sal_UCS4 cChar
, FontWeight eWeight
,
132 const OUString
& rFontName
, bool bEmbolden
,
133 const ItalicMatrix
& rMatrix
)
135 MapEntry
& rEntry
= maUnicodeFallbackList
[std::pair
<sal_UCS4
, FontWeight
>(cChar
, eWeight
)];
136 rEntry
.sFontName
= rFontName
;
137 rEntry
.bEmbolden
= bEmbolden
;
138 rEntry
.aItalicMatrix
= rMatrix
;
141 bool LogicalFontInstance::GetFallbackForUnicode(sal_UCS4 cChar
, FontWeight eWeight
,
142 OUString
* pFontName
, bool* pEmbolden
,
143 ItalicMatrix
* pMatrix
) const
145 UnicodeFallbackList::const_iterator it
146 = maUnicodeFallbackList
.find(std::pair
<sal_UCS4
, FontWeight
>(cChar
, eWeight
));
147 if (it
== maUnicodeFallbackList
.end())
150 const MapEntry
& rEntry
= (*it
).second
;
151 *pFontName
= rEntry
.sFontName
;
152 *pEmbolden
= rEntry
.bEmbolden
;
153 *pMatrix
= rEntry
.aItalicMatrix
;
157 void LogicalFontInstance::IgnoreFallbackForUnicode(sal_UCS4 cChar
, FontWeight eWeight
,
158 std::u16string_view rFontName
)
160 UnicodeFallbackList::iterator it
161 = maUnicodeFallbackList
.find(std::pair
<sal_UCS4
, FontWeight
>(cChar
, eWeight
));
162 if (it
== maUnicodeFallbackList
.end())
164 const MapEntry
& rEntry
= (*it
).second
;
165 if (rEntry
.sFontName
== rFontName
)
166 maUnicodeFallbackList
.erase(it
);
169 bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId nID
, tools::Rectangle
& rRect
,
170 bool bVertical
) const
172 if (mpFontCache
&& mpFontCache
->GetCachedGlyphBoundRect(this, nID
, rRect
))
175 bool res
= ImplGetGlyphBoundRect(nID
, rRect
, bVertical
);
176 if (mpFontCache
&& res
)
177 mpFontCache
->CacheGlyphBoundRect(this, nID
, rRect
);
181 sal_GlyphId
LogicalFontInstance::GetGlyphIndex(uint32_t nUnicode
, uint32_t nVariationSelector
) const
183 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
184 sal_GlyphId nGlyph
= 0;
185 if (hb_font_get_glyph(pHbFont
, nUnicode
, nVariationSelector
, &nGlyph
))
190 double LogicalFontInstance::GetGlyphWidth(sal_GlyphId nGlyph
, bool bVertical
, bool bScale
) const
192 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
195 nWidth
= hb_font_get_glyph_v_advance(pHbFont
, nGlyph
);
197 nWidth
= hb_font_get_glyph_h_advance(pHbFont
, nGlyph
);
203 GetScale(&nScale
, nullptr);
204 return double(nWidth
* nScale
);
207 bool LogicalFontInstance::IsGraphiteFont()
209 if (!m_xbIsGraphiteFont
)
212 = hb_graphite2_face_get_gr_face(hb_font_get_face(GetHbFont())) != nullptr;
214 return *m_xbIsGraphiteFont
;
217 bool LogicalFontInstance::NeedOffsetCorrection(sal_Int32 nYOffset
)
219 if (!m_xeFontFamilyEnum
)
221 m_xeFontFamilyEnum
= FontFamilyEnum::Unclassified
;
223 // DFKai-SB (ukai.ttf) is a built-in font under traditional Chinese
224 // Windows. It has wrong extent values in glyf table. The problem results
225 // in wrong positioning of glyphs in vertical writing.
226 // Check https://github.com/harfbuzz/harfbuzz/issues/3521 for reference.
227 if (GetFontFace()->GetName(vcl::font::NAME_ID_FONT_FAMILY
) == "DFKai-SB")
228 m_xeFontFamilyEnum
= FontFamilyEnum::DFKaiSB
;
233 switch (*m_xeFontFamilyEnum
)
235 case FontFamilyEnum::DFKaiSB
:
236 // -839: optimization for one third of ukai.ttf
237 if (nYOffset
== -839)
247 bool LogicalFontInstance::NeedsArtificialBold() const
249 return m_aFontSelData
.GetWeight() > WEIGHT_MEDIUM
&& m_pFontFace
->GetWeight() <= WEIGHT_MEDIUM
;
252 bool LogicalFontInstance::NeedsArtificialItalic() const
254 return m_aFontSelData
.GetItalic() != ITALIC_NONE
&& m_pFontFace
->GetItalic() == ITALIC_NONE
;
259 void move_to_func(hb_draw_funcs_t
*, void* /*pDrawData*/, hb_draw_state_t
*, float to_x
, float to_y
,
262 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
263 pPoly
->append(basegfx::B2DPoint(to_x
, -to_y
));
266 void line_to_func(hb_draw_funcs_t
*, void* /*pDrawData*/, hb_draw_state_t
*, float to_x
, float to_y
,
269 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
270 pPoly
->append(basegfx::B2DPoint(to_x
, -to_y
));
273 void cubic_to_func(hb_draw_funcs_t
*, void* /*pDrawData*/, hb_draw_state_t
*, float control1_x
,
274 float control1_y
, float control2_x
, float control2_y
, float to_x
, float to_y
,
277 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
278 pPoly
->appendBezierSegment(basegfx::B2DPoint(control1_x
, -control1_y
),
279 basegfx::B2DPoint(control2_x
, -control2_y
),
280 basegfx::B2DPoint(to_x
, -to_y
));
283 void close_path_func(hb_draw_funcs_t
*, void* pDrawData
, hb_draw_state_t
*, void* pUserData
)
285 auto pPolyPoly
= static_cast<basegfx::B2DPolyPolygon
*>(pDrawData
);
286 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
287 pPolyPoly
->append(*pPoly
);
292 basegfx::B2DPolyPolygon
LogicalFontInstance::GetGlyphOutlineUntransformed(sal_GlyphId nGlyph
) const
296 m_pHbDrawFuncs
= hb_draw_funcs_create();
297 auto pUserData
= const_cast<basegfx::B2DPolygon
*>(&m_aDrawPolygon
);
298 hb_draw_funcs_set_move_to_func(m_pHbDrawFuncs
, move_to_func
, pUserData
, nullptr);
299 hb_draw_funcs_set_line_to_func(m_pHbDrawFuncs
, line_to_func
, pUserData
, nullptr);
300 hb_draw_funcs_set_cubic_to_func(m_pHbDrawFuncs
, cubic_to_func
, pUserData
, nullptr);
301 // B2DPolyPolygon does not support quadratic curves, HarfBuzz will
302 // convert them to cubic curves for us if we don’t set a callback
304 //hb_draw_funcs_set_quadratic_to_func(m_pHbDrawFuncs, quadratic_to_func, pUserData, nullptr);
305 hb_draw_funcs_set_close_path_func(m_pHbDrawFuncs
, close_path_func
, pUserData
, nullptr);
308 basegfx::B2DPolyPolygon aPolyPoly
;
309 #if HB_VERSION_ATLEAST(7, 0, 0)
310 hb_font_draw_glyph(GetHbFontUntransformed(), nGlyph
, m_pHbDrawFuncs
, &aPolyPoly
);
312 hb_font_get_glyph_shape(GetHbFontUntransformed(), nGlyph
, m_pHbDrawFuncs
, &aPolyPoly
);
317 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */