1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Foundation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Vladimir Vukicevic <vladimir@mozilla.com>
23 : * Masayuki Nakano <masayuki@d-toybox.com>
24 : * Behdad Esfahbod <behdad@gnome.org>
25 : * Mats Palmgren <mats.palmgren@bredband.net>
26 : * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
27 : * Takuro Ashie <ashie@clear-code.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either the GNU General Public License Version 2 or later (the "GPL"), or
31 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "gfxFT2FontBase.h"
44 : #include "gfxFT2Utils.h"
45 : #include FT_TRUETYPE_TAGS_H
46 : #include FT_TRUETYPE_TABLES_H
47 :
48 : #ifdef HAVE_FONTCONFIG_FCFREETYPE_H
49 : #include <fontconfig/fcfreetype.h>
50 : #endif
51 :
52 : #include "prlink.h"
53 :
54 : // aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
55 : static inline FT_Long
56 0 : ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
57 : {
58 0 : FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
59 0 : return ROUND_26_6_TO_INT(fixed26dot6);
60 : }
61 :
62 : // Snap a line to pixels while keeping the center and size of the line as
63 : // close to the original position as possible.
64 : //
65 : // Pango does similar snapping for underline and strikethrough when fonts are
66 : // hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
67 : // top and size of lines. Optimizing the distance between the line and
68 : // baseline is probably good for the gap between text and underline, but
69 : // optimizing the center of the line is better for positioning strikethough.
70 : static void
71 0 : SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
72 : {
73 0 : gfxFloat snappedSize = NS_MAX(floor(aSize + 0.5), 1.0);
74 : // Correct offset for change in size
75 0 : gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
76 : // Snap offset
77 0 : aOffset = floor(offset + 0.5);
78 0 : aSize = snappedSize;
79 0 : }
80 :
81 : void
82 0 : gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
83 : PRUint32* aSpaceGlyph)
84 : {
85 0 : NS_PRECONDITION(aMetrics != NULL, "aMetrics must not be NULL");
86 0 : NS_PRECONDITION(aSpaceGlyph != NULL, "aSpaceGlyph must not be NULL");
87 :
88 0 : if (NS_UNLIKELY(!mFace)) {
89 : // No face. This unfortunate situation might happen if the font
90 : // file is (re)moved at the wrong time.
91 0 : aMetrics->emHeight = mGfxFont->GetStyle()->size;
92 0 : aMetrics->emAscent = 0.8 * aMetrics->emHeight;
93 0 : aMetrics->emDescent = 0.2 * aMetrics->emHeight;
94 0 : aMetrics->maxAscent = aMetrics->emAscent;
95 0 : aMetrics->maxDescent = aMetrics->maxDescent;
96 0 : aMetrics->maxHeight = aMetrics->emHeight;
97 0 : aMetrics->internalLeading = 0.0;
98 0 : aMetrics->externalLeading = 0.2 * aMetrics->emHeight;
99 0 : aSpaceGlyph = 0;
100 0 : aMetrics->spaceWidth = 0.5 * aMetrics->emHeight;
101 0 : aMetrics->maxAdvance = aMetrics->spaceWidth;
102 0 : aMetrics->aveCharWidth = aMetrics->spaceWidth;
103 0 : aMetrics->zeroOrAveCharWidth = aMetrics->spaceWidth;
104 0 : aMetrics->xHeight = 0.5 * aMetrics->emHeight;
105 0 : aMetrics->underlineSize = aMetrics->emHeight / 14.0;
106 0 : aMetrics->underlineOffset = -aMetrics->underlineSize;
107 0 : aMetrics->strikeoutOffset = 0.25 * aMetrics->emHeight;
108 0 : aMetrics->strikeoutSize = aMetrics->underlineSize;
109 0 : aMetrics->superscriptOffset = aMetrics->xHeight;
110 0 : aMetrics->subscriptOffset = aMetrics->xHeight;
111 :
112 0 : return;
113 : }
114 :
115 0 : const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
116 :
117 : gfxFloat emHeight;
118 : // Scale for vertical design metric conversion: pixels per design unit.
119 : gfxFloat yScale;
120 0 : if (FT_IS_SCALABLE(mFace)) {
121 : // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
122 : // have subpixel accuracy.
123 : //
124 : // FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
125 : // (fractional) value is a factor that converts vertical metrics from
126 : // design units to units of 1/64 pixels, so that the result may be
127 : // interpreted as pixels in 26.6 fixed point format.
128 0 : yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
129 0 : emHeight = mFace->units_per_EM * yScale;
130 : } else { // Not scalable.
131 : // FT_Size_Metrics doc says x_scale is "only relevant for scalable
132 : // font formats".
133 0 : gfxFloat emUnit = mFace->units_per_EM;
134 0 : emHeight = ftMetrics.y_ppem;
135 0 : yScale = emHeight / emUnit;
136 : }
137 :
138 : TT_OS2 *os2 =
139 0 : static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
140 :
141 0 : aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
142 0 : aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
143 0 : aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
144 :
145 : gfxFloat lineHeight;
146 0 : if (os2 && os2->sTypoAscender) {
147 0 : aMetrics->emAscent = os2->sTypoAscender * yScale;
148 0 : aMetrics->emDescent = -os2->sTypoDescender * yScale;
149 : FT_Short typoHeight =
150 0 : os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
151 0 : lineHeight = typoHeight * yScale;
152 :
153 : // maxAscent/maxDescent get used for frame heights, and some fonts
154 : // don't have the HHEA table ascent/descent set (bug 279032).
155 0 : if (aMetrics->emAscent > aMetrics->maxAscent)
156 0 : aMetrics->maxAscent = aMetrics->emAscent;
157 0 : if (aMetrics->emDescent > aMetrics->maxDescent)
158 0 : aMetrics->maxDescent = aMetrics->emDescent;
159 : } else {
160 0 : aMetrics->emAscent = aMetrics->maxAscent;
161 0 : aMetrics->emDescent = aMetrics->maxDescent;
162 0 : lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
163 : }
164 :
165 : cairo_text_extents_t extents;
166 0 : *aSpaceGlyph = GetCharExtents(' ', &extents);
167 0 : if (*aSpaceGlyph) {
168 0 : aMetrics->spaceWidth = extents.x_advance;
169 : } else {
170 0 : aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
171 : }
172 :
173 0 : aMetrics->zeroOrAveCharWidth = 0.0;
174 0 : if (GetCharExtents('0', &extents)) {
175 0 : aMetrics->zeroOrAveCharWidth = extents.x_advance;
176 : }
177 :
178 : // Prefering a measured x over sxHeight because sxHeight doesn't consider
179 : // hinting, but maybe the x extents are not quite right in some fancy
180 : // script fonts. CSS 2.1 suggests possibly using the height of an "o",
181 : // which would have a more consistent glyph across fonts.
182 0 : if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
183 0 : aMetrics->xHeight = -extents.y_bearing;
184 0 : aMetrics->aveCharWidth = extents.x_advance;
185 : } else {
186 0 : if (os2 && os2->sxHeight) {
187 0 : aMetrics->xHeight = os2->sxHeight * yScale;
188 : } else {
189 : // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
190 : // impossible or impractical to determine the x-height, a value of
191 : // 0.5em should be used."
192 0 : aMetrics->xHeight = 0.5 * emHeight;
193 : }
194 0 : aMetrics->aveCharWidth = 0.0; // updated below
195 : }
196 : // aveCharWidth is used for the width of text input elements so be
197 : // liberal rather than conservative in the estimate.
198 0 : if (os2 && os2->xAvgCharWidth) {
199 : // Round to pixels as this is compared with maxAdvance to guess
200 : // whether this is a fixed width font.
201 : gfxFloat avgCharWidth =
202 0 : ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
203 : aMetrics->aveCharWidth =
204 0 : NS_MAX(aMetrics->aveCharWidth, avgCharWidth);
205 : }
206 : aMetrics->aveCharWidth =
207 0 : NS_MAX(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
208 0 : if (aMetrics->aveCharWidth == 0.0) {
209 0 : aMetrics->aveCharWidth = aMetrics->spaceWidth;
210 : }
211 0 : if (aMetrics->zeroOrAveCharWidth == 0.0) {
212 0 : aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
213 : }
214 : // Apparently hinting can mean that max_advance is not always accurate.
215 : aMetrics->maxAdvance =
216 0 : NS_MAX(aMetrics->maxAdvance, aMetrics->aveCharWidth);
217 :
218 : // gfxFont::Metrics::underlineOffset is the position of the top of the
219 : // underline.
220 : //
221 : // FT_FaceRec documentation describes underline_position as "the
222 : // center of the underlining stem". This was the original definition
223 : // of the PostScript metric, but in the PostScript table of OpenType
224 : // fonts the metric is "the top of the underline"
225 : // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
226 : // (up to version 2.3.7) doesn't make any adjustment.
227 : //
228 : // Therefore get the underline position directly from the table
229 : // ourselves when this table exists. Use FreeType's metrics for
230 : // other (including older PostScript) fonts.
231 0 : if (mFace->underline_position && mFace->underline_thickness) {
232 0 : aMetrics->underlineSize = mFace->underline_thickness * yScale;
233 : TT_Postscript *post = static_cast<TT_Postscript*>
234 0 : (FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
235 0 : if (post && post->underlinePosition) {
236 0 : aMetrics->underlineOffset = post->underlinePosition * yScale;
237 : } else {
238 : aMetrics->underlineOffset = mFace->underline_position * yScale
239 0 : + 0.5 * aMetrics->underlineSize;
240 0 : }
241 : } else { // No underline info.
242 : // Imitate Pango.
243 0 : aMetrics->underlineSize = emHeight / 14.0;
244 0 : aMetrics->underlineOffset = -aMetrics->underlineSize;
245 : }
246 :
247 0 : if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition) {
248 0 : aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
249 0 : aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
250 : } else { // No strikeout info.
251 0 : aMetrics->strikeoutSize = aMetrics->underlineSize;
252 : // Use OpenType spec's suggested position for Roman font.
253 : aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
254 0 : + 0.5 * aMetrics->strikeoutSize;
255 : }
256 0 : SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
257 :
258 0 : if (os2 && os2->ySuperscriptYOffset) {
259 : gfxFloat val = ScaleRoundDesignUnits(os2->ySuperscriptYOffset,
260 0 : ftMetrics.y_scale);
261 0 : aMetrics->superscriptOffset = NS_MAX(1.0, val);
262 : } else {
263 0 : aMetrics->superscriptOffset = aMetrics->xHeight;
264 : }
265 :
266 0 : if (os2 && os2->ySubscriptYOffset) {
267 : gfxFloat val = ScaleRoundDesignUnits(os2->ySubscriptYOffset,
268 0 : ftMetrics.y_scale);
269 : // some fonts have the incorrect sign.
270 0 : val = fabs(val);
271 0 : aMetrics->subscriptOffset = NS_MAX(1.0, val);
272 : } else {
273 0 : aMetrics->subscriptOffset = aMetrics->xHeight;
274 : }
275 :
276 0 : aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
277 :
278 : // Make the line height an integer number of pixels so that lines will be
279 : // equally spaced (rather than just being snapped to pixels, some up and
280 : // some down). Layout calculates line height from the emHeight +
281 : // internalLeading + externalLeading, but first each of these is rounded
282 : // to layout units. To ensure that the result is an integer number of
283 : // pixels, round each of the components to pixels.
284 0 : aMetrics->emHeight = floor(emHeight + 0.5);
285 :
286 : // maxHeight will normally be an integer, but round anyway in case
287 : // FreeType is configured differently.
288 : aMetrics->internalLeading =
289 0 : floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
290 :
291 : // Text input boxes currently don't work well with lineHeight
292 : // significantly less than maxHeight (with Verdana, for example).
293 0 : lineHeight = floor(NS_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
294 : aMetrics->externalLeading =
295 0 : lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
296 :
297 : // Ensure emAscent + emDescent == emHeight
298 0 : gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
299 : aMetrics->emAscent = sum > 0.0 ?
300 0 : aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
301 0 : aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
302 : }
303 :
304 : PRUint32
305 0 : gfxFT2LockedFace::GetGlyph(PRUint32 aCharCode)
306 : {
307 0 : if (NS_UNLIKELY(!mFace))
308 0 : return 0;
309 :
310 : #ifdef HAVE_FONTCONFIG_FCFREETYPE_H
311 : // FcFreeTypeCharIndex will search starting from the most recently
312 : // selected charmap. This can cause non-determistic behavior when more
313 : // than one charmap supports a character but with different glyphs, as
314 : // with older versions of MS Gothic, for example. Always prefer a Unicode
315 : // charmap, if there is one. (FcFreeTypeCharIndex usually does the
316 : // appropriate Unicode conversion, but some fonts have non-Roman glyphs
317 : // for FT_ENCODING_APPLE_ROMAN characters.)
318 0 : if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
319 0 : FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
320 : }
321 :
322 0 : return FcFreeTypeCharIndex(mFace, aCharCode);
323 : #else
324 : return FT_Get_Char_Index(mFace, aCharCode);
325 : #endif
326 : }
327 :
328 : typedef FT_UInt (*GetCharVariantFunction)(FT_Face face,
329 : FT_ULong charcode,
330 : FT_ULong variantSelector);
331 :
332 : PRUint32
333 0 : gfxFT2LockedFace::GetUVSGlyph(PRUint32 aCharCode, PRUint32 aVariantSelector)
334 : {
335 0 : NS_PRECONDITION(aVariantSelector, "aVariantSelector should not be NULL");
336 :
337 0 : if (NS_UNLIKELY(!mFace))
338 0 : return 0;
339 :
340 : // This function is available from FreeType 2.3.6 (June 2008).
341 0 : static CharVariantFunction sGetCharVariantPtr = FindCharVariantFunction();
342 0 : if (!sGetCharVariantPtr)
343 0 : return 0;
344 :
345 : #ifdef HAVE_FONTCONFIG_FCFREETYPE_H
346 : // FcFreeTypeCharIndex may have changed the selected charmap.
347 : // FT_Face_GetCharVariantIndex needs a unicode charmap.
348 0 : if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
349 0 : FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
350 : }
351 : #endif
352 :
353 0 : return (*sGetCharVariantPtr)(mFace, aCharCode, aVariantSelector);
354 : }
355 :
356 : bool
357 0 : gfxFT2LockedFace::GetFontTable(PRUint32 aTag, FallibleTArray<PRUint8>& aBuffer)
358 : {
359 0 : if (!mFace || !FT_IS_SFNT(mFace))
360 0 : return false;
361 :
362 0 : FT_ULong length = 0;
363 : // TRUETYPE_TAG is defined equivalent to FT_MAKE_TAG
364 0 : FT_Error error = FT_Load_Sfnt_Table(mFace, aTag, 0, NULL, &length);
365 0 : if (error != 0)
366 0 : return false;
367 :
368 0 : if (NS_UNLIKELY(length > static_cast<FallibleTArray<PRUint8>::size_type>(-1))
369 0 : || NS_UNLIKELY(!aBuffer.SetLength(length)))
370 0 : return false;
371 :
372 0 : error = FT_Load_Sfnt_Table(mFace, aTag, 0, aBuffer.Elements(), &length);
373 0 : if (NS_UNLIKELY(error != 0)) {
374 0 : aBuffer.Clear();
375 0 : return false;
376 : }
377 :
378 0 : return true;
379 : }
380 :
381 : PRUint32
382 0 : gfxFT2LockedFace::GetCharExtents(char aChar, cairo_text_extents_t* aExtents)
383 : {
384 0 : NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
385 :
386 0 : if (!mFace)
387 0 : return 0;
388 :
389 0 : FT_UInt gid = mGfxFont->GetGlyph(aChar);
390 0 : if (gid) {
391 0 : mGfxFont->GetGlyphExtents(gid, aExtents);
392 : }
393 :
394 0 : return gid;
395 : }
396 :
397 : gfxFT2LockedFace::CharVariantFunction
398 0 : gfxFT2LockedFace::FindCharVariantFunction()
399 : {
400 : // This function is available from FreeType 2.3.6 (June 2008).
401 0 : PRLibrary *lib = nsnull;
402 : CharVariantFunction function =
403 : reinterpret_cast<CharVariantFunction>
404 0 : (PR_FindFunctionSymbolAndLibrary("FT_Face_GetCharVariantIndex", &lib));
405 0 : if (!lib) {
406 0 : return nsnull;
407 : }
408 :
409 : FT_Int major;
410 : FT_Int minor;
411 : FT_Int patch;
412 0 : FT_Library_Version(mFace->glyph->library, &major, &minor, &patch);
413 :
414 : // Versions 2.4.0 to 2.4.3 crash if configured with
415 : // FT_CONFIG_OPTION_OLD_INTERNALS. Presence of the symbol FT_Alloc
416 : // indicates FT_CONFIG_OPTION_OLD_INTERNALS.
417 0 : if (major == 2 && minor == 4 && patch < 4 &&
418 0 : PR_FindFunctionSymbol(lib, "FT_Alloc")) {
419 0 : function = nsnull;
420 : }
421 :
422 : // Decrement the reference count incremented in
423 : // PR_FindFunctionSymbolAndLibrary.
424 0 : PR_UnloadLibrary(lib);
425 :
426 0 : return function;
427 : }
|