|
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 } |