LCOV - code coverage report
Current view: directory - gfx/thebes - gfxHarfBuzzShaper.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 511 0 0.0 %
Date: 2012-06-02 Functions: 25 0 0.0 %

       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 Corporation code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2009-2010
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Jonathan Kew <jfkthame@gmail.com>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "prtypes.h"
      40                 : #include "nsAlgorithm.h"
      41                 : #include "prmem.h"
      42                 : #include "nsString.h"
      43                 : #include "nsBidiUtils.h"
      44                 : #include "nsMathUtils.h"
      45                 : 
      46                 : #include "gfxTypes.h"
      47                 : 
      48                 : #include "gfxContext.h"
      49                 : #include "gfxPlatform.h"
      50                 : #include "gfxHarfBuzzShaper.h"
      51                 : #include "gfxFontUtils.h"
      52                 : #include "nsUnicodeProperties.h"
      53                 : #include "nsUnicodeScriptCodes.h"
      54                 : #include "nsUnicodeNormalizer.h"
      55                 : 
      56                 : #include "harfbuzz/hb-unicode.h"
      57                 : #include "harfbuzz/hb-ot.h"
      58                 : 
      59                 : #include "cairo.h"
      60                 : 
      61                 : #include "nsCRT.h"
      62                 : 
      63                 : #if defined(XP_WIN)
      64                 : #include "gfxWindowsPlatform.h"
      65                 : #endif
      66                 : 
      67                 : #define FloatToFixed(f) (65536 * (f))
      68                 : #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
      69                 : // Right shifts of negative (signed) integers are undefined, as are overflows
      70                 : // when converting unsigned to negative signed integers.
      71                 : // (If speed were an issue we could make some 2's complement assumptions.)
      72                 : #define FixedToIntRound(f) ((f) > 0 ?  ((32768 + (f)) >> 16) \
      73                 :                                     : -((32767 - (f)) >> 16))
      74                 : 
      75                 : using namespace mozilla; // for AutoSwap_* types
      76                 : using namespace mozilla::unicode; // for Unicode property lookup
      77                 : 
      78                 : /*
      79                 :  * Creation and destruction; on deletion, release any font tables we're holding
      80                 :  */
      81                 : 
      82               0 : gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
      83                 :     : gfxFontShaper(aFont),
      84                 :       mHBFace(nsnull),
      85                 :       mKernTable(nsnull),
      86                 :       mHmtxTable(nsnull),
      87                 :       mNumLongMetrics(0),
      88                 :       mCmapTable(nsnull),
      89                 :       mCmapFormat(-1),
      90                 :       mSubtableOffset(0),
      91                 :       mUVSTableOffset(0),
      92               0 :       mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
      93               0 :       mUseFontGlyphWidths(false)
      94                 : {
      95               0 : }
      96                 : 
      97               0 : gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
      98                 : {
      99               0 :     hb_blob_destroy(mCmapTable);
     100               0 :     hb_blob_destroy(mHmtxTable);
     101               0 :     hb_blob_destroy(mKernTable);
     102               0 :     hb_face_destroy(mHBFace);
     103               0 : }
     104                 : 
     105                 : /*
     106                 :  * HarfBuzz callback access to font table data
     107                 :  */
     108                 : 
     109                 : // callback for HarfBuzz to get a font table (in hb_blob_t form)
     110                 : // from the shaper (passed as aUserData)
     111                 : static hb_blob_t *
     112               0 : HBGetTable(hb_face_t *face, hb_tag_t aTag, void *aUserData)
     113                 : {
     114               0 :     gfxHarfBuzzShaper *shaper = static_cast<gfxHarfBuzzShaper*>(aUserData);
     115               0 :     gfxFont *font = shaper->GetFont();
     116                 : 
     117                 :     // bug 589682 - ignore the GDEF table in buggy fonts (applies to
     118                 :     // Italic and BoldItalic faces of Times New Roman)
     119               0 :     if (aTag == TRUETYPE_TAG('G','D','E','F') &&
     120               0 :         font->GetFontEntry()->IgnoreGDEF()) {
     121               0 :         return nsnull;
     122                 :     }
     123                 : 
     124                 :     // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
     125                 :     // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
     126               0 :     if (aTag == TRUETYPE_TAG('G','S','U','B') &&
     127               0 :         font->GetFontEntry()->IgnoreGSUB()) {
     128               0 :         return nsnull;
     129                 :     }
     130                 : 
     131               0 :     return font->GetFontTable(aTag);
     132                 : }
     133                 : 
     134                 : /*
     135                 :  * HarfBuzz font callback functions; font_data is a ptr to a
     136                 :  * FontCallbackData struct
     137                 :  */
     138                 : 
     139                 : struct FontCallbackData {
     140               0 :     FontCallbackData(gfxHarfBuzzShaper *aShaper, gfxContext *aContext)
     141               0 :         : mShaper(aShaper), mContext(aContext)
     142               0 :     { }
     143                 : 
     144                 :     gfxHarfBuzzShaper *mShaper;
     145                 :     gfxContext        *mContext;
     146                 : };
     147                 : 
     148                 : #define UNICODE_BMP_LIMIT 0x10000
     149                 : 
     150                 : hb_codepoint_t
     151               0 : gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode,
     152                 :                             hb_codepoint_t variation_selector) const
     153                 : {
     154               0 :     if (mUseFontGetGlyph) {
     155               0 :         return mFont->GetGlyph(unicode, variation_selector);
     156                 :     }
     157                 : 
     158                 :     // we only instantiate a harfbuzz shaper if there's a cmap available
     159               0 :     NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
     160                 :                  "we cannot be using this font!");
     161                 : 
     162               0 :     NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
     163                 :                  "cmap data not correctly set up, expect disaster");
     164                 : 
     165               0 :     const PRUint8* data = (const PRUint8*)hb_blob_get_data(mCmapTable, nsnull);
     166                 : 
     167                 :     hb_codepoint_t gid;
     168               0 :     switch (mCmapFormat) {
     169                 :     case 4:
     170                 :         gid = unicode < UNICODE_BMP_LIMIT ?
     171               0 :             gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset, unicode) : 0;
     172               0 :         break;
     173                 :     case 12:
     174               0 :         gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset, unicode);
     175               0 :         break;
     176                 :     default:
     177               0 :         NS_WARNING("unsupported cmap format, glyphs will be missing");
     178               0 :         gid = 0;
     179               0 :         break;
     180                 :     }
     181                 : 
     182               0 :     if (gid && variation_selector && mUVSTableOffset) {
     183                 :         hb_codepoint_t varGID =
     184                 :             gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
     185               0 :                                                 unicode, variation_selector);
     186               0 :         if (varGID) {
     187               0 :             gid = varGID;
     188                 :         }
     189                 :         // else the variation sequence was not supported, use default mapping
     190                 :         // of the character code alone
     191                 :     }
     192                 : 
     193               0 :     return gid;
     194                 : }
     195                 : 
     196                 : static hb_bool_t
     197               0 : HBGetGlyph(hb_font_t *font, void *font_data,
     198                 :            hb_codepoint_t unicode, hb_codepoint_t variation_selector,
     199                 :            hb_codepoint_t *glyph,
     200                 :            void *user_data)
     201                 : {
     202                 :     const FontCallbackData *fcd =
     203               0 :         static_cast<const FontCallbackData*>(font_data);
     204               0 :     *glyph = fcd->mShaper->GetGlyph(unicode, variation_selector);
     205               0 :     return true;
     206                 : }
     207                 : 
     208                 : struct HMetricsHeader {
     209                 :     AutoSwap_PRUint32    tableVersionNumber;
     210                 :     AutoSwap_PRInt16     ascender;
     211                 :     AutoSwap_PRInt16     descender;
     212                 :     AutoSwap_PRInt16     lineGap;
     213                 :     AutoSwap_PRUint16    advanceWidthMax;
     214                 :     AutoSwap_PRInt16     minLeftSideBearing;
     215                 :     AutoSwap_PRInt16     minRightSideBearing;
     216                 :     AutoSwap_PRInt16     xMaxExtent;
     217                 :     AutoSwap_PRInt16     caretSlopeRise;
     218                 :     AutoSwap_PRInt16     caretSlopeRun;
     219                 :     AutoSwap_PRInt16     caretOffset;
     220                 :     AutoSwap_PRInt16     reserved[4];
     221                 :     AutoSwap_PRInt16     metricDataFormat;
     222                 :     AutoSwap_PRUint16    numberOfHMetrics;
     223                 : };
     224                 : 
     225                 : struct HLongMetric {
     226                 :     AutoSwap_PRUint16    advanceWidth;
     227                 :     AutoSwap_PRInt16     lsb;
     228                 : };
     229                 : 
     230                 : struct HMetrics {
     231                 :     HLongMetric          metrics[1]; // actually numberOfHMetrics
     232                 : // the variable-length metrics[] array is immediately followed by:
     233                 : //  AutoSwap_PRUint16    leftSideBearing[];
     234                 : };
     235                 : 
     236                 : hb_position_t
     237               0 : gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
     238                 :                                     hb_codepoint_t glyph) const
     239                 : {
     240               0 :     if (mUseFontGlyphWidths) {
     241               0 :         return mFont->GetGlyphWidth(aContext, glyph);
     242                 :     }
     243                 : 
     244                 :     // font did not implement GetHintedGlyphWidth, so get an unhinted value
     245                 :     // directly from the font tables
     246                 : 
     247               0 :     NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nsnull,
     248                 :                  "font is lacking metrics, we shouldn't be here");
     249                 : 
     250               0 :     if (glyph >= PRUint32(mNumLongMetrics)) {
     251               0 :         glyph = mNumLongMetrics - 1;
     252                 :     }
     253                 : 
     254                 :     // glyph must be valid now, because we checked during initialization
     255                 :     // that mNumLongMetrics is > 0, and that the hmtx table is large enough
     256                 :     // to contain mNumLongMetrics records
     257                 :     const HMetrics* hmtx =
     258               0 :         reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nsnull));
     259               0 :     return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
     260                 :                         PRUint16(hmtx->metrics[glyph].advanceWidth));
     261                 : }
     262                 : 
     263                 : static hb_position_t
     264               0 : HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
     265                 :                    hb_codepoint_t glyph, void *user_data)
     266                 : {
     267                 :     const FontCallbackData *fcd =
     268               0 :         static_cast<const FontCallbackData*>(font_data);
     269               0 :     return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
     270                 : }
     271                 : 
     272                 : static hb_bool_t
     273               0 : HBGetContourPoint(hb_font_t *font, void *font_data,
     274                 :                   unsigned int point_index, hb_codepoint_t glyph,
     275                 :                   hb_position_t *x, hb_position_t *y,
     276                 :                   void *user_data)
     277                 : {
     278                 :     /* not yet implemented - no support for used of hinted contour points
     279                 :        to fine-tune anchor positions in GPOS AnchorFormat2 */
     280               0 :     return false;
     281                 : }
     282                 : 
     283                 : struct KernHeaderFmt0 {
     284                 :     AutoSwap_PRUint16 nPairs;
     285                 :     AutoSwap_PRUint16 searchRange;
     286                 :     AutoSwap_PRUint16 entrySelector;
     287                 :     AutoSwap_PRUint16 rangeShift;
     288                 : };
     289                 : 
     290                 : struct KernPair {
     291                 :     AutoSwap_PRUint16 left;
     292                 :     AutoSwap_PRUint16 right;
     293                 :     AutoSwap_PRInt16  value;
     294                 : };
     295                 : 
     296                 : // Find a kern pair in a Format 0 subtable.
     297                 : // The aSubtable parameter points to the subtable itself, NOT its header,
     298                 : // as the header structure differs between Windows and Mac (v0 and v1.0)
     299                 : // versions of the 'kern' table.
     300                 : // aSubtableLen is the length of the subtable EXCLUDING its header.
     301                 : // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
     302                 : // added to aValue, so that multiple subtables can accumulate a total
     303                 : // kerning value for a given pair.
     304                 : static void
     305               0 : GetKernValueFmt0(const void* aSubtable,
     306                 :                  PRUint32 aSubtableLen,
     307                 :                  PRUint16 aFirstGlyph,
     308                 :                  PRUint16 aSecondGlyph,
     309                 :                  PRInt32& aValue,
     310                 :                  bool     aIsOverride = false,
     311                 :                  bool     aIsMinimum = false)
     312                 : {
     313                 :     const KernHeaderFmt0* hdr =
     314               0 :         reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
     315                 : 
     316               0 :     const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1);
     317               0 :     const KernPair *hi = lo + PRUint16(hdr->nPairs);
     318               0 :     const KernPair *limit = hi;
     319                 : 
     320               0 :     if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
     321                 :         reinterpret_cast<const char*>(hi)) {
     322                 :         // subtable is not large enough to contain the claimed number
     323                 :         // of kern pairs, so just ignore it
     324               0 :         return;
     325                 :     }
     326                 : 
     327                 : #define KERN_PAIR_KEY(l,r) (PRUint32((PRUint16(l) << 16) + PRUint16(r)))
     328                 : 
     329               0 :     PRUint32 key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
     330               0 :     while (lo < hi) {
     331               0 :         const KernPair *mid = lo + (hi - lo) / 2;
     332               0 :         if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
     333               0 :             lo = mid + 1;
     334                 :         } else {
     335               0 :             hi = mid;
     336                 :         }
     337                 :     }
     338                 : 
     339               0 :     if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
     340               0 :         if (aIsOverride) {
     341               0 :             aValue = PRInt16(lo->value);
     342               0 :         } else if (aIsMinimum) {
     343               0 :             aValue = NS_MAX(aValue, PRInt32(lo->value));
     344                 :         } else {
     345               0 :             aValue += PRInt16(lo->value);
     346                 :         }
     347                 :     }
     348                 : }
     349                 : 
     350                 : // Get kerning value from Apple (version 1.0) kern table,
     351                 : // subtable format 2 (simple N x M array of kerning values)
     352                 : 
     353                 : // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
     354                 : // for details of version 1.0 format 2 subtable.
     355                 : 
     356                 : struct KernHeaderVersion1Fmt2 {
     357                 :     KernTableSubtableHeaderVersion1 header;
     358                 :     AutoSwap_PRUint16 rowWidth;
     359                 :     AutoSwap_PRUint16 leftOffsetTable;
     360                 :     AutoSwap_PRUint16 rightOffsetTable;
     361                 :     AutoSwap_PRUint16 array;
     362                 : };
     363                 : 
     364                 : struct KernClassTableHdr {
     365                 :     AutoSwap_PRUint16 firstGlyph;
     366                 :     AutoSwap_PRUint16 nGlyphs;
     367                 :     AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries       
     368                 : };
     369                 : 
     370                 : static PRInt16
     371               0 : GetKernValueVersion1Fmt2(const void* aSubtable,
     372                 :                          PRUint32 aSubtableLen,
     373                 :                          PRUint16 aFirstGlyph,
     374                 :                          PRUint16 aSecondGlyph)
     375                 : {
     376               0 :     if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
     377               0 :         return 0;
     378                 :     }
     379                 : 
     380               0 :     const char* base = reinterpret_cast<const char*>(aSubtable);
     381               0 :     const char* subtableEnd = base + aSubtableLen;
     382                 : 
     383                 :     const KernHeaderVersion1Fmt2* h =
     384               0 :         reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
     385               0 :     PRUint32 offset = h->array;
     386                 : 
     387                 :     const KernClassTableHdr* leftClassTable =
     388                 :         reinterpret_cast<const KernClassTableHdr*>(base +
     389               0 :                                                    PRUint16(h->leftOffsetTable));
     390               0 :     if (reinterpret_cast<const char*>(leftClassTable) +
     391               0 :         sizeof(KernClassTableHdr) > subtableEnd) {
     392               0 :         return 0;
     393                 :     }
     394               0 :     if (aFirstGlyph >= PRUint16(leftClassTable->firstGlyph)) {
     395               0 :         aFirstGlyph -= PRUint16(leftClassTable->firstGlyph);
     396               0 :         if (aFirstGlyph < PRUint16(leftClassTable->nGlyphs)) {
     397               0 :             if (reinterpret_cast<const char*>(leftClassTable) +
     398                 :                 sizeof(KernClassTableHdr) +
     399               0 :                 aFirstGlyph * sizeof(PRUint16) >= subtableEnd) {
     400               0 :                 return 0;
     401                 :             }
     402               0 :             offset = PRUint16(leftClassTable->offsets[aFirstGlyph]);
     403                 :         }
     404                 :     }
     405                 : 
     406                 :     const KernClassTableHdr* rightClassTable =
     407                 :         reinterpret_cast<const KernClassTableHdr*>(base +
     408               0 :                                                    PRUint16(h->rightOffsetTable));
     409               0 :     if (reinterpret_cast<const char*>(rightClassTable) +
     410               0 :         sizeof(KernClassTableHdr) > subtableEnd) {
     411               0 :         return 0;
     412                 :     }
     413               0 :     if (aSecondGlyph >= PRUint16(rightClassTable->firstGlyph)) {
     414               0 :         aSecondGlyph -= PRUint16(rightClassTable->firstGlyph);
     415               0 :         if (aSecondGlyph < PRUint16(rightClassTable->nGlyphs)) {
     416               0 :             if (reinterpret_cast<const char*>(rightClassTable) +
     417                 :                 sizeof(KernClassTableHdr) +
     418               0 :                 aSecondGlyph * sizeof(PRUint16) >= subtableEnd) {
     419               0 :                 return 0;
     420                 :             }
     421               0 :             offset += PRUint16(rightClassTable->offsets[aSecondGlyph]);
     422                 :         }
     423                 :     }
     424                 : 
     425                 :     const AutoSwap_PRInt16* pval =
     426               0 :         reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
     427               0 :     if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
     428               0 :         return 0;
     429                 :     }
     430               0 :     return *pval;
     431                 : }
     432                 : 
     433                 : // Get kerning value from Apple (version 1.0) kern table,
     434                 : // subtable format 3 (simple N x M array of kerning values)
     435                 : 
     436                 : // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
     437                 : // for details of version 1.0 format 3 subtable.
     438                 : 
     439                 : struct KernHeaderVersion1Fmt3 {
     440                 :     KernTableSubtableHeaderVersion1 header;
     441                 :     AutoSwap_PRUint16 glyphCount;
     442                 :     PRUint8 kernValueCount;
     443                 :     PRUint8 leftClassCount;
     444                 :     PRUint8 rightClassCount;
     445                 :     PRUint8 flags;
     446                 : };
     447                 : 
     448                 : static PRInt16
     449               0 : GetKernValueVersion1Fmt3(const void* aSubtable,
     450                 :                          PRUint32 aSubtableLen,
     451                 :                          PRUint16 aFirstGlyph,
     452                 :                          PRUint16 aSecondGlyph)
     453                 : {
     454                 :     // check that we can safely read the header fields
     455               0 :     if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
     456               0 :         return 0;
     457                 :     }
     458                 : 
     459                 :     const KernHeaderVersion1Fmt3* hdr =
     460               0 :         reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
     461               0 :     if (hdr->flags != 0) {
     462               0 :         return 0;
     463                 :     }
     464                 : 
     465               0 :     PRUint16 glyphCount = hdr->glyphCount;
     466                 : 
     467                 :     // check that table is large enough for the arrays
     468               0 :     if (sizeof(KernHeaderVersion1Fmt3) +
     469                 :         hdr->kernValueCount * sizeof(PRInt16) +
     470                 :         glyphCount + glyphCount +
     471                 :         hdr->leftClassCount * hdr->rightClassCount > aSubtableLen) {
     472               0 :         return 0;
     473                 :     }
     474                 :         
     475               0 :     if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
     476                 :         // glyphs are out of range for the class tables
     477               0 :         return 0;
     478                 :     }
     479                 : 
     480                 :     // get pointers to the four arrays within the subtable
     481                 :     const AutoSwap_PRInt16* kernValue =
     482               0 :         reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
     483                 :     const PRUint8* leftClass =
     484               0 :         reinterpret_cast<const PRUint8*>(kernValue + hdr->kernValueCount);
     485               0 :     const PRUint8* rightClass = leftClass + glyphCount;
     486               0 :     const PRUint8* kernIndex = rightClass + glyphCount;
     487                 : 
     488               0 :     PRUint8 lc = leftClass[aFirstGlyph];
     489               0 :     PRUint8 rc = rightClass[aSecondGlyph];
     490               0 :     if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
     491               0 :         return 0;
     492                 :     }
     493                 : 
     494               0 :     PRUint8 ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
     495               0 :                            rightClass[aSecondGlyph]];
     496               0 :     if (ki >= hdr->kernValueCount) {
     497               0 :         return 0;
     498                 :     }
     499                 : 
     500               0 :     return kernValue[ki];
     501                 : }
     502                 : 
     503                 : #define KERN0_COVERAGE_HORIZONTAL   0x0001
     504                 : #define KERN0_COVERAGE_MINIMUM      0x0002
     505                 : #define KERN0_COVERAGE_CROSS_STREAM 0x0004
     506                 : #define KERN0_COVERAGE_OVERRIDE     0x0008
     507                 : #define KERN0_COVERAGE_RESERVED     0x00F0
     508                 : 
     509                 : #define KERN1_COVERAGE_VERTICAL     0x8000
     510                 : #define KERN1_COVERAGE_CROSS_STREAM 0x4000
     511                 : #define KERN1_COVERAGE_VARIATION    0x2000
     512                 : #define KERN1_COVERAGE_RESERVED     0x1F00
     513                 : 
     514                 : hb_position_t
     515               0 : gfxHarfBuzzShaper::GetHKerning(PRUint16 aFirstGlyph,
     516                 :                                PRUint16 aSecondGlyph) const
     517                 : {
     518                 :     // We want to ignore any kern pairs involving <space>, because we are
     519                 :     // handling words in isolation, the only space characters seen here are
     520                 :     // the ones artificially added by the textRun code.
     521               0 :     PRUint32 spaceGlyph = mFont->GetSpaceGlyph();
     522               0 :     if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
     523               0 :         return 0;
     524                 :     }
     525                 : 
     526               0 :     if (!mKernTable) {
     527               0 :         mKernTable = mFont->GetFontTable(TRUETYPE_TAG('k','e','r','n'));
     528               0 :         if (!mKernTable) {
     529               0 :             mKernTable = hb_blob_get_empty();
     530                 :         }
     531                 :     }
     532                 : 
     533                 :     PRUint32 len;
     534               0 :     const char* base = hb_blob_get_data(mKernTable, &len);
     535               0 :     if (len < sizeof(KernTableVersion0)) {
     536               0 :         return 0;
     537                 :     }
     538               0 :     PRInt32 value = 0;
     539                 : 
     540                 :     // First try to interpret as "version 0" kern table
     541                 :     // (see http://www.microsoft.com/typography/otspec/kern.htm)
     542                 :     const KernTableVersion0* kern0 =
     543               0 :         reinterpret_cast<const KernTableVersion0*>(base);
     544               0 :     if (PRUint16(kern0->version) == 0) {
     545               0 :         PRUint16 nTables = kern0->nTables;
     546               0 :         PRUint32 offs = sizeof(KernTableVersion0);
     547               0 :         for (PRUint16 i = 0; i < nTables; ++i) {
     548               0 :             if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
     549               0 :                 break;
     550                 :             }
     551                 :             const KernTableSubtableHeaderVersion0* st0 =
     552                 :                 reinterpret_cast<const KernTableSubtableHeaderVersion0*>
     553               0 :                                 (base + offs);
     554               0 :             PRUint16 subtableLen = PRUint16(st0->length);
     555               0 :             if (offs + subtableLen > len) {
     556               0 :                 break;
     557                 :             }
     558               0 :             offs += subtableLen;
     559               0 :             PRUint16 coverage = st0->coverage;
     560               0 :             if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
     561                 :                 // we only care about horizontal kerning (for now)
     562               0 :                 continue;
     563                 :             }
     564               0 :             if (coverage &
     565                 :                 (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
     566                 :                 // we don't support cross-stream kerning, and
     567                 :                 // reserved bits should be zero;
     568                 :                 // ignore the subtable if not
     569               0 :                 continue;
     570                 :             }
     571               0 :             PRUint8 format = (coverage >> 8);
     572               0 :             switch (format) {
     573                 :             case 0:
     574               0 :                 GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0),
     575                 :                                  aFirstGlyph, aSecondGlyph, value,
     576                 :                                  (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
     577               0 :                                  (coverage & KERN0_COVERAGE_MINIMUM) != 0);
     578               0 :                 break;
     579                 :             default:
     580                 :                 // TODO: implement support for other formats,
     581                 :                 // if they're ever used in practice
     582                 : #if DEBUG
     583                 :                 {
     584                 :                     char buf[1024];
     585                 :                     sprintf(buf, "unknown kern subtable in %s: "
     586                 :                                  "ver 0 format %d\n",
     587               0 :                             NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
     588               0 :                             format);
     589               0 :                     NS_WARNING(buf);
     590                 :                 }
     591                 : #endif
     592               0 :                 break;
     593                 :             }
     594                 :         }
     595                 :     } else {
     596                 :         // It wasn't a "version 0" table; check if it is Apple version 1.0
     597                 :         // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
     598                 :         const KernTableVersion1* kern1 =
     599               0 :             reinterpret_cast<const KernTableVersion1*>(base);
     600               0 :         if (PRUint32(kern1->version) == 0x00010000) {
     601               0 :             PRUint32 nTables = kern1->nTables;
     602               0 :             PRUint32 offs = sizeof(KernTableVersion1);
     603               0 :             for (PRUint32 i = 0; i < nTables; ++i) {
     604               0 :                 if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
     605               0 :                     break;
     606                 :                 }
     607                 :                 const KernTableSubtableHeaderVersion1* st1 =
     608                 :                     reinterpret_cast<const KernTableSubtableHeaderVersion1*>
     609               0 :                                     (base + offs);
     610               0 :                 PRUint32 subtableLen = PRUint32(st1->length);
     611               0 :                 offs += subtableLen;
     612               0 :                 PRUint16 coverage = st1->coverage;
     613               0 :                 if (coverage &
     614                 :                     (KERN1_COVERAGE_VERTICAL     |
     615                 :                      KERN1_COVERAGE_CROSS_STREAM |
     616                 :                      KERN1_COVERAGE_VARIATION    |
     617                 :                      KERN1_COVERAGE_RESERVED)) {
     618                 :                     // we only care about horizontal kerning (for now),
     619                 :                     // we don't support cross-stream kerning,
     620                 :                     // we don't support variations,
     621                 :                     // reserved bits should be zero;
     622                 :                     // ignore the subtable if not
     623               0 :                     continue;
     624                 :                 }
     625               0 :                 PRUint8 format = (coverage & 0xff);
     626               0 :                 switch (format) {
     627                 :                 case 0:
     628               0 :                     GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1),
     629               0 :                                      aFirstGlyph, aSecondGlyph, value);
     630               0 :                     break;
     631                 :                 case 2:
     632                 :                     value = GetKernValueVersion1Fmt2(st1, subtableLen,
     633               0 :                                                      aFirstGlyph, aSecondGlyph);
     634               0 :                     break;
     635                 :                 case 3:
     636                 :                     value = GetKernValueVersion1Fmt3(st1, subtableLen,
     637               0 :                                                      aFirstGlyph, aSecondGlyph);
     638               0 :                     break;
     639                 :                 default:
     640                 :                     // TODO: implement support for other formats.
     641                 :                     // Note that format 1 cannot be supported here,
     642                 :                     // as it requires the full glyph array to run the FSM,
     643                 :                     // not just the current glyph pair.
     644                 : #if DEBUG
     645                 :                     {
     646                 :                         char buf[1024];
     647                 :                         sprintf(buf, "unknown kern subtable in %s: "
     648                 :                                      "ver 0 format %d\n",
     649               0 :                                 NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
     650               0 :                                 format);
     651               0 :                         NS_WARNING(buf);
     652                 :                     }
     653                 : #endif
     654               0 :                     break;
     655                 :                 }
     656                 :             }
     657                 :         }
     658                 :     }
     659                 : 
     660               0 :     if (value != 0) {
     661               0 :         return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
     662                 :     }
     663               0 :     return 0;
     664                 : }
     665                 : 
     666                 : static hb_position_t
     667               0 : HBGetHKerning(hb_font_t *font, void *font_data,
     668                 :               hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
     669                 :               void *user_data)
     670                 : {
     671                 :     const FontCallbackData *fcd =
     672               0 :         static_cast<const FontCallbackData*>(font_data);
     673               0 :     return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
     674                 : }
     675                 : 
     676                 : /*
     677                 :  * HarfBuzz unicode property callbacks
     678                 :  */
     679                 : 
     680                 : static hb_codepoint_t
     681               0 : HBGetMirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
     682                 : {
     683               0 :     return GetMirroredChar(aCh);
     684                 : }
     685                 : 
     686                 : static hb_unicode_general_category_t
     687               0 : HBGetGeneralCategory(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
     688                 : {
     689               0 :     return hb_unicode_general_category_t(GetGeneralCategory(aCh));
     690                 : }
     691                 : 
     692                 : static hb_script_t
     693               0 : HBGetScript(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
     694                 : {
     695                 :     return hb_script_t(GetScriptTagForCode
     696               0 :         (GetScriptCode(aCh)));
     697                 : }
     698                 : 
     699                 : static unsigned int
     700               0 : HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
     701                 : {
     702               0 :     return GetCombiningClass(aCh);
     703                 : }
     704                 : 
     705                 : static unsigned int
     706               0 : HBGetEastAsianWidth(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
     707                 : {
     708               0 :     return GetEastAsianWidth(aCh);
     709                 : }
     710                 : 
     711                 : // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
     712                 : // note that some letters do not have a dagesh presForm encoded
     713                 : static const PRUnichar sDageshForms[0x05EA - 0x05D0 + 1] = {
     714                 :     0xFB30, // ALEF
     715                 :     0xFB31, // BET
     716                 :     0xFB32, // GIMEL
     717                 :     0xFB33, // DALET
     718                 :     0xFB34, // HE
     719                 :     0xFB35, // VAV
     720                 :     0xFB36, // ZAYIN
     721                 :     0, // HET
     722                 :     0xFB38, // TET
     723                 :     0xFB39, // YOD
     724                 :     0xFB3A, // FINAL KAF
     725                 :     0xFB3B, // KAF
     726                 :     0xFB3C, // LAMED
     727                 :     0, // FINAL MEM
     728                 :     0xFB3E, // MEM
     729                 :     0, // FINAL NUN
     730                 :     0xFB40, // NUN
     731                 :     0xFB41, // SAMEKH
     732                 :     0, // AYIN
     733                 :     0xFB43, // FINAL PE
     734                 :     0xFB44, // PE
     735                 :     0, // FINAL TSADI
     736                 :     0xFB46, // TSADI
     737                 :     0xFB47, // QOF
     738                 :     0xFB48, // RESH
     739                 :     0xFB49, // SHIN
     740                 :     0xFB4A // TAV
     741                 : };
     742                 : 
     743                 : static hb_bool_t
     744               0 : HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
     745                 :                  hb_codepoint_t      a,
     746                 :                  hb_codepoint_t      b,
     747                 :                  hb_codepoint_t     *ab,
     748                 :                  void               *user_data)
     749                 : {
     750               0 :     hb_bool_t found = nsUnicodeNormalizer::Compose(a, b, ab);
     751                 : 
     752               0 :     if (!found && (b & 0x1fff80) == 0x0580) {
     753                 :         // special-case Hebrew presentation forms that are excluded from
     754                 :         // standard normalization, but wanted for old fonts
     755               0 :         switch (b) {
     756                 :         case 0x05B4: // HIRIQ
     757               0 :             if (a == 0x05D9) { // YOD
     758               0 :                 *ab = 0xFB1D;
     759               0 :                 found = true;
     760                 :             }
     761               0 :             break;
     762                 :         case 0x05B7: // patah
     763               0 :             if (a == 0x05F2) { // YIDDISH YOD YOD
     764               0 :                 *ab = 0xFB1F;
     765               0 :                 found = true;
     766               0 :             } else if (a == 0x05D0) { // ALEF
     767               0 :                 *ab = 0xFB2E;
     768               0 :                 found = true;
     769                 :             }
     770               0 :             break;
     771                 :         case 0x05B8: // QAMATS
     772               0 :             if (a == 0x05D0) { // ALEF
     773               0 :                 *ab = 0xFB2F;
     774               0 :                 found = true;
     775                 :             }
     776               0 :             break;
     777                 :         case 0x05B9: // HOLAM
     778               0 :             if (a == 0x05D5) { // VAV
     779               0 :                 *ab = 0xFB4B;
     780               0 :                 found = true;
     781                 :             }
     782               0 :             break;
     783                 :         case 0x05BC: // DAGESH
     784               0 :             if (a >= 0x05D0 && a <= 0x05EA) {
     785               0 :                 *ab = sDageshForms[a - 0x05D0];
     786               0 :                 found = (*ab != 0);
     787               0 :             } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
     788               0 :                 *ab = 0xFB2C;
     789               0 :                 found = true;
     790               0 :             } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
     791               0 :                 *ab = 0xFB2D;
     792               0 :                 found = true;
     793                 :             }
     794               0 :             break;
     795                 :         case 0x05BF: // RAFE
     796               0 :             switch (a) {
     797                 :             case 0x05D1: // BET
     798               0 :                 *ab = 0xFB4C;
     799               0 :                 found = true;
     800               0 :                 break;
     801                 :             case 0x05DB: // KAF
     802               0 :                 *ab = 0xFB4D;
     803               0 :                 found = true;
     804               0 :                 break;
     805                 :             case 0x05E4: // PE
     806               0 :                 *ab = 0xFB4E;
     807               0 :                 found = true;
     808               0 :                 break;
     809                 :             }
     810               0 :             break;
     811                 :         case 0x05C1: // SHIN DOT
     812               0 :             if (a == 0x05E9) { // SHIN
     813               0 :                 *ab = 0xFB2A;
     814               0 :                 found = true;
     815               0 :             } else if (a == 0xFB49) { // SHIN WITH DAGESH
     816               0 :                 *ab = 0xFB2C;
     817               0 :                 found = true;
     818                 :             }
     819               0 :             break;
     820                 :         case 0x05C2: // SIN DOT
     821               0 :             if (a == 0x05E9) { // SHIN
     822               0 :                 *ab = 0xFB2B;
     823               0 :                 found = true;
     824               0 :             } else if (a == 0xFB49) { // SHIN WITH DAGESH
     825               0 :                 *ab = 0xFB2D;
     826               0 :                 found = true;
     827                 :             }
     828               0 :             break;
     829                 :         }
     830                 :     }
     831                 : 
     832               0 :     return found;
     833                 : }
     834                 : 
     835                 : static hb_bool_t
     836               0 : HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
     837                 :                    hb_codepoint_t      ab,
     838                 :                    hb_codepoint_t     *a,
     839                 :                    hb_codepoint_t     *b,
     840                 :                    void               *user_data)
     841                 : {
     842               0 :     return nsUnicodeNormalizer::DecomposeNonRecursively(ab, a, b);
     843                 : }
     844                 : 
     845                 : /*
     846                 :  * gfxFontShaper override to initialize the text run using HarfBuzz
     847                 :  */
     848                 : 
     849                 : static hb_font_funcs_t * sHBFontFuncs = nsnull;
     850                 : static hb_unicode_funcs_t * sHBUnicodeFuncs = nsnull;
     851                 : 
     852                 : bool
     853               0 : gfxHarfBuzzShaper::ShapeWord(gfxContext      *aContext,
     854                 :                              gfxShapedWord   *aShapedWord,
     855                 :                              const PRUnichar *aText)
     856                 : {
     857                 :     // some font back-ends require this in order to get proper hinted metrics
     858               0 :     mFont->SetupCairoFont(aContext);
     859                 : 
     860               0 :     if (!mHBFace) {
     861                 : 
     862               0 :         mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
     863                 : 
     864                 :         // set up the harfbuzz face etc the first time we use the font
     865                 : 
     866               0 :         if (!sHBFontFuncs) {
     867                 :             // static function callback pointers, initialized by the first
     868                 :             // harfbuzz shaper used
     869               0 :             sHBFontFuncs = hb_font_funcs_create();
     870                 :             hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
     871               0 :                                          nsnull, nsnull);
     872                 :             hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
     873                 :                                                    HBGetGlyphHAdvance,
     874               0 :                                                    nsnull, nsnull);
     875                 :             hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
     876                 :                                                        HBGetContourPoint,
     877               0 :                                                        nsnull, nsnull);
     878                 :             hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
     879                 :                                                    HBGetHKerning,
     880               0 :                                                    nsnull, nsnull);
     881                 : 
     882                 :             sHBUnicodeFuncs =
     883               0 :                 hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
     884                 :             hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
     885                 :                                                 HBGetMirroring,
     886               0 :                                                 nsnull, nsnull);
     887                 :             hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
     888               0 :                                              nsnull, nsnull);
     889                 :             hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
     890                 :                                                        HBGetGeneralCategory,
     891               0 :                                                        nsnull, nsnull);
     892                 :             hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
     893                 :                                                       HBGetCombiningClass,
     894               0 :                                                       nsnull, nsnull);
     895                 :             hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
     896                 :                                                       HBGetEastAsianWidth,
     897               0 :                                                       nsnull, nsnull);
     898                 :             hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
     899                 :                                               HBUnicodeCompose,
     900               0 :                                               nsnull, nsnull);
     901                 :             hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
     902                 :                                                 HBUnicodeDecompose,
     903               0 :                                                 nsnull, nsnull);
     904                 :         }
     905                 : 
     906               0 :         mHBFace = hb_face_create_for_tables(HBGetTable, this, nsnull);
     907                 : 
     908               0 :         if (!mUseFontGetGlyph) {
     909                 :             // get the cmap table and find offset to our subtable
     910               0 :             mCmapTable = mFont->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
     911               0 :             if (!mCmapTable) {
     912               0 :                 NS_WARNING("failed to load cmap, glyphs will be missing");
     913               0 :                 return false;
     914                 :             }
     915                 :             PRUint32 len;
     916               0 :             const PRUint8* data = (const PRUint8*)hb_blob_get_data(mCmapTable, &len);
     917                 :             bool symbol;
     918                 :             mCmapFormat = gfxFontUtils::
     919                 :                 FindPreferredSubtable(data, len,
     920                 :                                       &mSubtableOffset, &mUVSTableOffset,
     921               0 :                                       &symbol);
     922                 :         }
     923                 : 
     924               0 :         if (!mUseFontGlyphWidths) {
     925                 :             // if font doesn't implement GetGlyphWidth, we will be reading
     926                 :             // the hmtx table directly;
     927                 :             // read mNumLongMetrics from hhea table without caching its blob,
     928                 :             // and preload/cache the hmtx table
     929                 :             hb_blob_t *hheaTable =
     930               0 :                 mFont->GetFontTable(TRUETYPE_TAG('h','h','e','a'));
     931               0 :             if (hheaTable) {
     932                 :                 PRUint32 len;
     933                 :                 const HMetricsHeader* hhea =
     934                 :                     reinterpret_cast<const HMetricsHeader*>
     935               0 :                         (hb_blob_get_data(hheaTable, &len));
     936               0 :                 if (len >= sizeof(HMetricsHeader)) {
     937               0 :                     mNumLongMetrics = hhea->numberOfHMetrics;
     938               0 :                     if (mNumLongMetrics > 0 &&
     939               0 :                         PRInt16(hhea->metricDataFormat) == 0) {
     940                 :                         // no point reading hmtx if number of entries is zero!
     941                 :                         // in that case, we won't be able to use this font
     942                 :                         // (this method will return FALSE below if mHmtx is null)
     943                 :                         mHmtxTable =
     944               0 :                             mFont->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
     945               0 :                         if (hb_blob_get_length(mHmtxTable) <
     946                 :                             mNumLongMetrics * sizeof(HLongMetric)) {
     947                 :                             // hmtx table is not large enough for the claimed
     948                 :                             // number of entries: invalid, do not use.
     949               0 :                             hb_blob_destroy(mHmtxTable);
     950               0 :                             mHmtxTable = nsnull;
     951                 :                         }
     952                 :                     }
     953                 :                 }
     954                 :             }
     955               0 :             hb_blob_destroy(hheaTable);
     956                 :         }
     957                 :     }
     958                 : 
     959               0 :     if ((!mUseFontGetGlyph && mCmapFormat <= 0) ||
     960               0 :         (!mUseFontGlyphWidths && !mHmtxTable)) {
     961                 :         // unable to shape with this font
     962               0 :         return false;
     963                 :     }
     964                 : 
     965               0 :     FontCallbackData fcd(this, aContext);
     966               0 :     hb_font_t *font = hb_font_create(mHBFace);
     967               0 :     hb_font_set_funcs(font, sHBFontFuncs, &fcd, nsnull);
     968               0 :     hb_font_set_ppem(font, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
     969               0 :     PRUint32 scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
     970               0 :     hb_font_set_scale(font, scale, scale);
     971                 : 
     972               0 :     nsAutoTArray<hb_feature_t,20> features;
     973                 : 
     974                 :     // Ligature features are enabled by default in the generic shaper,
     975                 :     // so we explicitly turn them off if necessary (for letter-spacing)
     976               0 :     if (aShapedWord->DisableLigatures()) {
     977               0 :         hb_feature_t ligaOff = { HB_TAG('l','i','g','a'), 0, 0, UINT_MAX };
     978               0 :         hb_feature_t cligOff = { HB_TAG('c','l','i','g'), 0, 0, UINT_MAX };
     979               0 :         features.AppendElement(ligaOff);
     980               0 :         features.AppendElement(cligOff);
     981                 :     }
     982                 : 
     983                 :     // css features need to be merged with the existing ones, if any
     984               0 :     gfxFontEntry *entry = mFont->GetFontEntry();
     985               0 :     const gfxFontStyle *style = mFont->GetStyle();
     986               0 :     const nsTArray<gfxFontFeature> *cssFeatures = &style->featureSettings;
     987               0 :     if (cssFeatures->IsEmpty()) {
     988               0 :         cssFeatures = &entry->mFeatureSettings;
     989                 :     }
     990               0 :     for (PRUint32 i = 0; i < cssFeatures->Length(); ++i) {
     991                 :         PRUint32 j;
     992               0 :         for (j = 0; j < features.Length(); ++j) {
     993               0 :             if (cssFeatures->ElementAt(i).mTag == features[j].tag) {
     994               0 :                 features[j].value = cssFeatures->ElementAt(i).mValue;
     995               0 :                 break;
     996                 :             }
     997                 :         }
     998               0 :         if (j == features.Length()) {
     999               0 :             const gfxFontFeature& f = cssFeatures->ElementAt(i);
    1000               0 :             hb_feature_t hbf = { f.mTag, f.mValue, 0, UINT_MAX };
    1001               0 :             features.AppendElement(hbf);
    1002                 :         }
    1003                 :     }
    1004                 : 
    1005               0 :     bool isRightToLeft = aShapedWord->IsRightToLeft();
    1006               0 :     hb_buffer_t *buffer = hb_buffer_create();
    1007               0 :     hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
    1008                 :     hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
    1009               0 :                                                     HB_DIRECTION_LTR);
    1010                 :     // For unresolved "common" or "inherited" runs, default to Latin for now.
    1011                 :     // (Should we somehow use the language or locale to try and infer
    1012                 :     // a better default?)
    1013               0 :     PRInt32 scriptCode = aShapedWord->Script();
    1014                 :     hb_script_t scriptTag = (scriptCode <= MOZ_SCRIPT_INHERITED) ?
    1015                 :         HB_SCRIPT_LATIN :
    1016               0 :         hb_script_t(GetScriptTagForCode(scriptCode));
    1017               0 :     hb_buffer_set_script(buffer, scriptTag);
    1018                 : 
    1019                 :     hb_language_t language;
    1020               0 :     if (style->languageOverride) {
    1021               0 :         language = hb_ot_tag_to_language(style->languageOverride);
    1022               0 :     } else if (entry->mLanguageOverride) {
    1023               0 :         language = hb_ot_tag_to_language(entry->mLanguageOverride);
    1024                 :     } else {
    1025               0 :         nsCString langString;
    1026               0 :         style->language->ToUTF8String(langString);
    1027                 :         language =
    1028               0 :             hb_language_from_string(langString.get(), langString.Length());
    1029                 :     }
    1030               0 :     hb_buffer_set_language(buffer, language);
    1031                 : 
    1032               0 :     PRUint32 length = aShapedWord->Length();
    1033                 :     hb_buffer_add_utf16(buffer,
    1034                 :                         reinterpret_cast<const uint16_t*>(aText),
    1035               0 :                         length, 0, length);
    1036                 : 
    1037               0 :     hb_shape(font, buffer, features.Elements(), features.Length());
    1038                 : 
    1039               0 :     if (isRightToLeft) {
    1040               0 :         hb_buffer_reverse(buffer);
    1041                 :     }
    1042                 : 
    1043               0 :     nsresult rv = SetGlyphsFromRun(aContext, aShapedWord, buffer);
    1044                 : 
    1045               0 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
    1046               0 :     hb_buffer_destroy(buffer);
    1047               0 :     hb_font_destroy(font);
    1048                 : 
    1049               0 :     return NS_SUCCEEDED(rv);
    1050                 : }
    1051                 : 
    1052                 : /**
    1053                 :  * Work out whether cairo will snap inter-glyph spacing to pixels.
    1054                 :  *
    1055                 :  * Layout does not align text to pixel boundaries, so, with font drawing
    1056                 :  * backends that snap glyph positions to pixels, it is important that
    1057                 :  * inter-glyph spacing within words is always an integer number of pixels.
    1058                 :  * This ensures that the drawing backend snaps all of the word's glyphs in the
    1059                 :  * same direction and so inter-glyph spacing remains the same.
    1060                 :  */
    1061                 : static void
    1062               0 : GetRoundOffsetsToPixels(gfxContext *aContext,
    1063                 :                         bool *aRoundX, bool *aRoundY)
    1064                 : {
    1065               0 :     *aRoundX = false;
    1066                 :     // Could do something fancy here for ScaleFactors of
    1067                 :     // AxisAlignedTransforms, but we leave things simple.
    1068                 :     // Not much point rounding if a matrix will mess things up anyway.
    1069               0 :     if (aContext->CurrentMatrix().HasNonTranslation()) {
    1070               0 :         *aRoundY = false;
    1071               0 :         return;
    1072                 :     }
    1073                 : 
    1074                 :     // All raster backends snap glyphs to pixels vertically.
    1075                 :     // Print backends set CAIRO_HINT_METRICS_OFF.
    1076               0 :     *aRoundY = true;
    1077                 : 
    1078               0 :     cairo_t *cr = aContext->GetCairo();
    1079               0 :     cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
    1080                 :     // Sometimes hint metrics gets set for us, most notably for printing.
    1081               0 :     cairo_font_options_t *font_options = cairo_font_options_create();
    1082               0 :     cairo_scaled_font_get_font_options(scaled_font, font_options);
    1083                 :     cairo_hint_metrics_t hint_metrics =
    1084               0 :         cairo_font_options_get_hint_metrics(font_options);
    1085               0 :     cairo_font_options_destroy(font_options);
    1086                 : 
    1087               0 :     switch (hint_metrics) {
    1088                 :     case CAIRO_HINT_METRICS_OFF:
    1089               0 :         *aRoundY = false;
    1090               0 :         return;
    1091                 :     case CAIRO_HINT_METRICS_DEFAULT:
    1092                 :         // Here we mimic what cairo surface/font backends do.  Printing
    1093                 :         // surfaces have already been handled by hint_metrics.  The
    1094                 :         // fallback show_glyphs implementation composites pixel-aligned
    1095                 :         // glyph surfaces, so we just pick surface/font combinations that
    1096                 :         // override this.
    1097               0 :         switch (cairo_scaled_font_get_type(scaled_font)) {
    1098                 : #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
    1099                 :         case CAIRO_FONT_TYPE_DWRITE:
    1100                 :             // show_glyphs is implemented on the font and so is used for
    1101                 :             // all surface types; however, it may pixel-snap depending on
    1102                 :             // the dwrite rendering mode
    1103                 :             if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
    1104                 :                 gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
    1105                 :                     DWRITE_MEASURING_MODE_NATURAL) {
    1106                 :                 return;
    1107                 :             }
    1108                 : #endif
    1109                 :         case CAIRO_FONT_TYPE_QUARTZ:
    1110                 :             // Quartz surfaces implement show_glyphs for Quartz fonts
    1111               0 :             if (cairo_surface_get_type(cairo_get_target(cr)) ==
    1112                 :                 CAIRO_SURFACE_TYPE_QUARTZ) {
    1113               0 :                 return;
    1114                 :             }
    1115                 :         default:
    1116               0 :             break;
    1117                 :         }
    1118                 :         // fall through:
    1119                 :     case CAIRO_HINT_METRICS_ON:
    1120               0 :         break;
    1121                 :     }
    1122               0 :     *aRoundX = true;
    1123               0 :     return;
    1124                 : }
    1125                 : 
    1126                 : #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
    1127                 :                             // will fit without requiring separate allocation
    1128                 :                             // for charToGlyphArray
    1129                 : 
    1130                 : nsresult
    1131               0 : gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
    1132                 :                                     gfxShapedWord *aShapedWord,
    1133                 :                                     hb_buffer_t *aBuffer)
    1134                 : {
    1135                 :     PRUint32 numGlyphs;
    1136               0 :     const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
    1137               0 :     if (numGlyphs == 0) {
    1138               0 :         return NS_OK;
    1139                 :     }
    1140                 : 
    1141               0 :     nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
    1142                 : 
    1143               0 :     PRUint32 wordLength = aShapedWord->Length();
    1144                 :     static const PRInt32 NO_GLYPH = -1;
    1145               0 :     nsAutoTArray<PRInt32,SMALL_GLYPH_RUN> charToGlyphArray;
    1146               0 :     if (!charToGlyphArray.SetLength(wordLength)) {
    1147               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1148                 :     }
    1149                 : 
    1150               0 :     PRInt32 *charToGlyph = charToGlyphArray.Elements();
    1151               0 :     for (PRUint32 offset = 0; offset < wordLength; ++offset) {
    1152               0 :         charToGlyph[offset] = NO_GLYPH;
    1153                 :     }
    1154                 : 
    1155               0 :     for (PRUint32 i = 0; i < numGlyphs; ++i) {
    1156               0 :         PRUint32 loc = ginfo[i].cluster;
    1157               0 :         if (loc < wordLength) {
    1158               0 :             charToGlyph[loc] = i;
    1159                 :         }
    1160                 :     }
    1161                 : 
    1162               0 :     PRInt32 glyphStart = 0; // looking for a clump that starts at this glyph
    1163               0 :     PRInt32 charStart = 0; // and this char index within the range of the run
    1164                 : 
    1165                 :     bool roundX;
    1166                 :     bool roundY;
    1167               0 :     GetRoundOffsetsToPixels(aContext, &roundX, &roundY);
    1168                 : 
    1169               0 :     PRInt32 appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
    1170                 : 
    1171                 :     // factor to convert 16.16 fixed-point pixels to app units
    1172                 :     // (only used if not rounding)
    1173               0 :     double hb2appUnits = FixedToFloat(aShapedWord->AppUnitsPerDevUnit());
    1174                 : 
    1175                 :     // Residual from rounding of previous advance, for use in rounding the
    1176                 :     // subsequent offset or advance appropriately.  16.16 fixed-point
    1177                 :     //
    1178                 :     // When rounding, the goal is to make the distance between glyphs and
    1179                 :     // their base glyph equal to the integral number of pixels closest to that
    1180                 :     // suggested by that shaper.
    1181                 :     // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
    1182                 :     //
    1183                 :     // The value of the residual is the part of the desired distance that has
    1184                 :     // not been included in integer offsets.
    1185               0 :     hb_position_t x_residual = 0;
    1186                 : 
    1187                 :     // keep track of y-position to set glyph offsets if needed
    1188               0 :     nscoord yPos = 0;
    1189                 : 
    1190                 :     const hb_glyph_position_t *posInfo =
    1191               0 :         hb_buffer_get_glyph_positions(aBuffer, nsnull);
    1192                 : 
    1193               0 :     while (glyphStart < PRInt32(numGlyphs)) {
    1194                 : 
    1195               0 :         bool inOrder = true;
    1196               0 :         PRInt32 charEnd = ginfo[glyphStart].cluster;
    1197               0 :         PRInt32 glyphEnd = glyphStart;
    1198               0 :         PRInt32 charLimit = wordLength;
    1199               0 :         while (charEnd < charLimit) {
    1200                 :             // This is normally executed once for each iteration of the outer loop,
    1201                 :             // but in unusual cases where the character/glyph association is complex,
    1202                 :             // the initial character range might correspond to a non-contiguous
    1203                 :             // glyph range with "holes" in it. If so, we will repeat this loop to
    1204                 :             // extend the character range until we have a contiguous glyph sequence.
    1205               0 :             charEnd += 1;
    1206               0 :             while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
    1207               0 :                 charEnd += 1;
    1208                 :             }
    1209                 : 
    1210                 :             // find the maximum glyph index covered by the clump so far
    1211               0 :             for (PRInt32 i = charStart; i < charEnd; ++i) {
    1212               0 :                 if (charToGlyph[i] != NO_GLYPH) {
    1213               0 :                     glyphEnd = NS_MAX(glyphEnd, charToGlyph[i] + 1);
    1214                 :                     // update extent of glyph range
    1215                 :                 }
    1216                 :             }
    1217                 : 
    1218               0 :             if (glyphEnd == glyphStart + 1) {
    1219                 :                 // for the common case of a single-glyph clump,
    1220                 :                 // we can skip the following checks
    1221               0 :                 break;
    1222                 :             }
    1223                 : 
    1224               0 :             if (glyphEnd == glyphStart) {
    1225                 :                 // no glyphs, try to extend the clump
    1226               0 :                 continue;
    1227                 :             }
    1228                 : 
    1229                 :             // check whether all glyphs in the range are associated with the characters
    1230                 :             // in our clump; if not, we have a discontinuous range, and should extend it
    1231                 :             // unless we've reached the end of the text
    1232               0 :             bool allGlyphsAreWithinCluster = true;
    1233               0 :             PRInt32 prevGlyphCharIndex = charStart - 1;
    1234               0 :             for (PRInt32 i = glyphStart; i < glyphEnd; ++i) {
    1235               0 :                 PRInt32 glyphCharIndex = ginfo[i].cluster;
    1236               0 :                 if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
    1237               0 :                     allGlyphsAreWithinCluster = false;
    1238               0 :                     break;
    1239                 :                 }
    1240               0 :                 if (glyphCharIndex <= prevGlyphCharIndex) {
    1241               0 :                     inOrder = false;
    1242                 :                 }
    1243               0 :                 prevGlyphCharIndex = glyphCharIndex;
    1244                 :             }
    1245               0 :             if (allGlyphsAreWithinCluster) {
    1246               0 :                 break;
    1247                 :             }
    1248                 :         }
    1249                 : 
    1250               0 :         NS_ASSERTION(glyphStart < glyphEnd,
    1251                 :                      "character/glyph clump contains no glyphs!");
    1252               0 :         NS_ASSERTION(charStart != charEnd,
    1253                 :                      "character/glyph clump contains no characters!");
    1254                 : 
    1255                 :         // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
    1256                 :         // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
    1257                 :         // and endCharIndex to the limit (position beyond the last char),
    1258                 :         // adjusting for the offset of the stringRange relative to the textRun.
    1259                 :         PRInt32 baseCharIndex, endCharIndex;
    1260               0 :         while (charEnd < PRInt32(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
    1261               0 :             charEnd++;
    1262               0 :         baseCharIndex = charStart;
    1263               0 :         endCharIndex = charEnd;
    1264                 : 
    1265                 :         // Then we check if the clump falls outside our actual string range;
    1266                 :         // if so, just go to the next.
    1267               0 :         if (baseCharIndex >= PRInt32(wordLength)) {
    1268               0 :             glyphStart = glyphEnd;
    1269               0 :             charStart = charEnd;
    1270               0 :             continue;
    1271                 :         }
    1272                 :         // Ensure we won't try to go beyond the valid length of the textRun's text
    1273               0 :         endCharIndex = NS_MIN<PRInt32>(endCharIndex, wordLength);
    1274                 : 
    1275                 :         // Now we're ready to set the glyph info in the textRun
    1276               0 :         PRInt32 glyphsInClump = glyphEnd - glyphStart;
    1277                 : 
    1278                 :         // Check for default-ignorable char that didn't get filtered, combined,
    1279                 :         // etc by the shaping process, and remove from the run.
    1280                 :         // (This may be done within harfbuzz eventually.)
    1281               0 :         if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
    1282               0 :             aShapedWord->FilterIfIgnorable(baseCharIndex)) {
    1283               0 :             glyphStart = glyphEnd;
    1284               0 :             charStart = charEnd;
    1285               0 :             continue;
    1286                 :         }
    1287                 : 
    1288               0 :         hb_position_t x_offset = posInfo[glyphStart].x_offset;
    1289               0 :         hb_position_t x_advance = posInfo[glyphStart].x_advance;
    1290                 :         nscoord xOffset, advance;
    1291               0 :         if (roundX) {
    1292                 :             xOffset =
    1293               0 :                 appUnitsPerDevUnit * FixedToIntRound(x_offset + x_residual);
    1294                 :             // Desired distance from the base glyph to the next reference point.
    1295               0 :             hb_position_t width = x_advance - x_offset;
    1296               0 :             int intWidth = FixedToIntRound(width);
    1297               0 :             x_residual = width - FloatToFixed(intWidth);
    1298               0 :             advance = appUnitsPerDevUnit * intWidth + xOffset;
    1299                 :         } else {
    1300               0 :             xOffset = floor(hb2appUnits * x_offset + 0.5);
    1301               0 :             advance = floor(hb2appUnits * x_advance + 0.5);
    1302                 :         }
    1303                 :         // Check if it's a simple one-to-one mapping
    1304               0 :         if (glyphsInClump == 1 &&
    1305               0 :             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
    1306               0 :             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
    1307               0 :             aShapedWord->IsClusterStart(baseCharIndex) &&
    1308                 :             xOffset == 0 &&
    1309               0 :             posInfo[glyphStart].y_offset == 0 && yPos == 0)
    1310                 :         {
    1311               0 :             gfxTextRun::CompressedGlyph g;
    1312                 :             aShapedWord->SetSimpleGlyph(baseCharIndex,
    1313                 :                                      g.SetSimpleGlyph(advance,
    1314               0 :                                          ginfo[glyphStart].codepoint));
    1315                 :         } else {
    1316                 :             // collect all glyphs in a list to be assigned to the first char;
    1317                 :             // there must be at least one in the clump, and we already measured
    1318                 :             // its advance, hence the placement of the loop-exit test and the
    1319                 :             // measurement of the next glyph
    1320               0 :             while (1) {
    1321                 :                 gfxTextRun::DetailedGlyph* details =
    1322               0 :                     detailedGlyphs.AppendElement();
    1323               0 :                 details->mGlyphID = ginfo[glyphStart].codepoint;
    1324                 : 
    1325               0 :                 details->mXOffset = xOffset;
    1326               0 :                 details->mAdvance = advance;
    1327                 : 
    1328               0 :                 hb_position_t y_offset = posInfo[glyphStart].y_offset;
    1329                 :                 details->mYOffset = yPos -
    1330                 :                     (roundY ? appUnitsPerDevUnit * FixedToIntRound(y_offset)
    1331               0 :                      : floor(hb2appUnits * y_offset + 0.5));
    1332                 : 
    1333               0 :                 hb_position_t y_advance = posInfo[glyphStart].y_advance;
    1334               0 :                 if (y_advance != 0) {
    1335                 :                     yPos -=
    1336                 :                         roundY ? appUnitsPerDevUnit * FixedToIntRound(y_advance)
    1337               0 :                         : floor(hb2appUnits * y_advance + 0.5);
    1338                 :                 }
    1339               0 :                 if (++glyphStart >= glyphEnd) {
    1340                 :                     break;
    1341                 :                 }
    1342                 : 
    1343               0 :                 x_offset = posInfo[glyphStart].x_offset;
    1344               0 :                 x_advance = posInfo[glyphStart].x_advance;
    1345               0 :                 if (roundX) {
    1346                 :                     xOffset = appUnitsPerDevUnit *
    1347               0 :                         FixedToIntRound(x_offset + x_residual);
    1348                 :                     // Desired distance to the next reference point.  The
    1349                 :                     // residual is considered here, and includes the residual
    1350                 :                     // from the base glyph offset and subsequent advances, so
    1351                 :                     // that the distance from the base glyph is optimized
    1352                 :                     // rather than the distance from combining marks.
    1353               0 :                     x_advance += x_residual;
    1354               0 :                     int intAdvance = FixedToIntRound(x_advance);
    1355               0 :                     x_residual = x_advance - FloatToFixed(intAdvance);
    1356               0 :                     advance = appUnitsPerDevUnit * intAdvance;
    1357                 :                 } else {
    1358               0 :                     xOffset = floor(hb2appUnits * x_offset + 0.5);
    1359               0 :                     advance = floor(hb2appUnits * x_advance + 0.5);
    1360                 :                 }
    1361                 :             }
    1362                 : 
    1363               0 :             gfxTextRun::CompressedGlyph g;
    1364               0 :             g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
    1365               0 :                          true, detailedGlyphs.Length());
    1366                 :             aShapedWord->SetGlyphs(baseCharIndex,
    1367               0 :                                 g, detailedGlyphs.Elements());
    1368                 : 
    1369               0 :             detailedGlyphs.Clear();
    1370                 :         }
    1371                 : 
    1372                 :         // the rest of the chars in the group are ligature continuations,
    1373                 :         // no associated glyphs
    1374               0 :         while (++baseCharIndex != endCharIndex &&
    1375                 :                baseCharIndex < PRInt32(wordLength)) {
    1376               0 :             gfxTextRun::CompressedGlyph g;
    1377                 :             g.SetComplex(inOrder &&
    1378               0 :                          aShapedWord->IsClusterStart(baseCharIndex),
    1379               0 :                          false, 0);
    1380               0 :             aShapedWord->SetGlyphs(baseCharIndex, g, nsnull);
    1381                 :         }
    1382                 : 
    1383               0 :         glyphStart = glyphEnd;
    1384               0 :         charStart = charEnd;
    1385                 :     }
    1386                 : 
    1387               0 :     return NS_OK;
    1388                 : }

Generated by: LCOV version 1.7