1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "gfxFT2FontBase.h"
7 #include "gfxFT2Utils.h"
8 #include "mozilla/Likely.h"
9 #include FT_TRUETYPE_TAGS_H
10 #include FT_TRUETYPE_TABLES_H
13 #ifdef HAVE_FONTCONFIG_FCFREETYPE_H
14 #include <fontconfig/fcfreetype.h>
19 // aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
21 ScaleRoundDesignUnits(FT_Short aDesignMetric
, FT_Fixed aScale
)
23 FT_Long fixed26dot6
= FT_MulFix(aDesignMetric
, aScale
);
24 return ROUND_26_6_TO_INT(fixed26dot6
);
27 // Snap a line to pixels while keeping the center and size of the line as
28 // close to the original position as possible.
30 // Pango does similar snapping for underline and strikethrough when fonts are
31 // hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
32 // top and size of lines. Optimizing the distance between the line and
33 // baseline is probably good for the gap between text and underline, but
34 // optimizing the center of the line is better for positioning strikethough.
36 SnapLineToPixels(gfxFloat
& aOffset
, gfxFloat
& aSize
)
38 gfxFloat snappedSize
= std::max(floor(aSize
+ 0.5), 1.0);
39 // Correct offset for change in size
40 gfxFloat offset
= aOffset
- 0.5 * (aSize
- snappedSize
);
42 aOffset
= floor(offset
+ 0.5);
47 gfxFT2LockedFace::GetMetrics(gfxFont::Metrics
* aMetrics
,
48 uint32_t* aSpaceGlyph
)
50 NS_PRECONDITION(aMetrics
!= nullptr, "aMetrics must not be NULL");
51 NS_PRECONDITION(aSpaceGlyph
!= nullptr, "aSpaceGlyph must not be NULL");
53 if (MOZ_UNLIKELY(!mFace
)) {
54 // No face. This unfortunate situation might happen if the font
55 // file is (re)moved at the wrong time.
56 const gfxFloat emHeight
= mGfxFont
->GetStyle()->size
;
57 aMetrics
->emHeight
= emHeight
;
58 aMetrics
->maxAscent
= aMetrics
->emAscent
= 0.8 * emHeight
;
59 aMetrics
->maxDescent
= aMetrics
->emDescent
= 0.2 * emHeight
;
60 aMetrics
->maxHeight
= emHeight
;
61 aMetrics
->internalLeading
= 0.0;
62 aMetrics
->externalLeading
= 0.2 * emHeight
;
63 const gfxFloat spaceWidth
= 0.5 * emHeight
;
64 aMetrics
->spaceWidth
= spaceWidth
;
65 aMetrics
->maxAdvance
= spaceWidth
;
66 aMetrics
->aveCharWidth
= spaceWidth
;
67 aMetrics
->zeroOrAveCharWidth
= spaceWidth
;
68 const gfxFloat xHeight
= 0.5 * emHeight
;
69 aMetrics
->xHeight
= xHeight
;
70 const gfxFloat underlineSize
= emHeight
/ 14.0;
71 aMetrics
->underlineSize
= underlineSize
;
72 aMetrics
->underlineOffset
= -underlineSize
;
73 aMetrics
->strikeoutOffset
= 0.25 * emHeight
;
74 aMetrics
->strikeoutSize
= underlineSize
;
80 const FT_Size_Metrics
& ftMetrics
= mFace
->size
->metrics
;
83 // Scale for vertical design metric conversion: pixels per design unit.
84 // If this remains at 0.0, we can't use metrics from OS/2 etc.
85 gfxFloat yScale
= 0.0;
86 if (FT_IS_SCALABLE(mFace
)) {
87 // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
88 // have subpixel accuracy.
90 // FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
91 // (fractional) value is a factor that converts vertical metrics from
92 // design units to units of 1/64 pixels, so that the result may be
93 // interpreted as pixels in 26.6 fixed point format.
94 yScale
= FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics
.y_scale
));
95 emHeight
= mFace
->units_per_EM
* yScale
;
96 } else { // Not scalable.
97 emHeight
= ftMetrics
.y_ppem
;
98 // FT_Face doc says units_per_EM and a bunch of following fields
99 // are "only relevant to scalable outlines". If it's an sfnt,
100 // we can get units_per_EM from the 'head' table instead; otherwise,
101 // we don't have a unitsPerEm value so we can't compute/use yScale.
102 const TT_Header
* head
=
103 static_cast<TT_Header
*>(FT_Get_Sfnt_Table(mFace
, ft_sfnt_head
));
105 gfxFloat emUnit
= head
->Units_Per_EM
;
106 yScale
= emHeight
/ emUnit
;
111 static_cast<TT_OS2
*>(FT_Get_Sfnt_Table(mFace
, ft_sfnt_os2
));
113 aMetrics
->maxAscent
= FLOAT_FROM_26_6(ftMetrics
.ascender
);
114 aMetrics
->maxDescent
= -FLOAT_FROM_26_6(ftMetrics
.descender
);
115 aMetrics
->maxAdvance
= FLOAT_FROM_26_6(ftMetrics
.max_advance
);
118 if (os2
&& os2
->sTypoAscender
&& yScale
> 0.0) {
119 aMetrics
->emAscent
= os2
->sTypoAscender
* yScale
;
120 aMetrics
->emDescent
= -os2
->sTypoDescender
* yScale
;
121 FT_Short typoHeight
=
122 os2
->sTypoAscender
- os2
->sTypoDescender
+ os2
->sTypoLineGap
;
123 lineHeight
= typoHeight
* yScale
;
125 // If the OS/2 fsSelection USE_TYPO_METRICS bit is set,
126 // or if this is an OpenType Math font,
127 // set maxAscent/Descent from the sTypo* fields instead of hhea.
128 const uint16_t kUseTypoMetricsMask
= 1 << 7;
130 if ((os2
->fsSelection
& kUseTypoMetricsMask
) ||
131 0 == FT_Load_Sfnt_Table(mFace
, FT_MAKE_TAG('M','A','T','H'),
132 0, nullptr, &length
)) {
133 aMetrics
->maxAscent
= NS_round(aMetrics
->emAscent
);
134 aMetrics
->maxDescent
= NS_round(aMetrics
->emDescent
);
136 // maxAscent/maxDescent get used for frame heights, and some fonts
137 // don't have the HHEA table ascent/descent set (bug 279032).
138 // We use NS_round here to parallel the pixel-rounded values that
139 // freetype gives us for ftMetrics.ascender/descender.
140 aMetrics
->maxAscent
=
141 std::max(aMetrics
->maxAscent
, NS_round(aMetrics
->emAscent
));
142 aMetrics
->maxDescent
=
143 std::max(aMetrics
->maxDescent
, NS_round(aMetrics
->emDescent
));
146 aMetrics
->emAscent
= aMetrics
->maxAscent
;
147 aMetrics
->emDescent
= aMetrics
->maxDescent
;
148 lineHeight
= FLOAT_FROM_26_6(ftMetrics
.height
);
151 cairo_text_extents_t extents
;
152 *aSpaceGlyph
= GetCharExtents(' ', &extents
);
154 aMetrics
->spaceWidth
= extents
.x_advance
;
156 aMetrics
->spaceWidth
= aMetrics
->maxAdvance
; // guess
159 aMetrics
->zeroOrAveCharWidth
= 0.0;
160 if (GetCharExtents('0', &extents
)) {
161 aMetrics
->zeroOrAveCharWidth
= extents
.x_advance
;
164 // Prefering a measured x over sxHeight because sxHeight doesn't consider
165 // hinting, but maybe the x extents are not quite right in some fancy
166 // script fonts. CSS 2.1 suggests possibly using the height of an "o",
167 // which would have a more consistent glyph across fonts.
168 if (GetCharExtents('x', &extents
) && extents
.y_bearing
< 0.0) {
169 aMetrics
->xHeight
= -extents
.y_bearing
;
170 aMetrics
->aveCharWidth
= extents
.x_advance
;
172 if (os2
&& os2
->sxHeight
&& yScale
> 0.0) {
173 aMetrics
->xHeight
= os2
->sxHeight
* yScale
;
175 // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
176 // impossible or impractical to determine the x-height, a value of
177 // 0.5em should be used."
178 aMetrics
->xHeight
= 0.5 * emHeight
;
180 aMetrics
->aveCharWidth
= 0.0; // updated below
182 // aveCharWidth is used for the width of text input elements so be
183 // liberal rather than conservative in the estimate.
184 if (os2
&& os2
->xAvgCharWidth
) {
185 // Round to pixels as this is compared with maxAdvance to guess
186 // whether this is a fixed width font.
187 gfxFloat avgCharWidth
=
188 ScaleRoundDesignUnits(os2
->xAvgCharWidth
, ftMetrics
.x_scale
);
189 aMetrics
->aveCharWidth
=
190 std::max(aMetrics
->aveCharWidth
, avgCharWidth
);
192 aMetrics
->aveCharWidth
=
193 std::max(aMetrics
->aveCharWidth
, aMetrics
->zeroOrAveCharWidth
);
194 if (aMetrics
->aveCharWidth
== 0.0) {
195 aMetrics
->aveCharWidth
= aMetrics
->spaceWidth
;
197 if (aMetrics
->zeroOrAveCharWidth
== 0.0) {
198 aMetrics
->zeroOrAveCharWidth
= aMetrics
->aveCharWidth
;
200 // Apparently hinting can mean that max_advance is not always accurate.
201 aMetrics
->maxAdvance
=
202 std::max(aMetrics
->maxAdvance
, aMetrics
->aveCharWidth
);
204 // gfxFont::Metrics::underlineOffset is the position of the top of the
207 // FT_FaceRec documentation describes underline_position as "the
208 // center of the underlining stem". This was the original definition
209 // of the PostScript metric, but in the PostScript table of OpenType
210 // fonts the metric is "the top of the underline"
211 // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
212 // (up to version 2.3.7) doesn't make any adjustment.
214 // Therefore get the underline position directly from the table
215 // ourselves when this table exists. Use FreeType's metrics for
216 // other (including older PostScript) fonts.
217 if (mFace
->underline_position
&& mFace
->underline_thickness
&& yScale
> 0.0) {
218 aMetrics
->underlineSize
= mFace
->underline_thickness
* yScale
;
219 TT_Postscript
*post
= static_cast<TT_Postscript
*>
220 (FT_Get_Sfnt_Table(mFace
, ft_sfnt_post
));
221 if (post
&& post
->underlinePosition
) {
222 aMetrics
->underlineOffset
= post
->underlinePosition
* yScale
;
224 aMetrics
->underlineOffset
= mFace
->underline_position
* yScale
225 + 0.5 * aMetrics
->underlineSize
;
227 } else { // No underline info.
229 aMetrics
->underlineSize
= emHeight
/ 14.0;
230 aMetrics
->underlineOffset
= -aMetrics
->underlineSize
;
233 if (os2
&& os2
->yStrikeoutSize
&& os2
->yStrikeoutPosition
&& yScale
> 0.0) {
234 aMetrics
->strikeoutSize
= os2
->yStrikeoutSize
* yScale
;
235 aMetrics
->strikeoutOffset
= os2
->yStrikeoutPosition
* yScale
;
236 } else { // No strikeout info.
237 aMetrics
->strikeoutSize
= aMetrics
->underlineSize
;
238 // Use OpenType spec's suggested position for Roman font.
239 aMetrics
->strikeoutOffset
= emHeight
* 409.0 / 2048.0
240 + 0.5 * aMetrics
->strikeoutSize
;
242 SnapLineToPixels(aMetrics
->strikeoutOffset
, aMetrics
->strikeoutSize
);
244 aMetrics
->maxHeight
= aMetrics
->maxAscent
+ aMetrics
->maxDescent
;
246 // Make the line height an integer number of pixels so that lines will be
247 // equally spaced (rather than just being snapped to pixels, some up and
248 // some down). Layout calculates line height from the emHeight +
249 // internalLeading + externalLeading, but first each of these is rounded
250 // to layout units. To ensure that the result is an integer number of
251 // pixels, round each of the components to pixels.
252 aMetrics
->emHeight
= floor(emHeight
+ 0.5);
254 // maxHeight will normally be an integer, but round anyway in case
255 // FreeType is configured differently.
256 aMetrics
->internalLeading
=
257 floor(aMetrics
->maxHeight
- aMetrics
->emHeight
+ 0.5);
259 // Text input boxes currently don't work well with lineHeight
260 // significantly less than maxHeight (with Verdana, for example).
261 lineHeight
= floor(std::max(lineHeight
, aMetrics
->maxHeight
) + 0.5);
262 aMetrics
->externalLeading
=
263 lineHeight
- aMetrics
->internalLeading
- aMetrics
->emHeight
;
265 // Ensure emAscent + emDescent == emHeight
266 gfxFloat sum
= aMetrics
->emAscent
+ aMetrics
->emDescent
;
267 aMetrics
->emAscent
= sum
> 0.0 ?
268 aMetrics
->emAscent
* aMetrics
->emHeight
/ sum
: 0.0;
269 aMetrics
->emDescent
= aMetrics
->emHeight
- aMetrics
->emAscent
;
273 gfxFT2LockedFace::GetGlyph(uint32_t aCharCode
)
275 if (MOZ_UNLIKELY(!mFace
))
278 #ifdef HAVE_FONTCONFIG_FCFREETYPE_H
279 // FcFreeTypeCharIndex will search starting from the most recently
280 // selected charmap. This can cause non-determistic behavior when more
281 // than one charmap supports a character but with different glyphs, as
282 // with older versions of MS Gothic, for example. Always prefer a Unicode
283 // charmap, if there is one. (FcFreeTypeCharIndex usually does the
284 // appropriate Unicode conversion, but some fonts have non-Roman glyphs
285 // for FT_ENCODING_APPLE_ROMAN characters.)
286 if (!mFace
->charmap
|| mFace
->charmap
->encoding
!= FT_ENCODING_UNICODE
) {
287 FT_Select_Charmap(mFace
, FT_ENCODING_UNICODE
);
290 return FcFreeTypeCharIndex(mFace
, aCharCode
);
292 return FT_Get_Char_Index(mFace
, aCharCode
);
296 typedef FT_UInt (*GetCharVariantFunction
)(FT_Face face
,
298 FT_ULong variantSelector
);
301 gfxFT2LockedFace::GetUVSGlyph(uint32_t aCharCode
, uint32_t aVariantSelector
)
303 NS_PRECONDITION(aVariantSelector
, "aVariantSelector should not be NULL");
305 if (MOZ_UNLIKELY(!mFace
))
308 // This function is available from FreeType 2.3.6 (June 2008).
309 static CharVariantFunction sGetCharVariantPtr
= FindCharVariantFunction();
310 if (!sGetCharVariantPtr
)
313 #ifdef HAVE_FONTCONFIG_FCFREETYPE_H
314 // FcFreeTypeCharIndex may have changed the selected charmap.
315 // FT_Face_GetCharVariantIndex needs a unicode charmap.
316 if (!mFace
->charmap
|| mFace
->charmap
->encoding
!= FT_ENCODING_UNICODE
) {
317 FT_Select_Charmap(mFace
, FT_ENCODING_UNICODE
);
321 return (*sGetCharVariantPtr
)(mFace
, aCharCode
, aVariantSelector
);
325 gfxFT2LockedFace::GetCharExtents(char aChar
, cairo_text_extents_t
* aExtents
)
327 NS_PRECONDITION(aExtents
!= nullptr, "aExtents must not be NULL");
332 FT_UInt gid
= mGfxFont
->GetGlyph(aChar
);
334 mGfxFont
->GetGlyphExtents(gid
, aExtents
);
340 gfxFT2LockedFace::CharVariantFunction
341 gfxFT2LockedFace::FindCharVariantFunction()
343 // This function is available from FreeType 2.3.6 (June 2008).
344 PRLibrary
*lib
= nullptr;
345 CharVariantFunction function
=
346 reinterpret_cast<CharVariantFunction
>
347 (PR_FindFunctionSymbolAndLibrary("FT_Face_GetCharVariantIndex", &lib
));
355 FT_Library_Version(mFace
->glyph
->library
, &major
, &minor
, &patch
);
357 // Versions 2.4.0 to 2.4.3 crash if configured with
358 // FT_CONFIG_OPTION_OLD_INTERNALS. Presence of the symbol FT_Alloc
359 // indicates FT_CONFIG_OPTION_OLD_INTERNALS.
360 if (major
== 2 && minor
== 4 && patch
< 4 &&
361 PR_FindFunctionSymbol(lib
, "FT_Alloc")) {
365 // Decrement the reference count incremented in
366 // PR_FindFunctionSymbolAndLibrary.
367 PR_UnloadLibrary(lib
);