1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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()
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
) )
65 if (mbFullstopCentered
!= r
.mbFullstopCentered
)
67 if( mnAscent
!= r
.mnAscent
)
69 if( mnDescent
!= r
.mnDescent
)
71 if( mnIntLeading
!= r
.mnIntLeading
)
73 if( mnExtLeading
!= r
.mnExtLeading
)
75 if( mnSlant
!= r
.mnSlant
)
81 ImplFontMetricData::ImplFontMetricData( const FontSelectPattern
& rFontSelData
)
82 : FontAttributes( rFontSelData
)
83 , mnHeight ( rFontSelData
.mnHeight
)
84 , mnWidth ( rFontSelData
.mnWidth
)
85 , mnOrientation( static_cast<short>(rFontSelData
.mnOrientation
) )
92 , mbFullstopCentered( false )
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
;
131 nDescent
= mnAscent
/ 10;
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;
145 long nLineHeight2
= nLineHeight
/ 2;
149 long nBLineHeight
= ((nDescent
*50)+50) / 100;
150 if ( nBLineHeight
== nLineHeight
)
152 long nBLineHeight2
= nBLineHeight
/2;
153 if ( !nBLineHeight2
)
156 long n2LineHeight
= ((nDescent
*16)+50) / 100;
159 long n2LineDY
= n2LineHeight
;
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;
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
;
191 mnWUnderlineSize
= 3;
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;
228 long nLineHeight
= ((nIntLeading
*25)+50) / 100;
232 long nBLineHeight
= ((nIntLeading
*50)+50) / 100;
233 if ( nBLineHeight
== nLineHeight
)
236 long n2LineHeight
= ((nIntLeading
*16)+50) / 100;
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
;
258 mnAboveWUnderlineSize
= 3;
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())
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
);
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
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
),
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
;
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: */