Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "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"
14 #include "harfbuzz/hb.h"
15 #include "harfbuzz/hb-ot.h"
17 #include <algorithm>
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))
27 using namespace mozilla; // for AutoSwap_* types
28 using namespace mozilla::unicode; // for Unicode property lookup
30 /*
31 * Creation and destruction; on deletion, release any font tables we're holding
32 */
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 }
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 }
70 #define UNICODE_BMP_LIMIT 0x10000
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;
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!");
85 NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
86 "cmap data not correctly set up, expect disaster");
88 const uint8_t* data =
89 (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
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 }
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 }
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 }
144 return gid;
145 }
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 }
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 };
176 struct HLongMetric {
177 AutoSwap_PRUint16 advanceWidth;
178 AutoSwap_PRInt16 lsb;
179 };
181 struct HMetrics {
182 HLongMetric metrics[1]; // actually numberOfHMetrics
183 // the variable-length metrics[] array is immediately followed by:
184 // AutoSwap_PRUint16 leftSideBearing[];
185 };
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
194 NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nullptr,
195 "font is lacking metrics, we shouldn't be here");
197 if (glyph >= uint32_t(mNumLongMetrics)) {
198 glyph = mNumLongMetrics - 1;
199 }
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 }
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 }
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 }
236 struct KernHeaderFmt0 {
237 AutoSwap_PRUint16 nPairs;
238 AutoSwap_PRUint16 searchRange;
239 AutoSwap_PRUint16 entrySelector;
240 AutoSwap_PRUint16 rangeShift;
241 };
243 struct KernPair {
244 AutoSwap_PRUint16 left;
245 AutoSwap_PRUint16 right;
246 AutoSwap_PRInt16 value;
247 };
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);
269 const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1);
270 const KernPair *hi = lo + uint16_t(hdr->nPairs);
271 const KernPair *limit = hi;
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 }
280 #define KERN_PAIR_KEY(l,r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
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 }
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 }
303 // Get kerning value from Apple (version 1.0) kern table,
304 // subtable format 2 (simple N x M array of kerning values)
306 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
307 // for details of version 1.0 format 2 subtable.
309 struct KernHeaderVersion1Fmt2 {
310 KernTableSubtableHeaderVersion1 header;
311 AutoSwap_PRUint16 rowWidth;
312 AutoSwap_PRUint16 leftOffsetTable;
313 AutoSwap_PRUint16 rightOffsetTable;
314 AutoSwap_PRUint16 array;
315 };
317 struct KernClassTableHdr {
318 AutoSwap_PRUint16 firstGlyph;
319 AutoSwap_PRUint16 nGlyphs;
320 AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries
321 };
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 }
333 const char* base = reinterpret_cast<const char*>(aSubtable);
334 const char* subtableEnd = base + aSubtableLen;
336 const KernHeaderVersion1Fmt2* h =
337 reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
338 uint32_t offset = h->array;
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 }
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 }
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 }
386 // Get kerning value from Apple (version 1.0) kern table,
387 // subtable format 3 (simple N x M array of kerning values)
389 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
390 // for details of version 1.0 format 3 subtable.
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 };
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 }
412 const KernHeaderVersion1Fmt3* hdr =
413 reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
414 if (hdr->flags != 0) {
415 return 0;
416 }
418 uint16_t glyphCount = hdr->glyphCount;
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 }
428 if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
429 // glyphs are out of range for the class tables
430 return 0;
431 }
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;
441 uint8_t lc = leftClass[aFirstGlyph];
442 uint8_t rc = rightClass[aSecondGlyph];
443 if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
444 return 0;
445 }
447 uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
448 rightClass[aSecondGlyph]];
449 if (ki >= hdr->kernValueCount) {
450 return 0;
451 }
453 return kernValue[ki];
454 }
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
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
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 }
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 }
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;
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 }
613 if (value != 0) {
614 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
615 }
616 return 0;
617 }
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 }
629 /*
630 * HarfBuzz unicode property callbacks
631 */
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 }
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 }
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 }
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 }
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 }
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 };
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);
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 }
788 return found;
789 }
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 }
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);
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 }
822 /*
823 * gfxFontShaper override to initialize the text run using HarfBuzz
824 */
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'));
831 bool
832 gfxHarfBuzzShaper::Initialize()
833 {
834 if (mInitialized) {
835 return mHBFont != nullptr;
836 }
837 mInitialized = true;
838 mCallbackData.mShaper = this;
840 mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
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);
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 }
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 }
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 }
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);
943 return true;
944 }
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 }
959 mCallbackData.mContext = aContext;
961 if (!Initialize()) {
962 return false;
963 }
965 const gfxFontStyle *style = mFont->GetStyle();
967 nsAutoTArray<hb_feature_t,20> features;
968 nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
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 }
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);
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);
1012 uint32_t length = aLength;
1013 hb_buffer_add_utf16(buffer,
1014 reinterpret_cast<const uint16_t*>(aText),
1015 length, 0, length);
1017 hb_shape(mHBFont, buffer, features.Elements(), features.Length());
1019 if (isRightToLeft) {
1020 hb_buffer_reverse(buffer);
1021 }
1023 nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
1024 aText, buffer);
1026 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
1027 hb_buffer_destroy(buffer);
1029 return NS_SUCCEEDED(rv);
1030 }
1032 #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
1033 // will fit without requiring separate allocation
1034 // for charToGlyphArray
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 }
1050 nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
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 }
1059 int32_t *charToGlyph = charToGlyphArray.Elements();
1060 for (uint32_t offset = 0; offset < wordLength; ++offset) {
1061 charToGlyph[offset] = NO_GLYPH;
1062 }
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 }
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
1074 bool roundX;
1075 bool roundY;
1076 aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
1078 int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
1079 gfxShapedText::CompressedGlyph *charGlyphs =
1080 aShapedText->GetCharacterGlyphs() + aOffset;
1082 // factor to convert 16.16 fixed-point pixels to app units
1083 // (only used if not rounding)
1084 double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
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;
1098 // keep track of y-position to set glyph offsets if needed
1099 nscoord yPos = 0;
1101 const hb_glyph_position_t *posInfo =
1102 hb_buffer_get_glyph_positions(aBuffer, nullptr);
1104 while (glyphStart < int32_t(numGlyphs)) {
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 }
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 }
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 }
1134 if (glyphEnd == glyphStart) {
1135 // no glyphs, try to extend the clump
1136 continue;
1137 }
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 }
1155 NS_ASSERTION(glyphStart < glyphEnd,
1156 "character/glyph clump contains no glyphs!");
1157 NS_ASSERTION(charStart != charEnd,
1158 "character/glyph clump contains no characters!");
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;
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);
1180 // Now we're ready to set the glyph info in the textRun
1181 int32_t glyphsInClump = glyphEnd - glyphStart;
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 }
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;
1229 details->mXOffset = xOffset;
1230 details->mAdvance = advance;
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));
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 }
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 }
1267 gfxShapedText::CompressedGlyph g;
1268 g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
1269 true, detailedGlyphs.Length());
1270 aShapedText->SetGlyphs(aOffset + baseCharIndex,
1271 g, detailedGlyphs.Elements());
1273 detailedGlyphs.Clear();
1274 }
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 }
1285 glyphStart = glyphEnd;
1286 charStart = charEnd;
1287 }
1289 return NS_OK;
1290 }