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