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/. */
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"
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
),
56 mUseFontGetGlyph(aFont
->ProvidesGetGlyph()),
58 mUseFontGlyphWidths(aFont
->ProvidesGlyphWidths()),
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);
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");
93 const uint8_t* data
= (const uint8_t*)hb_blob_get_data(mCmapTable
, &length
);
95 switch (mCmapFormat
) {
98 unicode
< UNICODE_BMP_LIMIT
99 ? gfxFontUtils::MapCharToGlyphFormat4(
100 data
+ mSubtableOffset
, length
- mSubtableOffset
, unicode
)
104 gid
= gfxFontUtils::MapCharToGlyphFormat10(data
+ mSubtableOffset
,
109 gid
= gfxFontUtils::MapCharToGlyphFormat12or13(data
+ mSubtableOffset
,
113 NS_WARNING("unsupported cmap format, glyphs will be missing");
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
);
131 // if there's no glyph for , just use the space glyph instead.
132 gid
= mFont
->GetSpaceGlyph();
137 // For Unicode HYPHEN and NON-BREAKING HYPHEN, fall back to the ASCII
138 // HYPHEN-MINUS as a substitute.
139 gid
= GetGlyphUncached('-');
148 hb_codepoint_t
gfxHarfBuzzShaper::GetNominalGlyph(
149 hb_codepoint_t unicode
) const {
150 MutexAutoLock
lock(mCacheLock
);
151 auto cached
= mCmapCache
->Lookup(unicode
);
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
});
163 cached
.Set(CmapCacheData
{unicode
, 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
);
180 *first_glyph
= cached
.Data().mGlyphId
;
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
});
188 cached
.Set(CmapCacheData
{usv
, 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
);
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");
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
);
223 uint32_t compat
= gfxFontUtils::GetUVSFallback(unicode
, variation_selector
);
225 switch (mCmapFormat
) {
227 if (compat
< UNICODE_BMP_LIMIT
) {
228 return gfxFontUtils::MapCharToGlyphFormat4(
229 data
+ mSubtableOffset
, length
- mSubtableOffset
, compat
);
233 return gfxFontUtils::MapCharToGlyphFormat10(data
+ mSubtableOffset
,
238 return gfxFontUtils::MapCharToGlyphFormat12or13(data
+ mSubtableOffset
,
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
);
306 *glyph
= fcd
->mShaper
->GetNominalGlyph(verticalForm
);
311 // fall back to the non-vertical form if we didn't find an alternate
314 *glyph
= fcd
->mShaper
->GetNominalGlyph(unicode
);
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()) {
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
);
344 fcd
->mShaper
->GetVariationGlyph(verticalForm
, variation_selector
);
349 // fall back to the non-vertical form if we didn't find an alternate
352 *glyph
= fcd
->mShaper
->GetVariationGlyph(unicode
, variation_selector
);
356 // Glyph metrics structures, shared (with appropriate reinterpretation of
357 // field names) by horizontal and vertical metrics tables.
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
;
399 hb_position_t advance
= GetFont()->GetGlyphWidth(glyph
);
401 mWidthCache
->Put(glyph
, WidthCacheData
{glyph
, 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
;
422 // Unlock to avoid deadlock if the font needs internal locking.
424 hb_position_t advance
= GetFont()->GetGlyphWidth(gid
);
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
);
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();
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
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
,
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
);
501 // Not available (e.g. broken metrics in the font); use a fallback value.
502 advance
= FloatToFixed(fcd
->mShaper
->GetFont()
503 ->GetMetrics(nsFontMetrics::eVertical
)
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.
513 AutoSwap_PRUint16 majorVersion
;
514 AutoSwap_PRUint16 minorVersion
;
515 AutoSwap_PRInt16 defaultVertOriginY
;
516 AutoSwap_PRUint16 numVertOriginYMetrics
;
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
);
533 void gfxHarfBuzzShaper::GetGlyphVOrigin(hb_codepoint_t aGlyph
,
535 hb_position_t
* aY
) const {
536 *aX
= 0.5 * GetGlyphHAdvance(aGlyph
);
539 // We checked in Initialize() that the VORG table is safely readable,
540 // so no length/bounds-check needed here.
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
;
548 const VORGrec
* mid
= lo
+ (hi
- lo
) / 2;
549 if (uint16_t(mid
->glyphIndex
) < aGlyph
) {
556 if (lo
< limit
&& uint16_t(lo
->glyphIndex
) == aGlyph
) {
557 *aY
= FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
558 int16_t(lo
->vertOriginY
));
560 *aY
= FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
561 int16_t(vorg
->defaultVertOriginY
));
568 const Glyf
* glyf
= FindGlyf(aGlyph
, &emptyGlyf
);
575 const ::GlyphMetrics
* metrics
= reinterpret_cast<const ::GlyphMetrics
*>(
576 hb_blob_get_data(mVmtxTable
, nullptr));
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
);
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
)));
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'));
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();
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
628 mDefaultVOrg
= FloatToFixed(advance
* ascent
/ height
);
635 static hb_bool_t
HBGetGlyphExtents(hb_font_t
* font
, void* font_data
,
636 hb_codepoint_t glyph
,
637 hb_glyph_extents_t
* extents
,
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();
655 gfxFontEntry::AutoTable
headTable(entry
, TRUETYPE_TAG('h', 'e', 'a', 'd'));
659 const HeadTable
* head
=
660 reinterpret_cast<const HeadTable
*>(hb_blob_get_data(headTable
, &len
));
661 if (len
< sizeof(HeadTable
)) {
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
674 uint32_t offset
; // offset of glyph record in the 'glyf' table
676 const char* data
= hb_blob_get_data(mLocaTable
, &len
);
677 if (mLocaLongOffsets
) {
678 if ((aGlyph
+ 1) * sizeof(AutoSwap_PRUint32
) > len
) {
681 const AutoSwap_PRUint32
* offsets
=
682 reinterpret_cast<const AutoSwap_PRUint32
*>(data
);
683 offset
= offsets
[aGlyph
];
684 *aEmptyGlyf
= (offset
== uint16_t(offsets
[aGlyph
+ 1]));
686 if ((aGlyph
+ 1) * sizeof(AutoSwap_PRUint16
) > len
) {
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]));
696 data
= hb_blob_get_data(mGlyfTable
, &len
);
697 if (offset
+ sizeof(Glyf
) > len
) {
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 {
707 const Glyf
* glyf
= FindGlyf(aGlyph
, &emptyGlyf
);
709 // TODO: for non-truetype fonts, get extents some other way?
714 aExtents
->x_bearing
= 0;
715 aExtents
->y_bearing
= 0;
717 aExtents
->height
= 0;
721 double f
= mFont
->FUnitsToDevUnitsFactor();
722 aExtents
->x_bearing
= FloatToFixed(int16_t(glyf
->xMin
) * f
);
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
);
731 FloatToFixed((int16_t(glyf
->yMin
) - int16_t(glyf
->yMax
)) * f
);
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 */
745 struct KernHeaderFmt0
{
746 AutoSwap_PRUint16 nPairs
;
747 AutoSwap_PRUint16 searchRange
;
748 AutoSwap_PRUint16 entrySelector
;
749 AutoSwap_PRUint16 rangeShift
;
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
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
);
788 const KernPair
* mid
= lo
+ (hi
- lo
) / 2;
789 if (KERN_PAIR_KEY(mid
->left
, mid
->right
) < key
) {
796 if (lo
< limit
&& KERN_PAIR_KEY(lo
->left
, lo
->right
) == key
) {
798 aValue
= int16_t(lo
->value
);
799 } else if (aIsMinimum
) {
800 aValue
= std::max(aValue
, int32_t(lo
->value
));
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
)) {
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
) >
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) >=
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
) >
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) >=
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
) {
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
;
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
)) {
914 const KernHeaderVersion1Fmt3
* hdr
=
915 reinterpret_cast<const KernHeaderVersion1Fmt3
*>(aSubtable
);
916 if (hdr
->flags
!= 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
>
929 if (aFirstGlyph
>= glyphCount
|| aSecondGlyph
>= glyphCount
) {
930 // glyphs are out of range for the class tables
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
) {
948 uint8_t ki
= kernIndex
[leftClass
[aFirstGlyph
] * hdr
->rightClassCount
+
949 rightClass
[aSecondGlyph
]];
950 if (ki
>= hdr
->kernValueCount
) {
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
) {
980 mFont
->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k', 'e', 'r', 'n'));
982 mKernTable
= hb_blob_get_empty();
987 const char* base
= hb_blob_get_data(mKernTable
, &len
);
988 if (len
< sizeof(KernTableVersion0
)) {
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
) {
1004 const KernTableSubtableHeaderVersion0
* st0
=
1005 reinterpret_cast<const KernTableSubtableHeaderVersion0
*>(base
+ offs
);
1006 uint16_t subtableLen
= uint16_t(st0
->length
);
1007 if (offs
+ subtableLen
> len
) {
1010 offs
+= subtableLen
;
1011 uint16_t coverage
= st0
->coverage
;
1012 if (!(coverage
& KERN0_COVERAGE_HORIZONTAL
)) {
1013 // we only care about horizontal kerning (for now)
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
1022 uint8_t format
= (coverage
>> 8);
1025 GetKernValueFmt0(st0
+ 1, subtableLen
- sizeof(*st0
), aFirstGlyph
,
1026 aSecondGlyph
, value
,
1027 (coverage
& KERN0_COVERAGE_OVERRIDE
) != 0,
1028 (coverage
& KERN0_COVERAGE_MINIMUM
) != 0);
1031 // TODO: implement support for other formats,
1032 // if they're ever used in practice
1037 "unknown kern subtable in %s: "
1038 "ver 0 format %d\n",
1039 mFont
->GetName().get(), format
);
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
) {
1058 const KernTableSubtableHeaderVersion1
* st1
=
1059 reinterpret_cast<const KernTableSubtableHeaderVersion1
*>(base
+
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
1073 uint8_t format
= (coverage
& 0xff);
1076 GetKernValueFmt0(st1
+ 1, subtableLen
- sizeof(*st1
), aFirstGlyph
,
1077 aSecondGlyph
, value
);
1080 value
= GetKernValueVersion1Fmt2(st1
, subtableLen
, aFirstGlyph
,
1084 value
= GetKernValueVersion1Fmt3(st1
, subtableLen
, aFirstGlyph
,
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.
1096 "unknown kern subtable in %s: "
1097 "ver 0 format %d\n",
1098 mFont
->GetName().get(), format
);
1109 return FloatToFixed(mFont
->FUnitsToDevUnitsFactor() * value
);
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
,
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
,
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
,
1152 char32_t ch
= intl::String::ComposePairNFC(a
, b
);
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.
1174 char32_t decomp
[2] = {0};
1175 if (intl::String::DecomposeRawNFD(ab
, decomp
)) {
1176 if (decomp
[1] || decomp
[0] != ab
) {
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
};
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() {
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
,
1219 hb_font_funcs_set_nominal_glyphs_func(sHBFontFuncs
, HBGetNominalGlyphs
,
1221 hb_font_funcs_set_variation_glyph_func(sHBFontFuncs
, HBGetVariationGlyph
,
1223 hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs
, HBGetGlyphHAdvance
,
1225 hb_font_funcs_set_glyph_h_advances_func(sHBFontFuncs
, HBGetGlyphHAdvances
,
1227 hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs
, HBGetGlyphVAdvance
,
1229 hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs
, HBGetGlyphVOrigin
,
1231 hb_font_funcs_set_glyph_extents_func(sHBFontFuncs
, HBGetGlyphExtents
,
1233 hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs
, HBGetContourPoint
,
1235 hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs
, HBGetHKerning
, nullptr,
1237 hb_font_funcs_make_immutable(sHBFontFuncs
);
1239 sNominalGlyphFunc
= hb_font_funcs_create();
1240 hb_font_funcs_set_nominal_glyph_func(sNominalGlyphFunc
, HBGetNominalGlyph
,
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
,
1247 hb_unicode_funcs_set_script_func(sHBUnicodeFuncs
, HBGetScript
, 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
,
1255 hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs
, HBUnicodeDecompose
,
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'));
1265 NS_WARNING("failed to load cmap, glyphs will be missing");
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) {
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
>();
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()) {
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
);
1299 mFont
->GetFontEntry()->HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '))
1302 mHBFont
= CreateHBFont(mFont
, funcs
, &mCallbackData
);
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
);
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.
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());
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'));
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
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;
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
) {
1382 mVerticalInitialized
= true;
1385 if (!LoadHmtxTable()) {
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'));
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'
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'));
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'));
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()) {
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
);
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
)) {
1497 aVertical
? HB_TAG('v', 'k', 'r', 'n') : HB_TAG('k', 'e', 'r', 'n');
1499 aVertical
? HB_TAG('v', 'p', 'a', 'l') : HB_TAG('p', 'a', 'l', 't');
1501 bool Equals(const hb_feature_t
& a
, const hb_tag_t
& b
) const {
1505 constexpr auto NoIndex
= nsTArray
<hb_feature_t
>::NoIndex
;
1506 nsTArray
<hb_feature_t
>::index_type i
= features
.IndexOf(kern
, 0, Cmp());
1508 // Kerning was not explicitly set; override harfbuzz's default to disable
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(
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
;
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());
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
,
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
,
1583 RoundingFlags aRounding
) {
1584 typedef gfxShapedText::CompressedGlyph CompressedGlyph
;
1587 const hb_glyph_info_t
* ginfo
= hb_buffer_get_glyph_infos(mBuffer
, &numGlyphs
);
1588 if (numGlyphs
== 0) {
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
;
1618 roundI
= bool(aRounding
& RoundingFlags::kRoundY
);
1619 roundB
= bool(aRounding
& RoundingFlags::kRoundX
);
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
1647 const hb_glyph_position_t
* posInfo
=
1648 hb_buffer_get_glyph_positions(mBuffer
, nullptr);
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.
1665 while (charEnd
!= charLimit
&& charToGlyph
[charEnd
] == NO_GLYPH
) {
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
1683 if (glyphEnd
== glyphStart
) {
1684 // no glyphs, try to extend the clump
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;
1699 if (allGlyphsAreWithinCluster
) {
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
)
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
;
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
;
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
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
;
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
;
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
;
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
);
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.
1789 gfxTextRun::DetailedGlyph
* details
= detailedGlyphs
.AppendElement();
1790 details
->mGlyphID
= ginfo
[glyphStart
].codepoint
;
1792 details
->mAdvance
= advance
;
1795 details
->mOffset
.x
=
1796 bPos
- (roundB
? appUnitsPerDevUnit
* FixedToIntRound(b_offset
)
1797 : floor(hb2appUnits
* b_offset
+ 0.5));
1798 details
->mOffset
.y
= iOffset
;
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
) {
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
;
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
;
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
;
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
;