Bug 1758688 [wpt PR 33067] - [FedCM] Make revoke a non-static method, a=testonly
[gecko.git] / gfx / thebes / gfxHarfBuzzShaper.cpp
blob313d265a6757b5ace3cd1699e7e446a163738d45
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "gfxFontConstants.h"
9 #include "gfxHarfBuzzShaper.h"
10 #include "gfxFontUtils.h"
11 #include "gfxTextRun.h"
12 #include "mozilla/Sprintf.h"
13 #include "mozilla/intl/String.h"
14 #include "mozilla/intl/UnicodeProperties.h"
15 #include "mozilla/intl/UnicodeScriptCodes.h"
16 #include "nsUnicodeProperties.h"
18 #include "harfbuzz/hb.h"
19 #include "harfbuzz/hb-ot.h"
21 #include <algorithm>
23 #define FloatToFixed(f) (65536 * (f))
24 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
25 // Right shifts of negative (signed) integers are undefined, as are overflows
26 // when converting unsigned to negative signed integers.
27 // (If speed were an issue we could make some 2's complement assumptions.)
28 #define FixedToIntRound(f) \
29 ((f) > 0 ? ((32768 + (f)) >> 16) : -((32767 - (f)) >> 16))
31 using namespace mozilla; // for AutoSwap_* types
32 using namespace mozilla::unicode; // for Unicode property lookup
35 * Creation and destruction; on deletion, release any font tables we're holding
38 gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont* aFont)
39 : gfxFontShaper(aFont),
40 mHBFont(nullptr),
41 mBuffer(nullptr),
42 mCallbackData(),
43 mKernTable(nullptr),
44 mHmtxTable(nullptr),
45 mVmtxTable(nullptr),
46 mVORGTable(nullptr),
47 mLocaTable(nullptr),
48 mGlyfTable(nullptr),
49 mCmapTable(nullptr),
50 mCmapFormat(-1),
51 mSubtableOffset(0),
52 mUVSTableOffset(0),
53 mNumLongHMetrics(0),
54 mNumLongVMetrics(0),
55 mDefaultVOrg(-1.0),
56 mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
57 mIsSymbolFont(false),
58 mUseFontGlyphWidths(aFont->ProvidesGlyphWidths()),
59 mInitialized(false),
60 mVerticalInitialized(false),
61 mUseVerticalPresentationForms(false),
62 mLoadedLocaGlyf(false),
63 mLocaLongOffsets(false) {}
65 gfxHarfBuzzShaper::~gfxHarfBuzzShaper() {
66 // hb_*_destroy functions are safe to call on nullptr
67 hb_blob_destroy(mCmapTable);
68 hb_blob_destroy(mHmtxTable);
69 hb_blob_destroy(mKernTable);
70 hb_blob_destroy(mVmtxTable);
71 hb_blob_destroy(mVORGTable);
72 hb_blob_destroy(mLocaTable);
73 hb_blob_destroy(mGlyfTable);
74 hb_font_destroy(mHBFont);
75 hb_buffer_destroy(mBuffer);
78 #define UNICODE_BMP_LIMIT 0x10000
80 hb_codepoint_t gfxHarfBuzzShaper::GetNominalGlyph(
81 hb_codepoint_t unicode) const {
82 hb_codepoint_t gid = 0;
84 if (mUseFontGetGlyph) {
85 gid = mFont->GetGlyph(unicode, 0);
86 } else {
87 // we only instantiate a harfbuzz shaper if there's a cmap available
88 NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
89 "cmap data not correctly set up, expect disaster");
91 uint32_t length;
92 const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
94 switch (mCmapFormat) {
95 case 4:
96 gid =
97 unicode < UNICODE_BMP_LIMIT
98 ? gfxFontUtils::MapCharToGlyphFormat4(
99 data + mSubtableOffset, length - mSubtableOffset, unicode)
100 : 0;
101 break;
102 case 10:
103 gid = gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
104 unicode);
105 break;
106 case 12:
107 case 13:
108 gid = gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset,
109 unicode);
110 break;
111 default:
112 NS_WARNING("unsupported cmap format, glyphs will be missing");
113 break;
117 if (!gid) {
118 if (mIsSymbolFont) {
119 // For legacy MS Symbol fonts, we try mapping the given character code
120 // to the PUA range used by these fonts' cmaps.
121 if (auto pua = gfxFontUtils::MapLegacySymbolFontCharToPUA(unicode)) {
122 gid = GetNominalGlyph(pua);
124 if (gid) {
125 return gid;
128 switch (unicode) {
129 case 0xA0:
130 // if there's no glyph for &nbsp;, just use the space glyph instead.
131 gid = mFont->GetSpaceGlyph();
132 break;
133 case 0x2010:
134 case 0x2011:
135 // For Unicode HYPHEN and NON-BREAKING HYPHEN, fall back to the ASCII
136 // HYPHEN-MINUS as a substitute.
137 gid = GetNominalGlyph('-');
138 break;
142 return gid;
145 hb_codepoint_t gfxHarfBuzzShaper::GetVariationGlyph(
146 hb_codepoint_t unicode, hb_codepoint_t variation_selector) const {
147 if (mUseFontGetGlyph) {
148 return mFont->GetGlyph(unicode, variation_selector);
151 NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
152 "we cannot be using this font!");
153 NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
154 "cmap data not correctly set up, expect disaster");
156 uint32_t length;
157 const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
159 if (mUVSTableOffset) {
160 hb_codepoint_t gid = gfxFontUtils::MapUVSToGlyphFormat14(
161 data + mUVSTableOffset, unicode, variation_selector);
162 if (gid) {
163 return gid;
167 uint32_t compat = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
168 if (compat) {
169 switch (mCmapFormat) {
170 case 4:
171 if (compat < UNICODE_BMP_LIMIT) {
172 return gfxFontUtils::MapCharToGlyphFormat4(
173 data + mSubtableOffset, length - mSubtableOffset, compat);
175 break;
176 case 10:
177 return gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
178 compat);
179 break;
180 case 12:
181 case 13:
182 return gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset,
183 compat);
184 break;
188 return 0;
191 static int VertFormsGlyphCompare(const void* aKey, const void* aElem) {
192 return int(*((hb_codepoint_t*)(aKey))) - int(*((uint16_t*)(aElem)));
195 // Return a vertical presentation-form codepoint corresponding to the
196 // given Unicode value, or 0 if no such form is available.
197 hb_codepoint_t gfxHarfBuzzShaper::GetVerticalPresentationForm(
198 hb_codepoint_t aUnicode) {
199 static const uint16_t sVerticalForms[][2] = {
200 {0x2013, 0xfe32}, // EN DASH
201 {0x2014, 0xfe31}, // EM DASH
202 {0x2025, 0xfe30}, // TWO DOT LEADER
203 {0x2026, 0xfe19}, // HORIZONTAL ELLIPSIS
204 {0x3001, 0xfe11}, // IDEOGRAPHIC COMMA
205 {0x3002, 0xfe12}, // IDEOGRAPHIC FULL STOP
206 {0x3008, 0xfe3f}, // LEFT ANGLE BRACKET
207 {0x3009, 0xfe40}, // RIGHT ANGLE BRACKET
208 {0x300a, 0xfe3d}, // LEFT DOUBLE ANGLE BRACKET
209 {0x300b, 0xfe3e}, // RIGHT DOUBLE ANGLE BRACKET
210 {0x300c, 0xfe41}, // LEFT CORNER BRACKET
211 {0x300d, 0xfe42}, // RIGHT CORNER BRACKET
212 {0x300e, 0xfe43}, // LEFT WHITE CORNER BRACKET
213 {0x300f, 0xfe44}, // RIGHT WHITE CORNER BRACKET
214 {0x3010, 0xfe3b}, // LEFT BLACK LENTICULAR BRACKET
215 {0x3011, 0xfe3c}, // RIGHT BLACK LENTICULAR BRACKET
216 {0x3014, 0xfe39}, // LEFT TORTOISE SHELL BRACKET
217 {0x3015, 0xfe3a}, // RIGHT TORTOISE SHELL BRACKET
218 {0x3016, 0xfe17}, // LEFT WHITE LENTICULAR BRACKET
219 {0x3017, 0xfe18}, // RIGHT WHITE LENTICULAR BRACKET
220 {0xfe4f, 0xfe34}, // WAVY LOW LINE
221 {0xff01, 0xfe15}, // FULLWIDTH EXCLAMATION MARK
222 {0xff08, 0xfe35}, // FULLWIDTH LEFT PARENTHESIS
223 {0xff09, 0xfe36}, // FULLWIDTH RIGHT PARENTHESIS
224 {0xff0c, 0xfe10}, // FULLWIDTH COMMA
225 {0xff1a, 0xfe13}, // FULLWIDTH COLON
226 {0xff1b, 0xfe14}, // FULLWIDTH SEMICOLON
227 {0xff1f, 0xfe16}, // FULLWIDTH QUESTION MARK
228 {0xff3b, 0xfe47}, // FULLWIDTH LEFT SQUARE BRACKET
229 {0xff3d, 0xfe48}, // FULLWIDTH RIGHT SQUARE BRACKET
230 {0xff3f, 0xfe33}, // FULLWIDTH LOW LINE
231 {0xff5b, 0xfe37}, // FULLWIDTH LEFT CURLY BRACKET
232 {0xff5d, 0xfe38} // FULLWIDTH RIGHT CURLY BRACKET
234 const uint16_t* charPair = static_cast<const uint16_t*>(
235 bsearch(&aUnicode, sVerticalForms, ArrayLength(sVerticalForms),
236 sizeof(sVerticalForms[0]), VertFormsGlyphCompare));
237 return charPair ? charPair[1] : 0;
240 static hb_bool_t HBGetNominalGlyph(hb_font_t* font, void* font_data,
241 hb_codepoint_t unicode,
242 hb_codepoint_t* glyph, void* user_data) {
243 const gfxHarfBuzzShaper::FontCallbackData* fcd =
244 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
246 if (fcd->mShaper->UseVerticalPresentationForms()) {
247 hb_codepoint_t verticalForm =
248 gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
249 if (verticalForm) {
250 *glyph = fcd->mShaper->GetNominalGlyph(verticalForm);
251 if (*glyph != 0) {
252 return true;
255 // fall back to the non-vertical form if we didn't find an alternate
258 *glyph = fcd->mShaper->GetNominalGlyph(unicode);
259 return *glyph != 0;
262 static hb_bool_t HBGetVariationGlyph(hb_font_t* font, void* font_data,
263 hb_codepoint_t unicode,
264 hb_codepoint_t variation_selector,
265 hb_codepoint_t* glyph, void* user_data) {
266 const gfxHarfBuzzShaper::FontCallbackData* fcd =
267 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
269 if (fcd->mShaper->UseVerticalPresentationForms()) {
270 hb_codepoint_t verticalForm =
271 gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
272 if (verticalForm) {
273 *glyph =
274 fcd->mShaper->GetVariationGlyph(verticalForm, variation_selector);
275 if (*glyph != 0) {
276 return true;
279 // fall back to the non-vertical form if we didn't find an alternate
282 *glyph = fcd->mShaper->GetVariationGlyph(unicode, variation_selector);
283 return *glyph != 0;
286 // Glyph metrics structures, shared (with appropriate reinterpretation of
287 // field names) by horizontal and vertical metrics tables.
288 struct LongMetric {
289 AutoSwap_PRUint16 advanceWidth; // or advanceHeight, when vertical
290 AutoSwap_PRInt16 lsb; // or tsb, when vertical
293 struct GlyphMetrics {
294 LongMetric metrics[1]; // actually numberOfLongMetrics
295 // the variable-length metrics[] array is immediately followed by:
296 // AutoSwap_PRUint16 leftSideBearing[];
299 hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const {
300 // font did not implement GetGlyphWidth, so get an unhinted value
301 // directly from the font tables
303 NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
304 "font is lacking metrics, we shouldn't be here");
306 if (glyph >= uint32_t(mNumLongHMetrics)) {
307 glyph = mNumLongHMetrics - 1;
310 // glyph must be valid now, because we checked during initialization
311 // that mNumLongHMetrics is > 0, and that the metrics table is large enough
312 // to contain mNumLongHMetrics records
313 const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
314 hb_blob_get_data(mHmtxTable, nullptr));
315 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
316 uint16_t(metrics->metrics[glyph].advanceWidth));
319 hb_position_t gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) {
320 InitializeVertical();
322 if (!mVmtxTable) {
323 // Must be a "vertical" font that doesn't actually have vertical metrics;
324 // use a fixed advance.
325 return FloatToFixed(
326 mFont->GetMetrics(nsFontMetrics::eVertical).aveCharWidth);
329 NS_ASSERTION(mNumLongVMetrics > 0,
330 "font is lacking metrics, we shouldn't be here");
332 if (glyph >= uint32_t(mNumLongVMetrics)) {
333 glyph = mNumLongVMetrics - 1;
336 // glyph must be valid now, because we checked during initialization
337 // that mNumLongVMetrics is > 0, and that the metrics table is large enough
338 // to contain mNumLongVMetrics records
339 const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
340 hb_blob_get_data(mVmtxTable, nullptr));
341 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
342 uint16_t(metrics->metrics[glyph].advanceWidth));
345 /* static */
346 hb_position_t gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t* font,
347 void* font_data,
348 hb_codepoint_t glyph,
349 void* user_data) {
350 const gfxHarfBuzzShaper::FontCallbackData* fcd =
351 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
352 const gfxHarfBuzzShaper* shaper = fcd->mShaper;
353 if (shaper->mUseFontGlyphWidths) {
354 return shaper->GetFont()->GetGlyphWidth(glyph);
356 return shaper->GetGlyphHAdvance(glyph);
359 /* static */
360 hb_position_t gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t* font,
361 void* font_data,
362 hb_codepoint_t glyph,
363 void* user_data) {
364 const gfxHarfBuzzShaper::FontCallbackData* fcd =
365 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
366 // Currently, we don't offer gfxFont subclasses a method to override this
367 // and provide hinted platform-specific vertical advances (analogous to the
368 // GetGlyphWidth method for horizontal advances). If that proves necessary,
369 // we'll add a new gfxFont method and call it from here.
371 // We negate the value from GetGlyphVAdvance here because harfbuzz shapes
372 // with a coordinate system where positive is upwards, whereas the inline
373 // direction in which glyphs advance is downwards.
374 return -fcd->mShaper->GetGlyphVAdvance(glyph);
377 struct VORG {
378 AutoSwap_PRUint16 majorVersion;
379 AutoSwap_PRUint16 minorVersion;
380 AutoSwap_PRInt16 defaultVertOriginY;
381 AutoSwap_PRUint16 numVertOriginYMetrics;
384 struct VORGrec {
385 AutoSwap_PRUint16 glyphIndex;
386 AutoSwap_PRInt16 vertOriginY;
389 /* static */
390 hb_bool_t gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t* font, void* font_data,
391 hb_codepoint_t glyph,
392 hb_position_t* x,
393 hb_position_t* y,
394 void* user_data) {
395 const gfxHarfBuzzShaper::FontCallbackData* fcd =
396 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
397 fcd->mShaper->GetGlyphVOrigin(glyph, x, y);
398 return true;
401 void gfxHarfBuzzShaper::GetGlyphVOrigin(hb_codepoint_t aGlyph,
402 hb_position_t* aX,
403 hb_position_t* aY) const {
404 *aX = 0.5 * (mUseFontGlyphWidths ? mFont->GetGlyphWidth(aGlyph)
405 : GetGlyphHAdvance(aGlyph));
407 if (mVORGTable) {
408 // We checked in Initialize() that the VORG table is safely readable,
409 // so no length/bounds-check needed here.
410 const VORG* vorg =
411 reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
413 const VORGrec* lo = reinterpret_cast<const VORGrec*>(vorg + 1);
414 const VORGrec* hi = lo + uint16_t(vorg->numVertOriginYMetrics);
415 const VORGrec* limit = hi;
416 while (lo < hi) {
417 const VORGrec* mid = lo + (hi - lo) / 2;
418 if (uint16_t(mid->glyphIndex) < aGlyph) {
419 lo = mid + 1;
420 } else {
421 hi = mid;
425 if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) {
426 *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
427 int16_t(lo->vertOriginY));
428 } else {
429 *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
430 int16_t(vorg->defaultVertOriginY));
432 return;
435 if (mVmtxTable) {
436 bool emptyGlyf;
437 const Glyf* glyf = FindGlyf(aGlyph, &emptyGlyf);
438 if (glyf) {
439 if (emptyGlyf) {
440 *aY = 0;
441 return;
444 const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
445 hb_blob_get_data(mVmtxTable, nullptr));
446 int16_t lsb;
447 if (aGlyph < hb_codepoint_t(mNumLongVMetrics)) {
448 // Glyph is covered by the first (advance & sidebearing) array
449 lsb = int16_t(metrics->metrics[aGlyph].lsb);
450 } else {
451 // Glyph is covered by the second (sidebearing-only) array
452 const AutoSwap_PRInt16* sidebearings =
453 reinterpret_cast<const AutoSwap_PRInt16*>(
454 &metrics->metrics[mNumLongVMetrics]);
455 lsb = int16_t(sidebearings[aGlyph - mNumLongVMetrics]);
457 *aY = FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
458 (lsb + int16_t(glyf->yMax)));
459 return;
460 } else {
461 // XXX TODO: not a truetype font; need to get glyph extents
462 // via some other API?
463 // For now, fall through to default code below.
467 if (mDefaultVOrg < 0.0) {
468 // XXX should we consider using OS/2 sTypo* metrics if available?
470 gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
471 TRUETYPE_TAG('h', 'h', 'e', 'a'));
472 if (hheaTable) {
473 uint32_t len;
474 const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
475 hb_blob_get_data(hheaTable, &len));
476 if (len >= sizeof(MetricsHeader)) {
477 // divide up the default advance we're using (1em) in proportion
478 // to ascender:descender from the hhea table
479 int16_t a = int16_t(hhea->ascender);
480 int16_t d = int16_t(hhea->descender);
481 mDefaultVOrg = FloatToFixed(GetFont()->GetAdjustedSize() * a / (a - d));
485 if (mDefaultVOrg < 0.0) {
486 // Last resort, for non-sfnt fonts: get the horizontal metrics and
487 // compute a default VOrg from their ascent and descent.
488 const gfxFont::Metrics& mtx = mFont->GetHorizontalMetrics();
489 gfxFloat advance =
490 mFont->GetMetrics(nsFontMetrics::eVertical).aveCharWidth;
491 gfxFloat ascent = mtx.emAscent;
492 gfxFloat height = ascent + mtx.emDescent;
493 // vOrigin that will place the glyph so that its origin is shifted
494 // down most of the way within overall (vertical) advance, in
495 // proportion to the font ascent as a part of the overall font
496 // height.
497 mDefaultVOrg = FloatToFixed(advance * ascent / height);
501 *aY = mDefaultVOrg;
504 static hb_bool_t HBGetGlyphExtents(hb_font_t* font, void* font_data,
505 hb_codepoint_t glyph,
506 hb_glyph_extents_t* extents,
507 void* user_data) {
508 const gfxHarfBuzzShaper::FontCallbackData* fcd =
509 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
510 return fcd->mShaper->GetGlyphExtents(glyph, extents);
513 // Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
514 // Returns null if not found, otherwise pointer to the beginning of the
515 // glyph's data. Sets aEmptyGlyf true if there is no actual data;
516 // otherwise, it's guaranteed that we can read at least the bounding box.
517 const gfxHarfBuzzShaper::Glyf* gfxHarfBuzzShaper::FindGlyf(
518 hb_codepoint_t aGlyph, bool* aEmptyGlyf) const {
519 if (!mLoadedLocaGlyf) {
520 mLoadedLocaGlyf = true; // only try this once; if it fails, this
521 // isn't a truetype font
522 gfxFontEntry* entry = mFont->GetFontEntry();
523 uint32_t len;
524 gfxFontEntry::AutoTable headTable(entry, TRUETYPE_TAG('h', 'e', 'a', 'd'));
525 if (!headTable) {
526 return nullptr;
528 const HeadTable* head =
529 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, &len));
530 if (len < sizeof(HeadTable)) {
531 return nullptr;
533 mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
534 mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l', 'o', 'c', 'a'));
535 mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g', 'l', 'y', 'f'));
538 if (!mLocaTable || !mGlyfTable) {
539 // it's not a truetype font
540 return nullptr;
543 uint32_t offset; // offset of glyph record in the 'glyf' table
544 uint32_t len;
545 const char* data = hb_blob_get_data(mLocaTable, &len);
546 if (mLocaLongOffsets) {
547 if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
548 return nullptr;
550 const AutoSwap_PRUint32* offsets =
551 reinterpret_cast<const AutoSwap_PRUint32*>(data);
552 offset = offsets[aGlyph];
553 *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
554 } else {
555 if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
556 return nullptr;
558 const AutoSwap_PRUint16* offsets =
559 reinterpret_cast<const AutoSwap_PRUint16*>(data);
560 offset = uint16_t(offsets[aGlyph]);
561 *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
562 offset *= 2;
565 data = hb_blob_get_data(mGlyfTable, &len);
566 if (offset + sizeof(Glyf) > len) {
567 return nullptr;
570 return reinterpret_cast<const Glyf*>(data + offset);
573 hb_bool_t gfxHarfBuzzShaper::GetGlyphExtents(
574 hb_codepoint_t aGlyph, hb_glyph_extents_t* aExtents) const {
575 bool emptyGlyf;
576 const Glyf* glyf = FindGlyf(aGlyph, &emptyGlyf);
577 if (!glyf) {
578 // TODO: for non-truetype fonts, get extents some other way?
579 return false;
582 if (emptyGlyf) {
583 aExtents->x_bearing = 0;
584 aExtents->y_bearing = 0;
585 aExtents->width = 0;
586 aExtents->height = 0;
587 return true;
590 double f = mFont->FUnitsToDevUnitsFactor();
591 aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
592 aExtents->width =
593 FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
595 // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
596 // positive-upwards; hence the apparently-reversed subtractions here.
597 aExtents->y_bearing = FloatToFixed(int16_t(glyf->yMax) * f -
598 mFont->GetHorizontalMetrics().emAscent);
599 aExtents->height =
600 FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
602 return true;
605 static hb_bool_t HBGetContourPoint(hb_font_t* font, void* font_data,
606 unsigned int point_index,
607 hb_codepoint_t glyph, hb_position_t* x,
608 hb_position_t* y, void* user_data) {
609 /* not yet implemented - no support for used of hinted contour points
610 to fine-tune anchor positions in GPOS AnchorFormat2 */
611 return false;
614 struct KernHeaderFmt0 {
615 AutoSwap_PRUint16 nPairs;
616 AutoSwap_PRUint16 searchRange;
617 AutoSwap_PRUint16 entrySelector;
618 AutoSwap_PRUint16 rangeShift;
621 struct KernPair {
622 AutoSwap_PRUint16 left;
623 AutoSwap_PRUint16 right;
624 AutoSwap_PRInt16 value;
627 // Find a kern pair in a Format 0 subtable.
628 // The aSubtable parameter points to the subtable itself, NOT its header,
629 // as the header structure differs between Windows and Mac (v0 and v1.0)
630 // versions of the 'kern' table.
631 // aSubtableLen is the length of the subtable EXCLUDING its header.
632 // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
633 // added to aValue, so that multiple subtables can accumulate a total
634 // kerning value for a given pair.
635 static void GetKernValueFmt0(const void* aSubtable, uint32_t aSubtableLen,
636 uint16_t aFirstGlyph, uint16_t aSecondGlyph,
637 int32_t& aValue, bool aIsOverride = false,
638 bool aIsMinimum = false) {
639 const KernHeaderFmt0* hdr =
640 reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
642 const KernPair* lo = reinterpret_cast<const KernPair*>(hdr + 1);
643 const KernPair* hi = lo + uint16_t(hdr->nPairs);
644 const KernPair* limit = hi;
646 if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
647 reinterpret_cast<const char*>(hi)) {
648 // subtable is not large enough to contain the claimed number
649 // of kern pairs, so just ignore it
650 return;
653 #define KERN_PAIR_KEY(l, r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
655 uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
656 while (lo < hi) {
657 const KernPair* mid = lo + (hi - lo) / 2;
658 if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
659 lo = mid + 1;
660 } else {
661 hi = mid;
665 if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
666 if (aIsOverride) {
667 aValue = int16_t(lo->value);
668 } else if (aIsMinimum) {
669 aValue = std::max(aValue, int32_t(lo->value));
670 } else {
671 aValue += int16_t(lo->value);
676 // Get kerning value from Apple (version 1.0) kern table,
677 // subtable format 2 (simple N x M array of kerning values)
679 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
680 // for details of version 1.0 format 2 subtable.
682 struct KernHeaderVersion1Fmt2 {
683 KernTableSubtableHeaderVersion1 header;
684 AutoSwap_PRUint16 rowWidth;
685 AutoSwap_PRUint16 leftOffsetTable;
686 AutoSwap_PRUint16 rightOffsetTable;
687 AutoSwap_PRUint16 array;
690 struct KernClassTableHdr {
691 AutoSwap_PRUint16 firstGlyph;
692 AutoSwap_PRUint16 nGlyphs;
693 AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries
696 static int16_t GetKernValueVersion1Fmt2(const void* aSubtable,
697 uint32_t aSubtableLen,
698 uint16_t aFirstGlyph,
699 uint16_t aSecondGlyph) {
700 if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
701 return 0;
704 const char* base = reinterpret_cast<const char*>(aSubtable);
705 const char* subtableEnd = base + aSubtableLen;
707 const KernHeaderVersion1Fmt2* h =
708 reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
709 uint32_t offset = h->array;
711 const KernClassTableHdr* leftClassTable =
712 reinterpret_cast<const KernClassTableHdr*>(base +
713 uint16_t(h->leftOffsetTable));
714 if (reinterpret_cast<const char*>(leftClassTable) +
715 sizeof(KernClassTableHdr) >
716 subtableEnd) {
717 return 0;
719 if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) {
720 aFirstGlyph -= uint16_t(leftClassTable->firstGlyph);
721 if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) {
722 if (reinterpret_cast<const char*>(leftClassTable) +
723 sizeof(KernClassTableHdr) + aFirstGlyph * sizeof(uint16_t) >=
724 subtableEnd) {
725 return 0;
727 offset = uint16_t(leftClassTable->offsets[aFirstGlyph]);
731 const KernClassTableHdr* rightClassTable =
732 reinterpret_cast<const KernClassTableHdr*>(base +
733 uint16_t(h->rightOffsetTable));
734 if (reinterpret_cast<const char*>(rightClassTable) +
735 sizeof(KernClassTableHdr) >
736 subtableEnd) {
737 return 0;
739 if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) {
740 aSecondGlyph -= uint16_t(rightClassTable->firstGlyph);
741 if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) {
742 if (reinterpret_cast<const char*>(rightClassTable) +
743 sizeof(KernClassTableHdr) + aSecondGlyph * sizeof(uint16_t) >=
744 subtableEnd) {
745 return 0;
747 offset += uint16_t(rightClassTable->offsets[aSecondGlyph]);
751 const AutoSwap_PRInt16* pval =
752 reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
753 if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
754 return 0;
756 return *pval;
759 // Get kerning value from Apple (version 1.0) kern table,
760 // subtable format 3 (simple N x M array of kerning values)
762 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
763 // for details of version 1.0 format 3 subtable.
765 struct KernHeaderVersion1Fmt3 {
766 KernTableSubtableHeaderVersion1 header;
767 AutoSwap_PRUint16 glyphCount;
768 uint8_t kernValueCount;
769 uint8_t leftClassCount;
770 uint8_t rightClassCount;
771 uint8_t flags;
774 static int16_t GetKernValueVersion1Fmt3(const void* aSubtable,
775 uint32_t aSubtableLen,
776 uint16_t aFirstGlyph,
777 uint16_t aSecondGlyph) {
778 // check that we can safely read the header fields
779 if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
780 return 0;
783 const KernHeaderVersion1Fmt3* hdr =
784 reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
785 if (hdr->flags != 0) {
786 return 0;
789 uint16_t glyphCount = hdr->glyphCount;
791 // check that table is large enough for the arrays
792 if (sizeof(KernHeaderVersion1Fmt3) + hdr->kernValueCount * sizeof(int16_t) +
793 glyphCount + glyphCount + hdr->leftClassCount * hdr->rightClassCount >
794 aSubtableLen) {
795 return 0;
798 if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
799 // glyphs are out of range for the class tables
800 return 0;
803 // get pointers to the four arrays within the subtable
804 const AutoSwap_PRInt16* kernValue =
805 reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
806 const uint8_t* leftClass =
807 reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount);
808 const uint8_t* rightClass = leftClass + glyphCount;
809 const uint8_t* kernIndex = rightClass + glyphCount;
811 uint8_t lc = leftClass[aFirstGlyph];
812 uint8_t rc = rightClass[aSecondGlyph];
813 if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
814 return 0;
817 uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
818 rightClass[aSecondGlyph]];
819 if (ki >= hdr->kernValueCount) {
820 return 0;
823 return kernValue[ki];
826 #define KERN0_COVERAGE_HORIZONTAL 0x0001
827 #define KERN0_COVERAGE_MINIMUM 0x0002
828 #define KERN0_COVERAGE_CROSS_STREAM 0x0004
829 #define KERN0_COVERAGE_OVERRIDE 0x0008
830 #define KERN0_COVERAGE_RESERVED 0x00F0
832 #define KERN1_COVERAGE_VERTICAL 0x8000
833 #define KERN1_COVERAGE_CROSS_STREAM 0x4000
834 #define KERN1_COVERAGE_VARIATION 0x2000
835 #define KERN1_COVERAGE_RESERVED 0x1F00
837 hb_position_t gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph,
838 uint16_t aSecondGlyph) const {
839 // We want to ignore any kern pairs involving <space>, because we are
840 // handling words in isolation, the only space characters seen here are
841 // the ones artificially added by the textRun code.
842 uint32_t spaceGlyph = mFont->GetSpaceGlyph();
843 if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
844 return 0;
847 if (!mKernTable) {
848 mKernTable =
849 mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k', 'e', 'r', 'n'));
850 if (!mKernTable) {
851 mKernTable = hb_blob_get_empty();
855 uint32_t len;
856 const char* base = hb_blob_get_data(mKernTable, &len);
857 if (len < sizeof(KernTableVersion0)) {
858 return 0;
860 int32_t value = 0;
862 // First try to interpret as "version 0" kern table
863 // (see http://www.microsoft.com/typography/otspec/kern.htm)
864 const KernTableVersion0* kern0 =
865 reinterpret_cast<const KernTableVersion0*>(base);
866 if (uint16_t(kern0->version) == 0) {
867 uint16_t nTables = kern0->nTables;
868 uint32_t offs = sizeof(KernTableVersion0);
869 for (uint16_t i = 0; i < nTables; ++i) {
870 if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
871 break;
873 const KernTableSubtableHeaderVersion0* st0 =
874 reinterpret_cast<const KernTableSubtableHeaderVersion0*>(base + offs);
875 uint16_t subtableLen = uint16_t(st0->length);
876 if (offs + subtableLen > len) {
877 break;
879 offs += subtableLen;
880 uint16_t coverage = st0->coverage;
881 if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
882 // we only care about horizontal kerning (for now)
883 continue;
885 if (coverage & (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
886 // we don't support cross-stream kerning, and
887 // reserved bits should be zero;
888 // ignore the subtable if not
889 continue;
891 uint8_t format = (coverage >> 8);
892 switch (format) {
893 case 0:
894 GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0), aFirstGlyph,
895 aSecondGlyph, value,
896 (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
897 (coverage & KERN0_COVERAGE_MINIMUM) != 0);
898 break;
899 default:
900 // TODO: implement support for other formats,
901 // if they're ever used in practice
902 #if DEBUG
904 char buf[1024];
905 SprintfLiteral(buf,
906 "unknown kern subtable in %s: "
907 "ver 0 format %d\n",
908 mFont->GetName().get(), format);
909 NS_WARNING(buf);
911 #endif
912 break;
915 } else {
916 // It wasn't a "version 0" table; check if it is Apple version 1.0
917 // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
918 const KernTableVersion1* kern1 =
919 reinterpret_cast<const KernTableVersion1*>(base);
920 if (uint32_t(kern1->version) == 0x00010000) {
921 uint32_t nTables = kern1->nTables;
922 uint32_t offs = sizeof(KernTableVersion1);
923 for (uint32_t i = 0; i < nTables; ++i) {
924 if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
925 break;
927 const KernTableSubtableHeaderVersion1* st1 =
928 reinterpret_cast<const KernTableSubtableHeaderVersion1*>(base +
929 offs);
930 uint32_t subtableLen = uint32_t(st1->length);
931 offs += subtableLen;
932 uint16_t coverage = st1->coverage;
933 if (coverage & (KERN1_COVERAGE_VERTICAL | KERN1_COVERAGE_CROSS_STREAM |
934 KERN1_COVERAGE_VARIATION | KERN1_COVERAGE_RESERVED)) {
935 // we only care about horizontal kerning (for now),
936 // we don't support cross-stream kerning,
937 // we don't support variations,
938 // reserved bits should be zero;
939 // ignore the subtable if not
940 continue;
942 uint8_t format = (coverage & 0xff);
943 switch (format) {
944 case 0:
945 GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1), aFirstGlyph,
946 aSecondGlyph, value);
947 break;
948 case 2:
949 value = GetKernValueVersion1Fmt2(st1, subtableLen, aFirstGlyph,
950 aSecondGlyph);
951 break;
952 case 3:
953 value = GetKernValueVersion1Fmt3(st1, subtableLen, aFirstGlyph,
954 aSecondGlyph);
955 break;
956 default:
957 // TODO: implement support for other formats.
958 // Note that format 1 cannot be supported here,
959 // as it requires the full glyph array to run the FSM,
960 // not just the current glyph pair.
961 #if DEBUG
963 char buf[1024];
964 SprintfLiteral(buf,
965 "unknown kern subtable in %s: "
966 "ver 0 format %d\n",
967 mFont->GetName().get(), format);
968 NS_WARNING(buf);
970 #endif
971 break;
977 if (value != 0) {
978 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
980 return 0;
983 static hb_position_t HBGetHKerning(hb_font_t* font, void* font_data,
984 hb_codepoint_t first_glyph,
985 hb_codepoint_t second_glyph,
986 void* user_data) {
987 const gfxHarfBuzzShaper::FontCallbackData* fcd =
988 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
989 return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
993 * HarfBuzz unicode property callbacks
996 static hb_codepoint_t HBGetMirroring(hb_unicode_funcs_t* ufuncs,
997 hb_codepoint_t aCh, void* user_data) {
998 return intl::UnicodeProperties::CharMirror(aCh);
1001 static hb_unicode_general_category_t HBGetGeneralCategory(
1002 hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh, void* user_data) {
1003 return hb_unicode_general_category_t(GetGeneralCategory(aCh));
1006 static hb_script_t HBGetScript(hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh,
1007 void* user_data) {
1008 return hb_script_t(
1009 GetScriptTagForCode(intl::UnicodeProperties::GetScriptCode(aCh)));
1012 static hb_unicode_combining_class_t HBGetCombiningClass(
1013 hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh, void* user_data) {
1014 return hb_unicode_combining_class_t(
1015 intl::UnicodeProperties::GetCombiningClass(aCh));
1018 static hb_bool_t HBUnicodeCompose(hb_unicode_funcs_t* ufuncs, hb_codepoint_t a,
1019 hb_codepoint_t b, hb_codepoint_t* ab,
1020 void* user_data) {
1021 char32_t ch = intl::String::ComposePairNFC(a, b);
1022 if (ch > 0) {
1023 *ab = ch;
1024 return true;
1027 return false;
1030 static hb_bool_t HBUnicodeDecompose(hb_unicode_funcs_t* ufuncs,
1031 hb_codepoint_t ab, hb_codepoint_t* a,
1032 hb_codepoint_t* b, void* user_data) {
1033 #ifdef MOZ_WIDGET_ANDROID
1034 // Hack for the SamsungDevanagari font, bug 1012365:
1035 // support U+0972 by decomposing it.
1036 if (ab == 0x0972) {
1037 *a = 0x0905;
1038 *b = 0x0945;
1039 return true;
1041 #endif
1043 char32_t decomp[2] = {0};
1044 if (intl::String::DecomposeRawNFD(ab, decomp)) {
1045 if (decomp[1] || decomp[0] != ab) {
1046 *a = decomp[0];
1047 *b = decomp[1];
1048 return true;
1052 return false;
1055 static void AddOpenTypeFeature(const uint32_t& aTag, uint32_t& aValue,
1056 void* aUserArg) {
1057 nsTArray<hb_feature_t>* features =
1058 static_cast<nsTArray<hb_feature_t>*>(aUserArg);
1060 hb_feature_t feat = {0, 0, 0, UINT_MAX};
1061 feat.tag = aTag;
1062 feat.value = aValue;
1063 features->AppendElement(feat);
1067 * gfxFontShaper override to initialize the text run using HarfBuzz
1070 static hb_font_funcs_t* sHBFontFuncs = nullptr;
1071 static hb_font_funcs_t* sNominalGlyphFunc = nullptr;
1072 static hb_unicode_funcs_t* sHBUnicodeFuncs = nullptr;
1073 static const hb_script_t sMathScript =
1074 hb_ot_tag_to_script(HB_TAG('m', 'a', 't', 'h'));
1076 bool gfxHarfBuzzShaper::Initialize() {
1077 if (mInitialized) {
1078 return mHBFont != nullptr;
1080 mInitialized = true;
1081 mCallbackData.mShaper = this;
1083 if (!sHBFontFuncs) {
1084 // static function callback pointers, initialized by the first
1085 // harfbuzz shaper used
1086 sHBFontFuncs = hb_font_funcs_create();
1087 hb_font_funcs_set_nominal_glyph_func(sHBFontFuncs, HBGetNominalGlyph,
1088 nullptr, nullptr);
1089 hb_font_funcs_set_variation_glyph_func(sHBFontFuncs, HBGetVariationGlyph,
1090 nullptr, nullptr);
1091 hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs, HBGetGlyphHAdvance,
1092 nullptr, nullptr);
1093 hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs, HBGetGlyphVAdvance,
1094 nullptr, nullptr);
1095 hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs, HBGetGlyphVOrigin,
1096 nullptr, nullptr);
1097 hb_font_funcs_set_glyph_extents_func(sHBFontFuncs, HBGetGlyphExtents,
1098 nullptr, nullptr);
1099 hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs, HBGetContourPoint,
1100 nullptr, nullptr);
1101 hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs, HBGetHKerning, nullptr,
1102 nullptr);
1103 hb_font_funcs_make_immutable(sHBFontFuncs);
1105 sNominalGlyphFunc = hb_font_funcs_create();
1106 hb_font_funcs_set_nominal_glyph_func(sNominalGlyphFunc, HBGetNominalGlyph,
1107 nullptr, nullptr);
1108 hb_font_funcs_make_immutable(sNominalGlyphFunc);
1110 sHBUnicodeFuncs = hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
1111 hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs, HBGetMirroring,
1112 nullptr, nullptr);
1113 hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript, nullptr,
1114 nullptr);
1115 hb_unicode_funcs_set_general_category_func(
1116 sHBUnicodeFuncs, HBGetGeneralCategory, nullptr, nullptr);
1117 hb_unicode_funcs_set_combining_class_func(
1118 sHBUnicodeFuncs, HBGetCombiningClass, nullptr, nullptr);
1119 hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs, HBUnicodeCompose,
1120 nullptr, nullptr);
1121 hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs, HBUnicodeDecompose,
1122 nullptr, nullptr);
1123 hb_unicode_funcs_make_immutable(sHBUnicodeFuncs);
1126 gfxFontEntry* entry = mFont->GetFontEntry();
1127 if (!mUseFontGetGlyph) {
1128 // get the cmap table and find offset to our subtable
1129 mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1130 if (!mCmapTable) {
1131 NS_WARNING("failed to load cmap, glyphs will be missing");
1132 return false;
1134 uint32_t len;
1135 const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
1136 mCmapFormat = gfxFontUtils::FindPreferredSubtable(
1137 data, len, &mSubtableOffset, &mUVSTableOffset, &mIsSymbolFont);
1138 if (mCmapFormat <= 0) {
1139 return false;
1143 if (!mUseFontGlyphWidths) {
1144 // If font doesn't implement GetGlyphWidth, we will be reading
1145 // the metrics table directly, so make sure we can load it.
1146 if (!LoadHmtxTable()) {
1147 return false;
1151 mBuffer = hb_buffer_create();
1152 hb_buffer_set_unicode_funcs(mBuffer, sHBUnicodeFuncs);
1153 hb_buffer_set_cluster_level(mBuffer,
1154 HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
1156 auto* funcs =
1157 mFont->GetFontEntry()->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))
1158 ? sNominalGlyphFunc
1159 : sHBFontFuncs;
1160 mHBFont = CreateHBFont(mFont, funcs, &mCallbackData);
1162 return true;
1165 hb_font_t* gfxHarfBuzzShaper::CreateHBFont(gfxFont* aFont,
1166 hb_font_funcs_t* aFontFuncs,
1167 FontCallbackData* aCallbackData) {
1168 hb_face_t* hbFace = aFont->GetFontEntry()->GetHBFace();
1169 hb_font_t* result = hb_font_create(hbFace);
1170 hb_face_destroy(hbFace);
1172 if (aFontFuncs && aCallbackData) {
1173 if (aFontFuncs == sNominalGlyphFunc) {
1174 hb_font_t* subfont = hb_font_create_sub_font(result);
1175 hb_font_destroy(result);
1176 result = subfont;
1178 hb_font_set_funcs(result, aFontFuncs, aCallbackData, nullptr);
1180 hb_font_set_ppem(result, aFont->GetAdjustedSize(), aFont->GetAdjustedSize());
1181 uint32_t scale = FloatToFixed(aFont->GetAdjustedSize()); // 16.16 fixed-point
1182 hb_font_set_scale(result, scale, scale);
1184 AutoTArray<gfxFontVariation, 8> vars;
1185 aFont->GetFontEntry()->GetVariationsForStyle(vars, *aFont->GetStyle());
1186 if (vars.Length() > 0) {
1187 // Fortunately, the hb_variation_t struct is compatible with our
1188 // gfxFontVariation, so we can simply cast here.
1189 static_assert(
1190 sizeof(gfxFontVariation) == sizeof(hb_variation_t) &&
1191 offsetof(gfxFontVariation, mTag) == offsetof(hb_variation_t, tag) &&
1192 offsetof(gfxFontVariation, mValue) ==
1193 offsetof(hb_variation_t, value),
1194 "Gecko vs HarfBuzz struct mismatch!");
1195 auto hbVars = reinterpret_cast<const hb_variation_t*>(vars.Elements());
1196 hb_font_set_variations(result, hbVars, vars.Length());
1199 return result;
1202 bool gfxHarfBuzzShaper::LoadHmtxTable() {
1203 // Read mNumLongHMetrics from metrics-head table without caching its
1204 // blob, and preload/cache the metrics table.
1205 gfxFontEntry* entry = mFont->GetFontEntry();
1206 gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h', 'h', 'e', 'a'));
1207 if (hheaTable) {
1208 uint32_t len;
1209 const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
1210 hb_blob_get_data(hheaTable, &len));
1211 if (len >= sizeof(MetricsHeader)) {
1212 mNumLongHMetrics = hhea->numOfLongMetrics;
1213 if (mNumLongHMetrics > 0 && int16_t(hhea->metricDataFormat) == 0) {
1214 // no point reading metrics if number of entries is zero!
1215 // in that case, we won't be able to use this font
1216 // (this method will return FALSE below if mHmtxTable
1217 // is null)
1218 mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h', 'm', 't', 'x'));
1219 if (mHmtxTable && hb_blob_get_length(mHmtxTable) <
1220 mNumLongHMetrics * sizeof(LongMetric)) {
1221 // metrics table is not large enough for the claimed
1222 // number of entries: invalid, do not use.
1223 hb_blob_destroy(mHmtxTable);
1224 mHmtxTable = nullptr;
1229 if (!mHmtxTable) {
1230 return false;
1232 return true;
1235 void gfxHarfBuzzShaper::InitializeVertical() {
1236 // We only do this once. If we don't have a mHmtxTable after that,
1237 // we'll be making up fallback metrics.
1238 if (mVerticalInitialized) {
1239 return;
1241 mVerticalInitialized = true;
1243 if (!mHmtxTable) {
1244 if (!LoadHmtxTable()) {
1245 return;
1249 // Load vertical metrics if present in the font; if not, we'll synthesize
1250 // vertical glyph advances based on (horizontal) ascent/descent metrics.
1251 gfxFontEntry* entry = mFont->GetFontEntry();
1252 gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v', 'h', 'e', 'a'));
1253 if (vheaTable) {
1254 uint32_t len;
1255 const MetricsHeader* vhea = reinterpret_cast<const MetricsHeader*>(
1256 hb_blob_get_data(vheaTable, &len));
1257 if (len >= sizeof(MetricsHeader)) {
1258 mNumLongVMetrics = vhea->numOfLongMetrics;
1259 gfxFontEntry::AutoTable maxpTable(entry,
1260 TRUETYPE_TAG('m', 'a', 'x', 'p'));
1261 int numGlyphs = -1; // invalid if we fail to read 'maxp'
1262 if (maxpTable &&
1263 hb_blob_get_length(maxpTable) >= sizeof(MaxpTableHeader)) {
1264 const MaxpTableHeader* maxp = reinterpret_cast<const MaxpTableHeader*>(
1265 hb_blob_get_data(maxpTable, nullptr));
1266 numGlyphs = uint16_t(maxp->numGlyphs);
1268 if (mNumLongVMetrics > 0 && mNumLongVMetrics <= numGlyphs &&
1269 int16_t(vhea->metricDataFormat) == 0) {
1270 mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v', 'm', 't', 'x'));
1271 if (mVmtxTable &&
1272 hb_blob_get_length(mVmtxTable) <
1273 mNumLongVMetrics * sizeof(LongMetric) +
1274 (numGlyphs - mNumLongVMetrics) * sizeof(int16_t)) {
1275 // metrics table is not large enough for the claimed
1276 // number of entries: invalid, do not use.
1277 hb_blob_destroy(mVmtxTable);
1278 mVmtxTable = nullptr;
1284 // For CFF fonts only, load a VORG table if present.
1285 if (entry->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))) {
1286 mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V', 'O', 'R', 'G'));
1287 if (mVORGTable) {
1288 uint32_t len;
1289 const VORG* vorg =
1290 reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, &len));
1291 if (len < sizeof(VORG) || uint16_t(vorg->majorVersion) != 1 ||
1292 uint16_t(vorg->minorVersion) != 0 ||
1293 len < sizeof(VORG) +
1294 uint16_t(vorg->numVertOriginYMetrics) * sizeof(VORGrec)) {
1295 // VORG table is an unknown version, or not large enough
1296 // to be valid -- discard it.
1297 NS_WARNING("discarding invalid VORG table");
1298 hb_blob_destroy(mVORGTable);
1299 mVORGTable = nullptr;
1305 bool gfxHarfBuzzShaper::ShapeText(DrawTarget* aDrawTarget,
1306 const char16_t* aText, uint32_t aOffset,
1307 uint32_t aLength, Script aScript,
1308 nsAtom* aLanguage, bool aVertical,
1309 RoundingFlags aRounding,
1310 gfxShapedText* aShapedText) {
1311 mUseVerticalPresentationForms = false;
1313 if (!Initialize()) {
1314 return false;
1317 if (aVertical) {
1318 InitializeVertical();
1319 if (!mFont->GetFontEntry()->SupportsOpenTypeFeature(
1320 aScript, HB_TAG('v', 'e', 'r', 't'))) {
1321 mUseVerticalPresentationForms = true;
1325 const gfxFontStyle* style = mFont->GetStyle();
1327 // determine whether petite-caps falls back to small-caps
1328 bool addSmallCaps = false;
1329 if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
1330 switch (style->variantCaps) {
1331 case NS_FONT_VARIANT_CAPS_ALLPETITE:
1332 case NS_FONT_VARIANT_CAPS_PETITECAPS:
1333 bool synLower, synUpper;
1334 mFont->SupportsVariantCaps(aScript, style->variantCaps, addSmallCaps,
1335 synLower, synUpper);
1336 break;
1337 default:
1338 break;
1342 gfxFontEntry* entry = mFont->GetFontEntry();
1344 // insert any merged features into hb_feature array
1345 AutoTArray<hb_feature_t, 20> features;
1346 MergeFontFeatures(style, entry->mFeatureSettings,
1347 aShapedText->DisableLigatures(), entry->FamilyName(),
1348 addSmallCaps, AddOpenTypeFeature, &features);
1350 bool isRightToLeft = aShapedText->IsRightToLeft();
1352 hb_buffer_set_direction(
1353 mBuffer, aVertical
1354 ? HB_DIRECTION_TTB
1355 : (isRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR));
1356 hb_script_t scriptTag;
1357 if (aShapedText->GetFlags() & gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT) {
1358 scriptTag = sMathScript;
1359 } else {
1360 scriptTag = GetHBScriptUsedForShaping(aScript);
1362 hb_buffer_set_script(mBuffer, scriptTag);
1364 hb_language_t language;
1365 if (style->languageOverride) {
1366 language = hb_ot_tag_to_language(style->languageOverride);
1367 } else if (entry->mLanguageOverride) {
1368 language = hb_ot_tag_to_language(entry->mLanguageOverride);
1369 } else if (aLanguage) {
1370 nsCString langString;
1371 aLanguage->ToUTF8String(langString);
1372 language = hb_language_from_string(langString.get(), langString.Length());
1373 } else {
1374 language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE);
1376 hb_buffer_set_language(mBuffer, language);
1378 uint32_t length = aLength;
1379 hb_buffer_add_utf16(mBuffer, reinterpret_cast<const uint16_t*>(aText), length,
1380 0, length);
1382 hb_shape(mHBFont, mBuffer, features.Elements(), features.Length());
1384 if (isRightToLeft) {
1385 hb_buffer_reverse(mBuffer);
1388 nsresult rv = SetGlyphsFromRun(aShapedText, aOffset, aLength, aText,
1389 aVertical, aRounding);
1391 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1392 "failed to store glyphs into gfxShapedWord");
1393 hb_buffer_clear_contents(mBuffer);
1395 return NS_SUCCEEDED(rv);
1398 #define SMALL_GLYPH_RUN \
1399 128 // some testing indicates that 90%+ of text runs
1400 // will fit without requiring separate allocation
1401 // for charToGlyphArray
1403 nsresult gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText* aShapedText,
1404 uint32_t aOffset, uint32_t aLength,
1405 const char16_t* aText,
1406 bool aVertical,
1407 RoundingFlags aRounding) {
1408 typedef gfxShapedText::CompressedGlyph CompressedGlyph;
1410 uint32_t numGlyphs;
1411 const hb_glyph_info_t* ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
1412 if (numGlyphs == 0) {
1413 return NS_OK;
1416 AutoTArray<gfxTextRun::DetailedGlyph, 1> detailedGlyphs;
1418 uint32_t wordLength = aLength;
1419 static const int32_t NO_GLYPH = -1;
1420 AutoTArray<int32_t, SMALL_GLYPH_RUN> charToGlyphArray;
1421 if (!charToGlyphArray.SetLength(wordLength, fallible)) {
1422 return NS_ERROR_OUT_OF_MEMORY;
1425 int32_t* charToGlyph = charToGlyphArray.Elements();
1426 for (uint32_t offset = 0; offset < wordLength; ++offset) {
1427 charToGlyph[offset] = NO_GLYPH;
1430 for (uint32_t i = 0; i < numGlyphs; ++i) {
1431 uint32_t loc = ginfo[i].cluster;
1432 if (loc < wordLength) {
1433 charToGlyph[loc] = i;
1437 int32_t glyphStart = 0; // looking for a clump that starts at this glyph
1438 int32_t charStart = 0; // and this char index within the range of the run
1440 bool roundI, roundB;
1441 if (aVertical) {
1442 roundI = bool(aRounding & RoundingFlags::kRoundY);
1443 roundB = bool(aRounding & RoundingFlags::kRoundX);
1444 } else {
1445 roundI = bool(aRounding & RoundingFlags::kRoundX);
1446 roundB = bool(aRounding & RoundingFlags::kRoundY);
1449 int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
1450 CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
1452 // factor to convert 16.16 fixed-point pixels to app units
1453 // (only used if not rounding)
1454 double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
1456 // Residual from rounding of previous advance, for use in rounding the
1457 // subsequent offset or advance appropriately. 16.16 fixed-point
1459 // When rounding, the goal is to make the distance between glyphs and
1460 // their base glyph equal to the integral number of pixels closest to that
1461 // suggested by that shaper.
1462 // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
1464 // The value of the residual is the part of the desired distance that has
1465 // not been included in integer offsets.
1466 hb_position_t residual = 0;
1468 // keep track of y-position to set glyph offsets if needed
1469 nscoord bPos = 0;
1471 const hb_glyph_position_t* posInfo =
1472 hb_buffer_get_glyph_positions(mBuffer, nullptr);
1474 while (glyphStart < int32_t(numGlyphs)) {
1475 int32_t charEnd = ginfo[glyphStart].cluster;
1476 int32_t glyphEnd = glyphStart;
1477 int32_t charLimit = wordLength;
1478 while (charEnd < charLimit) {
1479 // This is normally executed once for each iteration of the outer loop,
1480 // but in unusual cases where the character/glyph association is complex,
1481 // the initial character range might correspond to a non-contiguous
1482 // glyph range with "holes" in it. If so, we will repeat this loop to
1483 // extend the character range until we have a contiguous glyph sequence.
1484 charEnd += 1;
1485 while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
1486 charEnd += 1;
1489 // find the maximum glyph index covered by the clump so far
1490 for (int32_t i = charStart; i < charEnd; ++i) {
1491 if (charToGlyph[i] != NO_GLYPH) {
1492 glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
1493 // update extent of glyph range
1497 if (glyphEnd == glyphStart + 1) {
1498 // for the common case of a single-glyph clump,
1499 // we can skip the following checks
1500 break;
1503 if (glyphEnd == glyphStart) {
1504 // no glyphs, try to extend the clump
1505 continue;
1508 // check whether all glyphs in the range are associated with the
1509 // characters in our clump; if not, we have a discontinuous range, and
1510 // should extend it unless we've reached the end of the text
1511 bool allGlyphsAreWithinCluster = true;
1512 for (int32_t i = glyphStart; i < glyphEnd; ++i) {
1513 int32_t glyphCharIndex = ginfo[i].cluster;
1514 if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
1515 allGlyphsAreWithinCluster = false;
1516 break;
1519 if (allGlyphsAreWithinCluster) {
1520 break;
1524 NS_ASSERTION(glyphStart < glyphEnd,
1525 "character/glyph clump contains no glyphs!");
1526 NS_ASSERTION(charStart != charEnd,
1527 "character/glyph clump contains no characters!");
1529 // Now charStart..charEnd is a ligature clump, corresponding to
1530 // glyphStart..glyphEnd; Set baseCharIndex to the char we'll actually attach
1531 // the glyphs to (1st of ligature), and endCharIndex to the limit (position
1532 // beyond the last char), adjusting for the offset of the stringRange
1533 // relative to the textRun.
1534 int32_t baseCharIndex, endCharIndex;
1535 while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
1536 charEnd++;
1537 baseCharIndex = charStart;
1538 endCharIndex = charEnd;
1540 // Then we check if the clump falls outside our actual string range;
1541 // if so, just go to the next.
1542 if (baseCharIndex >= int32_t(wordLength)) {
1543 glyphStart = glyphEnd;
1544 charStart = charEnd;
1545 continue;
1547 // Ensure we won't try to go beyond the valid length of the textRun's text
1548 endCharIndex = std::min<int32_t>(endCharIndex, wordLength);
1550 // Now we're ready to set the glyph info in the textRun
1551 int32_t glyphsInClump = glyphEnd - glyphStart;
1553 // Check for default-ignorable char that didn't get filtered, combined,
1554 // etc by the shaping process, and remove from the run.
1555 // (This may be done within harfbuzz eventually.)
1556 if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
1557 aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
1558 aText[baseCharIndex])) {
1559 glyphStart = glyphEnd;
1560 charStart = charEnd;
1561 continue;
1564 // HarfBuzz gives us physical x- and y-coordinates, but we will store
1565 // them as logical inline- and block-direction values in the textrun.
1567 hb_position_t i_offset, i_advance; // inline-direction offset/advance
1568 hb_position_t b_offset, b_advance; // block-direction offset/advance
1569 if (aVertical) {
1570 // our coordinate directions are the opposite of harfbuzz's
1571 // when doing top-to-bottom shaping
1572 i_offset = -posInfo[glyphStart].y_offset;
1573 i_advance = -posInfo[glyphStart].y_advance;
1574 b_offset = -posInfo[glyphStart].x_offset;
1575 b_advance = -posInfo[glyphStart].x_advance;
1576 } else {
1577 i_offset = posInfo[glyphStart].x_offset;
1578 i_advance = posInfo[glyphStart].x_advance;
1579 b_offset = posInfo[glyphStart].y_offset;
1580 b_advance = posInfo[glyphStart].y_advance;
1583 nscoord iOffset, advance;
1584 if (roundI) {
1585 iOffset = appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
1586 // Desired distance from the base glyph to the next reference point.
1587 hb_position_t width = i_advance - i_offset;
1588 int intWidth = FixedToIntRound(width);
1589 residual = width - FloatToFixed(intWidth);
1590 advance = appUnitsPerDevUnit * intWidth + iOffset;
1591 } else {
1592 iOffset = floor(hb2appUnits * i_offset + 0.5);
1593 advance = floor(hb2appUnits * i_advance + 0.5);
1595 // Check if it's a simple one-to-one mapping
1596 if (glyphsInClump == 1 &&
1597 CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
1598 CompressedGlyph::IsSimpleAdvance(advance) &&
1599 charGlyphs[baseCharIndex].IsClusterStart() && iOffset == 0 &&
1600 b_offset == 0 && b_advance == 0 && bPos == 0) {
1601 charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
1602 ginfo[glyphStart].codepoint);
1603 } else {
1604 // Collect all glyphs in a list to be assigned to the first char;
1605 // there must be at least one in the clump, and we already measured
1606 // its advance, hence the placement of the loop-exit test and the
1607 // measurement of the next glyph.
1608 while (1) {
1609 gfxTextRun::DetailedGlyph* details = detailedGlyphs.AppendElement();
1610 details->mGlyphID = ginfo[glyphStart].codepoint;
1612 details->mAdvance = advance;
1614 if (aVertical) {
1615 details->mOffset.x =
1616 bPos - (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
1617 : floor(hb2appUnits * b_offset + 0.5));
1618 details->mOffset.y = iOffset;
1619 } else {
1620 details->mOffset.x = iOffset;
1621 details->mOffset.y =
1622 bPos - (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
1623 : floor(hb2appUnits * b_offset + 0.5));
1626 if (b_advance != 0) {
1627 bPos -= roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance)
1628 : floor(hb2appUnits * b_advance + 0.5);
1630 if (++glyphStart >= glyphEnd) {
1631 break;
1634 if (aVertical) {
1635 i_offset = -posInfo[glyphStart].y_offset;
1636 i_advance = -posInfo[glyphStart].y_advance;
1637 b_offset = -posInfo[glyphStart].x_offset;
1638 b_advance = -posInfo[glyphStart].x_advance;
1639 } else {
1640 i_offset = posInfo[glyphStart].x_offset;
1641 i_advance = posInfo[glyphStart].x_advance;
1642 b_offset = posInfo[glyphStart].y_offset;
1643 b_advance = posInfo[glyphStart].y_advance;
1646 if (roundI) {
1647 iOffset = appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
1648 // Desired distance to the next reference point. The
1649 // residual is considered here, and includes the residual
1650 // from the base glyph offset and subsequent advances, so
1651 // that the distance from the base glyph is optimized
1652 // rather than the distance from combining marks.
1653 i_advance += residual;
1654 int intAdvance = FixedToIntRound(i_advance);
1655 residual = i_advance - FloatToFixed(intAdvance);
1656 advance = appUnitsPerDevUnit * intAdvance;
1657 } else {
1658 iOffset = floor(hb2appUnits * i_offset + 0.5);
1659 advance = floor(hb2appUnits * i_advance + 0.5);
1663 aShapedText->SetDetailedGlyphs(aOffset + baseCharIndex,
1664 detailedGlyphs.Length(),
1665 detailedGlyphs.Elements());
1667 detailedGlyphs.Clear();
1670 // the rest of the chars in the group are ligature continuations,
1671 // no associated glyphs
1672 while (++baseCharIndex != endCharIndex &&
1673 baseCharIndex < int32_t(wordLength)) {
1674 CompressedGlyph& g = charGlyphs[baseCharIndex];
1675 NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
1676 g.SetComplex(g.IsClusterStart(), false);
1679 glyphStart = glyphEnd;
1680 charStart = charEnd;
1683 return NS_OK;