LibreOffice Module vcl (master) 1
PhysicalFontFace.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 <sal/types.h>
23#include <tools/fontenum.hxx>
24#include <unotools/fontdefs.hxx>
25#include <osl/file.hxx>
26#include <osl/thread.h>
27
28#include <fontattributes.hxx>
29#include <impfontcharmap.hxx>
30#include <sft.hxx>
31#include <salgdi.hxx>
32
35#include <o3tl/string_view.hxx>
36
37#include <string_view>
38
39#include <hb-ot.h>
40
41namespace vcl::font
42{
44 : FontAttributes(rDFA)
45 , mpHbFace(nullptr)
46 , mpHbUnscaledFont(nullptr)
47{
48}
49
51{
52 if (mpHbFace)
53 hb_face_destroy(mpHbFace);
55 hb_font_destroy(mpHbUnscaledFont);
56}
57
59{
60 // compare their width, weight, italic, style name and family name
61 if (GetWidthType() < rOther.GetWidthType())
62 return -1;
63 else if (GetWidthType() > rOther.GetWidthType())
64 return 1;
65
66 if (GetWeight() < rOther.GetWeight())
67 return -1;
68 else if (GetWeight() > rOther.GetWeight())
69 return 1;
70
71 if (GetItalic() < rOther.GetItalic())
72 return -1;
73 else if (GetItalic() > rOther.GetItalic())
74 return 1;
75
76 sal_Int32 nRet = GetFamilyName().compareTo(rOther.GetFamilyName());
77
78 if (nRet == 0)
79 {
80 nRet = GetStyleName().compareTo(rOther.GetStyleName());
81 }
82
83 return nRet;
84}
85
86static int FamilyNameMatchValue(FontSelectPattern const& rFSP, std::u16string_view sFontFamily)
87{
88 const OUString& rFontName = rFSP.maTargetName;
89
90 if (rFontName.equalsIgnoreAsciiCase(sFontFamily))
91 return 240000;
92
93 return 0;
94}
95
96static int StyleNameMatchValue(FontMatchStatus const& rStatus, std::u16string_view rStyle)
97{
98 if (rStatus.mpTargetStyleName
100 return 120000;
101
102 return 0;
103}
104
105static int PitchMatchValue(FontSelectPattern const& rFSP, FontPitch ePitch)
106{
107 if ((rFSP.GetPitch() != PITCH_DONTKNOW) && (rFSP.GetPitch() == ePitch))
108 return 20000;
109
110 return 0;
111}
112
114{
115 // TODO: change when the upper layers can tell their width preference
116 if (eWidthType == WIDTH_NORMAL)
117 return 400;
118 else if ((eWidthType == WIDTH_SEMI_EXPANDED) || (eWidthType == WIDTH_SEMI_CONDENSED))
119 return 300;
120
121 return 0;
122}
123
124static int WeightMatchValue(FontSelectPattern const& rFSP, FontWeight eWeight)
125{
126 int nMatch = 0;
127
128 if (rFSP.GetWeight() != WEIGHT_DONTKNOW)
129 {
130 // if not bold or requiring emboldening prefer light fonts to bold fonts
131 FontWeight ePatternWeight = rFSP.mbEmbolden ? WEIGHT_NORMAL : rFSP.GetWeight();
132
133 int nReqWeight = static_cast<int>(ePatternWeight);
134 if (ePatternWeight > WEIGHT_MEDIUM)
135 nReqWeight += 100;
136
137 int nGivenWeight = static_cast<int>(eWeight);
138 if (eWeight > WEIGHT_MEDIUM)
139 nGivenWeight += 100;
140
141 int nWeightDiff = nReqWeight - nGivenWeight;
142
143 if (nWeightDiff == 0)
144 nMatch += 1000;
145 else if (nWeightDiff == +1 || nWeightDiff == -1)
146 nMatch += 700;
147 else if (nWeightDiff < +50 && nWeightDiff > -50)
148 nMatch += 200;
149 }
150 else
151 {
152 // prefer NORMAL font weight
153 // TODO: change when the upper layers can tell their weight preference
154 if (eWeight == WEIGHT_NORMAL)
155 nMatch += 450;
156 else if (eWeight == WEIGHT_MEDIUM)
157 nMatch += 350;
158 else if ((eWeight == WEIGHT_SEMILIGHT) || (eWeight == WEIGHT_SEMIBOLD))
159 nMatch += 200;
160 else if (eWeight == WEIGHT_LIGHT)
161 nMatch += 150;
162 }
163
164 return nMatch;
165}
166
167static int ItalicMatchValue(FontSelectPattern const& rFSP, FontItalic eItalic)
168{
169 // if requiring custom matrix to fake italic, prefer upright font
170 FontItalic ePatternItalic
171 = rFSP.maItalicMatrix != ItalicMatrix() ? ITALIC_NONE : rFSP.GetItalic();
172
173 if (ePatternItalic == ITALIC_NONE)
174 {
175 if (eItalic == ITALIC_NONE)
176 return 900;
177 }
178 else
179 {
180 if (ePatternItalic == eItalic)
181 return 900;
182 else if (eItalic != ITALIC_NONE)
183 return 600;
184 }
185
186 return 0;
187}
188
190{
191 int nMatch = FamilyNameMatchValue(rFSP, GetFamilyName());
192 nMatch += StyleNameMatchValue(rStatus, GetStyleName());
193 nMatch += PitchMatchValue(rFSP, GetPitch());
195 nMatch += WeightMatchValue(rFSP, GetWeight());
196 nMatch += ItalicMatchValue(rFSP, GetItalic());
197
198 if (rFSP.mnOrientation != 0_deg10)
199 nMatch += 80;
200 else if (rFSP.mnWidth != 0)
201 nMatch += 25;
202 else
203 nMatch += 5;
204
205 if (rStatus.mnFaceMatch > nMatch)
206 {
207 return false;
208 }
209 else if (rStatus.mnFaceMatch < nMatch)
210 {
211 rStatus.mnFaceMatch = nMatch;
212 return true;
213 }
214
215 return true;
216}
217
219{
220 auto pHbFace = GetHbFace();
221 // If nTag is 0, reference the whole font.
222 if (!nTag)
223 return RawFontData(hb_face_reference_blob(pHbFace));
224 return RawFontData(hb_face_reference_table(pHbFace, nTag));
225}
226
227static hb_blob_t* getTable(hb_face_t*, hb_tag_t nTag, void* pUserData)
228{
229 return static_cast<const PhysicalFontFace*>(pUserData)->GetHbTable(nTag);
230}
231
233{
234 if (mpHbFace == nullptr)
236 = hb_face_create_for_tables(getTable, const_cast<PhysicalFontFace*>(this), nullptr);
237 return mpHbFace;
238}
239
241{
242 if (mpHbUnscaledFont == nullptr)
243 mpHbUnscaledFont = hb_font_create(GetHbFace());
244 return mpHbUnscaledFont;
245}
246
248{
249 if (mxCharMap.is())
250 return mxCharMap;
251
252 // Check if this font is using symbol cmap subtable, most likely redundant
253 // since HarfBuzz handles mapping symbol fonts for us.
254 RawFontData aData(GetRawFontData(HB_TAG('c', 'm', 'a', 'p')));
255 bool bSymbol = HasMicrosoftSymbolCmap(aData.data(), aData.size());
256
257 hb_face_t* pHbFace = GetHbFace();
258 hb_set_t* pUnicodes = hb_set_create();
259 hb_face_collect_unicodes(pHbFace, pUnicodes);
260
261 if (hb_set_get_population(pUnicodes))
262 {
263 // Convert HarfBuzz set to code ranges.
264 std::vector<sal_UCS4> aRangeCodes;
265 hb_codepoint_t nFirst, nLast = HB_SET_VALUE_INVALID;
266 while (hb_set_next_range(pUnicodes, &nFirst, &nLast))
267 {
268 aRangeCodes.push_back(nFirst);
269 aRangeCodes.push_back(nLast + 1);
270 }
271
272 mxCharMap = new FontCharMap(bSymbol, std::move(aRangeCodes));
273 }
274
275 hb_set_destroy(pUnicodes);
276
277 if (!mxCharMap.is())
279
280 return mxCharMap;
281}
282
284{
286 {
287 mxFontCapabilities.emplace();
288 RawFontData aData(GetRawFontData(HB_TAG('O', 'S', '/', '2')));
289 getTTCoverage(mxFontCapabilities->oUnicodeRange, mxFontCapabilities->oCodePageRange,
290 aData.data(), aData.size());
291 }
292
293 rFontCapabilities = *mxFontCapabilities;
294 return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
295}
296
297namespace
298{
299class RawFace
300{
301public:
302 RawFace(hb_face_t* pFace)
303 : mpFace(hb_face_reference(pFace))
304 {
305 }
306
307 RawFace(const RawFace& rOther)
308 : mpFace(hb_face_reference(rOther.mpFace))
309 {
310 }
311
312 ~RawFace() { hb_face_destroy(mpFace); }
313
314 RawFontData GetTable(uint32_t nTag) const
315 {
316 return RawFontData(hb_face_reference_table(mpFace, nTag));
317 }
318
319private:
320 hb_face_t* mpFace;
321};
322
323class TrueTypeFace final : public AbstractTrueTypeFont
324{
325 const RawFace m_aFace;
326 mutable std::array<RawFontData, NUM_TAGS> m_aTableList;
327
328 const RawFontData& table(sal_uInt32 nIdx) const
329 {
330 assert(nIdx < NUM_TAGS);
331 static const uint32_t aTags[NUM_TAGS] = {
334 };
335 if (m_aTableList[nIdx].empty())
336 m_aTableList[nIdx] = std::move(m_aFace.GetTable(aTags[nIdx]));
337 return m_aTableList[nIdx];
338 }
339
340public:
341 TrueTypeFace(RawFace aFace, const FontCharMapRef rCharMap)
342 : AbstractTrueTypeFont(nullptr, rCharMap)
343 , m_aFace(std::move(aFace))
344 {
345 }
346
347 bool hasTable(sal_uInt32 nIdx) const override { return !table(nIdx).empty(); }
348 const sal_uInt8* table(sal_uInt32 nIdx, sal_uInt32& nSize) const override
349 {
350 auto& rTable = table(nIdx);
351 nSize = rTable.size();
352 return rTable.data();
353 }
354};
355}
356
357bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer,
358 const sal_GlyphId* pGlyphIds, const sal_uInt8* pEncoding,
359 const int nGlyphCount, FontSubsetInfo& rInfo) const
360{
361 // Prepare data for font subsetter.
362 TrueTypeFace aSftFont(RawFace(GetHbFace()), GetFontCharMap());
363 if (aSftFont.initialize() != SFErrCodes::Ok)
364 return false;
365
366 // write subset into destination file
367 return CreateTTFfontSubset(aSftFont, rOutBuffer, pGlyphIds, pEncoding, nGlyphCount, rInfo);
368}
369
371{
372 const auto pHbFace = GetHbFace();
373 return hb_ot_color_has_layers(pHbFace) && hb_ot_color_has_palettes(pHbFace);
374}
375
376const std::vector<ColorPalette>& PhysicalFontFace::GetColorPalettes() const
377{
378 if (!mxColorPalettes)
379 {
380 mxColorPalettes.emplace();
381 const auto pHbFace = GetHbFace();
382 auto nPalettes = hb_ot_color_palette_get_count(pHbFace);
383 mxColorPalettes->reserve(nPalettes);
384 for (auto nPalette = 0u; nPalette < nPalettes; nPalette++)
385 {
386 auto nColors = hb_ot_color_palette_get_colors(pHbFace, nPalette, 0, nullptr, nullptr);
387 ColorPalette aPalette(nColors);
388 for (auto nColor = 0u; nColor < nColors; nColor++)
389 {
390 uint32_t nCount = 1;
391 hb_color_t aColor;
392 hb_ot_color_palette_get_colors(pHbFace, nPalette, nColor, &nCount, &aColor);
393 auto a = hb_color_get_alpha(aColor);
394 auto r = hb_color_get_red(aColor);
395 auto g = hb_color_get_green(aColor);
396 auto b = hb_color_get_blue(aColor);
397 aPalette[nColor] = Color(ColorAlphaTag::ColorAlpha, a, r, g, b);
398 }
399 mxColorPalettes->push_back(aPalette);
400 }
401 }
402
403 return *mxColorPalettes;
404}
405
406std::vector<ColorLayer> PhysicalFontFace::GetGlyphColorLayers(sal_GlyphId nGlyphIndex) const
407{
408 if (!HasColorLayers())
409 return {};
410
411 const auto pHbFace = GetHbFace();
412
413 auto nLayers = hb_ot_color_glyph_get_layers(pHbFace, nGlyphIndex, 0, nullptr, nullptr);
414 std::vector<ColorLayer> aLayers(nLayers);
415 for (auto nLayer = 0u; nLayer < nLayers; nLayer++)
416 {
417 hb_ot_color_layer_t aLayer;
418 uint32_t nCount = 1;
419 hb_ot_color_glyph_get_layers(pHbFace, nGlyphIndex, nLayer, &nCount, &aLayer);
420 aLayers[nLayer] = { aLayer.glyph, aLayer.color_index };
421 }
422
423 return aLayers;
424}
425
426bool PhysicalFontFace::HasColorBitmaps() const { return hb_ot_color_has_png(GetHbFace()); }
427
429 tools::Rectangle& rRect) const
430{
431 if (!HasColorBitmaps())
432 return {};
433
434 hb_font_t* pHbFont = GetHbUnscaledFont();
435 auto aData = RawFontData(hb_ot_color_glyph_reference_png(pHbFont, nGlyphIndex));
436 if (!aData.empty())
437 {
438 hb_glyph_extents_t aExtents;
439 if (hb_font_get_glyph_extents(pHbFont, nGlyphIndex, &aExtents))
440 {
441 auto aPoint = Point(aExtents.x_bearing, aExtents.y_bearing + aExtents.height);
442 auto aSize = Size(aExtents.width, -aExtents.height);
443 rRect = tools::Rectangle(aPoint, aSize);
444 }
445 }
446
447 return aData;
448}
449
450OString PhysicalFontFace::GetGlyphName(sal_GlyphId nGlyphIndex, bool bValidate) const
451{
452 char aBuffer[256];
453 hb_font_glyph_to_string(GetHbUnscaledFont(), nGlyphIndex, aBuffer, 256);
454 if (bValidate)
455 {
456 // https://learn.microsoft.com/en-us/typography/opentype/spec/post#version-20
457 // Valid characters are limited to A–Z, a–z, 0–9, “.” (FULL STOP), and “_” (LOW LINE).
458 const char* p = aBuffer;
459 while ((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z')
460 || *p == '.' || *p == '_')
461 ++p;
462 if (*p != '\0')
463 return "g" + OString::number(nGlyphIndex);
464 }
465
466 return OString(aBuffer);
467}
468
469OUString PhysicalFontFace::GetName(NameID aNameID, const LanguageTag& rLanguageTag) const
470{
471 auto pHbFace = GetHbFace();
472
473 auto aHbLang = HB_LANGUAGE_INVALID;
474 if (rLanguageTag.getLanguageType() != LANGUAGE_NONE)
475 {
476 auto aLanguage(rLanguageTag.getBcp47().toUtf8());
477 aHbLang = hb_language_from_string(aLanguage.getStr(), aLanguage.getLength());
478 }
479
480 auto nName = hb_ot_name_get_utf16(pHbFace, aNameID, aHbLang, nullptr, nullptr);
481 if (!nName && aHbLang == HB_LANGUAGE_INVALID)
482 {
483 // Fallback to English if localized name is missing.
484 aHbLang = hb_language_from_string("en", 2);
485 nName = hb_ot_name_get_utf16(pHbFace, aNameID, aHbLang, nullptr, nullptr);
486 }
487
488 OUString sName;
489 if (nName)
490 {
491 std::vector<uint16_t> aBuf(++nName); // make space for terminating NUL.
492 hb_ot_name_get_utf16(pHbFace, aNameID, aHbLang, &nName, aBuf.data());
493 sName = OUString(reinterpret_cast<sal_Unicode*>(aBuf.data()), nName);
494 }
495
496 return sName;
497}
498
499const std::vector<hb_variation_t>& PhysicalFontFace::GetVariations(const LogicalFontInstance&) const
500{
501 if (!mxVariations)
502 {
503 SAL_WARN("vcl.fonts", "Getting font variations is not supported.");
504 mxVariations.emplace();
505 }
506 return *mxVariations;
507}
508}
509
510/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
hb_face_t * mpFace
const RawFace m_aFace
std::array< RawFontData, NUM_TAGS > m_aTableList
bool IsMicrosoftSymbolEncoded() const
FontItalic GetItalic() const
FontWeight GetWeight() const
const OUString & GetFamilyName() const
FontPitch GetPitch() const
const OUString & GetStyleName() const
FontWidth GetWidthType() const
static FontCharMapRef GetDefaultMap(bool bMicrosoftSymbolMap)
Get the default font character map.
LanguageType getLanguageType(bool bResolveSystem=true) const
const OUString & getBcp47(bool bResolveSystem=true) const
bool is() const
abstract base class for physical font faces
virtual FontCharMapRef GetFontCharMap() const
RawFontData GetGlyphColorBitmap(sal_GlyphId, tools::Rectangle &) const
OUString GetName(NameID, const LanguageTag &) const
sal_Int32 CompareIgnoreSize(const PhysicalFontFace &) const
bool CreateFontSubset(std::vector< sal_uInt8 > &, const sal_GlyphId *, const sal_uInt8 *, const int, FontSubsetInfo &) const
std::vector< ColorLayer > GetGlyphColorLayers(sal_GlyphId) const
hb_font_t * GetHbUnscaledFont() const
virtual const std::vector< hb_variation_t > & GetVariations(const LogicalFontInstance &) const
const std::vector< ColorPalette > & GetColorPalettes() const
PhysicalFontFace(const FontAttributes &)
RawFontData GetRawFontData(uint32_t) const
virtual bool GetFontCapabilities(vcl::FontCapabilities &) const
std::optional< std::vector< ColorPalette > > mxColorPalettes
bool IsBetterMatch(const vcl::font::FontSelectPattern &, FontMatchStatus &) const
std::optional< vcl::FontCapabilities > mxFontCapabilities
OString GetGlyphName(sal_GlyphId, bool=false) const
virtual hb_face_t * GetHbFace() const
std::optional< std::vector< hb_variation_t > > mxVariations
int nCount
float u
bool HasMicrosoftSymbolCmap(const unsigned char *pCmap, int nLength)
Definition: fontcharmap.cxx:62
FontPitch
PITCH_DONTKNOW
FontItalic
ITALIC_NONE
FontWidth
WIDTH_SEMI_CONDENSED
WIDTH_SEMI_EXPANDED
WIDTH_NORMAL
WEIGHT_NORMAL
WEIGHT_LIGHT
WEIGHT_DONTKNOW
WEIGHT_SEMIBOLD
WEIGHT_SEMILIGHT
WEIGHT_MEDIUM
OUString sName
uint32_t sal_GlyphId
Definition: glyphid.hxx:24
void * p
uno_Any a
#define LANGUAGE_NONE
#define SAL_WARN(area, stream)
aBuf
@ table
constexpr OUStringLiteral aData
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
FontWeight
A PhysicalFontFaceCollection is created by a PhysicalFontCollection and becomes invalid when original...
static int ItalicMatchValue(FontSelectPattern const &rFSP, FontItalic eItalic)
static int WeightMatchValue(FontSelectPattern const &rFSP, FontWeight eWeight)
static hb_blob_t * getTable(hb_face_t *, hb_tag_t nTag, void *pUserData)
static int PreferNormalFontWidthMatchValue(FontWidth eWidthType)
static int PitchMatchValue(FontSelectPattern const &rFSP, FontPitch ePitch)
std::vector< Color > ColorPalette
static int FamilyNameMatchValue(FontSelectPattern const &rFSP, std::u16string_view sFontFamily)
static int StyleNameMatchValue(FontMatchStatus const &rStatus, std::u16string_view rStyle)
constexpr sal_uInt32 T_post
Definition: sft.hxx:447
constexpr sal_uInt32 T_cvt
Definition: sft.hxx:448
constexpr sal_uInt32 T_vhea
Definition: sft.hxx:444
constexpr sal_uInt32 T_loca
Definition: sft.hxx:439
constexpr sal_uInt32 T_prep
Definition: sft.hxx:449
constexpr sal_uInt32 T_glyf
Definition: sft.hxx:437
constexpr sal_uInt32 T_hhea
Definition: sft.hxx:441
constexpr int NUM_TAGS
Definition: sft.hxx:688
bool getTTCoverage(std::optional< std::bitset< UnicodeCoverage::MAX_UC_ENUM > > &rUnicodeRange, std::optional< std::bitset< CodePageCoverage::MAX_CP_ENUM > > &rCodePageRange, const unsigned char *pTable, size_t nLength)
Definition: sft.cxx:2499
constexpr sal_uInt32 T_head
Definition: sft.hxx:438
bool CreateTTFfontSubset(vcl::AbstractTrueTypeFont &rTTF, std::vector< sal_uInt8 > &rOutBuffer, const sal_GlyphId *pGlyphIds, const sal_uInt8 *pEncoding, const int nOrigGlyphCount, FontSubsetInfo &rInfo)
Definition: sft.cxx:1912
constexpr sal_uInt32 T_maxp
Definition: sft.hxx:436
constexpr sal_uInt32 T_OS2
Definition: sft.hxx:446
constexpr sal_uInt32 T_CFF
Definition: sft.hxx:451
constexpr sal_uInt32 T_hmtx
Definition: sft.hxx:442
constexpr sal_uInt32 T_name
Definition: sft.hxx:440
constexpr sal_uInt32 T_fpgm
Definition: sft.hxx:450
constexpr sal_uInt32 T_cmap
Definition: sft.hxx:443
constexpr sal_uInt32 T_vmtx
Definition: sft.hxx:445
Sun Font Tools.
std::optional< std::bitset< CodePageCoverage::MAX_CP_ENUM > > oCodePageRange
std::optional< std::bitset< UnicodeCoverage::MAX_UC_ENUM > > oUnicodeRange
const OUString * mpTargetStyleName
unsigned char sal_uInt8
sal_uInt16 sal_Unicode
std::unique_ptr< char[]> aBuffer