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