Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / font / fontmetric.cxx
blob3b7a7ed09f7f0843c26570a5c68b7f87f4c82eb7
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 <i18nlangtag/mslangid.hxx>
21 #include <officecfg/Office/Common.hxx>
22 #include <unotools/configmgr.hxx>
23 #include <vcl/metric.hxx>
24 #include <vcl/outdev.hxx>
25 #include <sal/log.hxx>
27 #include <fontinstance.hxx>
28 #include <fontselect.hxx>
29 #include <impfontmetricdata.hxx>
30 #include <sft.hxx>
32 #include <com/sun/star/uno/Sequence.hxx>
33 #include <comphelper/sequence.hxx>
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
37 using namespace ::rtl;
38 using namespace ::utl;
40 FontMetric::FontMetric()
41 : mnAscent( 0 ),
42 mnDescent( 0 ),
43 mnIntLeading( 0 ),
44 mnExtLeading( 0 ),
45 mnLineHeight( 0 ),
46 mnSlant( 0 ),
47 mnBulletOffset( 0 ),
48 mbFullstopCentered( false )
51 FontMetric::FontMetric( const FontMetric& rFontMetric ) = default;
53 FontMetric::~FontMetric()
57 FontMetric& FontMetric::operator=(const FontMetric& rFontMetric) = default;
59 FontMetric& FontMetric::operator=(FontMetric&& rFontMetric) = default;
61 bool FontMetric::operator==( const FontMetric& r ) const
63 if( Font::operator!=(r) )
64 return false;
65 if (mbFullstopCentered != r.mbFullstopCentered)
66 return false;
67 if( mnAscent != r.mnAscent )
68 return false;
69 if( mnDescent != r.mnDescent )
70 return false;
71 if( mnIntLeading != r.mnIntLeading )
72 return false;
73 if( mnExtLeading != r.mnExtLeading )
74 return false;
75 if( mnSlant != r.mnSlant )
76 return false;
78 return true;
81 ImplFontMetricData::ImplFontMetricData( const FontSelectPattern& rFontSelData )
82 : FontAttributes( rFontSelData )
83 , mnHeight ( rFontSelData.mnHeight )
84 , mnWidth ( rFontSelData.mnWidth )
85 , mnOrientation( static_cast<short>(rFontSelData.mnOrientation) )
86 , mnAscent( 0 )
87 , mnDescent( 0 )
88 , mnIntLeading( 0 )
89 , mnExtLeading( 0 )
90 , mnSlant( 0 )
91 , mnMinKashida( 0 )
92 , mbFullstopCentered( false )
93 , mnBulletOffset( 0 )
94 , mnUnderlineSize( 0 )
95 , mnUnderlineOffset( 0 )
96 , mnBUnderlineSize( 0 )
97 , mnBUnderlineOffset( 0 )
98 , mnDUnderlineSize( 0 )
99 , mnDUnderlineOffset1( 0 )
100 , mnDUnderlineOffset2( 0 )
101 , mnWUnderlineSize( 0 )
102 , mnWUnderlineOffset( 0 )
103 , mnAboveUnderlineSize( 0 )
104 , mnAboveUnderlineOffset( 0 )
105 , mnAboveBUnderlineSize( 0 )
106 , mnAboveBUnderlineOffset( 0 )
107 , mnAboveDUnderlineSize( 0 )
108 , mnAboveDUnderlineOffset1( 0 )
109 , mnAboveDUnderlineOffset2( 0 )
110 , mnAboveWUnderlineSize( 0 )
111 , mnAboveWUnderlineOffset( 0 )
112 , mnStrikeoutSize( 0 )
113 , mnStrikeoutOffset( 0 )
114 , mnBStrikeoutSize( 0 )
115 , mnBStrikeoutOffset( 0 )
116 , mnDStrikeoutSize( 0 )
117 , mnDStrikeoutOffset1( 0 )
118 , mnDStrikeoutOffset2( 0 )
120 // initialize the used font name
121 sal_Int32 nTokenPos = 0;
122 SetFamilyName( GetNextFontToken( rFontSelData.GetFamilyName(), nTokenPos ) );
123 SetStyleName( rFontSelData.GetStyleName() );
126 void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
128 long nDescent = mnDescent;
129 if ( nDescent <= 0 )
131 nDescent = mnAscent / 10;
132 if ( !nDescent )
133 nDescent = 1;
136 // #i55341# for some fonts it is not a good idea to calculate
137 // their text line metrics from the real font descent
138 // => work around this problem just for these fonts
139 if( 3*nDescent > mnAscent )
140 nDescent = mnAscent / 3;
142 long nLineHeight = ((nDescent*25)+50) / 100;
143 if ( !nLineHeight )
144 nLineHeight = 1;
145 long nLineHeight2 = nLineHeight / 2;
146 if ( !nLineHeight2 )
147 nLineHeight2 = 1;
149 long nBLineHeight = ((nDescent*50)+50) / 100;
150 if ( nBLineHeight == nLineHeight )
151 nBLineHeight++;
152 long nBLineHeight2 = nBLineHeight/2;
153 if ( !nBLineHeight2 )
154 nBLineHeight2 = 1;
156 long n2LineHeight = ((nDescent*16)+50) / 100;
157 if ( !n2LineHeight )
158 n2LineHeight = 1;
159 long n2LineDY = n2LineHeight;
160 /* #117909#
161 * add some pixels to minimum double line distance on higher resolution devices
163 long nMin2LineDY = 1 + pDev->GetDPIY()/150;
164 if ( n2LineDY < nMin2LineDY )
165 n2LineDY = nMin2LineDY;
166 long n2LineDY2 = n2LineDY/2;
167 if ( !n2LineDY2 )
168 n2LineDY2 = 1;
170 const vcl::Font& rFont ( pDev->GetFont() );
171 bool bCJKVertical = MsLangId::isCJK(rFont.GetLanguage()) && rFont.IsVertical();
172 long nUnderlineOffset = bCJKVertical ? mnDescent : (mnDescent/2 + 1);
173 long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3);
175 mnUnderlineSize = nLineHeight;
176 mnUnderlineOffset = nUnderlineOffset - nLineHeight2;
178 mnBUnderlineSize = nBLineHeight;
179 mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2;
181 mnDUnderlineSize = n2LineHeight;
182 mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight;
183 mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;
185 long nWCalcSize = mnDescent;
186 if ( nWCalcSize < 6 )
188 if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
189 mnWUnderlineSize = nWCalcSize;
190 else
191 mnWUnderlineSize = 3;
193 else
194 mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;
197 // Don't assume that wavelines are never placed below the descent, because for most fonts the waveline
198 // is drawn into the text
199 mnWUnderlineOffset = nUnderlineOffset;
201 mnStrikeoutSize = nLineHeight;
202 mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2;
204 mnBStrikeoutSize = nBLineHeight;
205 mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2;
207 mnDStrikeoutSize = n2LineHeight;
208 mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
209 mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
211 mnBulletOffset = ( pDev->GetTextWidth( OUString( u' ' ) ) - pDev->GetTextWidth( OUString( u'\x00b7' ) ) ) >> 1 ;
216 void ImplFontMetricData::ImplInitAboveTextLineSize()
218 long nIntLeading = mnIntLeading;
219 // TODO: assess usage of nLeading below (changed in extleading CWS)
220 // if no leading is available, we assume 15% of the ascent
221 if ( nIntLeading <= 0 )
223 nIntLeading = mnAscent*15/100;
224 if ( !nIntLeading )
225 nIntLeading = 1;
228 long nLineHeight = ((nIntLeading*25)+50) / 100;
229 if ( !nLineHeight )
230 nLineHeight = 1;
232 long nBLineHeight = ((nIntLeading*50)+50) / 100;
233 if ( nBLineHeight == nLineHeight )
234 nBLineHeight++;
236 long n2LineHeight = ((nIntLeading*16)+50) / 100;
237 if ( !n2LineHeight )
238 n2LineHeight = 1;
240 long nCeiling = -mnAscent;
242 mnAboveUnderlineSize = nLineHeight;
243 mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2;
245 mnAboveBUnderlineSize = nBLineHeight;
246 mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;
248 mnAboveDUnderlineSize = n2LineHeight;
249 mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
250 mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2;
252 long nWCalcSize = nIntLeading;
253 if ( nWCalcSize < 6 )
255 if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
256 mnAboveWUnderlineSize = nWCalcSize;
257 else
258 mnAboveWUnderlineSize = 3;
260 else
261 mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;
263 mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
266 void ImplFontMetricData::ImplInitFlags( const OutputDevice* pDev )
268 const vcl::Font& rFont ( pDev->GetFont() );
269 bool bCentered = true;
270 if (MsLangId::isCJK(rFont.GetLanguage()))
272 const OUString sFullstop( u'\x3001' ); // Fullwidth fullstop
273 tools::Rectangle aRect;
274 pDev->GetTextBoundRect( aRect, sFullstop );
275 const auto nH = rFont.GetFontSize().Height();
276 const auto nB = aRect.Left();
277 // Use 18.75% as a threshold to define a centered fullwidth fullstop.
278 // In general, nB/nH < 5% for most Japanese fonts.
279 bCentered = nB > (((nH >> 1)+nH)>>3);
281 SetFullstopCenteredFlag( bCentered );
284 bool ImplFontMetricData::ShouldUseWinMetrics(const vcl::TTGlobalFontInfo& rInfo)
286 if (utl::ConfigManager::IsFuzzing())
287 return false;
289 OUString aFontIdentifier(
290 GetFamilyName() + ","
291 + OUString::number(rInfo.ascender) + "," + OUString::number(rInfo.descender) + ","
292 + OUString::number(rInfo.typoAscender) + "," + OUString::number(rInfo.typoDescender) + ","
293 + OUString::number(rInfo.winAscent) + "," + OUString::number(rInfo.winDescent));
295 css::uno::Sequence<OUString> rWinMetricFontList(
296 officecfg::Office::Common::Misc::FontsUseWinMetrics::get());
297 if (comphelper::findValue(rWinMetricFontList, aFontIdentifier) != -1)
299 SAL_INFO("vcl.gdi.fontmetric", "Using win metrics for: " << aFontIdentifier);
300 return true;
302 return false;
306 * Calculate line spacing:
308 * - hhea metrics should be used, since hhea is a mandatory font table and
309 * should always be present.
310 * - But if OS/2 is present, it should be used since it is mandatory in
311 * Windows.
312 * OS/2 has Typo and Win metrics, but the later was meant to control
313 * text clipping not line spacing and can be ridiculously large.
314 * Unfortunately many Windows application incorrectly use the Win metrics
315 * (thanks to GDI’s TEXTMETRIC) and old fonts might be designed with this
316 * in mind, so OpenType introduced a flag for fonts to indicate that they
317 * really want to use Typo metrics. So for best backward compatibility:
318 * - Use Win metrics if available.
319 * - Unless USE_TYPO_METRICS flag is set, in which case use Typo metrics.
321 void ImplFontMetricData::ImplCalcLineSpacing(LogicalFontInstance *pFontInstance)
323 mnAscent = mnDescent = mnExtLeading = mnIntLeading = 0;
325 hb_font_t* pHbFont = pFontInstance->GetHbFont();
326 hb_face_t* pHbFace = hb_font_get_face(pHbFont);
328 hb_blob_t* pHhea = hb_face_reference_table(pHbFace, HB_TAG('h', 'h', 'e', 'a'));
329 hb_blob_t* pOS2 = hb_face_reference_table(pHbFace, HB_TAG('O', 'S', '/', '2'));
331 vcl::TTGlobalFontInfo rInfo = {};
332 GetTTFontMetrics(reinterpret_cast<const uint8_t*>(hb_blob_get_data(pHhea, nullptr)), hb_blob_get_length(pHhea),
333 reinterpret_cast<const uint8_t*>(hb_blob_get_data(pOS2, nullptr)), hb_blob_get_length(pOS2),
334 &rInfo);
336 hb_blob_destroy(pHhea);
337 hb_blob_destroy(pOS2);
339 double nUPEM = hb_face_get_upem(pHbFace);
340 double fScale = mnHeight / nUPEM;
341 double fAscent = 0, fDescent = 0, fExtLeading = 0;
343 // Try hhea table first.
344 // tdf#107605: Some fonts have weird values here, so check that ascender is
345 // +ve and descender is -ve as they normally should.
346 if (rInfo.ascender >= 0 && rInfo.descender <= 0)
348 fAscent = rInfo.ascender * fScale;
349 fDescent = -rInfo.descender * fScale;
350 fExtLeading = rInfo.linegap * fScale;
353 // But if OS/2 is present, prefer it.
354 if (rInfo.winAscent || rInfo.winDescent ||
355 rInfo.typoAscender || rInfo.typoDescender)
357 if (ShouldUseWinMetrics(rInfo) || (fAscent == 0.0 && fDescent == 0.0))
359 fAscent = rInfo.winAscent * fScale;
360 fDescent = rInfo.winDescent * fScale;
361 fExtLeading = 0;
364 const uint16_t kUseTypoMetricsMask = 1 << 7;
365 if (rInfo.fsSelection & kUseTypoMetricsMask &&
366 rInfo.typoAscender >= 0 && rInfo.typoDescender <= 0)
368 fAscent = rInfo.typoAscender * fScale;
369 fDescent = -rInfo.typoDescender * fScale;
370 fExtLeading = rInfo.typoLineGap * fScale;
374 mnAscent = round(fAscent);
375 mnDescent = round(fDescent);
376 mnExtLeading = round(fExtLeading);
378 if (mnAscent || mnDescent)
379 mnIntLeading = mnAscent + mnDescent - mnHeight;
381 SAL_INFO("vcl.gdi.fontmetric", GetFamilyName()
382 << ": fsSelection: " << rInfo.fsSelection
383 << ", typoAscender: " << rInfo.typoAscender
384 << ", typoDescender: " << rInfo.typoDescender
385 << ", typoLineGap: " << rInfo.typoLineGap
386 << ", winAscent: " << rInfo.winAscent
387 << ", winDescent: " << rInfo.winDescent
388 << ", ascender: " << rInfo.ascender
389 << ", descender: " << rInfo.descender
390 << ", linegap: " << rInfo.linegap
394 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */