1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "gfxMathTable.h"
7 #include "harfbuzz/hb.h"
8 #include "harfbuzz/hb-ot.h"
10 #define FloatToFixed(f) (65536 * (f))
11 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
13 using namespace mozilla
;
15 gfxMathTable::gfxMathTable(hb_face_t
* aFace
, gfxFloat aSize
) {
16 mMathVariantCache
.vertical
= false;
17 mHBFont
= hb_font_create(aFace
);
19 hb_font_set_ppem(mHBFont
, aSize
, aSize
);
20 uint32_t scale
= FloatToFixed(aSize
);
21 hb_font_set_scale(mHBFont
, scale
, scale
);
24 mMathVariantCache
.glyphID
= 0;
28 gfxMathTable::~gfxMathTable() {
30 hb_font_destroy(mHBFont
);
34 gfxFloat
gfxMathTable::Constant(MathConstant aConstant
) const {
35 int32_t value
= hb_ot_math_get_constant(
36 mHBFont
, static_cast<hb_ot_math_constant_t
>(aConstant
));
37 if (aConstant
== ScriptPercentScaleDown
||
38 aConstant
== ScriptScriptPercentScaleDown
||
39 aConstant
== RadicalDegreeBottomRaisePercent
) {
42 return FixedToFloat(value
);
45 gfxFloat
gfxMathTable::ItalicsCorrection(uint32_t aGlyphID
) const {
47 hb_ot_math_get_glyph_italics_correction(mHBFont
, aGlyphID
));
50 uint32_t gfxMathTable::VariantsSize(uint32_t aGlyphID
, bool aVertical
,
51 uint16_t aSize
) const {
52 UpdateMathVariantCache(aGlyphID
, aVertical
);
53 if (aSize
< kMaxCachedSizeCount
) {
54 return mMathVariantCache
.sizes
[aSize
];
57 // If the size index exceeds the cache size, we just read the value with
58 // hb_ot_math_get_glyph_variants.
59 hb_direction_t direction
= aVertical
? HB_DIRECTION_BTT
: HB_DIRECTION_LTR
;
60 hb_ot_math_glyph_variant_t variant
;
61 unsigned int count
= 1;
62 hb_ot_math_get_glyph_variants(mHBFont
, aGlyphID
, direction
, aSize
, &count
,
64 return count
> 0 ? variant
.glyph
: 0;
67 bool gfxMathTable::VariantsParts(uint32_t aGlyphID
, bool aVertical
,
68 uint32_t aGlyphs
[4]) const {
69 UpdateMathVariantCache(aGlyphID
, aVertical
);
70 memcpy(aGlyphs
, mMathVariantCache
.parts
, sizeof(mMathVariantCache
.parts
));
71 return mMathVariantCache
.arePartsValid
;
74 void gfxMathTable::ClearCache() const {
75 memset(mMathVariantCache
.sizes
, 0, sizeof(mMathVariantCache
.sizes
));
76 memset(mMathVariantCache
.parts
, 0, sizeof(mMathVariantCache
.parts
));
77 mMathVariantCache
.arePartsValid
= false;
80 void gfxMathTable::UpdateMathVariantCache(uint32_t aGlyphID
,
81 bool aVertical
) const {
82 if (aGlyphID
== mMathVariantCache
.glyphID
&&
83 aVertical
== mMathVariantCache
.vertical
)
86 mMathVariantCache
.glyphID
= aGlyphID
;
87 mMathVariantCache
.vertical
= aVertical
;
90 // Cache the first size variants.
91 hb_direction_t direction
= aVertical
? HB_DIRECTION_BTT
: HB_DIRECTION_LTR
;
92 hb_ot_math_glyph_variant_t variant
[kMaxCachedSizeCount
];
93 unsigned int count
= kMaxCachedSizeCount
;
94 hb_ot_math_get_glyph_variants(mHBFont
, aGlyphID
, direction
, 0, &count
,
96 for (unsigned int i
= 0; i
< count
; i
++) {
97 mMathVariantCache
.sizes
[i
] = variant
[i
].glyph
;
100 // Try and cache the parts of the glyph assembly.
101 // XXXfredw The structure of the Open Type Math table is a bit more general
102 // than the one currently used by the nsMathMLChar code, so we try to fallback
103 // in reasonable way. We use the approach of the copyComponents function in
104 // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
106 // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
107 // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
108 // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
109 // stored from bottom to top in the Open Type MATH table while they are
110 // stored from top to bottom in nsMathMLChar.
112 hb_ot_math_glyph_part_t parts
[5];
113 count
= std::size(parts
);
114 unsigned int offset
= 0;
115 if (hb_ot_math_get_glyph_assembly(mHBFont
, aGlyphID
, direction
, offset
,
116 &count
, parts
, NULL
) > std::size(parts
))
117 return; // Not supported: Too many pieces.
118 if (count
<= 0) return; // Not supported: No pieces.
120 // Count the number of non extender pieces
121 uint16_t nonExtenderCount
= 0;
122 for (uint16_t i
= 0; i
< count
; i
++) {
123 if (!(parts
[i
].flags
& HB_MATH_GLYPH_PART_FLAG_EXTENDER
)) {
127 if (nonExtenderCount
> 3) {
128 // Not supported: too many pieces
132 // Now browse the list of pieces
134 // 0 = look for a left/bottom glyph
135 // 1 = look for an extender between left/bottom and mid
136 // 2 = look for a middle glyph
137 // 3 = look for an extender between middle and right/top
138 // 4 = look for a right/top glyph
139 // 5 = no more piece expected
142 // First extender char found.
143 uint32_t extenderChar
= 0;
145 for (uint16_t i
= 0; i
< count
; i
++) {
146 bool isExtender
= parts
[i
].flags
& HB_MATH_GLYPH_PART_FLAG_EXTENDER
;
147 uint32_t glyph
= parts
[i
].glyph
;
149 if ((state
== 1 || state
== 2) && nonExtenderCount
< 3) {
150 // do not try to find a middle glyph
156 extenderChar
= glyph
;
157 mMathVariantCache
.parts
[3] = extenderChar
;
158 } else if (extenderChar
!= glyph
) {
159 // Not supported: different extenders
163 if (state
== 0) { // or state == 1
164 // ignore left/bottom piece and multiple successive extenders
166 } else if (state
== 2) { // or state == 3
167 // ignore middle piece and multiple successive extenders
169 } else if (state
>= 4) {
170 // Not supported: unexpected extender
178 // copy left/bottom part
179 mMathVariantCache
.parts
[aVertical
? 2 : 0] = glyph
;
184 if (state
== 1 || state
== 2) {
186 mMathVariantCache
.parts
[1] = glyph
;
191 if (state
== 3 || state
== 4) {
192 // copy right/top part
193 mMathVariantCache
.parts
[aVertical
? 0 : 2] = glyph
;
198 mMathVariantCache
.arePartsValid
= true;