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"
8 #include "nsBidiUtils.h"
9 #include "nsMathUtils.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"
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
),
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)
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()) {
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()) {
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
113 gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode
,
114 hb_codepoint_t variation_selector
) const
118 if (mUseFontGetGlyph
) {
119 gid
= mFont
->GetGlyph(unicode
, variation_selector
);
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
) {
133 gid
= unicode
< UNICODE_BMP_LIMIT
?
134 gfxFontUtils::MapCharToGlyphFormat4(data
+ mSubtableOffset
,
138 gid
= gfxFontUtils::MapCharToGlyphFormat12(data
+ mSubtableOffset
,
142 NS_WARNING("unsupported cmap format, glyphs will be missing");
147 if (gid
&& variation_selector
&& mUVSTableOffset
) {
148 hb_codepoint_t varGID
=
149 gfxFontUtils::MapUVSToGlyphFormat14(data
+ mUVSTableOffset
,
155 // else the variation sequence was not supported, use default
156 // mapping of the character code alone
161 // if there's no glyph for , just use the space glyph instead
162 if (unicode
== 0xA0) {
163 gid
= mFont
->GetSpaceGlyph();
171 HBGetGlyph(hb_font_t
*font
, void *font_data
,
172 hb_codepoint_t unicode
, hb_codepoint_t variation_selector
,
173 hb_codepoint_t
*glyph
,
176 const FontCallbackData
*fcd
=
177 static_cast<const FontCallbackData
*>(font_data
);
178 *glyph
= fcd
->mShaper
->GetGlyph(unicode
, variation_selector
);
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
;
200 AutoSwap_PRUint16 advanceWidth
;
201 AutoSwap_PRInt16 lsb
;
205 HLongMetric metrics
[1]; // actually numberOfHMetrics
206 // the variable-length metrics[] array is immediately followed by:
207 // AutoSwap_PRUint16 leftSideBearing[];
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
));
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
);
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
,
252 /* not yet implemented - no support for used of hinted contour points
253 to fine-tune anchor positions in GPOS AnchorFormat2 */
257 struct KernHeaderFmt0
{
258 AutoSwap_PRUint16 nPairs
;
259 AutoSwap_PRUint16 searchRange
;
260 AutoSwap_PRUint16 entrySelector
;
261 AutoSwap_PRUint16 rangeShift
;
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.
279 GetKernValueFmt0(const void* aSubtable
,
280 uint32_t aSubtableLen
,
281 uint16_t aFirstGlyph
,
282 uint16_t aSecondGlyph
,
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
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
);
305 const KernPair
*mid
= lo
+ (hi
- lo
) / 2;
306 if (KERN_PAIR_KEY(mid
->left
, mid
->right
) < key
) {
313 if (lo
< limit
&& KERN_PAIR_KEY(lo
->left
, lo
->right
) == key
) {
315 aValue
= int16_t(lo
->value
);
316 } else if (aIsMinimum
) {
317 aValue
= std::max(aValue
, int32_t(lo
->value
));
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
345 GetKernValueVersion1Fmt2(const void* aSubtable
,
346 uint32_t aSubtableLen
,
347 uint16_t aFirstGlyph
,
348 uint16_t aSecondGlyph
)
350 if (aSubtableLen
< sizeof(KernHeaderVersion1Fmt2
)) {
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
) {
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
) {
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
) {
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
) {
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
) {
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
;
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
)) {
433 const KernHeaderVersion1Fmt3
* hdr
=
434 reinterpret_cast<const KernHeaderVersion1Fmt3
*>(aSubtable
);
435 if (hdr
->flags
!= 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
) {
449 if (aFirstGlyph
>= glyphCount
|| aSecondGlyph
>= glyphCount
) {
450 // glyphs are out of range for the class tables
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
) {
468 uint8_t ki
= kernIndex
[leftClass
[aFirstGlyph
] * hdr
->rightClassCount
+
469 rightClass
[aSecondGlyph
]];
470 if (ki
>= hdr
->kernValueCount
) {
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
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
) {
501 mKernTable
= mFont
->GetFontTable(TRUETYPE_TAG('k','e','r','n'));
503 mKernTable
= hb_blob_get_empty();
508 const char* base
= hb_blob_get_data(mKernTable
, &len
);
509 if (len
< sizeof(KernTableVersion0
)) {
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
) {
525 const KernTableSubtableHeaderVersion0
* st0
=
526 reinterpret_cast<const KernTableSubtableHeaderVersion0
*>
528 uint16_t subtableLen
= uint16_t(st0
->length
);
529 if (offs
+ subtableLen
> len
) {
533 uint16_t coverage
= st0
->coverage
;
534 if (!(coverage
& KERN0_COVERAGE_HORIZONTAL
)) {
535 // we only care about horizontal kerning (for now)
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
545 uint8_t format
= (coverage
>> 8);
548 GetKernValueFmt0(st0
+ 1, subtableLen
- sizeof(*st0
),
549 aFirstGlyph
, aSecondGlyph
, value
,
550 (coverage
& KERN0_COVERAGE_OVERRIDE
) != 0,
551 (coverage
& KERN0_COVERAGE_MINIMUM
) != 0);
554 // TODO: implement support for other formats,
555 // if they're ever used in practice
559 sprintf(buf
, "unknown kern subtable in %s: "
561 NS_ConvertUTF16toUTF8(mFont
->GetName()).get(),
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
) {
581 const KernTableSubtableHeaderVersion1
* st1
=
582 reinterpret_cast<const KernTableSubtableHeaderVersion1
*>
584 uint32_t subtableLen
= uint32_t(st1
->length
);
586 uint16_t coverage
= st1
->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
599 uint8_t format
= (coverage
& 0xff);
602 GetKernValueFmt0(st1
+ 1, subtableLen
- sizeof(*st1
),
603 aFirstGlyph
, aSecondGlyph
, value
);
606 value
= GetKernValueVersion1Fmt2(st1
, subtableLen
,
607 aFirstGlyph
, aSecondGlyph
);
610 value
= GetKernValueVersion1Fmt3(st1
, subtableLen
,
611 aFirstGlyph
, aSecondGlyph
);
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.
621 sprintf(buf
, "unknown kern subtable in %s: "
623 NS_ConvertUTF16toUTF8(mFont
->GetName()).get(),
635 return FloatToFixed(mFont
->FUnitsToDevUnitsFactor() * value
);
641 HBGetHKerning(hb_font_t
*font
, void *font_data
,
642 hb_codepoint_t first_glyph
, hb_codepoint_t second_glyph
,
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
,
658 return GetMirroredChar(aCh
);
661 static hb_unicode_general_category_t
662 HBGetGeneralCategory(hb_unicode_funcs_t
*ufuncs
, hb_codepoint_t aCh
,
665 return hb_unicode_general_category_t(GetGeneralCategory(aCh
));
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
,
678 return hb_unicode_combining_class_t(GetCombiningClass(aCh
));
682 HBGetEastAsianWidth(hb_unicode_funcs_t
*ufuncs
, hb_codepoint_t aCh
,
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] = {
721 HBUnicodeCompose(hb_unicode_funcs_t
*ufuncs
,
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
733 case 0x05B4: // HIRIQ
734 if (a
== 0x05D9) { // YOD
739 case 0x05B7: // patah
740 if (a
== 0x05F2) { // YIDDISH YOD YOD
743 } else if (a
== 0x05D0) { // ALEF
748 case 0x05B8: // QAMATS
749 if (a
== 0x05D0) { // ALEF
754 case 0x05B9: // HOLAM
755 if (a
== 0x05D5) { // VAV
760 case 0x05BC: // DAGESH
761 if (a
>= 0x05D0 && a
<= 0x05EA) {
762 *ab
= sDageshForms
[a
- 0x05D0];
764 } else if (a
== 0xFB2A) { // SHIN WITH SHIN DOT
767 } else if (a
== 0xFB2B) { // SHIN WITH SIN DOT
788 case 0x05C1: // SHIN DOT
789 if (a
== 0x05E9) { // SHIN
792 } else if (a
== 0xFB49) { // SHIN WITH DAGESH
797 case 0x05C2: // SIN DOT
798 if (a
== 0x05E9) { // SHIN
801 } else if (a
== 0xFB49) { // SHIN WITH DAGESH
813 HBUnicodeDecompose(hb_unicode_funcs_t
*ufuncs
,
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
};
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;
842 gfxHarfBuzzShaper::ShapeText(gfxContext
*aContext
,
843 const PRUnichar
*aText
,
847 gfxShapedText
*aShapedText
)
849 // some font back-ends require this in order to get proper hinted metrics
850 if (!mFont
->SetupCairoFont(aContext
)) {
856 mUseFontGlyphWidths
= mFont
->ProvidesGlyphWidths();
858 // set up the harfbuzz face etc the first time we use the font
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
,
866 hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs
,
869 hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs
,
872 hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs
,
877 hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
878 hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs
,
881 hb_unicode_funcs_set_script_func(sHBUnicodeFuncs
, HBGetScript
,
883 hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs
,
884 HBGetGeneralCategory
,
886 hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs
,
889 hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs
,
892 hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs
,
895 hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs
,
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'));
906 NS_WARNING("failed to load cmap, glyphs will be missing");
910 const uint8_t* data
= (const uint8_t*)hb_blob_get_data(mCmapTable
, &len
);
912 mCmapFormat
= gfxFontUtils::
913 FindPreferredSubtable(data
, len
,
914 &mSubtableOffset
, &mUVSTableOffset
,
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'));
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)
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
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
:
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
) ?
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
);
999 nsCString langString
;
1000 style
->language
->ToUTF8String(langString
);
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
),
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
,
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
1032 gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext
*aContext
,
1033 gfxShapedText
*aShapedText
,
1036 const PRUnichar
*aText
,
1037 hb_buffer_t
*aBuffer
)
1040 const hb_glyph_info_t
*ginfo
= hb_buffer_get_glyph_infos(aBuffer
, &numGlyphs
);
1041 if (numGlyphs
== 0) {
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
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
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.
1111 while (charEnd
!= charLimit
&& charToGlyph
[charEnd
] == NO_GLYPH
) {
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
1129 if (glyphEnd
== glyphStart
) {
1130 // no glyphs, try to extend the clump
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;
1145 if (allGlyphsAreWithinCluster
) {
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
)
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
;
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
;
1189 hb_position_t x_offset
= posInfo
[glyphStart
].x_offset
;
1190 hb_position_t x_advance
= posInfo
[glyphStart
].x_advance
;
1191 nscoord xOffset
, advance
;
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
;
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() &&
1210 posInfo
[glyphStart
].y_offset
== 0 && yPos
== 0)
1212 charGlyphs
[baseCharIndex
].SetSimpleGlyph(advance
,
1213 ginfo
[glyphStart
].codepoint
);
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
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) {
1235 roundY
? appUnitsPerDevUnit
* FixedToIntRound(y_advance
)
1236 : floor(hb2appUnits
* y_advance
+ 0.5);
1238 if (++glyphStart
>= glyphEnd
) {
1242 x_offset
= posInfo
[glyphStart
].x_offset
;
1243 x_advance
= posInfo
[glyphStart
].x_advance
;
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
;
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
;