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