tdf#161341 show/hide controls/shapes/pictures in view/print
[LibreOffice.git] / vcl / source / font / LogicalFontInstance.cxx
blob971965385164994c85f2b19c980f876b29086bb5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <hb-ot.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)
35 , mnLineHeight(0)
36 , mnOwnOrientation(0)
37 , mnOrientation(0)
38 , mbInit(false)
39 , mpFontCache(nullptr)
40 , m_aFontSelData(rFontSelData)
41 , m_pHbFont(nullptr)
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;
53 if (m_pHbFont)
54 hb_font_destroy(m_pHbFont);
56 if (m_pHbFontUntransformed)
57 hb_font_destroy(m_pHbFontUntransformed);
59 if (m_pHbDrawFuncs)
60 hb_draw_funcs_destroy(m_pHbDrawFuncs);
63 hb_font_t* LogicalFontInstance::InitHbFont()
65 auto pFace = GetFontFace();
66 hb_face_t* pHbFace = pFace->GetHbFace();
67 assert(pHbFace);
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);
85 return 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;
104 return pHbFont;
107 double LogicalFontInstance::GetKashidaWidth() const
109 sal_GlyphId nGlyph = GetGlyphIndex(0x0640);
110 if (nGlyph)
111 return GetGlyphWidth(nGlyph);
112 return 0;
115 void LogicalFontInstance::GetScale(double* nXScale, double* nYScale) const
117 double nUPEM = GetFontFace()->UnitsPerEm();
119 if (nYScale)
120 *nYScale = m_aFontSelData.mnHeight / nUPEM;
122 if (nXScale)
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
127 // positions.
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())
151 return false;
153 const MapEntry& rEntry = (*it).second;
154 *pFontName = rEntry.sFontName;
155 *pEmbolden = rEntry.bEmbolden;
156 *pMatrix = rEntry.aItalicMatrix;
157 return true;
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())
166 return;
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))
179 return true;
181 auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
182 hb_glyph_extents_t aExtents;
183 if (!hb_font_get_glyph_extents(pHbFont, nID, &aExtents))
184 return false;
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;
196 if (bVertical)
197 orientation += 900_deg10;
198 if (orientation)
200 // Apply font rotation.
201 rRect.transform(basegfx::utils::createRotateB2DHomMatrix(-toRadians(orientation)));
204 if (mpFontCache)
205 mpFontCache->CacheGlyphBoundRect(this, nID, rRect);
207 return true;
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))
215 return nGlyph;
216 return 0;
219 double LogicalFontInstance::GetGlyphWidth(sal_GlyphId nGlyph, bool bVertical, bool bScale) const
221 auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
222 int nWidth;
223 if (bVertical)
224 nWidth = hb_font_get_glyph_v_advance(pHbFont, nGlyph);
225 else
226 nWidth = hb_font_get_glyph_h_advance(pHbFont, nGlyph);
228 if (!bScale)
229 return nWidth;
231 double nScale = 0;
232 GetScale(&nScale, nullptr);
233 return double(nWidth * nScale);
236 bool LogicalFontInstance::IsGraphiteFont()
238 if (!m_xbIsGraphiteFont.has_value())
240 m_xbIsGraphiteFont
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;
260 bool bRet = true;
262 switch (*m_xeFontFamilyEnum)
264 case FontFamilyEnum::DFKaiSB:
265 // -839: optimization for one third of ukai.ttf
266 if (nYOffset == -839)
267 bRet = false;
268 break;
269 default:
270 bRet = false;
273 return bRet;
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;
286 namespace
288 void move_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
289 void* pUserData)
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,
296 void* pUserData)
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,
304 void* pUserData)
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);
317 pPoly->clear();
321 basegfx::B2DPolyPolygon LogicalFontInstance::GetGlyphOutlineUntransformed(sal_GlyphId nGlyph) const
323 if (!m_pHbDrawFuncs)
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
332 // function.
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);
340 #else
341 hb_font_get_glyph_shape(GetHbFontUntransformed(), nGlyph, m_pHbDrawFuncs, &aPolyPoly);
342 #endif
343 return aPolyPoly;
346 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */