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