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 #include <basegfx/matrix/b2dhommatrixtools.hxx>
31 LogicalFontInstance::LogicalFontInstance(const vcl::font::PhysicalFontFace
& rFontFace
,
32 const vcl::font::FontSelectPattern
& rFontSelData
)
33 : mxFontMetric(new FontMetricData(rFontSelData
))
34 , mpConversion(nullptr)
39 , mpFontCache(nullptr)
40 , m_aFontSelData(rFontSelData
)
42 , m_nAveWidthFactor(1.0f
)
43 , m_pFontFace(&const_cast<vcl::font::PhysicalFontFace
&>(rFontFace
))
47 LogicalFontInstance::~LogicalFontInstance()
49 maUnicodeFallbackList
.clear();
50 mpFontCache
= nullptr;
51 mxFontMetric
= nullptr;
54 hb_font_destroy(m_pHbFont
);
56 if (m_pHbFontUntransformed
)
57 hb_font_destroy(m_pHbFontUntransformed
);
60 hb_draw_funcs_destroy(m_pHbDrawFuncs
);
63 hb_font_t
* LogicalFontInstance::InitHbFont()
65 auto pFace
= GetFontFace();
66 hb_face_t
* pHbFace
= pFace
->GetHbFace();
68 auto nUPEM
= pFace
->UnitsPerEm();
70 hb_font_t
* pHbFont
= hb_font_create(pHbFace
);
71 hb_font_set_scale(pHbFont
, nUPEM
, nUPEM
);
72 hb_ot_font_set_funcs(pHbFont
);
74 auto aVariations
= pFace
->GetVariations(*this);
75 if (!aVariations
.empty())
76 hb_font_set_variations(pHbFont
, aVariations
.data(), aVariations
.size());
78 // If we are applying artificial italic, instruct HarfBuzz to do the same
79 // so that mark positioning is also transformed.
80 if (NeedsArtificialItalic())
81 hb_font_set_synthetic_slant(pHbFont
, ARTIFICIAL_ITALIC_SKEW
);
83 ImplInitHbFont(pHbFont
);
88 hb_font_t
* LogicalFontInstance::GetHbFontUntransformed() const
90 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
92 if (NeedsArtificialItalic()) // || NeedsArtificialBold()
94 if (!m_pHbFontUntransformed
)
96 m_pHbFontUntransformed
= hb_font_create_sub_font(pHbFont
);
97 // Unset slant set on parent font.
98 // Does not actually work: https://github.com/harfbuzz/harfbuzz/issues/3890
99 hb_font_set_synthetic_slant(m_pHbFontUntransformed
, 0);
101 return m_pHbFontUntransformed
;
107 double LogicalFontInstance::GetKashidaWidth() const
109 sal_GlyphId nGlyph
= GetGlyphIndex(0x0640);
111 return GetGlyphWidth(nGlyph
);
115 void LogicalFontInstance::GetScale(double* nXScale
, double* nYScale
) const
117 double nUPEM
= GetFontFace()->UnitsPerEm();
120 *nYScale
= m_aFontSelData
.mnHeight
/ nUPEM
;
124 // On Windows, mnWidth is relative to average char width not font height,
125 // and we need to keep it that way for GDI to correctly scale the glyphs.
126 // Here we compensate for this so that HarfBuzz gives us the correct glyph
128 double nWidth(m_aFontSelData
.mnWidth
? m_aFontSelData
.mnWidth
* GetAverageWidthFactor()
129 : m_aFontSelData
.mnHeight
);
130 *nXScale
= nWidth
/ nUPEM
;
134 void LogicalFontInstance::AddFallbackForUnicode(sal_UCS4 cChar
, FontWeight eWeight
,
135 const OUString
& rFontName
, bool bEmbolden
,
136 const ItalicMatrix
& rMatrix
)
138 MapEntry
& rEntry
= maUnicodeFallbackList
[std::pair
<sal_UCS4
, FontWeight
>(cChar
, eWeight
)];
139 rEntry
.sFontName
= rFontName
;
140 rEntry
.bEmbolden
= bEmbolden
;
141 rEntry
.aItalicMatrix
= rMatrix
;
144 bool LogicalFontInstance::GetFallbackForUnicode(sal_UCS4 cChar
, FontWeight eWeight
,
145 OUString
* pFontName
, bool* pEmbolden
,
146 ItalicMatrix
* pMatrix
) const
148 UnicodeFallbackList::const_iterator it
149 = maUnicodeFallbackList
.find(std::pair
<sal_UCS4
, FontWeight
>(cChar
, eWeight
));
150 if (it
== maUnicodeFallbackList
.end())
153 const MapEntry
& rEntry
= (*it
).second
;
154 *pFontName
= rEntry
.sFontName
;
155 *pEmbolden
= rEntry
.bEmbolden
;
156 *pMatrix
= rEntry
.aItalicMatrix
;
160 void LogicalFontInstance::IgnoreFallbackForUnicode(sal_UCS4 cChar
, FontWeight eWeight
,
161 std::u16string_view rFontName
)
163 UnicodeFallbackList::iterator it
164 = maUnicodeFallbackList
.find(std::pair
<sal_UCS4
, FontWeight
>(cChar
, eWeight
));
165 if (it
== maUnicodeFallbackList
.end())
167 const MapEntry
& rEntry
= (*it
).second
;
168 if (rEntry
.sFontName
== rFontName
)
169 maUnicodeFallbackList
.erase(it
);
172 bool LogicalFontInstance::GetGlyphBoundRect(sal_GlyphId nID
, basegfx::B2DRectangle
& rRect
,
173 bool bVertical
) const
175 // TODO: find out if it's possible for the same glyph in the same font to be used both
176 // normally and vertically; if yes, then these two variants must be cached separately
178 if (mpFontCache
&& mpFontCache
->GetCachedGlyphBoundRect(this, nID
, rRect
))
181 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
182 hb_glyph_extents_t aExtents
;
183 if (!hb_font_get_glyph_extents(pHbFont
, nID
, &aExtents
))
186 double nXScale
= 0, nYScale
= 0;
187 GetScale(&nXScale
, &nYScale
);
189 double fMinX
= aExtents
.x_bearing
* nXScale
;
190 double fMinY
= -aExtents
.y_bearing
* nYScale
;
191 double fMaxX
= (aExtents
.x_bearing
+ aExtents
.width
) * nXScale
;
192 double fMaxY
= -(aExtents
.y_bearing
+ aExtents
.height
) * nYScale
;
193 rRect
= basegfx::B2DRectangle(fMinX
, fMinY
, fMaxX
, fMaxY
);
195 auto orientation
= mnOrientation
;
197 orientation
+= 900_deg10
;
200 // Apply font rotation.
201 rRect
.transform(basegfx::utils::createRotateB2DHomMatrix(-toRadians(orientation
)));
205 mpFontCache
->CacheGlyphBoundRect(this, nID
, rRect
);
210 sal_GlyphId
LogicalFontInstance::GetGlyphIndex(uint32_t nUnicode
, uint32_t nVariationSelector
) const
212 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
213 sal_GlyphId nGlyph
= 0;
214 if (hb_font_get_glyph(pHbFont
, nUnicode
, nVariationSelector
, &nGlyph
))
219 double LogicalFontInstance::GetGlyphWidth(sal_GlyphId nGlyph
, bool bVertical
, bool bScale
) const
221 auto* pHbFont
= const_cast<LogicalFontInstance
*>(this)->GetHbFont();
224 nWidth
= hb_font_get_glyph_v_advance(pHbFont
, nGlyph
);
226 nWidth
= hb_font_get_glyph_h_advance(pHbFont
, nGlyph
);
232 GetScale(&nScale
, nullptr);
233 return double(nWidth
* nScale
);
236 bool LogicalFontInstance::IsGraphiteFont()
238 if (!m_xbIsGraphiteFont
.has_value())
241 = hb_graphite2_face_get_gr_face(hb_font_get_face(GetHbFont())) != nullptr;
243 return *m_xbIsGraphiteFont
;
246 bool LogicalFontInstance::NeedOffsetCorrection(sal_Int32 nYOffset
)
248 if (!m_xeFontFamilyEnum
)
250 m_xeFontFamilyEnum
= FontFamilyEnum::Unclassified
;
252 // DFKai-SB (ukai.ttf) is a built-in font under traditional Chinese
253 // Windows. It has wrong extent values in glyf table. The problem results
254 // in wrong positioning of glyphs in vertical writing.
255 // Check https://github.com/harfbuzz/harfbuzz/issues/3521 for reference.
256 if (GetFontFace()->GetName(vcl::font::NAME_ID_FONT_FAMILY
) == "DFKai-SB")
257 m_xeFontFamilyEnum
= FontFamilyEnum::DFKaiSB
;
262 switch (*m_xeFontFamilyEnum
)
264 case FontFamilyEnum::DFKaiSB
:
265 // -839: optimization for one third of ukai.ttf
266 if (nYOffset
== -839)
276 bool LogicalFontInstance::NeedsArtificialBold() const
278 return m_aFontSelData
.GetWeight() > WEIGHT_MEDIUM
&& m_pFontFace
->GetWeight() <= WEIGHT_MEDIUM
;
281 bool LogicalFontInstance::NeedsArtificialItalic() const
283 return m_aFontSelData
.GetItalic() != ITALIC_NONE
&& m_pFontFace
->GetItalic() == ITALIC_NONE
;
288 void move_to_func(hb_draw_funcs_t
*, void* /*pDrawData*/, hb_draw_state_t
*, float to_x
, float to_y
,
291 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
292 pPoly
->append(basegfx::B2DPoint(to_x
, -to_y
));
295 void line_to_func(hb_draw_funcs_t
*, void* /*pDrawData*/, hb_draw_state_t
*, float to_x
, float to_y
,
298 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
299 pPoly
->append(basegfx::B2DPoint(to_x
, -to_y
));
302 void cubic_to_func(hb_draw_funcs_t
*, void* /*pDrawData*/, hb_draw_state_t
*, float control1_x
,
303 float control1_y
, float control2_x
, float control2_y
, float to_x
, float to_y
,
306 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
307 pPoly
->appendBezierSegment(basegfx::B2DPoint(control1_x
, -control1_y
),
308 basegfx::B2DPoint(control2_x
, -control2_y
),
309 basegfx::B2DPoint(to_x
, -to_y
));
312 void close_path_func(hb_draw_funcs_t
*, void* pDrawData
, hb_draw_state_t
*, void* pUserData
)
314 auto pPolyPoly
= static_cast<basegfx::B2DPolyPolygon
*>(pDrawData
);
315 auto pPoly
= static_cast<basegfx::B2DPolygon
*>(pUserData
);
316 pPolyPoly
->append(*pPoly
);
321 basegfx::B2DPolyPolygon
LogicalFontInstance::GetGlyphOutlineUntransformed(sal_GlyphId nGlyph
) const
325 m_pHbDrawFuncs
= hb_draw_funcs_create();
326 auto pUserData
= const_cast<basegfx::B2DPolygon
*>(&m_aDrawPolygon
);
327 hb_draw_funcs_set_move_to_func(m_pHbDrawFuncs
, move_to_func
, pUserData
, nullptr);
328 hb_draw_funcs_set_line_to_func(m_pHbDrawFuncs
, line_to_func
, pUserData
, nullptr);
329 hb_draw_funcs_set_cubic_to_func(m_pHbDrawFuncs
, cubic_to_func
, pUserData
, nullptr);
330 // B2DPolyPolygon does not support quadratic curves, HarfBuzz will
331 // convert them to cubic curves for us if we don’t set a callback
333 //hb_draw_funcs_set_quadratic_to_func(m_pHbDrawFuncs, quadratic_to_func, pUserData, nullptr);
334 hb_draw_funcs_set_close_path_func(m_pHbDrawFuncs
, close_path_func
, pUserData
, nullptr);
337 basegfx::B2DPolyPolygon aPolyPoly
;
338 #if HB_VERSION_ATLEAST(7, 0, 0)
339 hb_font_draw_glyph(GetHbFontUntransformed(), nGlyph
, m_pHbDrawFuncs
, &aPolyPoly
);
341 hb_font_get_glyph_shape(GetHbFontUntransformed(), nGlyph
, m_pHbDrawFuncs
, &aPolyPoly
);
346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */