gfx/thebes/gfxCoreTextShaper.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     7 #include "gfxCoreTextShaper.h"
     8 #include "gfxMacFont.h"
     9 #include "gfxFontUtils.h"
    10 #include "mozilla/gfx/2D.h"
    12 #include <algorithm>
    14 using namespace mozilla;
    16 // standard font descriptors that we construct the first time they're needed
    17 CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
    18 CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
    20 gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont)
    21     : gfxFontShaper(aFont)
    22 {
    23     // Create our CTFontRef
    24     mCTFont = ::CTFontCreateWithGraphicsFont(aFont->GetCGFontRef(),
    25                                              aFont->GetAdjustedSize(),
    26                                              nullptr,
    27                                              GetDefaultFeaturesDescriptor());
    29     // Set up the default attribute dictionary that we will need each time we create a CFAttributedString
    30     mAttributesDict = ::CFDictionaryCreate(kCFAllocatorDefault,
    31                                            (const void**) &kCTFontAttributeName,
    32                                            (const void**) &mCTFont,
    33                                            1, // count of attributes
    34                                            &kCFTypeDictionaryKeyCallBacks,
    35                                            &kCFTypeDictionaryValueCallBacks);
    36 }
    38 gfxCoreTextShaper::~gfxCoreTextShaper()
    39 {
    40     if (mAttributesDict) {
    41         ::CFRelease(mAttributesDict);
    42     }
    43     if (mCTFont) {
    44         ::CFRelease(mCTFont);
    45     }
    46 }
    48 bool
    49 gfxCoreTextShaper::ShapeText(gfxContext      *aContext,
    50                              const char16_t *aText,
    51                              uint32_t         aOffset,
    52                              uint32_t         aLength,
    53                              int32_t          aScript,
    54                              gfxShapedText   *aShapedText)
    55 {
    56     // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
    58     bool isRightToLeft = aShapedText->IsRightToLeft();
    59     uint32_t length = aLength;
    61     // we need to bidi-wrap the text if the run is RTL,
    62     // or if it is an LTR run but may contain (overridden) RTL chars
    63     bool bidiWrap = isRightToLeft;
    64     if (!bidiWrap && !aShapedText->TextIs8Bit()) {
    65         uint32_t i;
    66         for (i = 0; i < length; ++i) {
    67             if (gfxFontUtils::PotentialRTLChar(aText[i])) {
    68                 bidiWrap = true;
    69                 break;
    70             }
    71         }
    72     }
    74     // If there's a possibility of any bidi, we wrap the text with direction overrides
    75     // to ensure neutrals or characters that were bidi-overridden in HTML behave properly.
    76     const UniChar beginLTR[]    = { 0x202d, 0x20 };
    77     const UniChar beginRTL[]    = { 0x202e, 0x20 };
    78     const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c };
    80     uint32_t startOffset;
    81     CFStringRef stringObj;
    82     if (bidiWrap) {
    83         startOffset = isRightToLeft ?
    84             mozilla::ArrayLength(beginRTL) : mozilla::ArrayLength(beginLTR);
    85         CFMutableStringRef mutableString =
    86             ::CFStringCreateMutable(kCFAllocatorDefault,
    87                                     length + startOffset + mozilla::ArrayLength(endBidiWrap));
    88         ::CFStringAppendCharacters(mutableString,
    89                                    isRightToLeft ? beginRTL : beginLTR,
    90                                    startOffset);
    91         ::CFStringAppendCharacters(mutableString, reinterpret_cast<const UniChar*>(aText), length);
    92         ::CFStringAppendCharacters(mutableString,
    93                                    endBidiWrap, mozilla::ArrayLength(endBidiWrap));
    94         stringObj = mutableString;
    95     } else {
    96         startOffset = 0;
    97         stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
    98                                                          reinterpret_cast<const UniChar*>(aText),
    99                                                          length, kCFAllocatorNull);
   100     }
   102     CFDictionaryRef attrObj;
   103     if (aShapedText->DisableLigatures()) {
   104         // For letterspacing (or maybe other situations) we need to make a copy of the CTFont
   105         // with the ligature feature disabled
   106         CTFontRef ctFont =
   107             CreateCTFontWithDisabledLigatures(::CTFontGetSize(mCTFont));
   109         attrObj =
   110             ::CFDictionaryCreate(kCFAllocatorDefault,
   111                                  (const void**) &kCTFontAttributeName,
   112                                  (const void**) &ctFont,
   113                                  1, // count of attributes
   114                                  &kCFTypeDictionaryKeyCallBacks,
   115                                  &kCFTypeDictionaryValueCallBacks);
   116         // Having created the dict, we're finished with our ligature-disabled CTFontRef
   117         ::CFRelease(ctFont);
   118     } else {
   119         attrObj = mAttributesDict;
   120         ::CFRetain(attrObj);
   121     }
   123     // Now we can create an attributed string
   124     CFAttributedStringRef attrStringObj =
   125         ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
   126     ::CFRelease(stringObj);
   127     ::CFRelease(attrObj);
   129     // Create the CoreText line from our string, then we're done with it
   130     CTLineRef line = ::CTLineCreateWithAttributedString(attrStringObj);
   131     ::CFRelease(attrStringObj);
   133     // and finally retrieve the glyph data and store into the gfxTextRun
   134     CFArrayRef glyphRuns = ::CTLineGetGlyphRuns(line);
   135     uint32_t numRuns = ::CFArrayGetCount(glyphRuns);
   137     // Iterate through the glyph runs.
   138     // Note that this includes the bidi wrapper, so we have to be careful
   139     // not to include the extra glyphs from there
   140     bool success = true;
   141     for (uint32_t runIndex = 0; runIndex < numRuns; runIndex++) {
   142         CTRunRef aCTRun =
   143             (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
   144         if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun, startOffset) != NS_OK) {
   145             success = false;
   146             break;
   147         }
   148     }
   150     ::CFRelease(line);
   152     return success;
   153 }
   155 #define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;
   156                             // some testing indicates that 90%+ of glyph runs will fit
   157                             // without requiring a separate allocation
   159 nsresult
   160 gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
   161                                     uint32_t       aOffset,
   162                                     uint32_t       aLength,
   163                                     CTRunRef       aCTRun,
   164                                     int32_t        aStringOffset)
   165 {
   166     // The word has been bidi-wrapped; aStringOffset is the number
   167     // of chars at the beginning of the CTLine that we should skip.
   168     // aCTRun is a glyph run from the CoreText layout process.
   170     int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
   172     int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
   173     if (numGlyphs == 0) {
   174         return NS_OK;
   175     }
   177     int32_t wordLength = aLength;
   179     // character offsets get really confusing here, as we have to keep track of
   180     // (a) the text in the actual textRun we're constructing
   181     // (c) the string that was handed to CoreText, which contains the text of the font run
   182     //     plus directional-override padding
   183     // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line
   184     //     (but may extend beyond the actual font run into the bidi wrapping text).
   185     //     aStringOffset tells us how many initial characters of the line to ignore.
   187     // get the source string range within the CTLine's text
   188     CFRange stringRange = ::CTRunGetStringRange(aCTRun);
   189     // skip the run if it is entirely outside the actual range of the font run
   190     if (stringRange.location - aStringOffset + stringRange.length <= 0 ||
   191         stringRange.location - aStringOffset >= wordLength) {
   192         return NS_OK;
   193     }
   195     // retrieve the laid-out glyph data from the CTRun
   196     nsAutoArrayPtr<CGGlyph> glyphsArray;
   197     nsAutoArrayPtr<CGPoint> positionsArray;
   198     nsAutoArrayPtr<CFIndex> glyphToCharArray;
   199     const CGGlyph* glyphs = nullptr;
   200     const CGPoint* positions = nullptr;
   201     const CFIndex* glyphToChar = nullptr;
   203     // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
   204     // and so allocating a new array and copying data with CTRunGetGlyphs
   205     // will be extremely rare.
   206     // If this were not the case, we could use an nsAutoTArray<> to
   207     // try and avoid the heap allocation for small runs.
   208     // It's possible that some future change to CoreText will mean that
   209     // CTRunGetGlyphsPtr fails more often; if this happens, nsAutoTArray<>
   210     // may become an attractive option.
   211     glyphs = ::CTRunGetGlyphsPtr(aCTRun);
   212     if (!glyphs) {
   213         glyphsArray = new (std::nothrow) CGGlyph[numGlyphs];
   214         if (!glyphsArray) {
   215             return NS_ERROR_OUT_OF_MEMORY;
   216         }
   217         ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get());
   218         glyphs = glyphsArray.get();
   219     }
   221     positions = ::CTRunGetPositionsPtr(aCTRun);
   222     if (!positions) {
   223         positionsArray = new (std::nothrow) CGPoint[numGlyphs];
   224         if (!positionsArray) {
   225             return NS_ERROR_OUT_OF_MEMORY;
   226         }
   227         ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get());
   228         positions = positionsArray.get();
   229     }
   231     // Remember that the glyphToChar indices relate to the CoreText line,
   232     // not to the beginning of the textRun, the font run,
   233     // or the stringRange of the glyph run
   234     glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun);
   235     if (!glyphToChar) {
   236         glyphToCharArray = new (std::nothrow) CFIndex[numGlyphs];
   237         if (!glyphToCharArray) {
   238             return NS_ERROR_OUT_OF_MEMORY;
   239         }
   240         ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
   241         glyphToChar = glyphToCharArray.get();
   242     }
   244     double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
   245                                                   nullptr, nullptr, nullptr);
   247     nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
   248     gfxShapedText::CompressedGlyph g;
   249     gfxShapedText::CompressedGlyph *charGlyphs =
   250         aShapedText->GetCharacterGlyphs() + aOffset;
   252     // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
   253     // to a source text character; we also need the charindex-to-glyphindex mapping to
   254     // find the glyph for a given char. Note that some chars may not map to any glyph
   255     // (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
   256     // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
   257     // record the last glyph index for cases where the char maps to several glyphs,
   258     // so that our clumping will include all the glyph fragments for the character.
   260     // The charToGlyph array is indexed by char position within the stringRange of the glyph run.
   262     static const int32_t NO_GLYPH = -1;
   263     AutoFallibleTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray;
   264     if (!charToGlyphArray.SetLength(stringRange.length)) {
   265         return NS_ERROR_OUT_OF_MEMORY;
   266     }
   267     int32_t *charToGlyph = charToGlyphArray.Elements();
   268     for (int32_t offset = 0; offset < stringRange.length; ++offset) {
   269         charToGlyph[offset] = NO_GLYPH;
   270     }
   271     for (int32_t i = 0; i < numGlyphs; ++i) {
   272         int32_t loc = glyphToChar[i] - stringRange.location;
   273         if (loc >= 0 && loc < stringRange.length) {
   274             charToGlyph[loc] = i;
   275         }
   276     }
   278     // Find character and glyph clumps that correspond, allowing for ligatures,
   279     // indic reordering, split glyphs, etc.
   280     //
   281     // The idea is that we'll find a character sequence starting at the first char of stringRange,
   282     // and extend it until it includes the character associated with the first glyph;
   283     // we also extend it as long as there are "holes" in the range of glyphs. So we
   284     // will eventually have a contiguous sequence of characters, starting at the beginning
   285     // of the range, that map to a contiguous sequence of glyphs, starting at the beginning
   286     // of the glyph array. That's a clump; then we update the starting positions and repeat.
   287     //
   288     // NB: In the case of RTL layouts, we iterate over the stringRange in reverse.
   289     //
   291     // This may find characters that fall outside the range 0:wordLength,
   292     // so we won't necessarily use everything we find here.
   294     bool isRightToLeft = aShapedText->IsRightToLeft();
   295     int32_t glyphStart = 0; // looking for a clump that starts at this glyph index
   296     int32_t charStart = isRightToLeft ?
   297         stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)
   299     while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
   300         bool inOrder = true;
   301         int32_t charEnd = glyphToChar[glyphStart] - stringRange.location;
   302         NS_WARN_IF_FALSE(charEnd >= 0 && charEnd < stringRange.length,
   303                          "glyph-to-char mapping points outside string range");
   304         // clamp charEnd to the valid range of the string
   305         charEnd = std::max(charEnd, 0);
   306         charEnd = std::min(charEnd, int32_t(stringRange.length));
   308         int32_t glyphEnd = glyphStart;
   309         int32_t charLimit = isRightToLeft ? -1 : stringRange.length;
   310         do {
   311             // This is normally executed once for each iteration of the outer loop,
   312             // but in unusual cases where the character/glyph association is complex,
   313             // the initial character range might correspond to a non-contiguous
   314             // glyph range with "holes" in it. If so, we will repeat this loop to
   315             // extend the character range until we have a contiguous glyph sequence.
   316             NS_ASSERTION((direction > 0 && charEnd < charLimit) ||
   317                          (direction < 0 && charEnd > charLimit),
   318                          "no characters left in range?");
   319             charEnd += direction;
   320             while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
   321                 charEnd += direction;
   322             }
   324             // find the maximum glyph index covered by the clump so far
   325             if (isRightToLeft) {
   326                 for (int32_t i = charStart; i > charEnd; --i) {
   327                     if (charToGlyph[i] != NO_GLYPH) {
   328                         // update extent of glyph range
   329                         glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
   330                     }
   331                 }
   332             } else {
   333                 for (int32_t i = charStart; i < charEnd; ++i) {
   334                     if (charToGlyph[i] != NO_GLYPH) {
   335                         // update extent of glyph range
   336                         glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
   337                     }
   338                 }
   339             }
   341             if (glyphEnd == glyphStart + 1) {
   342                 // for the common case of a single-glyph clump, we can skip the following checks
   343                 break;
   344             }
   346             if (glyphEnd == glyphStart) {
   347                 // no glyphs, try to extend the clump
   348                 continue;
   349             }
   351             // check whether all glyphs in the range are associated with the characters
   352             // in our clump; if not, we have a discontinuous range, and should extend it
   353             // unless we've reached the end of the text
   354             bool allGlyphsAreWithinCluster = true;
   355             int32_t prevGlyphCharIndex = charStart;
   356             for (int32_t i = glyphStart; i < glyphEnd; ++i) {
   357                 int32_t glyphCharIndex = glyphToChar[i] - stringRange.location;
   358                 if (isRightToLeft) {
   359                     if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) {
   360                         allGlyphsAreWithinCluster = false;
   361                         break;
   362                     }
   363                     if (glyphCharIndex > prevGlyphCharIndex) {
   364                         inOrder = false;
   365                     }
   366                     prevGlyphCharIndex = glyphCharIndex;
   367                 } else {
   368                     if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
   369                         allGlyphsAreWithinCluster = false;
   370                         break;
   371                     }
   372                     if (glyphCharIndex < prevGlyphCharIndex) {
   373                         inOrder = false;
   374                     }
   375                     prevGlyphCharIndex = glyphCharIndex;
   376                 }
   377             }
   378             if (allGlyphsAreWithinCluster) {
   379                 break;
   380             }
   381         } while (charEnd != charLimit);
   383         NS_WARN_IF_FALSE(glyphStart < glyphEnd,
   384                          "character/glyph clump contains no glyphs!");
   385         if (glyphStart == glyphEnd) {
   386             ++glyphStart; // make progress - avoid potential infinite loop
   387             charStart = charEnd;
   388             continue;
   389         }
   391         NS_WARN_IF_FALSE(charStart != charEnd,
   392                          "character/glyph clump contains no characters!");
   393         if (charStart == charEnd) {
   394             glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s),
   395                                    // as there's nowhere to attach them
   396             continue;
   397         }
   399         // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
   400         // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
   401         // and endCharIndex to the limit (position beyond the last char),
   402         // adjusting for the offset of the stringRange relative to the textRun.
   403         int32_t baseCharIndex, endCharIndex;
   404         if (isRightToLeft) {
   405             while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) {
   406                 charEnd--;
   407             }
   408             baseCharIndex = charEnd + stringRange.location - aStringOffset + 1;
   409             endCharIndex = charStart + stringRange.location - aStringOffset + 1;
   410         } else {
   411             while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) {
   412                 charEnd++;
   413             }
   414             baseCharIndex = charStart + stringRange.location - aStringOffset;
   415             endCharIndex = charEnd + stringRange.location - aStringOffset;
   416         }
   418         // Then we check if the clump falls outside our actual string range; if so, just go to the next.
   419         if (endCharIndex <= 0 || baseCharIndex >= wordLength) {
   420             glyphStart = glyphEnd;
   421             charStart = charEnd;
   422             continue;
   423         }
   424         // Ensure we won't try to go beyond the valid length of the word's text
   425         baseCharIndex = std::max(baseCharIndex, 0);
   426         endCharIndex = std::min(endCharIndex, wordLength);
   428         // Now we're ready to set the glyph info in the textRun; measure the glyph width
   429         // of the first (perhaps only) glyph, to see if it is "Simple"
   430         int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
   431         double toNextGlyph;
   432         if (glyphStart < numGlyphs-1) {
   433             toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
   434         } else {
   435             toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
   436         }
   437         int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
   439         // Check if it's a simple one-to-one mapping
   440         int32_t glyphsInClump = glyphEnd - glyphStart;
   441         if (glyphsInClump == 1 &&
   442             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
   443             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
   444             charGlyphs[baseCharIndex].IsClusterStart() &&
   445             positions[glyphStart].y == 0.0)
   446         {
   447             charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
   448                                                      glyphs[glyphStart]);
   449         } else {
   450             // collect all glyphs in a list to be assigned to the first char;
   451             // there must be at least one in the clump, and we already measured its advance,
   452             // hence the placement of the loop-exit test and the measurement of the next glyph
   453             while (1) {
   454                 gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
   455                 details->mGlyphID = glyphs[glyphStart];
   456                 details->mXOffset = 0;
   457                 details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit;
   458                 details->mAdvance = advance;
   459                 if (++glyphStart >= glyphEnd) {
   460                    break;
   461                 }
   462                 if (glyphStart < numGlyphs-1) {
   463                     toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
   464                 } else {
   465                     toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
   466                 }
   467                 advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
   468             }
   470             gfxTextRun::CompressedGlyph g;
   471             g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
   472                          true, detailedGlyphs.Length());
   473             aShapedText->SetGlyphs(aOffset + baseCharIndex, g, detailedGlyphs.Elements());
   475             detailedGlyphs.Clear();
   476         }
   478         // the rest of the chars in the group are ligature continuations, no associated glyphs
   479         while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
   480             gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
   481             NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
   482             g.SetComplex(inOrder && g.IsClusterStart(), false, 0);
   483         }
   485         glyphStart = glyphEnd;
   486         charStart = charEnd;
   487     }
   489     return NS_OK;
   490 }
   492 #undef SMALL_GLYPH_RUN
   494 // Construct the font attribute descriptor that we'll apply by default when creating a CTFontRef.
   495 // This will turn off line-edge swashes by default, because we don't know the actual line breaks
   496 // when doing glyph shaping.
   497 void
   498 gfxCoreTextShaper::CreateDefaultFeaturesDescriptor()
   499 {
   500     if (sDefaultFeaturesDescriptor != nullptr) {
   501         return;
   502     }
   504     SInt16 val = kSmartSwashType;
   505     CFNumberRef swashesType =
   506         ::CFNumberCreate(kCFAllocatorDefault,
   507                          kCFNumberSInt16Type,
   508                          &val);
   509     val = kLineInitialSwashesOffSelector;
   510     CFNumberRef lineInitialsOffSelector =
   511         ::CFNumberCreate(kCFAllocatorDefault,
   512                          kCFNumberSInt16Type,
   513                          &val);
   515     CFTypeRef keys[]   = { kCTFontFeatureTypeIdentifierKey,
   516                            kCTFontFeatureSelectorIdentifierKey };
   517     CFTypeRef values[] = { swashesType,
   518                            lineInitialsOffSelector };
   519     CFDictionaryRef featureSettings[2];
   520     featureSettings[0] =
   521         ::CFDictionaryCreate(kCFAllocatorDefault,
   522                              (const void **) keys,
   523                              (const void **) values,
   524                              ArrayLength(keys),
   525                              &kCFTypeDictionaryKeyCallBacks,
   526                              &kCFTypeDictionaryValueCallBacks);
   527     ::CFRelease(lineInitialsOffSelector);
   529     val = kLineFinalSwashesOffSelector;
   530     CFNumberRef lineFinalsOffSelector =
   531         ::CFNumberCreate(kCFAllocatorDefault,
   532                          kCFNumberSInt16Type,
   533                          &val);
   534     values[1] = lineFinalsOffSelector;
   535     featureSettings[1] =
   536         ::CFDictionaryCreate(kCFAllocatorDefault,
   537                              (const void **) keys,
   538                              (const void **) values,
   539                              ArrayLength(keys),
   540                              &kCFTypeDictionaryKeyCallBacks,
   541                              &kCFTypeDictionaryValueCallBacks);
   542     ::CFRelease(lineFinalsOffSelector);
   543     ::CFRelease(swashesType);
   545     CFArrayRef featuresArray =
   546         ::CFArrayCreate(kCFAllocatorDefault,
   547                         (const void **) featureSettings,
   548                         ArrayLength(featureSettings),
   549                         &kCFTypeArrayCallBacks);
   550     ::CFRelease(featureSettings[0]);
   551     ::CFRelease(featureSettings[1]);
   553     const CFTypeRef attrKeys[]   = { kCTFontFeatureSettingsAttribute };
   554     const CFTypeRef attrValues[] = { featuresArray };
   555     CFDictionaryRef attributesDict =
   556         ::CFDictionaryCreate(kCFAllocatorDefault,
   557                              (const void **) attrKeys,
   558                              (const void **) attrValues,
   559                              ArrayLength(attrKeys),
   560                              &kCFTypeDictionaryKeyCallBacks,
   561                              &kCFTypeDictionaryValueCallBacks);
   562     ::CFRelease(featuresArray);
   564     sDefaultFeaturesDescriptor =
   565         ::CTFontDescriptorCreateWithAttributes(attributesDict);
   566     ::CFRelease(attributesDict);
   567 }
   569 // Create a CTFontRef, with the Common Ligatures feature disabled
   570 CTFontRef
   571 gfxCoreTextShaper::CreateCTFontWithDisabledLigatures(CGFloat aSize)
   572 {
   573     if (sDisableLigaturesDescriptor == nullptr) {
   574         // initialize cached descriptor to turn off the Common Ligatures feature
   575         SInt16 val = kLigaturesType;
   576         CFNumberRef ligaturesType =
   577             ::CFNumberCreate(kCFAllocatorDefault,
   578                              kCFNumberSInt16Type,
   579                              &val);
   580         val = kCommonLigaturesOffSelector;
   581         CFNumberRef commonLigaturesOffSelector =
   582             ::CFNumberCreate(kCFAllocatorDefault,
   583                              kCFNumberSInt16Type,
   584                              &val);
   586         const CFTypeRef keys[]   = { kCTFontFeatureTypeIdentifierKey,
   587                                      kCTFontFeatureSelectorIdentifierKey };
   588         const CFTypeRef values[] = { ligaturesType,
   589                                      commonLigaturesOffSelector };
   590         CFDictionaryRef featureSettingDict =
   591             ::CFDictionaryCreate(kCFAllocatorDefault,
   592                                  (const void **) keys,
   593                                  (const void **) values,
   594                                  ArrayLength(keys),
   595                                  &kCFTypeDictionaryKeyCallBacks,
   596                                  &kCFTypeDictionaryValueCallBacks);
   597         ::CFRelease(ligaturesType);
   598         ::CFRelease(commonLigaturesOffSelector);
   600         CFArrayRef featuresArray =
   601             ::CFArrayCreate(kCFAllocatorDefault,
   602                             (const void **) &featureSettingDict,
   603                             1,
   604                             &kCFTypeArrayCallBacks);
   605         ::CFRelease(featureSettingDict);
   607         CFDictionaryRef attributesDict =
   608             ::CFDictionaryCreate(kCFAllocatorDefault,
   609                                  (const void **) &kCTFontFeatureSettingsAttribute,
   610                                  (const void **) &featuresArray,
   611                                  1, // count of keys & values
   612                                  &kCFTypeDictionaryKeyCallBacks,
   613                                  &kCFTypeDictionaryValueCallBacks);
   614         ::CFRelease(featuresArray);
   616         sDisableLigaturesDescriptor =
   617             ::CTFontDescriptorCreateCopyWithAttributes(GetDefaultFeaturesDescriptor(),
   618                                                        attributesDict);
   619         ::CFRelease(attributesDict);
   620     }
   622     gfxMacFont *f = static_cast<gfxMacFont*>(mFont);
   623     return ::CTFontCreateWithGraphicsFont(f->GetCGFontRef(), aSize, nullptr,
   624                                           sDisableLigaturesDescriptor);
   625 }
   627 void
   628 gfxCoreTextShaper::Shutdown() // [static]
   629 {
   630     if (sDisableLigaturesDescriptor != nullptr) {
   631         ::CFRelease(sDisableLigaturesDescriptor);
   632         sDisableLigaturesDescriptor = nullptr;
   633     }        
   634     if (sDefaultFeaturesDescriptor != nullptr) {
   635         ::CFRelease(sDefaultFeaturesDescriptor);
   636         sDefaultFeaturesDescriptor = nullptr;
   637     }
   638 }

mercurial