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 <vcl/fontcharmap.hxx>
22 #include <vcl/metric.hxx>
24 #include "impfontmetric.hxx"
25 #include "impfontmetricdata.hxx"
26 #include "PhysicalFontFace.hxx"
33 using namespace ::com::sun::star
;
34 using namespace ::com::sun::star::uno
;
35 using namespace ::rtl
;
36 using namespace ::utl
;
38 FontMetric::FontMetric()
39 : mxImplMetric( new ImplFontMetric() )
42 FontMetric::FontMetric( const FontMetric
& rFontMetric
)
44 , mxImplMetric( rFontMetric
.mxImplMetric
)
47 FontMetric::~FontMetric()
49 mxImplMetric
= nullptr;
52 FontMetric
& FontMetric::operator=(const FontMetric
& rFontMetric
)
54 Font::operator=(rFontMetric
);
55 mxImplMetric
= rFontMetric
.mxImplMetric
;
59 FontMetric
& FontMetric::operator=(FontMetric
&& rFontMetric
)
61 Font::operator=(std::move(rFontMetric
));
62 mxImplMetric
= std::move(rFontMetric
.mxImplMetric
);
66 bool FontMetric::operator==( const FontMetric
& rFontMetric
) const
68 if( !Font::operator==( rFontMetric
) )
70 if( mxImplMetric
== rFontMetric
.mxImplMetric
)
72 if( *mxImplMetric
== *rFontMetric
.mxImplMetric
)
77 long FontMetric::GetAscent() const
79 return mxImplMetric
->GetAscent();
82 void FontMetric::SetAscent( long nAscent
)
84 mxImplMetric
->SetAscent( nAscent
);
87 long FontMetric::GetDescent() const
89 return mxImplMetric
->GetDescent();
92 void FontMetric::SetDescent( long nDescent
)
94 mxImplMetric
->SetDescent( nDescent
);
97 long FontMetric::GetInternalLeading() const
99 return mxImplMetric
->GetInternalLeading();
102 void FontMetric::SetInternalLeading( long nLeading
)
104 mxImplMetric
->SetInternalLeading( nLeading
);
107 long FontMetric::GetExternalLeading() const
109 return mxImplMetric
->GetExternalLeading();
112 void FontMetric::SetExternalLeading( long nLeading
)
114 mxImplMetric
->SetExternalLeading( nLeading
);
117 long FontMetric::GetLineHeight() const
119 return mxImplMetric
->GetLineHeight();
122 void FontMetric::SetLineHeight( long nHeight
)
124 mxImplMetric
->SetLineHeight( nHeight
);
127 long FontMetric::GetSlant() const
129 return mxImplMetric
->GetSlant();
132 void FontMetric::SetSlant( long nSlant
)
134 mxImplMetric
->SetSlant( nSlant
);
137 long FontMetric::GetBulletOffset() const
139 return mxImplMetric
->GetBulletOffset();
142 void FontMetric::SetBulletOffset( long nOffset
)
144 mxImplMetric
->SetBulletOffset( nOffset
);
147 bool FontMetric::IsFullstopCentered() const
149 return mxImplMetric
->IsFullstopCentered();
152 void FontMetric::SetFullstopCenteredFlag(bool bScalable
)
154 mxImplMetric
->SetFullstopCenteredFlag( bScalable
);
158 ImplFontMetric::ImplFontMetric()
166 mbFullstopCentered( false )
169 bool ImplFontMetric::operator==( const ImplFontMetric
& r
) const
171 if (mbFullstopCentered
!= r
.mbFullstopCentered
)
173 if( mnAscent
!= r
.mnAscent
)
175 if( mnDescent
!= r
.mnDescent
)
177 if( mnIntLeading
!= r
.mnIntLeading
)
179 if( mnExtLeading
!= r
.mnExtLeading
)
181 if( mnSlant
!= r
.mnSlant
)
187 ImplFontMetricData::ImplFontMetricData( const FontSelectPattern
& rFontSelData
)
188 : FontAttributes( rFontSelData
)
189 , mnHeight ( rFontSelData
.mnHeight
)
190 , mnWidth ( rFontSelData
.mnWidth
)
191 , mnOrientation( (short)(rFontSelData
.mnOrientation
) )
198 , mbFullstopCentered( false )
199 , mnBulletOffset( 0 )
200 , mnUnderlineSize( 0 )
201 , mnUnderlineOffset( 0 )
202 , mnBUnderlineSize( 0 )
203 , mnBUnderlineOffset( 0 )
204 , mnDUnderlineSize( 0 )
205 , mnDUnderlineOffset1( 0 )
206 , mnDUnderlineOffset2( 0 )
207 , mnWUnderlineSize( 0 )
208 , mnWUnderlineOffset( 0 )
209 , mnAboveUnderlineSize( 0 )
210 , mnAboveUnderlineOffset( 0 )
211 , mnAboveBUnderlineSize( 0 )
212 , mnAboveBUnderlineOffset( 0 )
213 , mnAboveDUnderlineSize( 0 )
214 , mnAboveDUnderlineOffset1( 0 )
215 , mnAboveDUnderlineOffset2( 0 )
216 , mnAboveWUnderlineSize( 0 )
217 , mnAboveWUnderlineOffset( 0 )
218 , mnStrikeoutSize( 0 )
219 , mnStrikeoutOffset( 0 )
220 , mnBStrikeoutSize( 0 )
221 , mnBStrikeoutOffset( 0 )
222 , mnDStrikeoutSize( 0 )
223 , mnDStrikeoutOffset1( 0 )
224 , mnDStrikeoutOffset2( 0 )
226 // intialize the used font name
227 if( rFontSelData
.mpFontData
)
229 SetFamilyName( rFontSelData
.mpFontData
->GetFamilyName() );
230 SetStyleName( rFontSelData
.mpFontData
->GetStyleName() );
234 sal_Int32 nTokenPos
= 0;
235 SetFamilyName( GetNextFontToken( rFontSelData
.GetFamilyName(), nTokenPos
) );
236 SetStyleName( rFontSelData
.GetStyleName() );
241 void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice
* pDev
)
243 long nDescent
= mnDescent
;
246 nDescent
= mnAscent
/ 10;
251 // #i55341# for some fonts it is not a good idea to calculate
252 // their text line metrics from the real font descent
253 // => work around this problem just for these fonts
254 if( 3*nDescent
> mnAscent
)
255 nDescent
= mnAscent
/ 3;
257 long nLineHeight
= ((nDescent
*25)+50) / 100;
260 long nLineHeight2
= nLineHeight
/ 2;
264 long nBLineHeight
= ((nDescent
*50)+50) / 100;
265 if ( nBLineHeight
== nLineHeight
)
267 long nBLineHeight2
= nBLineHeight
/2;
268 if ( !nBLineHeight2
)
271 long n2LineHeight
= ((nDescent
*16)+50) / 100;
274 long n2LineDY
= n2LineHeight
;
276 * add some pixels to minimum double line distance on higher resolution devices
278 long nMin2LineDY
= 1 + pDev
->GetDPIY()/150;
279 if ( n2LineDY
< nMin2LineDY
)
280 n2LineDY
= nMin2LineDY
;
281 long n2LineDY2
= n2LineDY
/2;
285 long nUnderlineOffset
= mnDescent
/2 + 1;
286 long nStrikeoutOffset
= -((mnAscent
- mnIntLeading
) / 3);
288 mnUnderlineSize
= nLineHeight
;
289 mnUnderlineOffset
= nUnderlineOffset
- nLineHeight2
;
291 mnBUnderlineSize
= nBLineHeight
;
292 mnBUnderlineOffset
= nUnderlineOffset
- nBLineHeight2
;
294 mnDUnderlineSize
= n2LineHeight
;
295 mnDUnderlineOffset1
= nUnderlineOffset
- n2LineDY2
- n2LineHeight
;
296 mnDUnderlineOffset2
= mnDUnderlineOffset1
+ n2LineDY
+ n2LineHeight
;
298 long nWCalcSize
= mnDescent
;
299 if ( nWCalcSize
< 6 )
301 if ( (nWCalcSize
== 1) || (nWCalcSize
== 2) )
302 mnWUnderlineSize
= nWCalcSize
;
304 mnWUnderlineSize
= 3;
307 mnWUnderlineSize
= ((nWCalcSize
*50)+50) / 100;
309 // Don't assume that wavelines are never placed below the descent, because for most fonts the waveline
310 // is drawn into the text
311 mnWUnderlineOffset
= nUnderlineOffset
;
313 mnStrikeoutSize
= nLineHeight
;
314 mnStrikeoutOffset
= nStrikeoutOffset
- nLineHeight2
;
316 mnBStrikeoutSize
= nBLineHeight
;
317 mnBStrikeoutOffset
= nStrikeoutOffset
- nBLineHeight2
;
319 mnDStrikeoutSize
= n2LineHeight
;
320 mnDStrikeoutOffset1
= nStrikeoutOffset
- n2LineDY2
- n2LineHeight
;
321 mnDStrikeoutOffset2
= mnDStrikeoutOffset1
+ n2LineDY
+ n2LineHeight
;
323 const vcl::Font
& rFont ( pDev
->GetFont() );
324 bool bCentered
= true;
325 if (MsLangId::isCJK(rFont
.GetLanguage()))
327 const OUString
sFullstop( sal_Unicode( 0x3001 ) ); // Fullwidth fullstop
329 pDev
->GetTextBoundRect( aRect
, sFullstop
);
330 const sal_uInt16 nH
= rFont
.GetFontSize().Height();
331 const sal_uInt16 nB
= aRect
.Left();
332 // Use 18.75% as a threshold to define a centered fullwidth fullstop.
333 // In general, nB/nH < 5% for most Japanese fonts.
334 bCentered
= nB
> (((nH
>> 1)+nH
)>>3);
336 SetFullstopCenteredFlag( bCentered
);
338 mnBulletOffset
= ( pDev
->GetTextWidth( OUString( sal_Unicode( 0x20 ) ) ) - pDev
->GetTextWidth( OUString( sal_Unicode( 0xb7 ) ) ) ) >> 1 ;
343 void ImplFontMetricData::ImplInitAboveTextLineSize()
345 long nIntLeading
= mnIntLeading
;
346 // TODO: assess usage of nLeading below (changed in extleading CWS)
347 // if no leading is available, we assume 15% of the ascent
348 if ( nIntLeading
<= 0 )
350 nIntLeading
= mnAscent
*15/100;
355 long nLineHeight
= ((nIntLeading
*25)+50) / 100;
359 long nBLineHeight
= ((nIntLeading
*50)+50) / 100;
360 if ( nBLineHeight
== nLineHeight
)
363 long n2LineHeight
= ((nIntLeading
*16)+50) / 100;
367 long nCeiling
= -mnAscent
;
369 mnAboveUnderlineSize
= nLineHeight
;
370 mnAboveUnderlineOffset
= nCeiling
+ (nIntLeading
- nLineHeight
+ 1) / 2;
372 mnAboveBUnderlineSize
= nBLineHeight
;
373 mnAboveBUnderlineOffset
= nCeiling
+ (nIntLeading
- nBLineHeight
+ 1) / 2;
375 mnAboveDUnderlineSize
= n2LineHeight
;
376 mnAboveDUnderlineOffset1
= nCeiling
+ (nIntLeading
- 3*n2LineHeight
+ 1) / 2;
377 mnAboveDUnderlineOffset2
= nCeiling
+ (nIntLeading
+ n2LineHeight
+ 1) / 2;
379 long nWCalcSize
= nIntLeading
;
380 if ( nWCalcSize
< 6 )
382 if ( (nWCalcSize
== 1) || (nWCalcSize
== 2) )
383 mnAboveWUnderlineSize
= nWCalcSize
;
385 mnAboveWUnderlineSize
= 3;
388 mnAboveWUnderlineSize
= ((nWCalcSize
*50)+50) / 100;
390 mnAboveWUnderlineOffset
= nCeiling
+ (nIntLeading
+ 1) / 2;
394 * Calculate line spacing:
396 * - hhea metrics should be used, since hhea is a mandatory font table and
397 * should always be present.
398 * - But if OS/2 is present, it should be used since it is mandatory in
400 * OS/2 has Typo and Win metrics, but the later was meant to control
401 * text clipping not line spacing and can be ridiculously large.
402 * Unfortunately many Windows application incorrectly use the Win metrics
403 * (thanks to GDI’s TEXTMETRIC) and old fonts might be designed with this
404 * in mind, so OpenType introduced a flag for fonts to indicate that they
405 * really want to use Typo metrics. So for best backward compatibility:
406 * - Use Win metrics if available.
407 * - Unless USE_TYPO_METRICS flag is set, in which case use Typo metrics.
409 void ImplFontMetricData::ImplCalcLineSpacing(const std::vector
<uint8_t>& rHheaData
,
410 const std::vector
<uint8_t>& rOS2Data
, int nUPEM
)
412 mnAscent
= mnDescent
= mnExtLeading
= mnIntLeading
= 0;
414 double fScale
= static_cast<double>(mnHeight
) / nUPEM
;
415 double fAscent
= 0, fDescent
= 0, fExtLeading
= 0;
417 vcl::TTGlobalFontInfo rInfo
;
418 memset(&rInfo
, 0, sizeof(vcl::TTGlobalFontInfo
));
419 GetTTFontMterics(rHheaData
, rOS2Data
, &rInfo
);
421 // Try hhea table first.
422 if (rInfo
.ascender
|| rInfo
.descender
)
424 fAscent
= rInfo
.ascender
* fScale
;
425 fDescent
= -rInfo
.descender
* fScale
;
426 fExtLeading
= rInfo
.linegap
* fScale
;
429 // But if OS/2 is present, prefer it.
430 if (rInfo
.winAscent
|| rInfo
.winDescent
|| rInfo
.typoAscender
|| rInfo
.typoDescender
)
432 if (fAscent
== 0 && fDescent
== 0)
434 fAscent
= rInfo
.winAscent
* fScale
;
435 fDescent
= rInfo
.winDescent
* fScale
;
439 const uint16_t kUseTypoMetricsMask
= 1 << 7;
440 if (rInfo
.fsSelection
& kUseTypoMetricsMask
)
442 fAscent
= rInfo
.typoAscender
* fScale
;
443 fDescent
= -rInfo
.typoDescender
* fScale
;
444 fExtLeading
= rInfo
.typoLineGap
* fScale
;
448 mnAscent
= round(fAscent
);
449 mnDescent
= round(fDescent
);
450 mnExtLeading
= round(fExtLeading
);
452 if (mnAscent
|| mnDescent
)
453 mnIntLeading
= mnAscent
+ mnDescent
- mnHeight
;
455 SAL_INFO("vcl.gdi.fontmetric",
456 "fsSelection: " << rInfo
.fsSelection
457 << ", typoAscender: " << rInfo
.typoAscender
458 << ", typoDescender: " << rInfo
.typoDescender
459 << ", typoLineGap: " << rInfo
.typoLineGap
460 << ", winAscent: " << rInfo
.winAscent
461 << ", winDescent: " << rInfo
.winDescent
462 << ", ascender: " << rInfo
.ascender
463 << ", descender: " << rInfo
.descender
464 << ", linegap: " << rInfo
.linegap
468 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */