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