1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxCoreTextShaper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,638 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/ArrayUtils.h" 1.10 +#include "gfxCoreTextShaper.h" 1.11 +#include "gfxMacFont.h" 1.12 +#include "gfxFontUtils.h" 1.13 +#include "mozilla/gfx/2D.h" 1.14 + 1.15 +#include <algorithm> 1.16 + 1.17 +using namespace mozilla; 1.18 + 1.19 +// standard font descriptors that we construct the first time they're needed 1.20 +CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr; 1.21 +CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr; 1.22 + 1.23 +gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont) 1.24 + : gfxFontShaper(aFont) 1.25 +{ 1.26 + // Create our CTFontRef 1.27 + mCTFont = ::CTFontCreateWithGraphicsFont(aFont->GetCGFontRef(), 1.28 + aFont->GetAdjustedSize(), 1.29 + nullptr, 1.30 + GetDefaultFeaturesDescriptor()); 1.31 + 1.32 + // Set up the default attribute dictionary that we will need each time we create a CFAttributedString 1.33 + mAttributesDict = ::CFDictionaryCreate(kCFAllocatorDefault, 1.34 + (const void**) &kCTFontAttributeName, 1.35 + (const void**) &mCTFont, 1.36 + 1, // count of attributes 1.37 + &kCFTypeDictionaryKeyCallBacks, 1.38 + &kCFTypeDictionaryValueCallBacks); 1.39 +} 1.40 + 1.41 +gfxCoreTextShaper::~gfxCoreTextShaper() 1.42 +{ 1.43 + if (mAttributesDict) { 1.44 + ::CFRelease(mAttributesDict); 1.45 + } 1.46 + if (mCTFont) { 1.47 + ::CFRelease(mCTFont); 1.48 + } 1.49 +} 1.50 + 1.51 +bool 1.52 +gfxCoreTextShaper::ShapeText(gfxContext *aContext, 1.53 + const char16_t *aText, 1.54 + uint32_t aOffset, 1.55 + uint32_t aLength, 1.56 + int32_t aScript, 1.57 + gfxShapedText *aShapedText) 1.58 +{ 1.59 + // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out. 1.60 + 1.61 + bool isRightToLeft = aShapedText->IsRightToLeft(); 1.62 + uint32_t length = aLength; 1.63 + 1.64 + // we need to bidi-wrap the text if the run is RTL, 1.65 + // or if it is an LTR run but may contain (overridden) RTL chars 1.66 + bool bidiWrap = isRightToLeft; 1.67 + if (!bidiWrap && !aShapedText->TextIs8Bit()) { 1.68 + uint32_t i; 1.69 + for (i = 0; i < length; ++i) { 1.70 + if (gfxFontUtils::PotentialRTLChar(aText[i])) { 1.71 + bidiWrap = true; 1.72 + break; 1.73 + } 1.74 + } 1.75 + } 1.76 + 1.77 + // If there's a possibility of any bidi, we wrap the text with direction overrides 1.78 + // to ensure neutrals or characters that were bidi-overridden in HTML behave properly. 1.79 + const UniChar beginLTR[] = { 0x202d, 0x20 }; 1.80 + const UniChar beginRTL[] = { 0x202e, 0x20 }; 1.81 + const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c }; 1.82 + 1.83 + uint32_t startOffset; 1.84 + CFStringRef stringObj; 1.85 + if (bidiWrap) { 1.86 + startOffset = isRightToLeft ? 1.87 + mozilla::ArrayLength(beginRTL) : mozilla::ArrayLength(beginLTR); 1.88 + CFMutableStringRef mutableString = 1.89 + ::CFStringCreateMutable(kCFAllocatorDefault, 1.90 + length + startOffset + mozilla::ArrayLength(endBidiWrap)); 1.91 + ::CFStringAppendCharacters(mutableString, 1.92 + isRightToLeft ? beginRTL : beginLTR, 1.93 + startOffset); 1.94 + ::CFStringAppendCharacters(mutableString, reinterpret_cast<const UniChar*>(aText), length); 1.95 + ::CFStringAppendCharacters(mutableString, 1.96 + endBidiWrap, mozilla::ArrayLength(endBidiWrap)); 1.97 + stringObj = mutableString; 1.98 + } else { 1.99 + startOffset = 0; 1.100 + stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, 1.101 + reinterpret_cast<const UniChar*>(aText), 1.102 + length, kCFAllocatorNull); 1.103 + } 1.104 + 1.105 + CFDictionaryRef attrObj; 1.106 + if (aShapedText->DisableLigatures()) { 1.107 + // For letterspacing (or maybe other situations) we need to make a copy of the CTFont 1.108 + // with the ligature feature disabled 1.109 + CTFontRef ctFont = 1.110 + CreateCTFontWithDisabledLigatures(::CTFontGetSize(mCTFont)); 1.111 + 1.112 + attrObj = 1.113 + ::CFDictionaryCreate(kCFAllocatorDefault, 1.114 + (const void**) &kCTFontAttributeName, 1.115 + (const void**) &ctFont, 1.116 + 1, // count of attributes 1.117 + &kCFTypeDictionaryKeyCallBacks, 1.118 + &kCFTypeDictionaryValueCallBacks); 1.119 + // Having created the dict, we're finished with our ligature-disabled CTFontRef 1.120 + ::CFRelease(ctFont); 1.121 + } else { 1.122 + attrObj = mAttributesDict; 1.123 + ::CFRetain(attrObj); 1.124 + } 1.125 + 1.126 + // Now we can create an attributed string 1.127 + CFAttributedStringRef attrStringObj = 1.128 + ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj); 1.129 + ::CFRelease(stringObj); 1.130 + ::CFRelease(attrObj); 1.131 + 1.132 + // Create the CoreText line from our string, then we're done with it 1.133 + CTLineRef line = ::CTLineCreateWithAttributedString(attrStringObj); 1.134 + ::CFRelease(attrStringObj); 1.135 + 1.136 + // and finally retrieve the glyph data and store into the gfxTextRun 1.137 + CFArrayRef glyphRuns = ::CTLineGetGlyphRuns(line); 1.138 + uint32_t numRuns = ::CFArrayGetCount(glyphRuns); 1.139 + 1.140 + // Iterate through the glyph runs. 1.141 + // Note that this includes the bidi wrapper, so we have to be careful 1.142 + // not to include the extra glyphs from there 1.143 + bool success = true; 1.144 + for (uint32_t runIndex = 0; runIndex < numRuns; runIndex++) { 1.145 + CTRunRef aCTRun = 1.146 + (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex); 1.147 + if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun, startOffset) != NS_OK) { 1.148 + success = false; 1.149 + break; 1.150 + } 1.151 + } 1.152 + 1.153 + ::CFRelease(line); 1.154 + 1.155 + return success; 1.156 +} 1.157 + 1.158 +#define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data; 1.159 + // some testing indicates that 90%+ of glyph runs will fit 1.160 + // without requiring a separate allocation 1.161 + 1.162 +nsresult 1.163 +gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText, 1.164 + uint32_t aOffset, 1.165 + uint32_t aLength, 1.166 + CTRunRef aCTRun, 1.167 + int32_t aStringOffset) 1.168 +{ 1.169 + // The word has been bidi-wrapped; aStringOffset is the number 1.170 + // of chars at the beginning of the CTLine that we should skip. 1.171 + // aCTRun is a glyph run from the CoreText layout process. 1.172 + 1.173 + int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1; 1.174 + 1.175 + int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun); 1.176 + if (numGlyphs == 0) { 1.177 + return NS_OK; 1.178 + } 1.179 + 1.180 + int32_t wordLength = aLength; 1.181 + 1.182 + // character offsets get really confusing here, as we have to keep track of 1.183 + // (a) the text in the actual textRun we're constructing 1.184 + // (c) the string that was handed to CoreText, which contains the text of the font run 1.185 + // plus directional-override padding 1.186 + // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line 1.187 + // (but may extend beyond the actual font run into the bidi wrapping text). 1.188 + // aStringOffset tells us how many initial characters of the line to ignore. 1.189 + 1.190 + // get the source string range within the CTLine's text 1.191 + CFRange stringRange = ::CTRunGetStringRange(aCTRun); 1.192 + // skip the run if it is entirely outside the actual range of the font run 1.193 + if (stringRange.location - aStringOffset + stringRange.length <= 0 || 1.194 + stringRange.location - aStringOffset >= wordLength) { 1.195 + return NS_OK; 1.196 + } 1.197 + 1.198 + // retrieve the laid-out glyph data from the CTRun 1.199 + nsAutoArrayPtr<CGGlyph> glyphsArray; 1.200 + nsAutoArrayPtr<CGPoint> positionsArray; 1.201 + nsAutoArrayPtr<CFIndex> glyphToCharArray; 1.202 + const CGGlyph* glyphs = nullptr; 1.203 + const CGPoint* positions = nullptr; 1.204 + const CFIndex* glyphToChar = nullptr; 1.205 + 1.206 + // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds, 1.207 + // and so allocating a new array and copying data with CTRunGetGlyphs 1.208 + // will be extremely rare. 1.209 + // If this were not the case, we could use an nsAutoTArray<> to 1.210 + // try and avoid the heap allocation for small runs. 1.211 + // It's possible that some future change to CoreText will mean that 1.212 + // CTRunGetGlyphsPtr fails more often; if this happens, nsAutoTArray<> 1.213 + // may become an attractive option. 1.214 + glyphs = ::CTRunGetGlyphsPtr(aCTRun); 1.215 + if (!glyphs) { 1.216 + glyphsArray = new (std::nothrow) CGGlyph[numGlyphs]; 1.217 + if (!glyphsArray) { 1.218 + return NS_ERROR_OUT_OF_MEMORY; 1.219 + } 1.220 + ::CTRunGetGlyphs(aCTRun, ::CFRangeMake(0, 0), glyphsArray.get()); 1.221 + glyphs = glyphsArray.get(); 1.222 + } 1.223 + 1.224 + positions = ::CTRunGetPositionsPtr(aCTRun); 1.225 + if (!positions) { 1.226 + positionsArray = new (std::nothrow) CGPoint[numGlyphs]; 1.227 + if (!positionsArray) { 1.228 + return NS_ERROR_OUT_OF_MEMORY; 1.229 + } 1.230 + ::CTRunGetPositions(aCTRun, ::CFRangeMake(0, 0), positionsArray.get()); 1.231 + positions = positionsArray.get(); 1.232 + } 1.233 + 1.234 + // Remember that the glyphToChar indices relate to the CoreText line, 1.235 + // not to the beginning of the textRun, the font run, 1.236 + // or the stringRange of the glyph run 1.237 + glyphToChar = ::CTRunGetStringIndicesPtr(aCTRun); 1.238 + if (!glyphToChar) { 1.239 + glyphToCharArray = new (std::nothrow) CFIndex[numGlyphs]; 1.240 + if (!glyphToCharArray) { 1.241 + return NS_ERROR_OUT_OF_MEMORY; 1.242 + } 1.243 + ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get()); 1.244 + glyphToChar = glyphToCharArray.get(); 1.245 + } 1.246 + 1.247 + double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0), 1.248 + nullptr, nullptr, nullptr); 1.249 + 1.250 + nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs; 1.251 + gfxShapedText::CompressedGlyph g; 1.252 + gfxShapedText::CompressedGlyph *charGlyphs = 1.253 + aShapedText->GetCharacterGlyphs() + aOffset; 1.254 + 1.255 + // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph 1.256 + // to a source text character; we also need the charindex-to-glyphindex mapping to 1.257 + // find the glyph for a given char. Note that some chars may not map to any glyph 1.258 + // (ligature continuations), and some may map to several glyphs (eg Indic split vowels). 1.259 + // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we 1.260 + // record the last glyph index for cases where the char maps to several glyphs, 1.261 + // so that our clumping will include all the glyph fragments for the character. 1.262 + 1.263 + // The charToGlyph array is indexed by char position within the stringRange of the glyph run. 1.264 + 1.265 + static const int32_t NO_GLYPH = -1; 1.266 + AutoFallibleTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray; 1.267 + if (!charToGlyphArray.SetLength(stringRange.length)) { 1.268 + return NS_ERROR_OUT_OF_MEMORY; 1.269 + } 1.270 + int32_t *charToGlyph = charToGlyphArray.Elements(); 1.271 + for (int32_t offset = 0; offset < stringRange.length; ++offset) { 1.272 + charToGlyph[offset] = NO_GLYPH; 1.273 + } 1.274 + for (int32_t i = 0; i < numGlyphs; ++i) { 1.275 + int32_t loc = glyphToChar[i] - stringRange.location; 1.276 + if (loc >= 0 && loc < stringRange.length) { 1.277 + charToGlyph[loc] = i; 1.278 + } 1.279 + } 1.280 + 1.281 + // Find character and glyph clumps that correspond, allowing for ligatures, 1.282 + // indic reordering, split glyphs, etc. 1.283 + // 1.284 + // The idea is that we'll find a character sequence starting at the first char of stringRange, 1.285 + // and extend it until it includes the character associated with the first glyph; 1.286 + // we also extend it as long as there are "holes" in the range of glyphs. So we 1.287 + // will eventually have a contiguous sequence of characters, starting at the beginning 1.288 + // of the range, that map to a contiguous sequence of glyphs, starting at the beginning 1.289 + // of the glyph array. That's a clump; then we update the starting positions and repeat. 1.290 + // 1.291 + // NB: In the case of RTL layouts, we iterate over the stringRange in reverse. 1.292 + // 1.293 + 1.294 + // This may find characters that fall outside the range 0:wordLength, 1.295 + // so we won't necessarily use everything we find here. 1.296 + 1.297 + bool isRightToLeft = aShapedText->IsRightToLeft(); 1.298 + int32_t glyphStart = 0; // looking for a clump that starts at this glyph index 1.299 + int32_t charStart = isRightToLeft ? 1.300 + stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run) 1.301 + 1.302 + while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for 1.303 + bool inOrder = true; 1.304 + int32_t charEnd = glyphToChar[glyphStart] - stringRange.location; 1.305 + NS_WARN_IF_FALSE(charEnd >= 0 && charEnd < stringRange.length, 1.306 + "glyph-to-char mapping points outside string range"); 1.307 + // clamp charEnd to the valid range of the string 1.308 + charEnd = std::max(charEnd, 0); 1.309 + charEnd = std::min(charEnd, int32_t(stringRange.length)); 1.310 + 1.311 + int32_t glyphEnd = glyphStart; 1.312 + int32_t charLimit = isRightToLeft ? -1 : stringRange.length; 1.313 + do { 1.314 + // This is normally executed once for each iteration of the outer loop, 1.315 + // but in unusual cases where the character/glyph association is complex, 1.316 + // the initial character range might correspond to a non-contiguous 1.317 + // glyph range with "holes" in it. If so, we will repeat this loop to 1.318 + // extend the character range until we have a contiguous glyph sequence. 1.319 + NS_ASSERTION((direction > 0 && charEnd < charLimit) || 1.320 + (direction < 0 && charEnd > charLimit), 1.321 + "no characters left in range?"); 1.322 + charEnd += direction; 1.323 + while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) { 1.324 + charEnd += direction; 1.325 + } 1.326 + 1.327 + // find the maximum glyph index covered by the clump so far 1.328 + if (isRightToLeft) { 1.329 + for (int32_t i = charStart; i > charEnd; --i) { 1.330 + if (charToGlyph[i] != NO_GLYPH) { 1.331 + // update extent of glyph range 1.332 + glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); 1.333 + } 1.334 + } 1.335 + } else { 1.336 + for (int32_t i = charStart; i < charEnd; ++i) { 1.337 + if (charToGlyph[i] != NO_GLYPH) { 1.338 + // update extent of glyph range 1.339 + glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); 1.340 + } 1.341 + } 1.342 + } 1.343 + 1.344 + if (glyphEnd == glyphStart + 1) { 1.345 + // for the common case of a single-glyph clump, we can skip the following checks 1.346 + break; 1.347 + } 1.348 + 1.349 + if (glyphEnd == glyphStart) { 1.350 + // no glyphs, try to extend the clump 1.351 + continue; 1.352 + } 1.353 + 1.354 + // check whether all glyphs in the range are associated with the characters 1.355 + // in our clump; if not, we have a discontinuous range, and should extend it 1.356 + // unless we've reached the end of the text 1.357 + bool allGlyphsAreWithinCluster = true; 1.358 + int32_t prevGlyphCharIndex = charStart; 1.359 + for (int32_t i = glyphStart; i < glyphEnd; ++i) { 1.360 + int32_t glyphCharIndex = glyphToChar[i] - stringRange.location; 1.361 + if (isRightToLeft) { 1.362 + if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) { 1.363 + allGlyphsAreWithinCluster = false; 1.364 + break; 1.365 + } 1.366 + if (glyphCharIndex > prevGlyphCharIndex) { 1.367 + inOrder = false; 1.368 + } 1.369 + prevGlyphCharIndex = glyphCharIndex; 1.370 + } else { 1.371 + if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) { 1.372 + allGlyphsAreWithinCluster = false; 1.373 + break; 1.374 + } 1.375 + if (glyphCharIndex < prevGlyphCharIndex) { 1.376 + inOrder = false; 1.377 + } 1.378 + prevGlyphCharIndex = glyphCharIndex; 1.379 + } 1.380 + } 1.381 + if (allGlyphsAreWithinCluster) { 1.382 + break; 1.383 + } 1.384 + } while (charEnd != charLimit); 1.385 + 1.386 + NS_WARN_IF_FALSE(glyphStart < glyphEnd, 1.387 + "character/glyph clump contains no glyphs!"); 1.388 + if (glyphStart == glyphEnd) { 1.389 + ++glyphStart; // make progress - avoid potential infinite loop 1.390 + charStart = charEnd; 1.391 + continue; 1.392 + } 1.393 + 1.394 + NS_WARN_IF_FALSE(charStart != charEnd, 1.395 + "character/glyph clump contains no characters!"); 1.396 + if (charStart == charEnd) { 1.397 + glyphStart = glyphEnd; // this is bad - we'll discard the glyph(s), 1.398 + // as there's nowhere to attach them 1.399 + continue; 1.400 + } 1.401 + 1.402 + // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd; 1.403 + // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature), 1.404 + // and endCharIndex to the limit (position beyond the last char), 1.405 + // adjusting for the offset of the stringRange relative to the textRun. 1.406 + int32_t baseCharIndex, endCharIndex; 1.407 + if (isRightToLeft) { 1.408 + while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) { 1.409 + charEnd--; 1.410 + } 1.411 + baseCharIndex = charEnd + stringRange.location - aStringOffset + 1; 1.412 + endCharIndex = charStart + stringRange.location - aStringOffset + 1; 1.413 + } else { 1.414 + while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) { 1.415 + charEnd++; 1.416 + } 1.417 + baseCharIndex = charStart + stringRange.location - aStringOffset; 1.418 + endCharIndex = charEnd + stringRange.location - aStringOffset; 1.419 + } 1.420 + 1.421 + // Then we check if the clump falls outside our actual string range; if so, just go to the next. 1.422 + if (endCharIndex <= 0 || baseCharIndex >= wordLength) { 1.423 + glyphStart = glyphEnd; 1.424 + charStart = charEnd; 1.425 + continue; 1.426 + } 1.427 + // Ensure we won't try to go beyond the valid length of the word's text 1.428 + baseCharIndex = std::max(baseCharIndex, 0); 1.429 + endCharIndex = std::min(endCharIndex, wordLength); 1.430 + 1.431 + // Now we're ready to set the glyph info in the textRun; measure the glyph width 1.432 + // of the first (perhaps only) glyph, to see if it is "Simple" 1.433 + int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); 1.434 + double toNextGlyph; 1.435 + if (glyphStart < numGlyphs-1) { 1.436 + toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x; 1.437 + } else { 1.438 + toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x; 1.439 + } 1.440 + int32_t advance = int32_t(toNextGlyph * appUnitsPerDevUnit); 1.441 + 1.442 + // Check if it's a simple one-to-one mapping 1.443 + int32_t glyphsInClump = glyphEnd - glyphStart; 1.444 + if (glyphsInClump == 1 && 1.445 + gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) && 1.446 + gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && 1.447 + charGlyphs[baseCharIndex].IsClusterStart() && 1.448 + positions[glyphStart].y == 0.0) 1.449 + { 1.450 + charGlyphs[baseCharIndex].SetSimpleGlyph(advance, 1.451 + glyphs[glyphStart]); 1.452 + } else { 1.453 + // collect all glyphs in a list to be assigned to the first char; 1.454 + // there must be at least one in the clump, and we already measured its advance, 1.455 + // hence the placement of the loop-exit test and the measurement of the next glyph 1.456 + while (1) { 1.457 + gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement(); 1.458 + details->mGlyphID = glyphs[glyphStart]; 1.459 + details->mXOffset = 0; 1.460 + details->mYOffset = -positions[glyphStart].y * appUnitsPerDevUnit; 1.461 + details->mAdvance = advance; 1.462 + if (++glyphStart >= glyphEnd) { 1.463 + break; 1.464 + } 1.465 + if (glyphStart < numGlyphs-1) { 1.466 + toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x; 1.467 + } else { 1.468 + toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x; 1.469 + } 1.470 + advance = int32_t(toNextGlyph * appUnitsPerDevUnit); 1.471 + } 1.472 + 1.473 + gfxTextRun::CompressedGlyph g; 1.474 + g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(), 1.475 + true, detailedGlyphs.Length()); 1.476 + aShapedText->SetGlyphs(aOffset + baseCharIndex, g, detailedGlyphs.Elements()); 1.477 + 1.478 + detailedGlyphs.Clear(); 1.479 + } 1.480 + 1.481 + // the rest of the chars in the group are ligature continuations, no associated glyphs 1.482 + while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) { 1.483 + gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex]; 1.484 + NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph"); 1.485 + g.SetComplex(inOrder && g.IsClusterStart(), false, 0); 1.486 + } 1.487 + 1.488 + glyphStart = glyphEnd; 1.489 + charStart = charEnd; 1.490 + } 1.491 + 1.492 + return NS_OK; 1.493 +} 1.494 + 1.495 +#undef SMALL_GLYPH_RUN 1.496 + 1.497 +// Construct the font attribute descriptor that we'll apply by default when creating a CTFontRef. 1.498 +// This will turn off line-edge swashes by default, because we don't know the actual line breaks 1.499 +// when doing glyph shaping. 1.500 +void 1.501 +gfxCoreTextShaper::CreateDefaultFeaturesDescriptor() 1.502 +{ 1.503 + if (sDefaultFeaturesDescriptor != nullptr) { 1.504 + return; 1.505 + } 1.506 + 1.507 + SInt16 val = kSmartSwashType; 1.508 + CFNumberRef swashesType = 1.509 + ::CFNumberCreate(kCFAllocatorDefault, 1.510 + kCFNumberSInt16Type, 1.511 + &val); 1.512 + val = kLineInitialSwashesOffSelector; 1.513 + CFNumberRef lineInitialsOffSelector = 1.514 + ::CFNumberCreate(kCFAllocatorDefault, 1.515 + kCFNumberSInt16Type, 1.516 + &val); 1.517 + 1.518 + CFTypeRef keys[] = { kCTFontFeatureTypeIdentifierKey, 1.519 + kCTFontFeatureSelectorIdentifierKey }; 1.520 + CFTypeRef values[] = { swashesType, 1.521 + lineInitialsOffSelector }; 1.522 + CFDictionaryRef featureSettings[2]; 1.523 + featureSettings[0] = 1.524 + ::CFDictionaryCreate(kCFAllocatorDefault, 1.525 + (const void **) keys, 1.526 + (const void **) values, 1.527 + ArrayLength(keys), 1.528 + &kCFTypeDictionaryKeyCallBacks, 1.529 + &kCFTypeDictionaryValueCallBacks); 1.530 + ::CFRelease(lineInitialsOffSelector); 1.531 + 1.532 + val = kLineFinalSwashesOffSelector; 1.533 + CFNumberRef lineFinalsOffSelector = 1.534 + ::CFNumberCreate(kCFAllocatorDefault, 1.535 + kCFNumberSInt16Type, 1.536 + &val); 1.537 + values[1] = lineFinalsOffSelector; 1.538 + featureSettings[1] = 1.539 + ::CFDictionaryCreate(kCFAllocatorDefault, 1.540 + (const void **) keys, 1.541 + (const void **) values, 1.542 + ArrayLength(keys), 1.543 + &kCFTypeDictionaryKeyCallBacks, 1.544 + &kCFTypeDictionaryValueCallBacks); 1.545 + ::CFRelease(lineFinalsOffSelector); 1.546 + ::CFRelease(swashesType); 1.547 + 1.548 + CFArrayRef featuresArray = 1.549 + ::CFArrayCreate(kCFAllocatorDefault, 1.550 + (const void **) featureSettings, 1.551 + ArrayLength(featureSettings), 1.552 + &kCFTypeArrayCallBacks); 1.553 + ::CFRelease(featureSettings[0]); 1.554 + ::CFRelease(featureSettings[1]); 1.555 + 1.556 + const CFTypeRef attrKeys[] = { kCTFontFeatureSettingsAttribute }; 1.557 + const CFTypeRef attrValues[] = { featuresArray }; 1.558 + CFDictionaryRef attributesDict = 1.559 + ::CFDictionaryCreate(kCFAllocatorDefault, 1.560 + (const void **) attrKeys, 1.561 + (const void **) attrValues, 1.562 + ArrayLength(attrKeys), 1.563 + &kCFTypeDictionaryKeyCallBacks, 1.564 + &kCFTypeDictionaryValueCallBacks); 1.565 + ::CFRelease(featuresArray); 1.566 + 1.567 + sDefaultFeaturesDescriptor = 1.568 + ::CTFontDescriptorCreateWithAttributes(attributesDict); 1.569 + ::CFRelease(attributesDict); 1.570 +} 1.571 + 1.572 +// Create a CTFontRef, with the Common Ligatures feature disabled 1.573 +CTFontRef 1.574 +gfxCoreTextShaper::CreateCTFontWithDisabledLigatures(CGFloat aSize) 1.575 +{ 1.576 + if (sDisableLigaturesDescriptor == nullptr) { 1.577 + // initialize cached descriptor to turn off the Common Ligatures feature 1.578 + SInt16 val = kLigaturesType; 1.579 + CFNumberRef ligaturesType = 1.580 + ::CFNumberCreate(kCFAllocatorDefault, 1.581 + kCFNumberSInt16Type, 1.582 + &val); 1.583 + val = kCommonLigaturesOffSelector; 1.584 + CFNumberRef commonLigaturesOffSelector = 1.585 + ::CFNumberCreate(kCFAllocatorDefault, 1.586 + kCFNumberSInt16Type, 1.587 + &val); 1.588 + 1.589 + const CFTypeRef keys[] = { kCTFontFeatureTypeIdentifierKey, 1.590 + kCTFontFeatureSelectorIdentifierKey }; 1.591 + const CFTypeRef values[] = { ligaturesType, 1.592 + commonLigaturesOffSelector }; 1.593 + CFDictionaryRef featureSettingDict = 1.594 + ::CFDictionaryCreate(kCFAllocatorDefault, 1.595 + (const void **) keys, 1.596 + (const void **) values, 1.597 + ArrayLength(keys), 1.598 + &kCFTypeDictionaryKeyCallBacks, 1.599 + &kCFTypeDictionaryValueCallBacks); 1.600 + ::CFRelease(ligaturesType); 1.601 + ::CFRelease(commonLigaturesOffSelector); 1.602 + 1.603 + CFArrayRef featuresArray = 1.604 + ::CFArrayCreate(kCFAllocatorDefault, 1.605 + (const void **) &featureSettingDict, 1.606 + 1, 1.607 + &kCFTypeArrayCallBacks); 1.608 + ::CFRelease(featureSettingDict); 1.609 + 1.610 + CFDictionaryRef attributesDict = 1.611 + ::CFDictionaryCreate(kCFAllocatorDefault, 1.612 + (const void **) &kCTFontFeatureSettingsAttribute, 1.613 + (const void **) &featuresArray, 1.614 + 1, // count of keys & values 1.615 + &kCFTypeDictionaryKeyCallBacks, 1.616 + &kCFTypeDictionaryValueCallBacks); 1.617 + ::CFRelease(featuresArray); 1.618 + 1.619 + sDisableLigaturesDescriptor = 1.620 + ::CTFontDescriptorCreateCopyWithAttributes(GetDefaultFeaturesDescriptor(), 1.621 + attributesDict); 1.622 + ::CFRelease(attributesDict); 1.623 + } 1.624 + 1.625 + gfxMacFont *f = static_cast<gfxMacFont*>(mFont); 1.626 + return ::CTFontCreateWithGraphicsFont(f->GetCGFontRef(), aSize, nullptr, 1.627 + sDisableLigaturesDescriptor); 1.628 +} 1.629 + 1.630 +void 1.631 +gfxCoreTextShaper::Shutdown() // [static] 1.632 +{ 1.633 + if (sDisableLigaturesDescriptor != nullptr) { 1.634 + ::CFRelease(sDisableLigaturesDescriptor); 1.635 + sDisableLigaturesDescriptor = nullptr; 1.636 + } 1.637 + if (sDefaultFeaturesDescriptor != nullptr) { 1.638 + ::CFRelease(sDefaultFeaturesDescriptor); 1.639 + sDefaultFeaturesDescriptor = nullptr; 1.640 + } 1.641 +}