gfx/thebes/gfxFT2Utils.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial