Bug 1919083 - [ci] Enable os-integration variant for more suites, r=jmaher
[gecko.git] / gfx / thebes / gfxHarfBuzzShaper.cpp
blob4a42b3407c863d5b392ba7e38c69c7b8930b09d5
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::GetGlyphUncached(
81 hb_codepoint_t unicode) const {
82 hb_codepoint_t gid = 0;
84 if (mUseFontGetGlyph) {
85 MutexAutoUnlock unlock(mCacheLock);
86 gid = mFont->GetGlyph(unicode, 0);
87 } else {
88 // we only instantiate a harfbuzz shaper if there's a cmap available
89 NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
90 "cmap data not correctly set up, expect disaster");
92 uint32_t length;
93 const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
95 switch (mCmapFormat) {
96 case 4:
97 gid =
98 unicode < UNICODE_BMP_LIMIT
99 ? gfxFontUtils::MapCharToGlyphFormat4(
100 data + mSubtableOffset, length - mSubtableOffset, unicode)
101 : 0;
102 break;
103 case 10:
104 gid = gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
105 unicode);
106 break;
107 case 12:
108 case 13:
109 gid = gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset,
110 unicode);
111 break;
112 default:
113 NS_WARNING("unsupported cmap format, glyphs will be missing");
114 break;
118 if (!gid) {
119 if (mIsSymbolFont) {
120 // For legacy MS Symbol fonts, we try mapping the given character code
121 // to the PUA range used by these fonts' cmaps.
122 if (auto pua = gfxFontUtils::MapLegacySymbolFontCharToPUA(unicode)) {
123 gid = GetGlyphUncached(pua);
125 if (gid) {
126 return gid;
129 switch (unicode) {
130 case 0xA0: {
131 // if there's no glyph for &nbsp;, just use the space glyph instead.
132 gid = mFont->GetSpaceGlyph();
133 break;
135 case 0x2010:
136 case 0x2011: {
137 // For Unicode HYPHEN and NON-BREAKING HYPHEN, fall back to the ASCII
138 // HYPHEN-MINUS as a substitute.
139 gid = GetGlyphUncached('-');
140 break;
145 return gid;
148 hb_codepoint_t gfxHarfBuzzShaper::GetNominalGlyph(
149 hb_codepoint_t unicode) const {
150 MutexAutoLock lock(mCacheLock);
151 auto cached = mCmapCache->Lookup(unicode);
152 if (cached) {
153 return cached.Data().mGlyphId;
156 // This call can temporarily unlock the cache if mUseFontGetGlyph is true.
157 hb_codepoint_t gid = GetGlyphUncached(unicode);
159 if (mUseFontGetGlyph) {
160 // GetGlyphUncached may have invalidated our earlier cache lookup!
161 mCmapCache->Put(unicode, CmapCacheData{unicode, gid});
162 } else {
163 cached.Set(CmapCacheData{unicode, gid});
166 return gid;
169 unsigned int gfxHarfBuzzShaper::GetNominalGlyphs(
170 unsigned int count, const hb_codepoint_t* first_unicode,
171 unsigned int unicode_stride, hb_codepoint_t* first_glyph,
172 unsigned int glyph_stride) {
173 MutexAutoLock lock(mCacheLock);
174 unsigned int result = 0;
175 while (result < count) {
176 hb_codepoint_t usv = *first_unicode;
177 auto cached = mCmapCache->Lookup(usv);
178 if (cached) {
179 // Cache hit :)
180 *first_glyph = cached.Data().mGlyphId;
181 } else {
182 // Cache miss: call GetGlyphUncached (which handles things like symbol-
183 // encoding fallback) and fill in the cache entry with the result.
184 hb_codepoint_t gid = GetGlyphUncached(usv);
185 if (mUseFontGetGlyph) {
186 mCmapCache->Put(usv, CmapCacheData{usv, gid});
187 } else {
188 cached.Set(CmapCacheData{usv, gid});
190 *first_glyph = gid;
192 first_unicode = reinterpret_cast<const hb_codepoint_t*>(
193 reinterpret_cast<const char*>(first_unicode) + unicode_stride);
194 first_glyph = reinterpret_cast<hb_codepoint_t*>(
195 reinterpret_cast<char*>(first_glyph) + glyph_stride);
196 result++;
198 return result;
201 hb_codepoint_t gfxHarfBuzzShaper::GetVariationGlyph(
202 hb_codepoint_t unicode, hb_codepoint_t variation_selector) const {
203 if (mUseFontGetGlyph) {
204 return mFont->GetGlyph(unicode, variation_selector);
207 NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
208 "we cannot be using this font!");
209 NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
210 "cmap data not correctly set up, expect disaster");
212 uint32_t length;
213 const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
215 if (mUVSTableOffset) {
216 hb_codepoint_t gid = gfxFontUtils::MapUVSToGlyphFormat14(
217 data + mUVSTableOffset, unicode, variation_selector);
218 if (gid) {
219 return gid;
223 uint32_t compat = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
224 if (compat) {
225 switch (mCmapFormat) {
226 case 4:
227 if (compat < UNICODE_BMP_LIMIT) {
228 return gfxFontUtils::MapCharToGlyphFormat4(
229 data + mSubtableOffset, length - mSubtableOffset, compat);
231 break;
232 case 10:
233 return gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset,
234 compat);
235 break;
236 case 12:
237 case 13:
238 return gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset,
239 compat);
240 break;
244 return 0;
247 static int VertFormsGlyphCompare(const void* aKey, const void* aElem) {
248 return int(*((hb_codepoint_t*)(aKey))) - int(*((uint16_t*)(aElem)));
251 // Return a vertical presentation-form codepoint corresponding to the
252 // given Unicode value, or 0 if no such form is available.
253 hb_codepoint_t gfxHarfBuzzShaper::GetVerticalPresentationForm(
254 hb_codepoint_t aUnicode) {
255 static const uint16_t sVerticalForms[][2] = {
256 {0x2013, 0xfe32}, // EN DASH
257 {0x2014, 0xfe31}, // EM DASH
258 {0x2025, 0xfe30}, // TWO DOT LEADER
259 {0x2026, 0xfe19}, // HORIZONTAL ELLIPSIS
260 {0x3001, 0xfe11}, // IDEOGRAPHIC COMMA
261 {0x3002, 0xfe12}, // IDEOGRAPHIC FULL STOP
262 {0x3008, 0xfe3f}, // LEFT ANGLE BRACKET
263 {0x3009, 0xfe40}, // RIGHT ANGLE BRACKET
264 {0x300a, 0xfe3d}, // LEFT DOUBLE ANGLE BRACKET
265 {0x300b, 0xfe3e}, // RIGHT DOUBLE ANGLE BRACKET
266 {0x300c, 0xfe41}, // LEFT CORNER BRACKET
267 {0x300d, 0xfe42}, // RIGHT CORNER BRACKET
268 {0x300e, 0xfe43}, // LEFT WHITE CORNER BRACKET
269 {0x300f, 0xfe44}, // RIGHT WHITE CORNER BRACKET
270 {0x3010, 0xfe3b}, // LEFT BLACK LENTICULAR BRACKET
271 {0x3011, 0xfe3c}, // RIGHT BLACK LENTICULAR BRACKET
272 {0x3014, 0xfe39}, // LEFT TORTOISE SHELL BRACKET
273 {0x3015, 0xfe3a}, // RIGHT TORTOISE SHELL BRACKET
274 {0x3016, 0xfe17}, // LEFT WHITE LENTICULAR BRACKET
275 {0x3017, 0xfe18}, // RIGHT WHITE LENTICULAR BRACKET
276 {0xfe4f, 0xfe34}, // WAVY LOW LINE
277 {0xff01, 0xfe15}, // FULLWIDTH EXCLAMATION MARK
278 {0xff08, 0xfe35}, // FULLWIDTH LEFT PARENTHESIS
279 {0xff09, 0xfe36}, // FULLWIDTH RIGHT PARENTHESIS
280 {0xff0c, 0xfe10}, // FULLWIDTH COMMA
281 {0xff1a, 0xfe13}, // FULLWIDTH COLON
282 {0xff1b, 0xfe14}, // FULLWIDTH SEMICOLON
283 {0xff1f, 0xfe16}, // FULLWIDTH QUESTION MARK
284 {0xff3b, 0xfe47}, // FULLWIDTH LEFT SQUARE BRACKET
285 {0xff3d, 0xfe48}, // FULLWIDTH RIGHT SQUARE BRACKET
286 {0xff3f, 0xfe33}, // FULLWIDTH LOW LINE
287 {0xff5b, 0xfe37}, // FULLWIDTH LEFT CURLY BRACKET
288 {0xff5d, 0xfe38} // FULLWIDTH RIGHT CURLY BRACKET
290 const uint16_t* charPair = static_cast<const uint16_t*>(
291 bsearch(&aUnicode, sVerticalForms, std::size(sVerticalForms),
292 sizeof(sVerticalForms[0]), VertFormsGlyphCompare));
293 return charPair ? charPair[1] : 0;
296 static hb_bool_t HBGetNominalGlyph(hb_font_t* font, void* font_data,
297 hb_codepoint_t unicode,
298 hb_codepoint_t* glyph, void* user_data) {
299 const gfxHarfBuzzShaper::FontCallbackData* fcd =
300 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
302 if (fcd->mShaper->UseVerticalPresentationForms()) {
303 hb_codepoint_t verticalForm =
304 gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
305 if (verticalForm) {
306 *glyph = fcd->mShaper->GetNominalGlyph(verticalForm);
307 if (*glyph != 0) {
308 return true;
311 // fall back to the non-vertical form if we didn't find an alternate
314 *glyph = fcd->mShaper->GetNominalGlyph(unicode);
315 return *glyph != 0;
318 static unsigned int HBGetNominalGlyphs(
319 hb_font_t* font, void* font_data, unsigned int count,
320 const hb_codepoint_t* first_unicode, unsigned int unicode_stride,
321 hb_codepoint_t* first_glyph, unsigned int glyph_stride, void* user_data) {
322 const gfxHarfBuzzShaper::FontCallbackData* fcd =
323 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
324 if (fcd->mShaper->UseVerticalPresentationForms()) {
325 return 0;
328 return fcd->mShaper->GetNominalGlyphs(count, first_unicode, unicode_stride,
329 first_glyph, glyph_stride);
332 static hb_bool_t HBGetVariationGlyph(hb_font_t* font, void* font_data,
333 hb_codepoint_t unicode,
334 hb_codepoint_t variation_selector,
335 hb_codepoint_t* glyph, void* user_data) {
336 const gfxHarfBuzzShaper::FontCallbackData* fcd =
337 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
339 if (fcd->mShaper->UseVerticalPresentationForms()) {
340 hb_codepoint_t verticalForm =
341 gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode);
342 if (verticalForm) {
343 *glyph =
344 fcd->mShaper->GetVariationGlyph(verticalForm, variation_selector);
345 if (*glyph != 0) {
346 return true;
349 // fall back to the non-vertical form if we didn't find an alternate
352 *glyph = fcd->mShaper->GetVariationGlyph(unicode, variation_selector);
353 return *glyph != 0;
356 // Glyph metrics structures, shared (with appropriate reinterpretation of
357 // field names) by horizontal and vertical metrics tables.
358 struct LongMetric {
359 AutoSwap_PRUint16 advanceWidth; // or advanceHeight, when vertical
360 AutoSwap_PRInt16 lsb; // or tsb, when vertical
363 struct GlyphMetrics {
364 LongMetric metrics[1]; // actually numberOfLongMetrics
365 // the variable-length metrics[] array is immediately followed by:
366 // AutoSwap_PRUint16 leftSideBearing[];
369 hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvanceUncached(
370 hb_codepoint_t glyph) const {
371 if (mUseFontGlyphWidths) {
372 return GetFont()->GetGlyphWidth(glyph);
375 // Get an unhinted value directly from the font tables.
376 NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
377 "font is lacking metrics, we shouldn't be here");
379 if (glyph >= uint32_t(mNumLongHMetrics)) {
380 glyph = mNumLongHMetrics - 1;
383 // glyph must be valid now, because we checked during initialization
384 // that mNumLongHMetrics is > 0, and that the metrics table is large enough
385 // to contain mNumLongHMetrics records
386 const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
387 hb_blob_get_data(mHmtxTable, nullptr));
388 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
389 uint16_t(metrics->metrics[glyph].advanceWidth));
392 hb_position_t gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const {
393 if (mUseFontGlyphWidths) {
394 MutexAutoLock lock(mCacheLock);
395 if (auto cached = mWidthCache->Lookup(glyph)) {
396 return cached.Data().mAdvance;
398 mCacheLock.Unlock();
399 hb_position_t advance = GetFont()->GetGlyphWidth(glyph);
400 mCacheLock.Lock();
401 mWidthCache->Put(glyph, WidthCacheData{glyph, advance});
402 return advance;
405 return GetGlyphHAdvanceUncached(glyph);
408 void gfxHarfBuzzShaper::GetGlyphHAdvances(unsigned int count,
409 const hb_codepoint_t* first_glyph,
410 unsigned int glyph_stride,
411 hb_position_t* first_advance,
412 unsigned int advance_stride) const {
413 if (mUseFontGlyphWidths) {
414 // Take the cache lock here, hoping we'll be able to retrieve a bunch of
415 // widths from the cache for the cost of a single locking operation.
416 MutexAutoLock lock(mCacheLock);
417 for (unsigned int i = 0; i < count; ++i) {
418 hb_codepoint_t gid = *first_glyph;
419 if (auto cached = mWidthCache->Lookup(gid)) {
420 *first_advance = cached.Data().mAdvance;
421 } else {
422 // Unlock to avoid deadlock if the font needs internal locking.
423 mCacheLock.Unlock();
424 hb_position_t advance = GetFont()->GetGlyphWidth(gid);
425 mCacheLock.Lock();
426 mWidthCache->Put(gid, WidthCacheData{gid, advance});
427 *first_advance = advance;
429 first_glyph = reinterpret_cast<const hb_codepoint_t*>(
430 reinterpret_cast<const char*>(first_glyph) + glyph_stride);
431 first_advance = reinterpret_cast<hb_position_t*>(
432 reinterpret_cast<char*>(first_advance) + advance_stride);
434 return;
437 for (unsigned int i = 0; i < count; ++i) {
438 *first_advance = GetGlyphHAdvanceUncached(*first_glyph);
439 first_glyph = reinterpret_cast<const hb_codepoint_t*>(
440 reinterpret_cast<const char*>(first_glyph) + glyph_stride);
441 first_advance = reinterpret_cast<hb_position_t*>(
442 reinterpret_cast<char*>(first_advance) + advance_stride);
446 hb_position_t gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) {
447 InitializeVertical();
449 if (!mVmtxTable) {
450 // Must be a "vertical" font that doesn't actually have vertical metrics.
451 // Return an invalid (negative) value to tell the caller to fall back to
452 // something else.
453 return -1;
456 NS_ASSERTION(mNumLongVMetrics > 0,
457 "font is lacking metrics, we shouldn't be here");
459 if (glyph >= uint32_t(mNumLongVMetrics)) {
460 glyph = mNumLongVMetrics - 1;
463 // glyph must be valid now, because we checked during initialization
464 // that mNumLongVMetrics is > 0, and that the metrics table is large enough
465 // to contain mNumLongVMetrics records
466 const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
467 hb_blob_get_data(mVmtxTable, nullptr));
468 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
469 uint16_t(metrics->metrics[glyph].advanceWidth));
472 static hb_position_t HBGetGlyphHAdvance(hb_font_t* font, void* font_data,
473 hb_codepoint_t glyph, void* user_data) {
474 const gfxHarfBuzzShaper::FontCallbackData* fcd =
475 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
476 return fcd->mShaper->GetGlyphHAdvance(glyph);
479 static void HBGetGlyphHAdvances(hb_font_t* font, void* font_data,
480 unsigned int count,
481 const hb_codepoint_t* first_glyph,
482 unsigned int glyph_stride,
483 hb_position_t* first_advance,
484 unsigned int advance_stride, void* user_data) {
485 const gfxHarfBuzzShaper::FontCallbackData* fcd =
486 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
487 fcd->mShaper->GetGlyphHAdvances(count, first_glyph, glyph_stride,
488 first_advance, advance_stride);
491 static hb_position_t HBGetGlyphVAdvance(hb_font_t* font, void* font_data,
492 hb_codepoint_t glyph, void* user_data) {
493 const gfxHarfBuzzShaper::FontCallbackData* fcd =
494 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
495 // Currently, we don't offer gfxFont subclasses a method to override this
496 // and provide hinted platform-specific vertical advances (analogous to the
497 // GetGlyphWidth method for horizontal advances). If that proves necessary,
498 // we'll add a new gfxFont method and call it from here.
499 hb_position_t advance = fcd->mShaper->GetGlyphVAdvance(glyph);
500 if (advance < 0) {
501 // Not available (e.g. broken metrics in the font); use a fallback value.
502 advance = FloatToFixed(fcd->mShaper->GetFont()
503 ->GetMetrics(nsFontMetrics::eVertical)
504 .aveCharWidth);
506 // We negate the value from GetGlyphVAdvance here because harfbuzz shapes
507 // with a coordinate system where positive is upwards, whereas the inline
508 // direction in which glyphs advance is downwards.
509 return -advance;
512 struct VORG {
513 AutoSwap_PRUint16 majorVersion;
514 AutoSwap_PRUint16 minorVersion;
515 AutoSwap_PRInt16 defaultVertOriginY;
516 AutoSwap_PRUint16 numVertOriginYMetrics;
519 struct VORGrec {
520 AutoSwap_PRUint16 glyphIndex;
521 AutoSwap_PRInt16 vertOriginY;
524 static hb_bool_t HBGetGlyphVOrigin(hb_font_t* font, void* font_data,
525 hb_codepoint_t glyph, hb_position_t* x,
526 hb_position_t* y, void* user_data) {
527 const gfxHarfBuzzShaper::FontCallbackData* fcd =
528 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
529 fcd->mShaper->GetGlyphVOrigin(glyph, x, y);
530 return true;
533 void gfxHarfBuzzShaper::GetGlyphVOrigin(hb_codepoint_t aGlyph,
534 hb_position_t* aX,
535 hb_position_t* aY) const {
536 *aX = 0.5 * GetGlyphHAdvance(aGlyph);
538 if (mVORGTable) {
539 // We checked in Initialize() that the VORG table is safely readable,
540 // so no length/bounds-check needed here.
541 const VORG* vorg =
542 reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
544 const VORGrec* lo = reinterpret_cast<const VORGrec*>(vorg + 1);
545 const VORGrec* hi = lo + uint16_t(vorg->numVertOriginYMetrics);
546 const VORGrec* limit = hi;
547 while (lo < hi) {
548 const VORGrec* mid = lo + (hi - lo) / 2;
549 if (uint16_t(mid->glyphIndex) < aGlyph) {
550 lo = mid + 1;
551 } else {
552 hi = mid;
556 if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) {
557 *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
558 int16_t(lo->vertOriginY));
559 } else {
560 *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
561 int16_t(vorg->defaultVertOriginY));
563 return;
566 if (mVmtxTable) {
567 bool emptyGlyf;
568 const Glyf* glyf = FindGlyf(aGlyph, &emptyGlyf);
569 if (glyf) {
570 if (emptyGlyf) {
571 *aY = 0;
572 return;
575 const ::GlyphMetrics* metrics = reinterpret_cast<const ::GlyphMetrics*>(
576 hb_blob_get_data(mVmtxTable, nullptr));
577 int16_t lsb;
578 if (aGlyph < hb_codepoint_t(mNumLongVMetrics)) {
579 // Glyph is covered by the first (advance & sidebearing) array
580 lsb = int16_t(metrics->metrics[aGlyph].lsb);
581 } else {
582 // Glyph is covered by the second (sidebearing-only) array
583 const AutoSwap_PRInt16* sidebearings =
584 reinterpret_cast<const AutoSwap_PRInt16*>(
585 &metrics->metrics[mNumLongVMetrics]);
586 lsb = int16_t(sidebearings[aGlyph - mNumLongVMetrics]);
588 *aY = FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
589 (lsb + int16_t(glyf->yMax)));
590 return;
591 } else {
592 // XXX TODO: not a truetype font; need to get glyph extents
593 // via some other API?
594 // For now, fall through to default code below.
598 if (mDefaultVOrg < 0.0) {
599 // XXX should we consider using OS/2 sTypo* metrics if available?
601 gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
602 TRUETYPE_TAG('h', 'h', 'e', 'a'));
603 if (hheaTable) {
604 uint32_t len;
605 const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
606 hb_blob_get_data(hheaTable, &len));
607 if (len >= sizeof(MetricsHeader)) {
608 // divide up the default advance we're using (1em) in proportion
609 // to ascender:descender from the hhea table
610 int16_t a = int16_t(hhea->ascender);
611 int16_t d = int16_t(hhea->descender);
612 mDefaultVOrg = FloatToFixed(GetFont()->GetAdjustedSize() * a / (a - d));
616 if (mDefaultVOrg < 0.0) {
617 // Last resort, for non-sfnt fonts: get the horizontal metrics and
618 // compute a default VOrg from their ascent and descent.
619 const gfxFont::Metrics& mtx = mFont->GetHorizontalMetrics();
620 gfxFloat advance =
621 mFont->GetMetrics(nsFontMetrics::eVertical).aveCharWidth;
622 gfxFloat ascent = mtx.emAscent;
623 gfxFloat height = ascent + mtx.emDescent;
624 // vOrigin that will place the glyph so that its origin is shifted
625 // down most of the way within overall (vertical) advance, in
626 // proportion to the font ascent as a part of the overall font
627 // height.
628 mDefaultVOrg = FloatToFixed(advance * ascent / height);
632 *aY = mDefaultVOrg;
635 static hb_bool_t HBGetGlyphExtents(hb_font_t* font, void* font_data,
636 hb_codepoint_t glyph,
637 hb_glyph_extents_t* extents,
638 void* user_data) {
639 const gfxHarfBuzzShaper::FontCallbackData* fcd =
640 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
641 return fcd->mShaper->GetGlyphExtents(glyph, extents);
644 // Find the data for glyph ID |aGlyph| in the 'glyf' table, if present.
645 // Returns null if not found, otherwise pointer to the beginning of the
646 // glyph's data. Sets aEmptyGlyf true if there is no actual data;
647 // otherwise, it's guaranteed that we can read at least the bounding box.
648 const gfxHarfBuzzShaper::Glyf* gfxHarfBuzzShaper::FindGlyf(
649 hb_codepoint_t aGlyph, bool* aEmptyGlyf) const {
650 if (!mLoadedLocaGlyf) {
651 mLoadedLocaGlyf = true; // only try this once; if it fails, this
652 // isn't a truetype font
653 gfxFontEntry* entry = mFont->GetFontEntry();
654 uint32_t len;
655 gfxFontEntry::AutoTable headTable(entry, TRUETYPE_TAG('h', 'e', 'a', 'd'));
656 if (!headTable) {
657 return nullptr;
659 const HeadTable* head =
660 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, &len));
661 if (len < sizeof(HeadTable)) {
662 return nullptr;
664 mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0;
665 mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l', 'o', 'c', 'a'));
666 mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g', 'l', 'y', 'f'));
669 if (!mLocaTable || !mGlyfTable) {
670 // it's not a truetype font
671 return nullptr;
674 uint32_t offset; // offset of glyph record in the 'glyf' table
675 uint32_t len;
676 const char* data = hb_blob_get_data(mLocaTable, &len);
677 if (mLocaLongOffsets) {
678 if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) {
679 return nullptr;
681 const AutoSwap_PRUint32* offsets =
682 reinterpret_cast<const AutoSwap_PRUint32*>(data);
683 offset = offsets[aGlyph];
684 *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
685 } else {
686 if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) {
687 return nullptr;
689 const AutoSwap_PRUint16* offsets =
690 reinterpret_cast<const AutoSwap_PRUint16*>(data);
691 offset = uint16_t(offsets[aGlyph]);
692 *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1]));
693 offset *= 2;
696 data = hb_blob_get_data(mGlyfTable, &len);
697 if (offset + sizeof(Glyf) > len) {
698 return nullptr;
701 return reinterpret_cast<const Glyf*>(data + offset);
704 hb_bool_t gfxHarfBuzzShaper::GetGlyphExtents(
705 hb_codepoint_t aGlyph, hb_glyph_extents_t* aExtents) const {
706 bool emptyGlyf;
707 const Glyf* glyf = FindGlyf(aGlyph, &emptyGlyf);
708 if (!glyf) {
709 // TODO: for non-truetype fonts, get extents some other way?
710 return false;
713 if (emptyGlyf) {
714 aExtents->x_bearing = 0;
715 aExtents->y_bearing = 0;
716 aExtents->width = 0;
717 aExtents->height = 0;
718 return true;
721 double f = mFont->FUnitsToDevUnitsFactor();
722 aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f);
723 aExtents->width =
724 FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f);
726 // Our y-coordinates are positive-downwards, whereas harfbuzz assumes
727 // positive-upwards; hence the apparently-reversed subtractions here.
728 aExtents->y_bearing = FloatToFixed(int16_t(glyf->yMax) * f -
729 mFont->GetHorizontalMetrics().emAscent);
730 aExtents->height =
731 FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f);
733 return true;
736 static hb_bool_t HBGetContourPoint(hb_font_t* font, void* font_data,
737 unsigned int point_index,
738 hb_codepoint_t glyph, hb_position_t* x,
739 hb_position_t* y, void* user_data) {
740 /* not yet implemented - no support for used of hinted contour points
741 to fine-tune anchor positions in GPOS AnchorFormat2 */
742 return false;
745 struct KernHeaderFmt0 {
746 AutoSwap_PRUint16 nPairs;
747 AutoSwap_PRUint16 searchRange;
748 AutoSwap_PRUint16 entrySelector;
749 AutoSwap_PRUint16 rangeShift;
752 struct KernPair {
753 AutoSwap_PRUint16 left;
754 AutoSwap_PRUint16 right;
755 AutoSwap_PRInt16 value;
758 // Find a kern pair in a Format 0 subtable.
759 // The aSubtable parameter points to the subtable itself, NOT its header,
760 // as the header structure differs between Windows and Mac (v0 and v1.0)
761 // versions of the 'kern' table.
762 // aSubtableLen is the length of the subtable EXCLUDING its header.
763 // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
764 // added to aValue, so that multiple subtables can accumulate a total
765 // kerning value for a given pair.
766 static void GetKernValueFmt0(const void* aSubtable, uint32_t aSubtableLen,
767 uint16_t aFirstGlyph, uint16_t aSecondGlyph,
768 int32_t& aValue, bool aIsOverride = false,
769 bool aIsMinimum = false) {
770 const KernHeaderFmt0* hdr =
771 reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
773 const KernPair* lo = reinterpret_cast<const KernPair*>(hdr + 1);
774 const KernPair* hi = lo + uint16_t(hdr->nPairs);
775 const KernPair* limit = hi;
777 if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
778 reinterpret_cast<const char*>(hi)) {
779 // subtable is not large enough to contain the claimed number
780 // of kern pairs, so just ignore it
781 return;
784 #define KERN_PAIR_KEY(l, r) (uint32_t((uint16_t(l) << 16) + uint16_t(r)))
786 uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
787 while (lo < hi) {
788 const KernPair* mid = lo + (hi - lo) / 2;
789 if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
790 lo = mid + 1;
791 } else {
792 hi = mid;
796 if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
797 if (aIsOverride) {
798 aValue = int16_t(lo->value);
799 } else if (aIsMinimum) {
800 aValue = std::max(aValue, int32_t(lo->value));
801 } else {
802 aValue += int16_t(lo->value);
807 // Get kerning value from Apple (version 1.0) kern table,
808 // subtable format 2 (simple N x M array of kerning values)
810 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
811 // for details of version 1.0 format 2 subtable.
813 struct KernHeaderVersion1Fmt2 {
814 KernTableSubtableHeaderVersion1 header;
815 AutoSwap_PRUint16 rowWidth;
816 AutoSwap_PRUint16 leftOffsetTable;
817 AutoSwap_PRUint16 rightOffsetTable;
818 AutoSwap_PRUint16 array;
821 struct KernClassTableHdr {
822 AutoSwap_PRUint16 firstGlyph;
823 AutoSwap_PRUint16 nGlyphs;
824 AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries
827 static int16_t GetKernValueVersion1Fmt2(const void* aSubtable,
828 uint32_t aSubtableLen,
829 uint16_t aFirstGlyph,
830 uint16_t aSecondGlyph) {
831 if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
832 return 0;
835 const char* base = reinterpret_cast<const char*>(aSubtable);
836 const char* subtableEnd = base + aSubtableLen;
838 const KernHeaderVersion1Fmt2* h =
839 reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
840 uint32_t offset = h->array;
842 const KernClassTableHdr* leftClassTable =
843 reinterpret_cast<const KernClassTableHdr*>(base +
844 uint16_t(h->leftOffsetTable));
845 if (reinterpret_cast<const char*>(leftClassTable) +
846 sizeof(KernClassTableHdr) >
847 subtableEnd) {
848 return 0;
850 if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) {
851 aFirstGlyph -= uint16_t(leftClassTable->firstGlyph);
852 if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) {
853 if (reinterpret_cast<const char*>(leftClassTable) +
854 sizeof(KernClassTableHdr) + aFirstGlyph * sizeof(uint16_t) >=
855 subtableEnd) {
856 return 0;
858 offset = uint16_t(leftClassTable->offsets[aFirstGlyph]);
862 const KernClassTableHdr* rightClassTable =
863 reinterpret_cast<const KernClassTableHdr*>(base +
864 uint16_t(h->rightOffsetTable));
865 if (reinterpret_cast<const char*>(rightClassTable) +
866 sizeof(KernClassTableHdr) >
867 subtableEnd) {
868 return 0;
870 if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) {
871 aSecondGlyph -= uint16_t(rightClassTable->firstGlyph);
872 if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) {
873 if (reinterpret_cast<const char*>(rightClassTable) +
874 sizeof(KernClassTableHdr) + aSecondGlyph * sizeof(uint16_t) >=
875 subtableEnd) {
876 return 0;
878 offset += uint16_t(rightClassTable->offsets[aSecondGlyph]);
882 const AutoSwap_PRInt16* pval =
883 reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
884 if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
885 return 0;
887 return *pval;
890 // Get kerning value from Apple (version 1.0) kern table,
891 // subtable format 3 (simple N x M array of kerning values)
893 // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
894 // for details of version 1.0 format 3 subtable.
896 struct KernHeaderVersion1Fmt3 {
897 KernTableSubtableHeaderVersion1 header;
898 AutoSwap_PRUint16 glyphCount;
899 uint8_t kernValueCount;
900 uint8_t leftClassCount;
901 uint8_t rightClassCount;
902 uint8_t flags;
905 static int16_t GetKernValueVersion1Fmt3(const void* aSubtable,
906 uint32_t aSubtableLen,
907 uint16_t aFirstGlyph,
908 uint16_t aSecondGlyph) {
909 // check that we can safely read the header fields
910 if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
911 return 0;
914 const KernHeaderVersion1Fmt3* hdr =
915 reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
916 if (hdr->flags != 0) {
917 return 0;
920 uint16_t glyphCount = hdr->glyphCount;
922 // check that table is large enough for the arrays
923 if (sizeof(KernHeaderVersion1Fmt3) + hdr->kernValueCount * sizeof(int16_t) +
924 glyphCount + glyphCount + hdr->leftClassCount * hdr->rightClassCount >
925 aSubtableLen) {
926 return 0;
929 if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
930 // glyphs are out of range for the class tables
931 return 0;
934 // get pointers to the four arrays within the subtable
935 const AutoSwap_PRInt16* kernValue =
936 reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
937 const uint8_t* leftClass =
938 reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount);
939 const uint8_t* rightClass = leftClass + glyphCount;
940 const uint8_t* kernIndex = rightClass + glyphCount;
942 uint8_t lc = leftClass[aFirstGlyph];
943 uint8_t rc = rightClass[aSecondGlyph];
944 if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
945 return 0;
948 uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
949 rightClass[aSecondGlyph]];
950 if (ki >= hdr->kernValueCount) {
951 return 0;
954 return kernValue[ki];
957 #define KERN0_COVERAGE_HORIZONTAL 0x0001
958 #define KERN0_COVERAGE_MINIMUM 0x0002
959 #define KERN0_COVERAGE_CROSS_STREAM 0x0004
960 #define KERN0_COVERAGE_OVERRIDE 0x0008
961 #define KERN0_COVERAGE_RESERVED 0x00F0
963 #define KERN1_COVERAGE_VERTICAL 0x8000
964 #define KERN1_COVERAGE_CROSS_STREAM 0x4000
965 #define KERN1_COVERAGE_VARIATION 0x2000
966 #define KERN1_COVERAGE_RESERVED 0x1F00
968 hb_position_t gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph,
969 uint16_t aSecondGlyph) const {
970 // We want to ignore any kern pairs involving <space>, because we are
971 // handling words in isolation, the only space characters seen here are
972 // the ones artificially added by the textRun code.
973 uint32_t spaceGlyph = mFont->GetSpaceGlyph();
974 if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
975 return 0;
978 if (!mKernTable) {
979 mKernTable =
980 mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k', 'e', 'r', 'n'));
981 if (!mKernTable) {
982 mKernTable = hb_blob_get_empty();
986 uint32_t len;
987 const char* base = hb_blob_get_data(mKernTable, &len);
988 if (len < sizeof(KernTableVersion0)) {
989 return 0;
991 int32_t value = 0;
993 // First try to interpret as "version 0" kern table
994 // (see http://www.microsoft.com/typography/otspec/kern.htm)
995 const KernTableVersion0* kern0 =
996 reinterpret_cast<const KernTableVersion0*>(base);
997 if (uint16_t(kern0->version) == 0) {
998 uint16_t nTables = kern0->nTables;
999 uint32_t offs = sizeof(KernTableVersion0);
1000 for (uint16_t i = 0; i < nTables; ++i) {
1001 if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
1002 break;
1004 const KernTableSubtableHeaderVersion0* st0 =
1005 reinterpret_cast<const KernTableSubtableHeaderVersion0*>(base + offs);
1006 uint16_t subtableLen = uint16_t(st0->length);
1007 if (offs + subtableLen > len) {
1008 break;
1010 offs += subtableLen;
1011 uint16_t coverage = st0->coverage;
1012 if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
1013 // we only care about horizontal kerning (for now)
1014 continue;
1016 if (coverage & (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
1017 // we don't support cross-stream kerning, and
1018 // reserved bits should be zero;
1019 // ignore the subtable if not
1020 continue;
1022 uint8_t format = (coverage >> 8);
1023 switch (format) {
1024 case 0:
1025 GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0), aFirstGlyph,
1026 aSecondGlyph, value,
1027 (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
1028 (coverage & KERN0_COVERAGE_MINIMUM) != 0);
1029 break;
1030 default:
1031 // TODO: implement support for other formats,
1032 // if they're ever used in practice
1033 #if DEBUG
1035 char buf[1024];
1036 SprintfLiteral(buf,
1037 "unknown kern subtable in %s: "
1038 "ver 0 format %d\n",
1039 mFont->GetName().get(), format);
1040 NS_WARNING(buf);
1042 #endif
1043 break;
1046 } else {
1047 // It wasn't a "version 0" table; check if it is Apple version 1.0
1048 // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
1049 const KernTableVersion1* kern1 =
1050 reinterpret_cast<const KernTableVersion1*>(base);
1051 if (uint32_t(kern1->version) == 0x00010000) {
1052 uint32_t nTables = kern1->nTables;
1053 uint32_t offs = sizeof(KernTableVersion1);
1054 for (uint32_t i = 0; i < nTables; ++i) {
1055 if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
1056 break;
1058 const KernTableSubtableHeaderVersion1* st1 =
1059 reinterpret_cast<const KernTableSubtableHeaderVersion1*>(base +
1060 offs);
1061 uint32_t subtableLen = uint32_t(st1->length);
1062 offs += subtableLen;
1063 uint16_t coverage = st1->coverage;
1064 if (coverage & (KERN1_COVERAGE_VERTICAL | KERN1_COVERAGE_CROSS_STREAM |
1065 KERN1_COVERAGE_VARIATION | KERN1_COVERAGE_RESERVED)) {
1066 // we only care about horizontal kerning (for now),
1067 // we don't support cross-stream kerning,
1068 // we don't support variations,
1069 // reserved bits should be zero;
1070 // ignore the subtable if not
1071 continue;
1073 uint8_t format = (coverage & 0xff);
1074 switch (format) {
1075 case 0:
1076 GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1), aFirstGlyph,
1077 aSecondGlyph, value);
1078 break;
1079 case 2:
1080 value = GetKernValueVersion1Fmt2(st1, subtableLen, aFirstGlyph,
1081 aSecondGlyph);
1082 break;
1083 case 3:
1084 value = GetKernValueVersion1Fmt3(st1, subtableLen, aFirstGlyph,
1085 aSecondGlyph);
1086 break;
1087 default:
1088 // TODO: implement support for other formats.
1089 // Note that format 1 cannot be supported here,
1090 // as it requires the full glyph array to run the FSM,
1091 // not just the current glyph pair.
1092 #if DEBUG
1094 char buf[1024];
1095 SprintfLiteral(buf,
1096 "unknown kern subtable in %s: "
1097 "ver 0 format %d\n",
1098 mFont->GetName().get(), format);
1099 NS_WARNING(buf);
1101 #endif
1102 break;
1108 if (value != 0) {
1109 return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
1111 return 0;
1114 static hb_position_t HBGetHKerning(hb_font_t* font, void* font_data,
1115 hb_codepoint_t first_glyph,
1116 hb_codepoint_t second_glyph,
1117 void* user_data) {
1118 const gfxHarfBuzzShaper::FontCallbackData* fcd =
1119 static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
1120 return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
1124 * HarfBuzz unicode property callbacks
1127 static hb_codepoint_t HBGetMirroring(hb_unicode_funcs_t* ufuncs,
1128 hb_codepoint_t aCh, void* user_data) {
1129 return intl::UnicodeProperties::CharMirror(aCh);
1132 static hb_unicode_general_category_t HBGetGeneralCategory(
1133 hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh, void* user_data) {
1134 return hb_unicode_general_category_t(GetGeneralCategory(aCh));
1137 static hb_script_t HBGetScript(hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh,
1138 void* user_data) {
1139 return hb_script_t(
1140 GetScriptTagForCode(intl::UnicodeProperties::GetScriptCode(aCh)));
1143 static hb_unicode_combining_class_t HBGetCombiningClass(
1144 hb_unicode_funcs_t* ufuncs, hb_codepoint_t aCh, void* user_data) {
1145 return hb_unicode_combining_class_t(
1146 intl::UnicodeProperties::GetCombiningClass(aCh));
1149 static hb_bool_t HBUnicodeCompose(hb_unicode_funcs_t* ufuncs, hb_codepoint_t a,
1150 hb_codepoint_t b, hb_codepoint_t* ab,
1151 void* user_data) {
1152 char32_t ch = intl::String::ComposePairNFC(a, b);
1153 if (ch > 0) {
1154 *ab = ch;
1155 return true;
1158 return false;
1161 static hb_bool_t HBUnicodeDecompose(hb_unicode_funcs_t* ufuncs,
1162 hb_codepoint_t ab, hb_codepoint_t* a,
1163 hb_codepoint_t* b, void* user_data) {
1164 #ifdef MOZ_WIDGET_ANDROID
1165 // Hack for the SamsungDevanagari font, bug 1012365:
1166 // support U+0972 by decomposing it.
1167 if (ab == 0x0972) {
1168 *a = 0x0905;
1169 *b = 0x0945;
1170 return true;
1172 #endif
1174 char32_t decomp[2] = {0};
1175 if (intl::String::DecomposeRawNFD(ab, decomp)) {
1176 if (decomp[1] || decomp[0] != ab) {
1177 *a = decomp[0];
1178 *b = decomp[1];
1179 return true;
1183 return false;
1186 static void AddOpenTypeFeature(uint32_t aTag, uint32_t aValue, void* aUserArg) {
1187 nsTArray<hb_feature_t>* features =
1188 static_cast<nsTArray<hb_feature_t>*>(aUserArg);
1190 hb_feature_t feat = {0, 0, 0, UINT_MAX};
1191 feat.tag = aTag;
1192 feat.value = aValue;
1193 features->AppendElement(feat);
1197 * gfxFontShaper override to initialize the text run using HarfBuzz
1200 static hb_font_funcs_t* sHBFontFuncs = nullptr;
1201 static hb_font_funcs_t* sNominalGlyphFunc = nullptr;
1202 static hb_unicode_funcs_t* sHBUnicodeFuncs = nullptr;
1203 MOZ_RUNINIT static const hb_script_t sMathScript =
1204 hb_ot_tag_to_script(HB_TAG('m', 'a', 't', 'h'));
1206 bool gfxHarfBuzzShaper::Initialize() {
1207 if (mInitialized) {
1208 return mHBFont != nullptr;
1210 mInitialized = true;
1211 mCallbackData.mShaper = this;
1213 if (!sHBFontFuncs) {
1214 // static function callback pointers, initialized by the first
1215 // harfbuzz shaper used
1216 sHBFontFuncs = hb_font_funcs_create();
1217 hb_font_funcs_set_nominal_glyph_func(sHBFontFuncs, HBGetNominalGlyph,
1218 nullptr, nullptr);
1219 hb_font_funcs_set_nominal_glyphs_func(sHBFontFuncs, HBGetNominalGlyphs,
1220 nullptr, nullptr);
1221 hb_font_funcs_set_variation_glyph_func(sHBFontFuncs, HBGetVariationGlyph,
1222 nullptr, nullptr);
1223 hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs, HBGetGlyphHAdvance,
1224 nullptr, nullptr);
1225 hb_font_funcs_set_glyph_h_advances_func(sHBFontFuncs, HBGetGlyphHAdvances,
1226 nullptr, nullptr);
1227 hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs, HBGetGlyphVAdvance,
1228 nullptr, nullptr);
1229 hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs, HBGetGlyphVOrigin,
1230 nullptr, nullptr);
1231 hb_font_funcs_set_glyph_extents_func(sHBFontFuncs, HBGetGlyphExtents,
1232 nullptr, nullptr);
1233 hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs, HBGetContourPoint,
1234 nullptr, nullptr);
1235 hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs, HBGetHKerning, nullptr,
1236 nullptr);
1237 hb_font_funcs_make_immutable(sHBFontFuncs);
1239 sNominalGlyphFunc = hb_font_funcs_create();
1240 hb_font_funcs_set_nominal_glyph_func(sNominalGlyphFunc, HBGetNominalGlyph,
1241 nullptr, nullptr);
1242 hb_font_funcs_make_immutable(sNominalGlyphFunc);
1244 sHBUnicodeFuncs = hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
1245 hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs, HBGetMirroring,
1246 nullptr, nullptr);
1247 hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript, nullptr,
1248 nullptr);
1249 hb_unicode_funcs_set_general_category_func(
1250 sHBUnicodeFuncs, HBGetGeneralCategory, nullptr, nullptr);
1251 hb_unicode_funcs_set_combining_class_func(
1252 sHBUnicodeFuncs, HBGetCombiningClass, nullptr, nullptr);
1253 hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs, HBUnicodeCompose,
1254 nullptr, nullptr);
1255 hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs, HBUnicodeDecompose,
1256 nullptr, nullptr);
1257 hb_unicode_funcs_make_immutable(sHBUnicodeFuncs);
1260 gfxFontEntry* entry = mFont->GetFontEntry();
1261 if (!mUseFontGetGlyph) {
1262 // get the cmap table and find offset to our subtable
1263 mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1264 if (!mCmapTable) {
1265 NS_WARNING("failed to load cmap, glyphs will be missing");
1266 return false;
1268 uint32_t len;
1269 const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
1270 mCmapFormat = gfxFontUtils::FindPreferredSubtable(
1271 data, len, &mSubtableOffset, &mUVSTableOffset, &mIsSymbolFont);
1272 if (mCmapFormat <= 0) {
1273 return false;
1277 // We don't need to take the cache lock here, as we're just initializing the
1278 // shaper and no other thread can yet be using it.
1279 MOZ_PUSH_IGNORE_THREAD_SAFETY
1280 mCmapCache = MakeUnique<CmapCache>();
1282 if (mUseFontGlyphWidths) {
1283 mWidthCache = MakeUnique<WidthCache>();
1284 } else {
1285 // If font doesn't implement GetGlyphWidth, we will be reading
1286 // the metrics table directly, so make sure we can load it.
1287 if (!LoadHmtxTable()) {
1288 return false;
1291 MOZ_POP_THREAD_SAFETY
1293 mBuffer = hb_buffer_create();
1294 hb_buffer_set_unicode_funcs(mBuffer, sHBUnicodeFuncs);
1295 hb_buffer_set_cluster_level(mBuffer,
1296 HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
1298 auto* funcs =
1299 mFont->GetFontEntry()->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))
1300 ? sNominalGlyphFunc
1301 : sHBFontFuncs;
1302 mHBFont = CreateHBFont(mFont, funcs, &mCallbackData);
1304 return true;
1307 hb_font_t* gfxHarfBuzzShaper::CreateHBFont(gfxFont* aFont,
1308 hb_font_funcs_t* aFontFuncs,
1309 FontCallbackData* aCallbackData) {
1310 auto face(aFont->GetFontEntry()->GetHBFace());
1311 hb_font_t* result = hb_font_create(face);
1313 if (aFontFuncs && aCallbackData) {
1314 if (aFontFuncs == sNominalGlyphFunc) {
1315 hb_font_t* subfont = hb_font_create_sub_font(result);
1316 hb_font_destroy(result);
1317 result = subfont;
1319 hb_font_set_funcs(result, aFontFuncs, aCallbackData, nullptr);
1321 hb_font_set_ppem(result, aFont->GetAdjustedSize(), aFont->GetAdjustedSize());
1322 uint32_t scale = FloatToFixed(aFont->GetAdjustedSize()); // 16.16 fixed-point
1323 hb_font_set_scale(result, scale, scale);
1325 AutoTArray<gfxFontVariation, 8> vars;
1326 aFont->GetFontEntry()->GetVariationsForStyle(vars, *aFont->GetStyle());
1327 if (vars.Length() > 0) {
1328 // Fortunately, the hb_variation_t struct is compatible with our
1329 // gfxFontVariation, so we can simply cast here.
1330 static_assert(
1331 sizeof(gfxFontVariation) == sizeof(hb_variation_t) &&
1332 offsetof(gfxFontVariation, mTag) == offsetof(hb_variation_t, tag) &&
1333 offsetof(gfxFontVariation, mValue) ==
1334 offsetof(hb_variation_t, value),
1335 "Gecko vs HarfBuzz struct mismatch!");
1336 auto hbVars = reinterpret_cast<const hb_variation_t*>(vars.Elements());
1337 hb_font_set_variations(result, hbVars, vars.Length());
1340 return result;
1343 bool gfxHarfBuzzShaper::LoadHmtxTable() {
1344 // Read mNumLongHMetrics from metrics-head table without caching its
1345 // blob, and preload/cache the metrics table.
1346 gfxFontEntry* entry = mFont->GetFontEntry();
1347 gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h', 'h', 'e', 'a'));
1348 if (hheaTable) {
1349 uint32_t len;
1350 const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
1351 hb_blob_get_data(hheaTable, &len));
1352 if (len >= sizeof(MetricsHeader)) {
1353 mNumLongHMetrics = hhea->numOfLongMetrics;
1354 if (mNumLongHMetrics > 0 && int16_t(hhea->metricDataFormat) == 0) {
1355 // no point reading metrics if number of entries is zero!
1356 // in that case, we won't be able to use this font
1357 // (this method will return FALSE below if mHmtxTable
1358 // is null)
1359 mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h', 'm', 't', 'x'));
1360 if (mHmtxTable && hb_blob_get_length(mHmtxTable) <
1361 mNumLongHMetrics * sizeof(LongMetric)) {
1362 // metrics table is not large enough for the claimed
1363 // number of entries: invalid, do not use.
1364 hb_blob_destroy(mHmtxTable);
1365 mHmtxTable = nullptr;
1370 if (!mHmtxTable) {
1371 return false;
1373 return true;
1376 void gfxHarfBuzzShaper::InitializeVertical() {
1377 // We only do this once. If we don't have a mHmtxTable after that,
1378 // we'll be making up fallback metrics.
1379 if (mVerticalInitialized) {
1380 return;
1382 mVerticalInitialized = true;
1384 if (!mHmtxTable) {
1385 if (!LoadHmtxTable()) {
1386 return;
1390 // Load vertical metrics if present in the font; if not, we'll synthesize
1391 // vertical glyph advances based on (horizontal) ascent/descent metrics.
1392 gfxFontEntry* entry = mFont->GetFontEntry();
1393 gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v', 'h', 'e', 'a'));
1394 if (vheaTable) {
1395 uint32_t len;
1396 const MetricsHeader* vhea = reinterpret_cast<const MetricsHeader*>(
1397 hb_blob_get_data(vheaTable, &len));
1398 if (len >= sizeof(MetricsHeader)) {
1399 mNumLongVMetrics = vhea->numOfLongMetrics;
1400 gfxFontEntry::AutoTable maxpTable(entry,
1401 TRUETYPE_TAG('m', 'a', 'x', 'p'));
1402 int numGlyphs = -1; // invalid if we fail to read 'maxp'
1403 if (maxpTable &&
1404 hb_blob_get_length(maxpTable) >= sizeof(MaxpTableHeader)) {
1405 const MaxpTableHeader* maxp = reinterpret_cast<const MaxpTableHeader*>(
1406 hb_blob_get_data(maxpTable, nullptr));
1407 numGlyphs = uint16_t(maxp->numGlyphs);
1409 if (mNumLongVMetrics > 0 && mNumLongVMetrics <= numGlyphs &&
1410 int16_t(vhea->metricDataFormat) == 0) {
1411 mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v', 'm', 't', 'x'));
1412 if (mVmtxTable &&
1413 hb_blob_get_length(mVmtxTable) <
1414 mNumLongVMetrics * sizeof(LongMetric) +
1415 (numGlyphs - mNumLongVMetrics) * sizeof(int16_t)) {
1416 // metrics table is not large enough for the claimed
1417 // number of entries: invalid, do not use.
1418 hb_blob_destroy(mVmtxTable);
1419 mVmtxTable = nullptr;
1425 // For CFF fonts only, load a VORG table if present.
1426 if (entry->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))) {
1427 mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V', 'O', 'R', 'G'));
1428 if (mVORGTable) {
1429 uint32_t len;
1430 const VORG* vorg =
1431 reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, &len));
1432 if (len < sizeof(VORG) || uint16_t(vorg->majorVersion) != 1 ||
1433 uint16_t(vorg->minorVersion) != 0 ||
1434 len < sizeof(VORG) +
1435 uint16_t(vorg->numVertOriginYMetrics) * sizeof(VORGrec)) {
1436 // VORG table is an unknown version, or not large enough
1437 // to be valid -- discard it.
1438 NS_WARNING("discarding invalid VORG table");
1439 hb_blob_destroy(mVORGTable);
1440 mVORGTable = nullptr;
1446 bool gfxHarfBuzzShaper::ShapeText(DrawTarget* aDrawTarget,
1447 const char16_t* aText, uint32_t aOffset,
1448 uint32_t aLength, Script aScript,
1449 nsAtom* aLanguage, bool aVertical,
1450 RoundingFlags aRounding,
1451 gfxShapedText* aShapedText) {
1452 mUseVerticalPresentationForms = false;
1454 if (!Initialize()) {
1455 return false;
1458 if (aVertical) {
1459 InitializeVertical();
1460 if (!mFont->GetFontEntry()->SupportsOpenTypeFeature(
1461 aScript, HB_TAG('v', 'e', 'r', 't'))) {
1462 mUseVerticalPresentationForms = true;
1466 const gfxFontStyle* style = mFont->GetStyle();
1468 // determine whether petite-caps falls back to small-caps
1469 bool addSmallCaps = false;
1470 if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
1471 switch (style->variantCaps) {
1472 case NS_FONT_VARIANT_CAPS_ALLPETITE:
1473 case NS_FONT_VARIANT_CAPS_PETITECAPS:
1474 bool synLower, synUpper;
1475 mFont->SupportsVariantCaps(aScript, style->variantCaps, addSmallCaps,
1476 synLower, synUpper);
1477 break;
1478 default:
1479 break;
1483 gfxFontEntry* entry = mFont->GetFontEntry();
1485 // insert any merged features into hb_feature array
1486 AutoTArray<hb_feature_t, 20> features;
1487 MergeFontFeatures(style, entry->mFeatureSettings,
1488 aShapedText->DisableLigatures(), entry->FamilyName(),
1489 addSmallCaps, AddOpenTypeFeature, &features);
1491 // For CJK script, match kerning and proportional-alternates (palt) features
1492 // (and their vertical counterparts) as per spec:
1493 // https://learn.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-palt
1494 // and disable kerning by default (for font-kerning:auto).
1495 if (gfxTextRun::IsCJKScript(aScript)) {
1496 hb_tag_t kern =
1497 aVertical ? HB_TAG('v', 'k', 'r', 'n') : HB_TAG('k', 'e', 'r', 'n');
1498 hb_tag_t alt =
1499 aVertical ? HB_TAG('v', 'p', 'a', 'l') : HB_TAG('p', 'a', 'l', 't');
1500 struct Cmp {
1501 bool Equals(const hb_feature_t& a, const hb_tag_t& b) const {
1502 return a.tag == b;
1505 constexpr auto NoIndex = nsTArray<hb_feature_t>::NoIndex;
1506 nsTArray<hb_feature_t>::index_type i = features.IndexOf(kern, 0, Cmp());
1507 if (i == NoIndex) {
1508 // Kerning was not explicitly set; override harfbuzz's default to disable
1509 // it.
1510 features.AppendElement(hb_feature_t{kern, 0, HB_FEATURE_GLOBAL_START,
1511 HB_FEATURE_GLOBAL_END});
1512 } else if (features[i].value) {
1513 // If kerning was explicitly enabled), we also turn on proportional
1514 // alternates, as per the OpenType feature registry.
1515 // Bug 1798297: for the Yu Gothic UI font, we don't do this, because its
1516 // 'palt' feature produces badly-spaced (overcrowded) kana glyphs.
1517 if (!entry->FamilyName().EqualsLiteral("Yu Gothic UI")) {
1518 if (features.IndexOf(alt, 0, Cmp()) == NoIndex) {
1519 features.AppendElement(hb_feature_t{alt, 1, HB_FEATURE_GLOBAL_START,
1520 HB_FEATURE_GLOBAL_END});
1526 bool isRightToLeft = aShapedText->IsRightToLeft();
1528 hb_buffer_set_direction(
1529 mBuffer, aVertical
1530 ? HB_DIRECTION_TTB
1531 : (isRightToLeft ? HB_DIRECTION_RTL : HB_DIRECTION_LTR));
1532 hb_script_t scriptTag;
1533 if (aShapedText->GetFlags() & gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT) {
1534 scriptTag = sMathScript;
1535 } else {
1536 scriptTag = GetHBScriptUsedForShaping(aScript);
1538 hb_buffer_set_script(mBuffer, scriptTag);
1540 hb_language_t language;
1541 if (style->languageOverride._0) {
1542 language = hb_ot_tag_to_language(style->languageOverride._0);
1543 } else if (entry->mLanguageOverride) {
1544 language = hb_ot_tag_to_language(entry->mLanguageOverride);
1545 } else if (aLanguage) {
1546 nsCString langString;
1547 aLanguage->ToUTF8String(langString);
1548 language = hb_language_from_string(langString.get(), langString.Length());
1549 } else {
1550 language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE);
1552 hb_buffer_set_language(mBuffer, language);
1554 uint32_t length = aLength;
1555 hb_buffer_add_utf16(mBuffer, reinterpret_cast<const uint16_t*>(aText), length,
1556 0, length);
1558 hb_shape(mHBFont, mBuffer, features.Elements(), features.Length());
1560 if (isRightToLeft) {
1561 hb_buffer_reverse(mBuffer);
1564 nsresult rv = SetGlyphsFromRun(aShapedText, aOffset, aLength, aText,
1565 aVertical, aRounding);
1567 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1568 "failed to store glyphs into gfxShapedWord");
1569 hb_buffer_clear_contents(mBuffer);
1571 return NS_SUCCEEDED(rv);
1574 #define SMALL_GLYPH_RUN \
1575 128 // some testing indicates that 90%+ of text runs
1576 // will fit without requiring separate allocation
1577 // for charToGlyphArray
1579 nsresult gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText* aShapedText,
1580 uint32_t aOffset, uint32_t aLength,
1581 const char16_t* aText,
1582 bool aVertical,
1583 RoundingFlags aRounding) {
1584 typedef gfxShapedText::CompressedGlyph CompressedGlyph;
1586 uint32_t numGlyphs;
1587 const hb_glyph_info_t* ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
1588 if (numGlyphs == 0) {
1589 return NS_OK;
1592 AutoTArray<gfxTextRun::DetailedGlyph, 1> detailedGlyphs;
1594 uint32_t wordLength = aLength;
1595 static const int32_t NO_GLYPH = -1;
1596 AutoTArray<int32_t, SMALL_GLYPH_RUN> charToGlyphArray;
1597 if (!charToGlyphArray.SetLength(wordLength, fallible)) {
1598 return NS_ERROR_OUT_OF_MEMORY;
1601 int32_t* charToGlyph = charToGlyphArray.Elements();
1602 for (uint32_t offset = 0; offset < wordLength; ++offset) {
1603 charToGlyph[offset] = NO_GLYPH;
1606 for (uint32_t i = 0; i < numGlyphs; ++i) {
1607 uint32_t loc = ginfo[i].cluster;
1608 if (loc < wordLength) {
1609 charToGlyph[loc] = i;
1613 int32_t glyphStart = 0; // looking for a clump that starts at this glyph
1614 int32_t charStart = 0; // and this char index within the range of the run
1616 bool roundI, roundB;
1617 if (aVertical) {
1618 roundI = bool(aRounding & RoundingFlags::kRoundY);
1619 roundB = bool(aRounding & RoundingFlags::kRoundX);
1620 } else {
1621 roundI = bool(aRounding & RoundingFlags::kRoundX);
1622 roundB = bool(aRounding & RoundingFlags::kRoundY);
1625 int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
1626 CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
1628 // factor to convert 16.16 fixed-point pixels to app units
1629 // (only used if not rounding)
1630 double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
1632 // Residual from rounding of previous advance, for use in rounding the
1633 // subsequent offset or advance appropriately. 16.16 fixed-point
1635 // When rounding, the goal is to make the distance between glyphs and
1636 // their base glyph equal to the integral number of pixels closest to that
1637 // suggested by that shaper.
1638 // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
1640 // The value of the residual is the part of the desired distance that has
1641 // not been included in integer offsets.
1642 hb_position_t residual = 0;
1644 // keep track of y-position to set glyph offsets if needed
1645 nscoord bPos = 0;
1647 const hb_glyph_position_t* posInfo =
1648 hb_buffer_get_glyph_positions(mBuffer, nullptr);
1649 if (!posInfo) {
1650 // Some kind of unexpected failure inside harfbuzz?
1651 return NS_ERROR_UNEXPECTED;
1654 while (glyphStart < int32_t(numGlyphs)) {
1655 int32_t charEnd = ginfo[glyphStart].cluster;
1656 int32_t glyphEnd = glyphStart;
1657 int32_t charLimit = wordLength;
1658 while (charEnd < charLimit) {
1659 // This is normally executed once for each iteration of the outer loop,
1660 // but in unusual cases where the character/glyph association is complex,
1661 // the initial character range might correspond to a non-contiguous
1662 // glyph range with "holes" in it. If so, we will repeat this loop to
1663 // extend the character range until we have a contiguous glyph sequence.
1664 charEnd += 1;
1665 while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
1666 charEnd += 1;
1669 // find the maximum glyph index covered by the clump so far
1670 for (int32_t i = charStart; i < charEnd; ++i) {
1671 if (charToGlyph[i] != NO_GLYPH) {
1672 glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1);
1673 // update extent of glyph range
1677 if (glyphEnd == glyphStart + 1) {
1678 // for the common case of a single-glyph clump,
1679 // we can skip the following checks
1680 break;
1683 if (glyphEnd == glyphStart) {
1684 // no glyphs, try to extend the clump
1685 continue;
1688 // check whether all glyphs in the range are associated with the
1689 // characters in our clump; if not, we have a discontinuous range, and
1690 // should extend it unless we've reached the end of the text
1691 bool allGlyphsAreWithinCluster = true;
1692 for (int32_t i = glyphStart; i < glyphEnd; ++i) {
1693 int32_t glyphCharIndex = ginfo[i].cluster;
1694 if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
1695 allGlyphsAreWithinCluster = false;
1696 break;
1699 if (allGlyphsAreWithinCluster) {
1700 break;
1704 NS_ASSERTION(glyphStart < glyphEnd,
1705 "character/glyph clump contains no glyphs!");
1706 NS_ASSERTION(charStart != charEnd,
1707 "character/glyph clump contains no characters!");
1709 // Now charStart..charEnd is a ligature clump, corresponding to
1710 // glyphStart..glyphEnd; Set baseCharIndex to the char we'll actually attach
1711 // the glyphs to (1st of ligature), and endCharIndex to the limit (position
1712 // beyond the last char), adjusting for the offset of the stringRange
1713 // relative to the textRun.
1714 int32_t baseCharIndex, endCharIndex;
1715 while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
1716 charEnd++;
1717 baseCharIndex = charStart;
1718 endCharIndex = charEnd;
1720 // Then we check if the clump falls outside our actual string range;
1721 // if so, just go to the next.
1722 if (baseCharIndex >= int32_t(wordLength)) {
1723 glyphStart = glyphEnd;
1724 charStart = charEnd;
1725 continue;
1727 // Ensure we won't try to go beyond the valid length of the textRun's text
1728 endCharIndex = std::min<int32_t>(endCharIndex, wordLength);
1730 // Now we're ready to set the glyph info in the textRun
1731 int32_t glyphsInClump = glyphEnd - glyphStart;
1733 // Check for default-ignorable char that didn't get filtered, combined,
1734 // etc by the shaping process, and remove from the run.
1735 // (This may be done within harfbuzz eventually.)
1736 if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
1737 aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
1738 aText[baseCharIndex])) {
1739 glyphStart = glyphEnd;
1740 charStart = charEnd;
1741 continue;
1744 // HarfBuzz gives us physical x- and y-coordinates, but we will store
1745 // them as logical inline- and block-direction values in the textrun.
1747 hb_position_t i_offset, i_advance; // inline-direction offset/advance
1748 hb_position_t b_offset, b_advance; // block-direction offset/advance
1749 if (aVertical) {
1750 // our coordinate directions are the opposite of harfbuzz's
1751 // when doing top-to-bottom shaping
1752 i_offset = -posInfo[glyphStart].y_offset;
1753 i_advance = -posInfo[glyphStart].y_advance;
1754 b_offset = -posInfo[glyphStart].x_offset;
1755 b_advance = -posInfo[glyphStart].x_advance;
1756 } else {
1757 i_offset = posInfo[glyphStart].x_offset;
1758 i_advance = posInfo[glyphStart].x_advance;
1759 b_offset = posInfo[glyphStart].y_offset;
1760 b_advance = posInfo[glyphStart].y_advance;
1763 nscoord iOffset, advance;
1764 if (roundI) {
1765 iOffset = appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
1766 // Desired distance from the base glyph to the next reference point.
1767 hb_position_t width = i_advance - i_offset;
1768 int intWidth = FixedToIntRound(width);
1769 residual = width - FloatToFixed(intWidth);
1770 advance = appUnitsPerDevUnit * intWidth + iOffset;
1771 } else {
1772 iOffset = floor(hb2appUnits * i_offset + 0.5);
1773 advance = floor(hb2appUnits * i_advance + 0.5);
1775 // Check if it's a simple one-to-one mapping
1776 if (glyphsInClump == 1 &&
1777 CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
1778 CompressedGlyph::IsSimpleAdvance(advance) &&
1779 charGlyphs[baseCharIndex].IsClusterStart() && iOffset == 0 &&
1780 b_offset == 0 && b_advance == 0 && bPos == 0) {
1781 charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
1782 ginfo[glyphStart].codepoint);
1783 } else {
1784 // Collect all glyphs in a list to be assigned to the first char;
1785 // there must be at least one in the clump, and we already measured
1786 // its advance, hence the placement of the loop-exit test and the
1787 // measurement of the next glyph.
1788 while (1) {
1789 gfxTextRun::DetailedGlyph* details = detailedGlyphs.AppendElement();
1790 details->mGlyphID = ginfo[glyphStart].codepoint;
1792 details->mAdvance = advance;
1794 if (aVertical) {
1795 details->mOffset.x =
1796 bPos - (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
1797 : floor(hb2appUnits * b_offset + 0.5));
1798 details->mOffset.y = iOffset;
1799 } else {
1800 details->mOffset.x = iOffset;
1801 details->mOffset.y =
1802 bPos - (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
1803 : floor(hb2appUnits * b_offset + 0.5));
1806 if (b_advance != 0) {
1807 bPos -= roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance)
1808 : floor(hb2appUnits * b_advance + 0.5);
1810 if (++glyphStart >= glyphEnd) {
1811 break;
1814 if (aVertical) {
1815 i_offset = -posInfo[glyphStart].y_offset;
1816 i_advance = -posInfo[glyphStart].y_advance;
1817 b_offset = -posInfo[glyphStart].x_offset;
1818 b_advance = -posInfo[glyphStart].x_advance;
1819 } else {
1820 i_offset = posInfo[glyphStart].x_offset;
1821 i_advance = posInfo[glyphStart].x_advance;
1822 b_offset = posInfo[glyphStart].y_offset;
1823 b_advance = posInfo[glyphStart].y_advance;
1826 if (roundI) {
1827 iOffset = appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
1828 // Desired distance to the next reference point. The
1829 // residual is considered here, and includes the residual
1830 // from the base glyph offset and subsequent advances, so
1831 // that the distance from the base glyph is optimized
1832 // rather than the distance from combining marks.
1833 i_advance += residual;
1834 int intAdvance = FixedToIntRound(i_advance);
1835 residual = i_advance - FloatToFixed(intAdvance);
1836 advance = appUnitsPerDevUnit * intAdvance;
1837 } else {
1838 iOffset = floor(hb2appUnits * i_offset + 0.5);
1839 advance = floor(hb2appUnits * i_advance + 0.5);
1843 aShapedText->SetDetailedGlyphs(aOffset + baseCharIndex,
1844 detailedGlyphs.Length(),
1845 detailedGlyphs.Elements());
1847 detailedGlyphs.Clear();
1850 // the rest of the chars in the group are ligature continuations,
1851 // no associated glyphs
1852 while (++baseCharIndex != endCharIndex &&
1853 baseCharIndex < int32_t(wordLength)) {
1854 CompressedGlyph& g = charGlyphs[baseCharIndex];
1855 NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
1856 g.SetComplex(g.IsClusterStart(), false);
1859 glyphStart = glyphEnd;
1860 charStart = charEnd;
1863 return NS_OK;