LibreOffice Module vcl (master)  1
fontmetric.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 <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>
26 
27 #include <fontinstance.hxx>
28 #include <fontselect.hxx>
29 #include <impfontmetricdata.hxx>
30 #include <sft.hxx>
31 
32 #include <com/sun/star/uno/Sequence.hxx>
33 #include <comphelper/sequence.hxx>
34 
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
37 using namespace ::rtl;
38 using namespace ::utl;
39 
41 : mnAscent( 0 ),
42  mnDescent( 0 ),
43  mnIntLeading( 0 ),
44  mnExtLeading( 0 ),
45  mnLineHeight( 0 ),
46  mnSlant( 0 ),
47  mnBulletOffset( 0 ),
48  mbFullstopCentered( false )
49 {}
50 
51 FontMetric::FontMetric( const FontMetric& rFontMetric ) = default;
52 
54 {
55 }
56 
57 FontMetric& FontMetric::operator=(const FontMetric& rFontMetric) = default;
58 
59 FontMetric& FontMetric::operator=(FontMetric&& rFontMetric) = default;
60 
61 bool FontMetric::operator==( const FontMetric& r ) const
62 {
63  if( Font::operator!=(r) )
64  return false;
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;
77 
78  return true;
79 }
80 
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 )
119 {
120  // initialize the used font name
121  sal_Int32 nTokenPos = 0;
122  SetFamilyName( GetNextFontToken( rFontSelData.GetFamilyName(), nTokenPos ) );
123  SetStyleName( rFontSelData.GetStyleName() );
124 }
125 
127 {
128  long nDescent = mnDescent;
129  if ( nDescent <= 0 )
130  {
131  nDescent = mnAscent / 10;
132  if ( !nDescent )
133  nDescent = 1;
134  }
135 
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;
141 
142  long nLineHeight = ((nDescent*25)+50) / 100;
143  if ( !nLineHeight )
144  nLineHeight = 1;
145  long nLineHeight2 = nLineHeight / 2;
146  if ( !nLineHeight2 )
147  nLineHeight2 = 1;
148 
149  long nBLineHeight = ((nDescent*50)+50) / 100;
150  if ( nBLineHeight == nLineHeight )
151  nBLineHeight++;
152  long nBLineHeight2 = nBLineHeight/2;
153  if ( !nBLineHeight2 )
154  nBLineHeight2 = 1;
155 
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
162  */
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;
169 
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);
174 
175  mnUnderlineSize = nLineHeight;
176  mnUnderlineOffset = nUnderlineOffset - nLineHeight2;
177 
178  mnBUnderlineSize = nBLineHeight;
179  mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2;
180 
181  mnDUnderlineSize = n2LineHeight;
182  mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight;
183  mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;
184 
185  long nWCalcSize = mnDescent;
186  if ( nWCalcSize < 6 )
187  {
188  if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
189  mnWUnderlineSize = nWCalcSize;
190  else
191  mnWUnderlineSize = 3;
192  }
193  else
194  mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;
195 
196 
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;
200 
201  mnStrikeoutSize = nLineHeight;
202  mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2;
203 
204  mnBStrikeoutSize = nBLineHeight;
205  mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2;
206 
207  mnDStrikeoutSize = n2LineHeight;
208  mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
209  mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
210 
211  mnBulletOffset = ( pDev->GetTextWidth( OUString( u' ' ) ) - pDev->GetTextWidth( OUString( u'\x00b7' ) ) ) >> 1 ;
212 
213 }
214 
215 
217 {
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 )
222  {
223  nIntLeading = mnAscent*15/100;
224  if ( !nIntLeading )
225  nIntLeading = 1;
226  }
227 
228  long nLineHeight = ((nIntLeading*25)+50) / 100;
229  if ( !nLineHeight )
230  nLineHeight = 1;
231 
232  long nBLineHeight = ((nIntLeading*50)+50) / 100;
233  if ( nBLineHeight == nLineHeight )
234  nBLineHeight++;
235 
236  long n2LineHeight = ((nIntLeading*16)+50) / 100;
237  if ( !n2LineHeight )
238  n2LineHeight = 1;
239 
240  long nCeiling = -mnAscent;
241 
242  mnAboveUnderlineSize = nLineHeight;
243  mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2;
244 
245  mnAboveBUnderlineSize = nBLineHeight;
246  mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;
247 
248  mnAboveDUnderlineSize = n2LineHeight;
249  mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
250  mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2;
251 
252  long nWCalcSize = nIntLeading;
253  if ( nWCalcSize < 6 )
254  {
255  if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
256  mnAboveWUnderlineSize = nWCalcSize;
257  else
259  }
260  else
261  mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;
262 
263  mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
264 }
265 
267 {
268  const vcl::Font& rFont ( pDev->GetFont() );
269  bool bCentered = true;
270  if (MsLangId::isCJK(rFont.GetLanguage()))
271  {
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);
280  }
281  SetFullstopCenteredFlag( bCentered );
282 }
283 
285 {
287  return false;
288 
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));
294 
295  css::uno::Sequence<OUString> rWinMetricFontList(
296  officecfg::Office::Common::Misc::FontsUseWinMetrics::get());
297  if (comphelper::findValue(rWinMetricFontList, aFontIdentifier) != -1)
298  {
299  SAL_INFO("vcl.gdi.fontmetric", "Using win metrics for: " << aFontIdentifier);
300  return true;
301  }
302  return false;
303 }
304 
305 /*
306  * Calculate line spacing:
307  *
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.
320 */
322 {
324 
325  hb_font_t* pHbFont = pFontInstance->GetHbFont();
326  hb_face_t* pHbFace = hb_font_get_face(pHbFont);
327 
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'));
330 
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);
335 
336  hb_blob_destroy(pHhea);
337  hb_blob_destroy(pOS2);
338 
339  double nUPEM = hb_face_get_upem(pHbFace);
340  double fScale = mnHeight / nUPEM;
341  double fAscent = 0, fDescent = 0, fExtLeading = 0;
342 
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)
347  {
348  fAscent = rInfo.ascender * fScale;
349  fDescent = -rInfo.descender * fScale;
350  fExtLeading = rInfo.linegap * fScale;
351  }
352 
353  // But if OS/2 is present, prefer it.
354  if (rInfo.winAscent || rInfo.winDescent ||
355  rInfo.typoAscender || rInfo.typoDescender)
356  {
357  if (ShouldUseWinMetrics(rInfo) || (fAscent == 0.0 && fDescent == 0.0))
358  {
359  fAscent = rInfo.winAscent * fScale;
360  fDescent = rInfo.winDescent * fScale;
361  fExtLeading = 0;
362  }
363 
364  const uint16_t kUseTypoMetricsMask = 1 << 7;
365  if (rInfo.fsSelection & kUseTypoMetricsMask &&
366  rInfo.typoAscender >= 0 && rInfo.typoDescender <= 0)
367  {
368  fAscent = rInfo.typoAscender * fScale;
369  fDescent = -rInfo.typoDescender * fScale;
370  fExtLeading = rInfo.typoLineGap * fScale;
371  }
372  }
373 
374  mnAscent = round(fAscent);
375  mnDescent = round(fDescent);
376  mnExtLeading = round(fExtLeading);
377 
378  if (mnAscent || mnDescent)
380 
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
391  );
392 }
393 
394 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double mnHeight
const OUString & GetStyleName() const
bool operator==(const FontMetric &rMetric) const
Definition: fontmetric.cxx:61
int descender
typographic descent.
Definition: sft.hxx:162
long mnDescent
Definition: metric.hxx:68
sal_Int32 findValue(const css::uno::Sequence< T1 > &_rList, const T2 &_rValue)
int ascender
typographic ascent.
Definition: sft.hxx:161
int typoAscender
OS/2 portable typographic ascender.
Definition: sft.hxx:165
Return value of GetTTGlobalFontInfo()
Definition: sft.hxx:146
hb_font_t * GetHbFont()
~FontMetric() override
Definition: fontmetric.cxx:53
int linegap
typographic line gap. Negative values are treated as zero in Win 3.1, System 6 and System 7...
Definition: sft.hxx:163
void ImplInitAboveTextLineSize()
Definition: fontmetric.cxx:216
void ImplCalcLineSpacing(LogicalFontInstance *pFontInstance)
Definition: fontmetric.cxx:321
bool mbFullstopCentered
Definition: metric.hxx:75
int typoLineGap
OS/2 portable typographic line gap.
Definition: sft.hxx:167
void ImplInitTextLineSize(const OutputDevice *pDev)
Definition: fontmetric.cxx:126
bool GetTextBoundRect(tools::Rectangle &rRect, const OUString &rStr, sal_Int32 nBase=0, sal_Int32 nIndex=0, sal_Int32 nLen=-1, sal_uLong nLayoutWidth=0, const long *pDXArray=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Return the exact bounding rectangle of rStr.
Definition: text.cxx:2306
int winDescent
descender metric for Windows
Definition: sft.hxx:169
int winAscent
ascender metric for Windows
Definition: sft.hxx:168
long mnAscent
Definition: metric.hxx:67
const vcl::Font & GetFont() const
Definition: outdev.hxx:637
static bool IsFuzzing()
void GetTTFontMetrics(const uint8_t *pHhea, size_t nHhea, const uint8_t *pOs2, size_t nOs2, TTGlobalFontInfo *info)
Returns fonts metrics.
Definition: sft.cxx:2338
static bool isCJK(LanguageType nLang)
void ImplInitFlags(const OutputDevice *pDev)
Definition: fontmetric.cxx:266
long mnSlant
Definition: metric.hxx:72
void SetFamilyName(const OUString &sFamilyName)
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:304
float u
sal_uInt16 fsSelection
OS/2 fsSelection.
Definition: sft.hxx:173
FontMetric & operator=(const FontMetric &rMetric)
int typoDescender
OS/2 portable typographic descender.
Definition: sft.hxx:166
ImplFontMetricData(const FontSelectPattern &)
Definition: fontmetric.cxx:81
long mnExtLeading
Definition: metric.hxx:70
#define SAL_INFO(area, stream)
Sun Font Tools.
double mnWidth
void SetStyleName(const OUString &sStyleName)
long Left() const
long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
Width of the text.
Definition: text.cxx:877
OUString GetNextFontToken(const OUString &rTokenStr, sal_Int32 &rIndex)
const OUString & GetFamilyName() const
SAL_DLLPRIVATE sal_Int32 GetDPIY() const
Get the output device's DPI y-axis value.
Definition: outdev.hxx:507
long mnIntLeading
Definition: metric.hxx:69
bool ShouldUseWinMetrics(const vcl::TTGlobalFontInfo &rInfo)
Definition: fontmetric.cxx:284
void SetFullstopCenteredFlag(bool bFullstopCentered)