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>
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>
28 #include <fontattributes.hxx>
29 #include <impfontcharmap.hxx>
33 #include <font/FontSelectPattern.hxx>
34 #include <font/PhysicalFontFace.hxx>
35 #include <o3tl/string_view.hxx>
37 #include <string_view>
43 PhysicalFontFace::PhysicalFontFace(const FontAttributes
& rDFA
)
44 : FontAttributes(rDFA
)
46 , mpHbUnscaledFont(nullptr)
50 PhysicalFontFace::~PhysicalFontFace()
53 hb_face_destroy(mpHbFace
);
55 hb_font_destroy(mpHbUnscaledFont
);
58 sal_Int32
PhysicalFontFace::CompareIgnoreSize(const PhysicalFontFace
& rOther
) const
60 // compare their width, weight, italic, style name and family name
61 if (GetWidthType() < rOther
.GetWidthType())
63 else if (GetWidthType() > rOther
.GetWidthType())
66 if (GetWeight() < rOther
.GetWeight())
68 else if (GetWeight() > rOther
.GetWeight())
71 if (GetItalic() < rOther
.GetItalic())
73 else if (GetItalic() > rOther
.GetItalic())
76 sal_Int32 nRet
= GetFamilyName().compareTo(rOther
.GetFamilyName());
80 nRet
= GetStyleName().compareTo(rOther
.GetStyleName());
86 static int FamilyNameMatchValue(FontSelectPattern
const& rFSP
, std::u16string_view sFontFamily
)
88 const OUString
& rFontName
= rFSP
.maTargetName
;
90 if (rFontName
.equalsIgnoreAsciiCase(sFontFamily
))
96 static int StyleNameMatchValue(FontMatchStatus
const& rStatus
, std::u16string_view rStyle
)
98 if (rStatus
.mpTargetStyleName
99 && o3tl::equalsIgnoreAsciiCase(rStyle
, *rStatus
.mpTargetStyleName
))
105 static int PitchMatchValue(FontSelectPattern
const& rFSP
, FontPitch ePitch
)
107 if ((rFSP
.GetPitch() != PITCH_DONTKNOW
) && (rFSP
.GetPitch() == ePitch
))
113 static int PreferNormalFontWidthMatchValue(FontWidth eWidthType
)
115 // TODO: change when the upper layers can tell their width preference
116 if (eWidthType
== WIDTH_NORMAL
)
118 else if ((eWidthType
== WIDTH_SEMI_EXPANDED
) || (eWidthType
== WIDTH_SEMI_CONDENSED
))
124 static int WeightMatchValue(FontSelectPattern
const& rFSP
, FontWeight eWeight
)
128 if (rFSP
.GetWeight() != WEIGHT_DONTKNOW
)
130 // if not bold or requiring emboldening prefer light fonts to bold fonts
131 FontWeight ePatternWeight
= rFSP
.mbEmbolden
? WEIGHT_NORMAL
: rFSP
.GetWeight();
133 int nReqWeight
= static_cast<int>(ePatternWeight
);
134 if (ePatternWeight
> WEIGHT_MEDIUM
)
137 int nGivenWeight
= static_cast<int>(eWeight
);
138 if (eWeight
> WEIGHT_MEDIUM
)
141 int nWeightDiff
= nReqWeight
- nGivenWeight
;
143 if (nWeightDiff
== 0)
145 else if (nWeightDiff
== +1 || nWeightDiff
== -1)
147 else if (nWeightDiff
< +50 && nWeightDiff
> -50)
152 // prefer NORMAL font weight
153 // TODO: change when the upper layers can tell their weight preference
154 if (eWeight
== WEIGHT_NORMAL
)
156 else if (eWeight
== WEIGHT_MEDIUM
)
158 else if ((eWeight
== WEIGHT_SEMILIGHT
) || (eWeight
== WEIGHT_SEMIBOLD
))
160 else if (eWeight
== WEIGHT_LIGHT
)
167 static int ItalicMatchValue(FontSelectPattern
const& rFSP
, FontItalic eItalic
)
169 // if requiring custom matrix to fake italic, prefer upright font
170 FontItalic ePatternItalic
171 = rFSP
.maItalicMatrix
!= ItalicMatrix() ? ITALIC_NONE
: rFSP
.GetItalic();
173 if (ePatternItalic
== ITALIC_NONE
)
175 if (eItalic
== ITALIC_NONE
)
180 if (ePatternItalic
== eItalic
)
182 else if (eItalic
!= ITALIC_NONE
)
189 bool PhysicalFontFace::IsBetterMatch(const FontSelectPattern
& rFSP
, FontMatchStatus
& rStatus
) const
191 int nMatch
= FamilyNameMatchValue(rFSP
, GetFamilyName());
192 nMatch
+= StyleNameMatchValue(rStatus
, GetStyleName());
193 nMatch
+= PitchMatchValue(rFSP
, GetPitch());
194 nMatch
+= PreferNormalFontWidthMatchValue(GetWidthType());
195 nMatch
+= WeightMatchValue(rFSP
, GetWeight());
196 nMatch
+= ItalicMatchValue(rFSP
, GetItalic());
198 if (rFSP
.mnOrientation
!= 0_deg10
)
200 else if (rFSP
.mnWidth
!= 0)
205 if (rStatus
.mnFaceMatch
> nMatch
)
209 else if (rStatus
.mnFaceMatch
< nMatch
)
211 rStatus
.mnFaceMatch
= nMatch
;
218 RawFontData
PhysicalFontFace::GetRawFontData(uint32_t nTag
) const
220 auto pHbFace
= GetHbFace();
221 // If nTag is 0, reference the whole font.
223 return RawFontData(hb_face_reference_blob(pHbFace
));
224 return RawFontData(hb_face_reference_table(pHbFace
, nTag
));
227 static hb_blob_t
* getTable(hb_face_t
*, hb_tag_t nTag
, void* pUserData
)
229 return static_cast<const PhysicalFontFace
*>(pUserData
)->GetHbTable(nTag
);
232 hb_face_t
* PhysicalFontFace::GetHbFace() const
234 if (mpHbFace
== nullptr)
236 = hb_face_create_for_tables(getTable
, const_cast<PhysicalFontFace
*>(this), nullptr);
240 hb_font_t
* PhysicalFontFace::GetHbUnscaledFont() const
242 if (mpHbUnscaledFont
== nullptr)
243 mpHbUnscaledFont
= hb_font_create(GetHbFace());
244 return mpHbUnscaledFont
;
247 FontCharMapRef
PhysicalFontFace::GetFontCharMap() const
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());
257 hb_face_t
* pHbFace
= GetHbFace();
258 hb_set_t
* pUnicodes
= hb_set_create();
259 hb_face_collect_unicodes(pHbFace
, pUnicodes
);
261 if (hb_set_get_population(pUnicodes
))
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
))
268 aRangeCodes
.push_back(nFirst
);
269 aRangeCodes
.push_back(nLast
+ 1);
272 mxCharMap
= new FontCharMap(bSymbol
, std::move(aRangeCodes
));
275 hb_set_destroy(pUnicodes
);
278 mxCharMap
= FontCharMap::GetDefaultMap(IsMicrosoftSymbolEncoded());
283 bool PhysicalFontFace::GetFontCapabilities(vcl::FontCapabilities
& rFontCapabilities
) const
285 if (!mxFontCapabilities
)
287 mxFontCapabilities
.emplace();
288 RawFontData
aData(GetRawFontData(HB_TAG('O', 'S', '/', '2')));
289 getTTCoverage(mxFontCapabilities
->oUnicodeRange
, mxFontCapabilities
->oCodePageRange
,
290 aData
.data(), aData
.size());
293 rFontCapabilities
= *mxFontCapabilities
;
294 return rFontCapabilities
.oUnicodeRange
|| rFontCapabilities
.oCodePageRange
;
302 RawFace(hb_face_t
* pFace
)
303 : mpFace(hb_face_reference(pFace
))
307 RawFace(const RawFace
& rOther
)
308 : mpFace(hb_face_reference(rOther
.mpFace
))
312 ~RawFace() { hb_face_destroy(mpFace
); }
314 RawFontData
GetTable(uint32_t nTag
) const
316 return RawFontData(hb_face_reference_table(mpFace
, nTag
));
323 class TrueTypeFace final
: public AbstractTrueTypeFont
325 const RawFace m_aFace
;
326 mutable std::array
<RawFontData
, NUM_TAGS
> m_aTableList
;
328 const RawFontData
& table(sal_uInt32 nIdx
) const
330 assert(nIdx
< NUM_TAGS
);
331 static const uint32_t aTags
[NUM_TAGS
] = {
332 T_maxp
, T_glyf
, T_head
, T_loca
, T_name
, T_hhea
, T_hmtx
, T_cmap
,
333 T_vhea
, T_vmtx
, T_OS2
, T_post
, T_cvt
, T_prep
, T_fpgm
, T_CFF
,
335 if (m_aTableList
[nIdx
].empty())
336 m_aTableList
[nIdx
] = std::move(m_aFace
.GetTable(aTags
[nIdx
]));
337 return m_aTableList
[nIdx
];
341 TrueTypeFace(RawFace aFace
, const FontCharMapRef rCharMap
)
342 : AbstractTrueTypeFont(nullptr, rCharMap
)
343 , m_aFace(std::move(aFace
))
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
350 auto& rTable
= table(nIdx
);
351 nSize
= rTable
.size();
352 return rTable
.data();
357 bool PhysicalFontFace::CreateFontSubset(std::vector
<sal_uInt8
>& rOutBuffer
,
358 const sal_GlyphId
* pGlyphIds
, const sal_uInt8
* pEncoding
,
359 const int nGlyphCount
, FontSubsetInfo
& rInfo
) const
361 // Prepare data for font subsetter.
362 TrueTypeFace
aSftFont(RawFace(GetHbFace()), GetFontCharMap());
363 if (aSftFont
.initialize() != SFErrCodes::Ok
)
366 // write subset into destination file
367 return CreateTTFfontSubset(aSftFont
, rOutBuffer
, pGlyphIds
, pEncoding
, nGlyphCount
, rInfo
);
370 bool PhysicalFontFace::HasColorLayers() const
372 const auto pHbFace
= GetHbFace();
373 return hb_ot_color_has_layers(pHbFace
) && hb_ot_color_has_palettes(pHbFace
);
376 const std::vector
<ColorPalette
>& PhysicalFontFace::GetColorPalettes() const
378 if (!mxColorPalettes
)
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
++)
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
++)
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
);
399 mxColorPalettes
->push_back(aPalette
);
403 return *mxColorPalettes
;
406 std::vector
<ColorLayer
> PhysicalFontFace::GetGlyphColorLayers(sal_GlyphId nGlyphIndex
) const
408 if (!HasColorLayers())
411 const auto pHbFace
= GetHbFace();
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
++)
417 hb_ot_color_layer_t aLayer
;
419 hb_ot_color_glyph_get_layers(pHbFace
, nGlyphIndex
, nLayer
, &nCount
, &aLayer
);
420 aLayers
[nLayer
] = { aLayer
.glyph
, aLayer
.color_index
};
426 bool PhysicalFontFace::HasColorBitmaps() const { return hb_ot_color_has_png(GetHbFace()); }
428 RawFontData
PhysicalFontFace::GetGlyphColorBitmap(sal_GlyphId nGlyphIndex
,
429 tools::Rectangle
& rRect
) const
431 if (!HasColorBitmaps())
434 hb_font_t
* pHbFont
= GetHbUnscaledFont();
435 auto aData
= RawFontData(hb_ot_color_glyph_reference_png(pHbFont
, nGlyphIndex
));
438 hb_glyph_extents_t aExtents
;
439 if (hb_font_get_glyph_extents(pHbFont
, nGlyphIndex
, &aExtents
))
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
);
450 OString
PhysicalFontFace::GetGlyphName(sal_GlyphId nGlyphIndex
, bool bValidate
) const
453 hb_font_glyph_to_string(GetHbUnscaledFont(), nGlyphIndex
, aBuffer
, 256);
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
== '_')
463 return "g" + OString::number(nGlyphIndex
);
466 return OString(aBuffer
);
469 OUString
PhysicalFontFace::GetName(NameID aNameID
, const LanguageTag
& rLanguageTag
) const
471 auto pHbFace
= GetHbFace();
473 auto aHbLang
= HB_LANGUAGE_INVALID
;
474 if (rLanguageTag
.getLanguageType() != LANGUAGE_NONE
)
476 auto aLanguage(rLanguageTag
.getBcp47().toUtf8());
477 aHbLang
= hb_language_from_string(aLanguage
.getStr(), aLanguage
.getLength());
480 auto nName
= hb_ot_name_get_utf16(pHbFace
, aNameID
, aHbLang
, nullptr, nullptr);
481 if (!nName
&& aHbLang
== HB_LANGUAGE_INVALID
)
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);
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
);
499 const std::vector
<hb_variation_t
>& PhysicalFontFace::GetVariations(const LogicalFontInstance
&) const
503 SAL_WARN("vcl.fonts", "Getting font variations is not supported.");
504 mxVariations
.emplace();
506 return *mxVariations
;
510 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */