/src/libreoffice/vcl/source/font/fontmetric.cxx
Line | Count | Source |
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 <i18nlangtag/mslangid.hxx> |
23 | | #include <officecfg/Office/Common.hxx> |
24 | | #include <sal/log.hxx> |
25 | | #include <tools/stream.hxx> |
26 | | #include <unotools/configmgr.hxx> |
27 | | #include <unotools/fontdefs.hxx> |
28 | | #include <vcl/metric.hxx> |
29 | | #include <vcl/outdev.hxx> |
30 | | |
31 | | #include <font/FontSelectPattern.hxx> |
32 | | #include <font/PhysicalFontFace.hxx> |
33 | | #include <font/LogicalFontInstance.hxx> |
34 | | #include <font/FontMetricData.hxx> |
35 | | #include <font/TrueTypeFont.hxx> |
36 | | |
37 | | #include <com/sun/star/uno/Sequence.hxx> |
38 | | #include <comphelper/sequence.hxx> |
39 | | #include <hb-ot.h> |
40 | | |
41 | | using namespace ::com::sun::star; |
42 | | using namespace ::com::sun::star::uno; |
43 | | using namespace ::rtl; |
44 | | |
45 | | FontMetric::FontMetric() |
46 | 19.4M | : mnAscent(0) |
47 | 19.4M | , mnDescent(0) |
48 | 19.4M | , mnIntLeading(0) |
49 | 19.4M | , mnExtLeading(0) |
50 | 19.4M | , mnLineHeight(0) |
51 | 19.4M | , mnSlant(0) |
52 | 19.4M | , mnBulletOffset(0) |
53 | 19.4M | , mnHangingBaseline(0) |
54 | 19.4M | , mdEmSize(0.0) |
55 | 19.4M | , mdHorCJKAdvanceSize(0.0) |
56 | 19.4M | , mdVertCJKAdvanceSize(0.0) |
57 | 19.4M | , mbFullstopCentered(false) |
58 | 19.4M | {} |
59 | | |
60 | 14.9M | FontMetric::FontMetric( const FontMetric& rFontMetric ) = default; |
61 | | |
62 | | FontMetric::FontMetric(vcl::font::PhysicalFontFace const& rFace) |
63 | 1.94M | : FontMetric() |
64 | 1.94M | { |
65 | 1.94M | SetFamilyName(rFace.GetFamilyName()); |
66 | 1.94M | SetStyleName(rFace.GetStyleName()); |
67 | 1.94M | SetCharSet(rFace.IsMicrosoftSymbolEncoded() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE); |
68 | 1.94M | SetFamily(rFace.GetFamilyType()); |
69 | 1.94M | SetPitch(rFace.GetPitch()); |
70 | 1.94M | SetWeight(rFace.GetWeight()); |
71 | 1.94M | SetItalic(rFace.GetItalic()); |
72 | 1.94M | SetAlignment(TextAlign::ALIGN_TOP); |
73 | 1.94M | SetWidthType(rFace.GetWidthType()); |
74 | 1.94M | SetQuality(rFace.GetQuality() ); |
75 | 1.94M | } |
76 | | |
77 | | FontMetric::~FontMetric() |
78 | 34.4M | { |
79 | 34.4M | } |
80 | | |
81 | 194k | FontMetric& FontMetric::operator=(const FontMetric& rFontMetric) = default; |
82 | | |
83 | 25.0k | FontMetric& FontMetric::operator=(FontMetric&& rFontMetric) = default; |
84 | | |
85 | | bool FontMetric::EqualNoBase( const FontMetric& r ) const |
86 | 6.94M | { |
87 | 6.94M | if (mbFullstopCentered != r.mbFullstopCentered) |
88 | 0 | return false; |
89 | 6.94M | if( mnAscent != r.mnAscent ) |
90 | 0 | return false; |
91 | 6.94M | if( mnDescent != r.mnDescent ) |
92 | 0 | return false; |
93 | 6.94M | if( mnIntLeading != r.mnIntLeading ) |
94 | 0 | return false; |
95 | 6.94M | if( mnExtLeading != r.mnExtLeading ) |
96 | 0 | return false; |
97 | 6.94M | if( mnSlant != r.mnSlant ) |
98 | 0 | return false; |
99 | | |
100 | 6.94M | return true; |
101 | 6.94M | } |
102 | | |
103 | | bool FontMetric::operator==( const FontMetric& r ) const |
104 | 0 | { |
105 | 0 | if( Font::operator!=(r) ) |
106 | 0 | return false; |
107 | 0 | return EqualNoBase(r); |
108 | 0 | } |
109 | | |
110 | | bool FontMetric::EqualIgnoreColor( const FontMetric& r ) const |
111 | 6.94M | { |
112 | 6.94M | if( !Font::EqualIgnoreColor(r) ) |
113 | 0 | return false; |
114 | 6.94M | return EqualNoBase(r); |
115 | 6.94M | } |
116 | | |
117 | | size_t FontMetric::GetHashValueNoBase() const |
118 | 10.7M | { |
119 | 10.7M | size_t hash = 0; |
120 | 10.7M | o3tl::hash_combine( hash, mbFullstopCentered ); |
121 | 10.7M | o3tl::hash_combine( hash, mnAscent ); |
122 | 10.7M | o3tl::hash_combine( hash, mnDescent ); |
123 | 10.7M | o3tl::hash_combine( hash, mnIntLeading ); |
124 | 10.7M | o3tl::hash_combine( hash, mnExtLeading ); |
125 | 10.7M | o3tl::hash_combine( hash, mnSlant ); |
126 | 10.7M | return hash; |
127 | 10.7M | } |
128 | | |
129 | | size_t FontMetric::GetHashValueIgnoreColor() const |
130 | 10.7M | { |
131 | 10.7M | size_t hash = GetHashValueNoBase(); |
132 | 10.7M | o3tl::hash_combine( hash, Font::GetHashValueIgnoreColor()); |
133 | 10.7M | return hash; |
134 | 10.7M | } |
135 | | |
136 | | FontMetricData::FontMetricData( const vcl::font::FontSelectPattern& rFontSelData ) |
137 | 161k | : FontAttributes( rFontSelData ) |
138 | 161k | , mnHeight ( rFontSelData.mnHeight ) |
139 | 161k | , mnWidth ( rFontSelData.mnWidth ) |
140 | 161k | , mnOrientation( static_cast<short>(rFontSelData.mnOrientation) ) |
141 | 161k | , mnAscent( 0 ) |
142 | 161k | , mnDescent( 0 ) |
143 | 161k | , mnIntLeading( 0 ) |
144 | 161k | , mnExtLeading( 0 ) |
145 | 161k | , mnSlant( 0 ) |
146 | 161k | , mnMinKashida( 0 ) |
147 | 161k | , mnHangingBaseline( 0 ) |
148 | 161k | , mdEmSize(0.0) |
149 | 161k | , mdHorCJKAdvanceSize(0.0) |
150 | 161k | , mdVertCJKAdvanceSize(0.0) |
151 | 161k | , mbFullstopCentered( false ) |
152 | 161k | , mnBulletOffset( 0 ) |
153 | 161k | , mnUnderlineSize( 0 ) |
154 | 161k | , mnUnderlineOffset( 0 ) |
155 | 161k | , mnBUnderlineSize( 0 ) |
156 | 161k | , mnBUnderlineOffset( 0 ) |
157 | 161k | , mnDUnderlineSize( 0 ) |
158 | 161k | , mnDUnderlineOffset1( 0 ) |
159 | 161k | , mnDUnderlineOffset2( 0 ) |
160 | 161k | , mnWUnderlineSize( 0 ) |
161 | 161k | , mnWUnderlineOffset( 0 ) |
162 | 161k | , mnAboveUnderlineSize( 0 ) |
163 | 161k | , mnAboveUnderlineOffset( 0 ) |
164 | 161k | , mnAboveBUnderlineSize( 0 ) |
165 | 161k | , mnAboveBUnderlineOffset( 0 ) |
166 | 161k | , mnAboveDUnderlineSize( 0 ) |
167 | 161k | , mnAboveDUnderlineOffset1( 0 ) |
168 | 161k | , mnAboveDUnderlineOffset2( 0 ) |
169 | 161k | , mnAboveWUnderlineSize( 0 ) |
170 | 161k | , mnAboveWUnderlineOffset( 0 ) |
171 | 161k | , mnStrikeoutSize( 0 ) |
172 | 161k | , mnStrikeoutOffset( 0 ) |
173 | 161k | , mnBStrikeoutSize( 0 ) |
174 | 161k | , mnBStrikeoutOffset( 0 ) |
175 | 161k | , mnDStrikeoutSize( 0 ) |
176 | 161k | , mnDStrikeoutOffset1( 0 ) |
177 | 161k | , mnDStrikeoutOffset2( 0 ) |
178 | 161k | { |
179 | | // initialize the used font name |
180 | 161k | sal_Int32 nTokenPos = 0; |
181 | 161k | SetFamilyName( OUString(GetNextFontToken( rFontSelData.GetFamilyName(), nTokenPos )) ); |
182 | 161k | SetStyleName( rFontSelData.GetStyleName() ); |
183 | 161k | } |
184 | | |
185 | | bool FontMetricData::ShouldNotUseUnderlineMetrics() const |
186 | 323k | { |
187 | 323k | if (comphelper::IsFuzzing()) |
188 | 323k | return false; |
189 | | |
190 | 0 | css::uno::Sequence<OUString> rNoUnderlineMetricsList( |
191 | 0 | officecfg::Office::Common::Misc::FontsDontUseUnderlineMetrics::get()); |
192 | 0 | if (comphelper::findValue(rNoUnderlineMetricsList, GetFamilyName()) != -1) |
193 | 0 | { |
194 | 0 | SAL_INFO("vcl.gdi.fontmetric", "Not using underline metrics for: " << GetFamilyName()); |
195 | 0 | return true; |
196 | 0 | } |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | | bool FontMetricData::ImplInitTextLineSizeHarfBuzz(LogicalFontInstance* pFont) |
201 | 323k | { |
202 | 323k | if (ShouldNotUseUnderlineMetrics()) |
203 | 0 | return false; |
204 | | |
205 | 323k | auto* pHbFont = pFont->GetHbFont(); |
206 | | |
207 | 323k | hb_position_t nUnderlineSize; |
208 | 323k | if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_UNDERLINE_SIZE, &nUnderlineSize)) |
209 | 0 | return false; |
210 | 323k | hb_position_t nUnderlineOffset; |
211 | 323k | if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_UNDERLINE_OFFSET, &nUnderlineOffset)) |
212 | 0 | return false; |
213 | 323k | hb_position_t nStrikeoutSize; |
214 | 323k | if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_STRIKEOUT_SIZE, &nStrikeoutSize)) |
215 | 0 | return false; |
216 | 323k | hb_position_t nStrikeoutOffset; |
217 | 323k | if (!hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, &nStrikeoutOffset)) |
218 | 0 | return false; |
219 | | |
220 | 323k | double fScale = 0; |
221 | 323k | pFont->GetScale(nullptr, &fScale); |
222 | | |
223 | 323k | double nOffset = -nUnderlineOffset * fScale; |
224 | 323k | double nSize = nUnderlineSize * fScale; |
225 | 323k | double nSize2 = nSize / 2.; |
226 | 323k | double nBSize = nSize * 2.; |
227 | 323k | double n2Size = nBSize / 3.; |
228 | | |
229 | 323k | mnUnderlineSize = std::ceil(nSize); |
230 | 323k | mnUnderlineOffset = std::ceil(nOffset); |
231 | | |
232 | 323k | mnBUnderlineSize = std::ceil(nBSize); |
233 | 323k | mnBUnderlineOffset = std::ceil(nOffset - nSize2); |
234 | | |
235 | 323k | mnDUnderlineSize = std::ceil(n2Size); |
236 | 323k | mnDUnderlineOffset1 = mnBUnderlineOffset; |
237 | 323k | mnDUnderlineOffset2 = mnBUnderlineOffset + mnDUnderlineSize * 2; |
238 | | |
239 | 323k | mnWUnderlineSize = mnBUnderlineSize; |
240 | 323k | mnWUnderlineOffset = std::ceil(nOffset + nSize); |
241 | | |
242 | 323k | nOffset = -nStrikeoutOffset * fScale; |
243 | 323k | nSize = nStrikeoutSize * fScale; |
244 | 323k | nSize2 = nSize / 2.; |
245 | 323k | nBSize = nSize * 2.; |
246 | 323k | n2Size = nBSize / 3.; |
247 | | |
248 | 323k | mnStrikeoutSize = std::ceil(nSize); |
249 | 323k | mnStrikeoutOffset = std::ceil(nOffset); |
250 | | |
251 | 323k | mnBStrikeoutSize = std::ceil(nBSize); |
252 | 323k | mnBStrikeoutOffset = std::round(nOffset - nSize2); |
253 | | |
254 | 323k | mnDStrikeoutSize = std::ceil(n2Size); |
255 | 323k | mnDStrikeoutOffset1 = mnBStrikeoutOffset; |
256 | 323k | mnDStrikeoutOffset2 = mnBStrikeoutOffset + mnDStrikeoutSize * 2; |
257 | | |
258 | 323k | return true; |
259 | 323k | } |
260 | | |
261 | | void FontMetricData::ImplInitTextLineSize( const OutputDevice* pDev ) |
262 | 323k | { |
263 | 323k | mnBulletOffset = ( pDev->GetTextWidth( OUString( u' ' ) ) - pDev->GetTextWidth( OUString( u'\x00b7' ) ) ) >> 1 ; |
264 | | |
265 | 323k | if (ImplInitTextLineSizeHarfBuzz(const_cast<LogicalFontInstance*>(pDev->GetFontInstance()))) |
266 | 323k | return; |
267 | | |
268 | 0 | tools::Long nDescent = mnDescent; |
269 | 0 | if ( nDescent <= 0 ) |
270 | 0 | { |
271 | 0 | nDescent = mnAscent / 10; |
272 | 0 | if ( !nDescent ) |
273 | 0 | nDescent = 1; |
274 | 0 | } |
275 | | |
276 | | // #i55341# for some fonts it is not a good idea to calculate |
277 | | // their text line metrics from the real font descent |
278 | | // => work around this problem just for these fonts |
279 | 0 | if( 3*nDescent > mnAscent ) |
280 | 0 | nDescent = mnAscent / 3; |
281 | |
|
282 | 0 | tools::Long nLineHeight = ((nDescent*25)+50) / 100; |
283 | 0 | if ( !nLineHeight ) |
284 | 0 | nLineHeight = 1; |
285 | 0 | tools::Long nLineHeight2 = nLineHeight / 2; |
286 | 0 | if ( !nLineHeight2 ) |
287 | 0 | nLineHeight2 = 1; |
288 | |
|
289 | 0 | tools::Long nBLineHeight = ((nDescent*50)+50) / 100; |
290 | 0 | if ( nBLineHeight == nLineHeight ) |
291 | 0 | nBLineHeight++; |
292 | 0 | tools::Long nBLineHeight2 = nBLineHeight/2; |
293 | 0 | if ( !nBLineHeight2 ) |
294 | 0 | nBLineHeight2 = 1; |
295 | |
|
296 | 0 | tools::Long n2LineHeight = ((nDescent*16)+50) / 100; |
297 | 0 | if ( !n2LineHeight ) |
298 | 0 | n2LineHeight = 1; |
299 | 0 | tools::Long n2LineDY = n2LineHeight; |
300 | | /* #117909# |
301 | | * add some pixels to minimum double line distance on higher resolution devices |
302 | | */ |
303 | 0 | tools::Long nMin2LineDY = 1 + pDev->GetDPIY()/150; |
304 | 0 | if ( n2LineDY < nMin2LineDY ) |
305 | 0 | n2LineDY = nMin2LineDY; |
306 | 0 | tools::Long n2LineDY2 = n2LineDY/2; |
307 | 0 | if ( !n2LineDY2 ) |
308 | 0 | n2LineDY2 = 1; |
309 | |
|
310 | 0 | const vcl::Font& rFont ( pDev->GetFont() ); |
311 | 0 | bool bCJKVertical = MsLangId::isCJK(rFont.GetLanguage()) && rFont.IsVertical(); |
312 | 0 | tools::Long nUnderlineOffset = bCJKVertical ? mnDescent : (mnDescent/2 + 1); |
313 | 0 | tools::Long nStrikeoutOffset = rFont.IsVertical() ? -((mnAscent - mnDescent) / 2) : -((mnAscent - mnIntLeading) / 3); |
314 | |
|
315 | 0 | mnUnderlineSize = nLineHeight; |
316 | 0 | mnUnderlineOffset = nUnderlineOffset - nLineHeight2; |
317 | |
|
318 | 0 | mnBUnderlineSize = nBLineHeight; |
319 | 0 | mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2; |
320 | |
|
321 | 0 | mnDUnderlineSize = n2LineHeight; |
322 | 0 | mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight; |
323 | 0 | mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight; |
324 | |
|
325 | 0 | tools::Long nWCalcSize = mnDescent; |
326 | 0 | if ( nWCalcSize < 6 ) |
327 | 0 | { |
328 | 0 | if ( (nWCalcSize == 1) || (nWCalcSize == 2) ) |
329 | 0 | mnWUnderlineSize = nWCalcSize; |
330 | 0 | else |
331 | 0 | mnWUnderlineSize = 3; |
332 | 0 | } |
333 | 0 | else |
334 | 0 | mnWUnderlineSize = ((nWCalcSize*50)+50) / 100; |
335 | | |
336 | | |
337 | | // Don't assume that wavelines are never placed below the descent, because for most fonts the waveline |
338 | | // is drawn into the text |
339 | 0 | mnWUnderlineOffset = nUnderlineOffset; |
340 | |
|
341 | 0 | mnStrikeoutSize = nLineHeight; |
342 | 0 | mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2; |
343 | |
|
344 | 0 | mnBStrikeoutSize = nBLineHeight; |
345 | 0 | mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2; |
346 | |
|
347 | 0 | mnDStrikeoutSize = n2LineHeight; |
348 | 0 | mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight; |
349 | 0 | mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight; |
350 | |
|
351 | 0 | } |
352 | | |
353 | | |
354 | | void FontMetricData::ImplInitAboveTextLineSize( const OutputDevice* pDev ) |
355 | 161k | { |
356 | 161k | ImplInitTextLineSize(pDev); |
357 | | |
358 | 161k | tools::Long nIntLeading = mnIntLeading; |
359 | | // TODO: assess usage of nLeading below (changed in extleading CWS) |
360 | | // if no leading is available, we assume 15% of the ascent |
361 | 161k | if ( nIntLeading <= 0 ) |
362 | 4.73k | { |
363 | 4.73k | nIntLeading = mnAscent*15/100; |
364 | 4.73k | if ( !nIntLeading ) |
365 | 4.73k | nIntLeading = 1; |
366 | 4.73k | } |
367 | | |
368 | 161k | tools::Long nCeiling = -mnAscent; |
369 | | |
370 | 161k | mnAboveUnderlineSize = mnUnderlineSize; |
371 | 161k | mnAboveUnderlineOffset = nCeiling + (nIntLeading - mnUnderlineSize + 1) / 2; |
372 | | |
373 | 161k | mnAboveBUnderlineSize = mnBUnderlineSize; |
374 | 161k | mnAboveBUnderlineOffset = nCeiling + (nIntLeading - mnBUnderlineSize + 1) / 2; |
375 | | |
376 | 161k | mnAboveDUnderlineSize = mnDUnderlineSize; |
377 | 161k | mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*mnDUnderlineSize + 1) / 2; |
378 | 161k | mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + mnDUnderlineSize + 1) / 2; |
379 | | |
380 | 161k | mnAboveWUnderlineSize = mnWUnderlineSize; |
381 | 161k | mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2; |
382 | 161k | } |
383 | | |
384 | | void FontMetricData::ImplInitFlags( const OutputDevice* pDev ) |
385 | 161k | { |
386 | 161k | const vcl::Font& rFont ( pDev->GetFont() ); |
387 | 161k | bool bCentered = true; |
388 | 161k | if (MsLangId::isCJK(rFont.GetLanguage())) |
389 | 2.02k | { |
390 | 2.02k | tools::Rectangle aRect; |
391 | 2.02k | pDev->GetTextBoundRect( aRect, u"\x3001"_ustr ); // Fullwidth fullstop |
392 | 2.02k | const auto nH = rFont.GetFontSize().Height(); |
393 | 2.02k | const auto nB = aRect.Left(); |
394 | | // Use 18.75% as a threshold to define a centered fullwidth fullstop. |
395 | | // In general, nB/nH < 5% for most Japanese fonts. |
396 | 2.02k | bCentered = nB > (((nH >> 1)+nH)>>3); |
397 | 2.02k | } |
398 | 161k | SetFullstopCenteredFlag( bCentered ); |
399 | 161k | } |
400 | | |
401 | | bool FontMetricData::ShouldUseWinMetrics(int nAscent, int nDescent, int nTypoAscent, |
402 | | int nTypoDescent, int nWinAscent, |
403 | | int nWinDescent) const |
404 | 161k | { |
405 | 161k | if (comphelper::IsFuzzing()) |
406 | 161k | return false; |
407 | | |
408 | 0 | OUString aFontIdentifier( |
409 | 0 | GetFamilyName() + "," |
410 | 0 | + OUString::number(nAscent) + "," + OUString::number(nDescent) + "," |
411 | 0 | + OUString::number(nTypoAscent) + "," + OUString::number(nTypoDescent) + "," |
412 | 0 | + OUString::number(nWinAscent) + "," + OUString::number(nWinDescent)); |
413 | |
|
414 | 0 | css::uno::Sequence<OUString> rWinMetricFontList( |
415 | 0 | officecfg::Office::Common::Misc::FontsUseWinMetrics::get()); |
416 | 0 | if (comphelper::findValue(rWinMetricFontList, aFontIdentifier) != -1) |
417 | 0 | { |
418 | 0 | SAL_INFO("vcl.gdi.fontmetric", "Using win metrics for: " << aFontIdentifier); |
419 | 0 | return true; |
420 | 0 | } |
421 | 0 | return false; |
422 | 0 | } |
423 | | |
424 | | // These are “private” HarfBuzz metrics tags, they are supported by not exposed |
425 | | // in the public header. They are safe to use, HarfBuzz just does not want to |
426 | | // advertise them. |
427 | | constexpr auto ASCENT_OS2 = static_cast<hb_ot_metrics_tag_t>(HB_TAG('O', 'a', 's', 'c')); |
428 | | constexpr auto DESCENT_OS2 = static_cast<hb_ot_metrics_tag_t>(HB_TAG('O', 'd', 's', 'c')); |
429 | | constexpr auto LINEGAP_OS2 = static_cast<hb_ot_metrics_tag_t>(HB_TAG('O', 'l', 'g', 'p')); |
430 | | constexpr auto ASCENT_HHEA = static_cast<hb_ot_metrics_tag_t>(HB_TAG('H', 'a', 's', 'c')); |
431 | | constexpr auto DESCENT_HHEA = static_cast<hb_ot_metrics_tag_t>(HB_TAG('H', 'd', 's', 'c')); |
432 | | constexpr auto LINEGAP_HHEA = static_cast<hb_ot_metrics_tag_t>(HB_TAG('H', 'l', 'g', 'p')); |
433 | | |
434 | | void FontMetricData::ImplCalcLineSpacing(LogicalFontInstance* pFontInstance) |
435 | 161k | { |
436 | 161k | mnAscent = mnDescent = mnExtLeading = mnIntLeading = 0; |
437 | 161k | auto* pFace = pFontInstance->GetFontFace(); |
438 | 161k | auto* pHbFont = pFontInstance->GetHbFont(); |
439 | | |
440 | 161k | double fScale = 0; |
441 | 161k | pFontInstance->GetScale(nullptr, &fScale); |
442 | 161k | double fAscent = 0, fDescent = 0, fExtLeading = 0; |
443 | | |
444 | 161k | if (hb_ot_var_has_data(pFace->GetHbFace())) |
445 | 0 | { |
446 | | // This is a variable font, trust HarfBuzz to give us the right metrics |
447 | | // and apply variations to them. |
448 | 0 | hb_position_t nAscent, nDescent, nLineGap; |
449 | 0 | if (hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &nAscent) |
450 | 0 | && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, |
451 | 0 | &nDescent) |
452 | 0 | && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, |
453 | 0 | &nLineGap)) |
454 | 0 | { |
455 | 0 | fAscent = nAscent * fScale; |
456 | 0 | fDescent = -nDescent * fScale; |
457 | 0 | fExtLeading = nLineGap * fScale; |
458 | 0 | } |
459 | 0 | } |
460 | 161k | else |
461 | 161k | { |
462 | | // This is not a variable font, we try to choose the best metrics |
463 | | // ourselves for backward comparability: |
464 | | // |
465 | | // - hhea metrics should be used, since hhea is a mandatory font table |
466 | | // and should always be present. |
467 | | // - But if OS/2 is present, it should be used since it is mandatory in |
468 | | // Windows. |
469 | | // OS/2 has Typo and Win metrics, but the later was meant to control |
470 | | // text clipping not line spacing and can be ridiculously large. |
471 | | // Unfortunately many Windows application incorrectly use the Win |
472 | | // metrics (thanks to GDI’s TEXTMETRIC) and old fonts might be |
473 | | // designed with this in mind, so OpenType introduced a flag for |
474 | | // fonts to indicate that they really want to use Typo metrics. So |
475 | | // for best backward compatibility: |
476 | | // - Use Win metrics if available. |
477 | | // - Unless USE_TYPO_METRICS flag is set, in which case use Typo |
478 | | // metrics. |
479 | | |
480 | | // Try hhea table first. |
481 | 161k | hb_position_t nAscent = 0, nDescent = 0, nLineGap = 0; |
482 | 161k | if (hb_ot_metrics_get_position(pHbFont, ASCENT_HHEA, &nAscent) |
483 | 161k | && hb_ot_metrics_get_position(pHbFont, DESCENT_HHEA, &nDescent) |
484 | 161k | && hb_ot_metrics_get_position(pHbFont, LINEGAP_HHEA, &nLineGap)) |
485 | 161k | { |
486 | | // tdf#107605: Some fonts have weird values here, so check that |
487 | | // ascender is +ve and descender is -ve as they normally should. |
488 | 161k | if (nAscent >= 0 && nDescent <= 0) |
489 | 161k | { |
490 | 161k | fAscent = nAscent * fScale; |
491 | 161k | fDescent = -nDescent * fScale; |
492 | 161k | fExtLeading = nLineGap * fScale; |
493 | 161k | } |
494 | 161k | } |
495 | | |
496 | | // But if OS/2 is present, prefer it. |
497 | 161k | hb_position_t nTypoAscent, nTypoDescent, nTypoLineGap, nWinAscent, nWinDescent; |
498 | 161k | if (hb_ot_metrics_get_position(pHbFont, ASCENT_OS2, &nTypoAscent) |
499 | 161k | && hb_ot_metrics_get_position(pHbFont, DESCENT_OS2, &nTypoDescent) |
500 | 161k | && hb_ot_metrics_get_position(pHbFont, LINEGAP_OS2, &nTypoLineGap) |
501 | 161k | && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT, |
502 | 161k | &nWinAscent) |
503 | 161k | && hb_ot_metrics_get_position(pHbFont, HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT, |
504 | 161k | &nWinDescent)) |
505 | 161k | { |
506 | 161k | if ((fAscent == 0.0 && fDescent == 0.0) |
507 | 161k | || ShouldUseWinMetrics(nAscent, nDescent, nTypoAscent, nTypoDescent, nWinAscent, |
508 | 161k | nWinDescent)) |
509 | 2 | { |
510 | 2 | fAscent = nWinAscent * fScale; |
511 | 2 | fDescent = nWinDescent * fScale; |
512 | 2 | fExtLeading = 0; |
513 | 2 | } |
514 | | |
515 | 161k | bool bUseTypoMetrics |
516 | 161k | = hb_ot_fetch_bits(pFace->GetHbFace(), HB_OT_BITS_TAG_FS_SELECTION) & (1 << 7); |
517 | 161k | if (bUseTypoMetrics && nTypoAscent >= 0 && nTypoDescent <= 0) |
518 | 0 | { |
519 | 0 | fAscent = nTypoAscent * fScale; |
520 | 0 | fDescent = -nTypoDescent * fScale; |
521 | 0 | fExtLeading = nTypoLineGap * fScale; |
522 | 0 | } |
523 | 161k | } |
524 | 161k | } |
525 | | |
526 | 161k | mnAscent = round(fAscent); |
527 | 161k | mnDescent = round(fDescent); |
528 | 161k | mnExtLeading = round(fExtLeading); |
529 | | |
530 | 161k | if (mnAscent || mnDescent) |
531 | 161k | mnIntLeading = mnAscent + mnDescent - mnHeight; |
532 | | |
533 | | // tdf#36709: Additional font metrics are needed for font-relative indentation |
534 | 161k | mdEmSize = static_cast<double>(mnHeight); |
535 | | |
536 | | // The ic is defined as the advance of CJK UNIFIED IDEOGRAPH-6C34 (水). |
537 | | // If this character does not exist, it defaults to em. |
538 | 161k | mdHorCJKAdvanceSize = mdEmSize; |
539 | 161k | mdVertCJKAdvanceSize = mdEmSize; |
540 | | |
541 | 161k | hb_codepoint_t nIcGlyph; |
542 | 161k | if (hb_font_get_glyph(pHbFont, 0x6C34, /*variation selector*/ 0, &nIcGlyph)) |
543 | 0 | { |
544 | 0 | auto nIcHAdvance = hb_font_get_glyph_h_advance(pHbFont, nIcGlyph); |
545 | 0 | mdHorCJKAdvanceSize = static_cast<double>(nIcHAdvance) * fScale; |
546 | |
|
547 | 0 | auto nIcVAdvance = hb_font_get_glyph_v_advance(pHbFont, nIcGlyph); |
548 | 0 | mdVertCJKAdvanceSize = static_cast<double>(nIcVAdvance) * fScale; |
549 | 0 | } |
550 | 161k | } |
551 | | |
552 | | void FontMetricData::ImplInitBaselines(LogicalFontInstance *pFontInstance) |
553 | 161k | { |
554 | 161k | hb_font_t* pHbFont = pFontInstance->GetHbFont(); |
555 | 161k | double fScale = 0; |
556 | 161k | pFontInstance->GetScale(nullptr, &fScale); |
557 | 161k | hb_position_t nBaseline = 0; |
558 | | |
559 | 161k | if (hb_ot_layout_get_baseline(pHbFont, |
560 | 161k | HB_OT_LAYOUT_BASELINE_TAG_HANGING, |
561 | 161k | HB_DIRECTION_INVALID, |
562 | 161k | HB_SCRIPT_UNKNOWN, |
563 | 161k | HB_TAG_NONE, |
564 | 161k | &nBaseline)) |
565 | 0 | { |
566 | 0 | mnHangingBaseline = nBaseline * fScale; |
567 | 0 | } |
568 | 161k | else |
569 | 161k | { |
570 | 161k | mnHangingBaseline = 0; |
571 | 161k | } |
572 | 161k | } |
573 | | |
574 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |