LCOV - code coverage report
Current view: top level - gfx/thebes - gfxFT2Utils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 88 167 52.7 %
Date: 2017-07-14 16:53:18 Functions: 5 7 71.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13