gfx/thebes/gfxCoreTextShaper.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:d30e741885c9
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "mozilla/ArrayUtils.h"
7 #include "gfxCoreTextShaper.h"
8 #include "gfxMacFont.h"
9 #include "gfxFontUtils.h"
10 #include "mozilla/gfx/2D.h"
11
12 #include <algorithm>
13
14 using namespace mozilla;
15
16 // standard font descriptors that we construct the first time they're needed
17 CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
18 CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
19
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());
28
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 }
37
38 gfxCoreTextShaper::~gfxCoreTextShaper()
39 {
40 if (mAttributesDict) {
41 ::CFRelease(mAttributesDict);
42 }
43 if (mCTFont) {
44 ::CFRelease(mCTFont);
45 }
46 }
47
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.
57
58 bool isRightToLeft = aShapedText->IsRightToLeft();
59 uint32_t length = aLength;
60
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 }
73
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 };
79
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 }
101
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));
108
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 }
122
123 // Now we can create an attributed string
124 CFAttributedStringRef attrStringObj =
125 ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
126 ::CFRelease(stringObj);
127 ::CFRelease(attrObj);
128
129 // Create the CoreText line from our string, then we're done with it
130 CTLineRef line = ::CTLineCreateWithAttributedString(attrStringObj);
131 ::CFRelease(attrStringObj);
132
133 // and finally retrieve the glyph data and store into the gfxTextRun
134 CFArrayRef glyphRuns = ::CTLineGetGlyphRuns(line);
135 uint32_t numRuns = ::CFArrayGetCount(glyphRuns);
136
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 }
149
150 ::CFRelease(line);
151
152 return success;
153 }
154
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
158
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.
169
170 int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
171
172 int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
173 if (numGlyphs == 0) {
174 return NS_OK;
175 }
176
177 int32_t wordLength = aLength;
178
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.
186
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 }
194
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;
202
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 }
220
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 }
230
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 }
243
244 double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
245 nullptr, nullptr, nullptr);
246
247 nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
248 gfxShapedText::CompressedGlyph g;
249 gfxShapedText::CompressedGlyph *charGlyphs =
250 aShapedText->GetCharacterGlyphs() + aOffset;
251
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.
259
260 // The charToGlyph array is indexed by char position within the stringRange of the glyph run.
261
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 }
277
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 //
290
291 // This may find characters that fall outside the range 0:wordLength,
292 // so we won't necessarily use everything we find here.
293
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)
298
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));
307
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 }
323
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 }
340
341 if (glyphEnd == glyphStart + 1) {
342 // for the common case of a single-glyph clump, we can skip the following checks
343 break;
344 }
345
346 if (glyphEnd == glyphStart) {
347 // no glyphs, try to extend the clump
348 continue;
349 }
350
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);
382
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 }
390
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 }
398
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 }
417
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);
427
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);
438
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 }
469
470 gfxTextRun::CompressedGlyph g;
471 g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
472 true, detailedGlyphs.Length());
473 aShapedText->SetGlyphs(aOffset + baseCharIndex, g, detailedGlyphs.Elements());
474
475 detailedGlyphs.Clear();
476 }
477
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 }
484
485 glyphStart = glyphEnd;
486 charStart = charEnd;
487 }
488
489 return NS_OK;
490 }
491
492 #undef SMALL_GLYPH_RUN
493
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 }
503
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);
514
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);
528
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);
544
545 CFArrayRef featuresArray =
546 ::CFArrayCreate(kCFAllocatorDefault,
547 (const void **) featureSettings,
548 ArrayLength(featureSettings),
549 &kCFTypeArrayCallBacks);
550 ::CFRelease(featureSettings[0]);
551 ::CFRelease(featureSettings[1]);
552
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);
563
564 sDefaultFeaturesDescriptor =
565 ::CTFontDescriptorCreateWithAttributes(attributesDict);
566 ::CFRelease(attributesDict);
567 }
568
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);
585
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);
599
600 CFArrayRef featuresArray =
601 ::CFArrayCreate(kCFAllocatorDefault,
602 (const void **) &featureSettingDict,
603 1,
604 &kCFTypeArrayCallBacks);
605 ::CFRelease(featureSettingDict);
606
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);
615
616 sDisableLigaturesDescriptor =
617 ::CTFontDescriptorCreateCopyWithAttributes(GetDefaultFeaturesDescriptor(),
618 attributesDict);
619 ::CFRelease(attributesDict);
620 }
621
622 gfxMacFont *f = static_cast<gfxMacFont*>(mFont);
623 return ::CTFontCreateWithGraphicsFont(f->GetCGFontRef(), aSize, nullptr,
624 sDisableLigaturesDescriptor);
625 }
626
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