/src/mozilla-central/gfx/thebes/gfxHarfBuzzShaper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
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/. */ |
5 | | |
6 | | #include "nsString.h" |
7 | | #include "gfxContext.h" |
8 | | #include "gfxFontConstants.h" |
9 | | #include "gfxHarfBuzzShaper.h" |
10 | | #include "gfxFontUtils.h" |
11 | | #include "gfxTextRun.h" |
12 | | #include "mozilla/Sprintf.h" |
13 | | #include "nsUnicodeProperties.h" |
14 | | #include "nsUnicodeScriptCodes.h" |
15 | | |
16 | | #include "harfbuzz/hb.h" |
17 | | #include "harfbuzz/hb-ot.h" |
18 | | |
19 | | #include "unicode/unorm.h" |
20 | | #include "unicode/utext.h" |
21 | | |
22 | | static const UNormalizer2* sNormalizer = nullptr; |
23 | | |
24 | | #include <algorithm> |
25 | | |
26 | 0 | #define FloatToFixed(f) (65536 * (f)) |
27 | 0 | #define FixedToFloat(f) ((f) * (1.0 / 65536.0)) |
28 | | // Right shifts of negative (signed) integers are undefined, as are overflows |
29 | | // when converting unsigned to negative signed integers. |
30 | | // (If speed were an issue we could make some 2's complement assumptions.) |
31 | 0 | #define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \ |
32 | 0 | : -((32767 - (f)) >> 16)) |
33 | | |
34 | | using namespace mozilla; // for AutoSwap_* types |
35 | | using namespace mozilla::unicode; // for Unicode property lookup |
36 | | |
37 | | /* |
38 | | * Creation and destruction; on deletion, release any font tables we're holding |
39 | | */ |
40 | | |
41 | | gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont) |
42 | | : gfxFontShaper(aFont), |
43 | | mHBFace(aFont->GetFontEntry()->GetHBFace()), |
44 | | mHBFont(nullptr), |
45 | | mBuffer(nullptr), |
46 | | mCallbackData(), |
47 | | mKernTable(nullptr), |
48 | | mHmtxTable(nullptr), |
49 | | mVmtxTable(nullptr), |
50 | | mVORGTable(nullptr), |
51 | | mLocaTable(nullptr), |
52 | | mGlyfTable(nullptr), |
53 | | mCmapTable(nullptr), |
54 | | mCmapFormat(-1), |
55 | | mSubtableOffset(0), |
56 | | mUVSTableOffset(0), |
57 | | mNumLongHMetrics(0), |
58 | | mNumLongVMetrics(0), |
59 | | mDefaultVOrg(-1.0), |
60 | | mUseFontGetGlyph(aFont->ProvidesGetGlyph()), |
61 | | mUseFontGlyphWidths(aFont->ProvidesGlyphWidths()), |
62 | | mInitialized(false), |
63 | | mVerticalInitialized(false), |
64 | | mUseVerticalPresentationForms(false), |
65 | | mLoadedLocaGlyf(false), |
66 | | mLocaLongOffsets(false) |
67 | 0 | { |
68 | 0 | } |
69 | | |
70 | | gfxHarfBuzzShaper::~gfxHarfBuzzShaper() |
71 | 0 | { |
72 | 0 | if (mCmapTable) { |
73 | 0 | hb_blob_destroy(mCmapTable); |
74 | 0 | } |
75 | 0 | if (mHmtxTable) { |
76 | 0 | hb_blob_destroy(mHmtxTable); |
77 | 0 | } |
78 | 0 | if (mKernTable) { |
79 | 0 | hb_blob_destroy(mKernTable); |
80 | 0 | } |
81 | 0 | if (mVmtxTable) { |
82 | 0 | hb_blob_destroy(mVmtxTable); |
83 | 0 | } |
84 | 0 | if (mVORGTable) { |
85 | 0 | hb_blob_destroy(mVORGTable); |
86 | 0 | } |
87 | 0 | if (mLocaTable) { |
88 | 0 | hb_blob_destroy(mLocaTable); |
89 | 0 | } |
90 | 0 | if (mGlyfTable) { |
91 | 0 | hb_blob_destroy(mGlyfTable); |
92 | 0 | } |
93 | 0 | if (mHBFont) { |
94 | 0 | hb_font_destroy(mHBFont); |
95 | 0 | } |
96 | 0 | if (mHBFace) { |
97 | 0 | hb_face_destroy(mHBFace); |
98 | 0 | } |
99 | 0 | if (mBuffer) { |
100 | 0 | hb_buffer_destroy(mBuffer); |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | 0 | #define UNICODE_BMP_LIMIT 0x10000 |
105 | | |
106 | | hb_codepoint_t |
107 | | gfxHarfBuzzShaper::GetNominalGlyph(hb_codepoint_t unicode) const |
108 | 0 | { |
109 | 0 | hb_codepoint_t gid = 0; |
110 | 0 |
|
111 | 0 | if (mUseFontGetGlyph) { |
112 | 0 | gid = mFont->GetGlyph(unicode, 0); |
113 | 0 | } else { |
114 | 0 | // we only instantiate a harfbuzz shaper if there's a cmap available |
115 | 0 | NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(), |
116 | 0 | "we cannot be using this font!"); |
117 | 0 |
|
118 | 0 | NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0), |
119 | 0 | "cmap data not correctly set up, expect disaster"); |
120 | 0 |
|
121 | 0 | uint32_t length; |
122 | 0 | const uint8_t* data = |
123 | 0 | (const uint8_t*)hb_blob_get_data(mCmapTable, &length); |
124 | 0 |
|
125 | 0 | switch (mCmapFormat) { |
126 | 0 | case 4: |
127 | 0 | gid = unicode < UNICODE_BMP_LIMIT ? |
128 | 0 | gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset, |
129 | 0 | length - mSubtableOffset, |
130 | 0 | unicode) : 0; |
131 | 0 | break; |
132 | 0 | case 10: |
133 | 0 | gid = gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset, |
134 | 0 | unicode); |
135 | 0 | break; |
136 | 0 | case 12: |
137 | 0 | case 13: |
138 | 0 | gid = |
139 | 0 | gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset, |
140 | 0 | unicode); |
141 | 0 | break; |
142 | 0 | default: |
143 | 0 | NS_WARNING("unsupported cmap format, glyphs will be missing"); |
144 | 0 | break; |
145 | 0 | } |
146 | 0 | } |
147 | 0 |
|
148 | 0 | if (!gid) { |
149 | 0 | // if there's no glyph for , just use the space glyph instead |
150 | 0 | if (unicode == 0xA0) { |
151 | 0 | gid = mFont->GetSpaceGlyph(); |
152 | 0 | } |
153 | 0 | } |
154 | 0 |
|
155 | 0 | return gid; |
156 | 0 | } |
157 | | |
158 | | hb_codepoint_t |
159 | | gfxHarfBuzzShaper::GetVariationGlyph(hb_codepoint_t unicode, |
160 | | hb_codepoint_t variation_selector) const |
161 | 0 | { |
162 | 0 | if (mUseFontGetGlyph) { |
163 | 0 | return mFont->GetGlyph(unicode, variation_selector); |
164 | 0 | } |
165 | 0 | |
166 | 0 | NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(), |
167 | 0 | "we cannot be using this font!"); |
168 | 0 | NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0), |
169 | 0 | "cmap data not correctly set up, expect disaster"); |
170 | 0 |
|
171 | 0 | uint32_t length; |
172 | 0 | const uint8_t* data = |
173 | 0 | (const uint8_t*)hb_blob_get_data(mCmapTable, &length); |
174 | 0 |
|
175 | 0 | if (mUVSTableOffset) { |
176 | 0 | hb_codepoint_t gid = |
177 | 0 | gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset, |
178 | 0 | unicode, variation_selector); |
179 | 0 | if (gid) { |
180 | 0 | return gid; |
181 | 0 | } |
182 | 0 | } |
183 | 0 | |
184 | 0 | uint32_t compat = |
185 | 0 | gfxFontUtils::GetUVSFallback(unicode, variation_selector); |
186 | 0 | if (compat) { |
187 | 0 | switch (mCmapFormat) { |
188 | 0 | case 4: |
189 | 0 | if (compat < UNICODE_BMP_LIMIT) { |
190 | 0 | return gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset, |
191 | 0 | length - mSubtableOffset, |
192 | 0 | compat); |
193 | 0 | } |
194 | 0 | break; |
195 | 0 | case 10: |
196 | 0 | return gfxFontUtils::MapCharToGlyphFormat10(data + mSubtableOffset, |
197 | 0 | compat); |
198 | 0 | break; |
199 | 0 | case 12: |
200 | 0 | case 13: |
201 | 0 | return |
202 | 0 | gfxFontUtils::MapCharToGlyphFormat12or13(data + mSubtableOffset, |
203 | 0 | compat); |
204 | 0 | break; |
205 | 0 | } |
206 | 0 | } |
207 | 0 | |
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | | static int |
212 | | VertFormsGlyphCompare(const void* aKey, const void* aElem) |
213 | 0 | { |
214 | 0 | return int(*((hb_codepoint_t*)(aKey))) - int(*((uint16_t*)(aElem))); |
215 | 0 | } |
216 | | |
217 | | // Return a vertical presentation-form codepoint corresponding to the |
218 | | // given Unicode value, or 0 if no such form is available. |
219 | | hb_codepoint_t |
220 | | gfxHarfBuzzShaper::GetVerticalPresentationForm(hb_codepoint_t aUnicode) |
221 | 0 | { |
222 | 0 | static const uint16_t sVerticalForms[][2] = { |
223 | 0 | { 0x2013, 0xfe32 }, // EN DASH |
224 | 0 | { 0x2014, 0xfe31 }, // EM DASH |
225 | 0 | { 0x2025, 0xfe30 }, // TWO DOT LEADER |
226 | 0 | { 0x2026, 0xfe19 }, // HORIZONTAL ELLIPSIS |
227 | 0 | { 0x3001, 0xfe11 }, // IDEOGRAPHIC COMMA |
228 | 0 | { 0x3002, 0xfe12 }, // IDEOGRAPHIC FULL STOP |
229 | 0 | { 0x3008, 0xfe3f }, // LEFT ANGLE BRACKET |
230 | 0 | { 0x3009, 0xfe40 }, // RIGHT ANGLE BRACKET |
231 | 0 | { 0x300a, 0xfe3d }, // LEFT DOUBLE ANGLE BRACKET |
232 | 0 | { 0x300b, 0xfe3e }, // RIGHT DOUBLE ANGLE BRACKET |
233 | 0 | { 0x300c, 0xfe41 }, // LEFT CORNER BRACKET |
234 | 0 | { 0x300d, 0xfe42 }, // RIGHT CORNER BRACKET |
235 | 0 | { 0x300e, 0xfe43 }, // LEFT WHITE CORNER BRACKET |
236 | 0 | { 0x300f, 0xfe44 }, // RIGHT WHITE CORNER BRACKET |
237 | 0 | { 0x3010, 0xfe3b }, // LEFT BLACK LENTICULAR BRACKET |
238 | 0 | { 0x3011, 0xfe3c }, // RIGHT BLACK LENTICULAR BRACKET |
239 | 0 | { 0x3014, 0xfe39 }, // LEFT TORTOISE SHELL BRACKET |
240 | 0 | { 0x3015, 0xfe3a }, // RIGHT TORTOISE SHELL BRACKET |
241 | 0 | { 0x3016, 0xfe17 }, // LEFT WHITE LENTICULAR BRACKET |
242 | 0 | { 0x3017, 0xfe18 }, // RIGHT WHITE LENTICULAR BRACKET |
243 | 0 | { 0xfe4f, 0xfe34 }, // WAVY LOW LINE |
244 | 0 | { 0xff01, 0xfe15 }, // FULLWIDTH EXCLAMATION MARK |
245 | 0 | { 0xff08, 0xfe35 }, // FULLWIDTH LEFT PARENTHESIS |
246 | 0 | { 0xff09, 0xfe36 }, // FULLWIDTH RIGHT PARENTHESIS |
247 | 0 | { 0xff0c, 0xfe10 }, // FULLWIDTH COMMA |
248 | 0 | { 0xff1a, 0xfe13 }, // FULLWIDTH COLON |
249 | 0 | { 0xff1b, 0xfe14 }, // FULLWIDTH SEMICOLON |
250 | 0 | { 0xff1f, 0xfe16 }, // FULLWIDTH QUESTION MARK |
251 | 0 | { 0xff3b, 0xfe47 }, // FULLWIDTH LEFT SQUARE BRACKET |
252 | 0 | { 0xff3d, 0xfe48 }, // FULLWIDTH RIGHT SQUARE BRACKET |
253 | 0 | { 0xff3f, 0xfe33 }, // FULLWIDTH LOW LINE |
254 | 0 | { 0xff5b, 0xfe37 }, // FULLWIDTH LEFT CURLY BRACKET |
255 | 0 | { 0xff5d, 0xfe38 } // FULLWIDTH RIGHT CURLY BRACKET |
256 | 0 | }; |
257 | 0 | const uint16_t* charPair = |
258 | 0 | static_cast<const uint16_t*>(bsearch(&aUnicode, |
259 | 0 | sVerticalForms, |
260 | 0 | ArrayLength(sVerticalForms), |
261 | 0 | sizeof(sVerticalForms[0]), |
262 | 0 | VertFormsGlyphCompare)); |
263 | 0 | return charPair ? charPair[1] : 0; |
264 | 0 | } |
265 | | |
266 | | static hb_bool_t |
267 | | HBGetNominalGlyph(hb_font_t *font, void *font_data, |
268 | | hb_codepoint_t unicode, |
269 | | hb_codepoint_t *glyph, |
270 | | void *user_data) |
271 | 0 | { |
272 | 0 | const gfxHarfBuzzShaper::FontCallbackData *fcd = |
273 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
274 | 0 |
|
275 | 0 | if (fcd->mShaper->UseVerticalPresentationForms()) { |
276 | 0 | hb_codepoint_t verticalForm = |
277 | 0 | gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode); |
278 | 0 | if (verticalForm) { |
279 | 0 | *glyph = fcd->mShaper->GetNominalGlyph(verticalForm); |
280 | 0 | if (*glyph != 0) { |
281 | 0 | return true; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | // fall back to the non-vertical form if we didn't find an alternate |
285 | 0 | } |
286 | 0 | |
287 | 0 | *glyph = fcd->mShaper->GetNominalGlyph(unicode); |
288 | 0 | return *glyph != 0; |
289 | 0 | } |
290 | | |
291 | | static hb_bool_t |
292 | | HBGetVariationGlyph(hb_font_t *font, void *font_data, |
293 | | hb_codepoint_t unicode, hb_codepoint_t variation_selector, |
294 | | hb_codepoint_t *glyph, |
295 | | void *user_data) |
296 | 0 | { |
297 | 0 | const gfxHarfBuzzShaper::FontCallbackData *fcd = |
298 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
299 | 0 |
|
300 | 0 | if (fcd->mShaper->UseVerticalPresentationForms()) { |
301 | 0 | hb_codepoint_t verticalForm = |
302 | 0 | gfxHarfBuzzShaper::GetVerticalPresentationForm(unicode); |
303 | 0 | if (verticalForm) { |
304 | 0 | *glyph = fcd->mShaper->GetVariationGlyph(verticalForm, |
305 | 0 | variation_selector); |
306 | 0 | if (*glyph != 0) { |
307 | 0 | return true; |
308 | 0 | } |
309 | 0 | } |
310 | 0 | // fall back to the non-vertical form if we didn't find an alternate |
311 | 0 | } |
312 | 0 | |
313 | 0 | *glyph = fcd->mShaper->GetVariationGlyph(unicode, variation_selector); |
314 | 0 | return *glyph != 0; |
315 | 0 | } |
316 | | |
317 | | // Glyph metrics structures, shared (with appropriate reinterpretation of |
318 | | // field names) by horizontal and vertical metrics tables. |
319 | | struct LongMetric { |
320 | | AutoSwap_PRUint16 advanceWidth; // or advanceHeight, when vertical |
321 | | AutoSwap_PRInt16 lsb; // or tsb, when vertical |
322 | | }; |
323 | | |
324 | | struct GlyphMetrics { |
325 | | LongMetric metrics[1]; // actually numberOfLongMetrics |
326 | | // the variable-length metrics[] array is immediately followed by: |
327 | | // AutoSwap_PRUint16 leftSideBearing[]; |
328 | | }; |
329 | | |
330 | | hb_position_t |
331 | | gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const |
332 | 0 | { |
333 | 0 | // font did not implement GetGlyphWidth, so get an unhinted value |
334 | 0 | // directly from the font tables |
335 | 0 |
|
336 | 0 | NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr, |
337 | 0 | "font is lacking metrics, we shouldn't be here"); |
338 | 0 |
|
339 | 0 | if (glyph >= uint32_t(mNumLongHMetrics)) { |
340 | 0 | glyph = mNumLongHMetrics - 1; |
341 | 0 | } |
342 | 0 |
|
343 | 0 | // glyph must be valid now, because we checked during initialization |
344 | 0 | // that mNumLongHMetrics is > 0, and that the metrics table is large enough |
345 | 0 | // to contain mNumLongHMetrics records |
346 | 0 | const ::GlyphMetrics* metrics = |
347 | 0 | reinterpret_cast<const ::GlyphMetrics*>(hb_blob_get_data(mHmtxTable, |
348 | 0 | nullptr)); |
349 | 0 | return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * |
350 | 0 | uint16_t(metrics->metrics[glyph].advanceWidth)); |
351 | 0 | } |
352 | | |
353 | | hb_position_t |
354 | | gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) const |
355 | 0 | { |
356 | 0 | if (!mVmtxTable) { |
357 | 0 | // Must be a "vertical" font that doesn't actually have vertical metrics; |
358 | 0 | // use a fixed advance. |
359 | 0 | return FloatToFixed(mFont->GetMetrics(gfxFont::eVertical).aveCharWidth); |
360 | 0 | } |
361 | 0 |
|
362 | 0 | NS_ASSERTION(mNumLongVMetrics > 0, |
363 | 0 | "font is lacking metrics, we shouldn't be here"); |
364 | 0 |
|
365 | 0 | if (glyph >= uint32_t(mNumLongVMetrics)) { |
366 | 0 | glyph = mNumLongVMetrics - 1; |
367 | 0 | } |
368 | 0 |
|
369 | 0 | // glyph must be valid now, because we checked during initialization |
370 | 0 | // that mNumLongVMetrics is > 0, and that the metrics table is large enough |
371 | 0 | // to contain mNumLongVMetrics records |
372 | 0 | const ::GlyphMetrics* metrics = |
373 | 0 | reinterpret_cast<const ::GlyphMetrics*>(hb_blob_get_data(mVmtxTable, |
374 | 0 | nullptr)); |
375 | 0 | return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * |
376 | 0 | uint16_t(metrics->metrics[glyph].advanceWidth)); |
377 | 0 | } |
378 | | |
379 | | /* static */ |
380 | | hb_position_t |
381 | | gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data, |
382 | | hb_codepoint_t glyph, void *user_data) |
383 | 0 | { |
384 | 0 | const gfxHarfBuzzShaper::FontCallbackData* fcd = |
385 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
386 | 0 | const gfxHarfBuzzShaper* shaper = fcd->mShaper; |
387 | 0 | if (shaper->mUseFontGlyphWidths) { |
388 | 0 | return shaper->GetFont()->GetGlyphWidth(*fcd->mDrawTarget, glyph); |
389 | 0 | } |
390 | 0 | return shaper->GetGlyphHAdvance(glyph); |
391 | 0 | } |
392 | | |
393 | | /* static */ |
394 | | hb_position_t |
395 | | gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t *font, void *font_data, |
396 | | hb_codepoint_t glyph, void *user_data) |
397 | 0 | { |
398 | 0 | const gfxHarfBuzzShaper::FontCallbackData *fcd = |
399 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
400 | 0 | // Currently, we don't offer gfxFont subclasses a method to override this |
401 | 0 | // and provide hinted platform-specific vertical advances (analogous to the |
402 | 0 | // GetGlyphWidth method for horizontal advances). If that proves necessary, |
403 | 0 | // we'll add a new gfxFont method and call it from here. |
404 | 0 | // |
405 | 0 | // We negate the value from GetGlyphVAdvance here because harfbuzz shapes |
406 | 0 | // with a coordinate system where positive is upwards, whereas the inline |
407 | 0 | // direction in which glyphs advance is downwards. |
408 | 0 | return -fcd->mShaper->GetGlyphVAdvance(glyph); |
409 | 0 | } |
410 | | |
411 | | struct VORG { |
412 | | AutoSwap_PRUint16 majorVersion; |
413 | | AutoSwap_PRUint16 minorVersion; |
414 | | AutoSwap_PRInt16 defaultVertOriginY; |
415 | | AutoSwap_PRUint16 numVertOriginYMetrics; |
416 | | }; |
417 | | |
418 | | struct VORGrec { |
419 | | AutoSwap_PRUint16 glyphIndex; |
420 | | AutoSwap_PRInt16 vertOriginY; |
421 | | }; |
422 | | |
423 | | /* static */ |
424 | | hb_bool_t |
425 | | gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t *font, void *font_data, |
426 | | hb_codepoint_t glyph, |
427 | | hb_position_t *x, hb_position_t *y, |
428 | | void *user_data) |
429 | 0 | { |
430 | 0 | const gfxHarfBuzzShaper::FontCallbackData *fcd = |
431 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
432 | 0 | fcd->mShaper->GetGlyphVOrigin(*fcd->mDrawTarget, glyph, x, y); |
433 | 0 | return true; |
434 | 0 | } |
435 | | |
436 | | void |
437 | | gfxHarfBuzzShaper::GetGlyphVOrigin(DrawTarget& aDT, hb_codepoint_t aGlyph, |
438 | | hb_position_t *aX, hb_position_t *aY) const |
439 | 0 | { |
440 | 0 | *aX = 0.5 * (mUseFontGlyphWidths |
441 | 0 | ? mFont->GetGlyphWidth(aDT, aGlyph) |
442 | 0 | : GetGlyphHAdvance(aGlyph)); |
443 | 0 |
|
444 | 0 | if (mVORGTable) { |
445 | 0 | // We checked in Initialize() that the VORG table is safely readable, |
446 | 0 | // so no length/bounds-check needed here. |
447 | 0 | const VORG* vorg = |
448 | 0 | reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr)); |
449 | 0 |
|
450 | 0 | const VORGrec *lo = reinterpret_cast<const VORGrec*>(vorg + 1); |
451 | 0 | const VORGrec *hi = lo + uint16_t(vorg->numVertOriginYMetrics); |
452 | 0 | const VORGrec *limit = hi; |
453 | 0 | while (lo < hi) { |
454 | 0 | const VORGrec *mid = lo + (hi - lo) / 2; |
455 | 0 | if (uint16_t(mid->glyphIndex) < aGlyph) { |
456 | 0 | lo = mid + 1; |
457 | 0 | } else { |
458 | 0 | hi = mid; |
459 | 0 | } |
460 | 0 | } |
461 | 0 |
|
462 | 0 | if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) { |
463 | 0 | *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() * |
464 | 0 | int16_t(lo->vertOriginY)); |
465 | 0 | } else { |
466 | 0 | *aY = FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() * |
467 | 0 | int16_t(vorg->defaultVertOriginY)); |
468 | 0 | } |
469 | 0 | return; |
470 | 0 | } |
471 | 0 |
|
472 | 0 | if (mVmtxTable) { |
473 | 0 | bool emptyGlyf; |
474 | 0 | const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf); |
475 | 0 | if (glyf) { |
476 | 0 | if (emptyGlyf) { |
477 | 0 | *aY = 0; |
478 | 0 | return; |
479 | 0 | } |
480 | 0 | |
481 | 0 | const ::GlyphMetrics* metrics = |
482 | 0 | reinterpret_cast<const ::GlyphMetrics*> |
483 | 0 | (hb_blob_get_data(mVmtxTable, nullptr)); |
484 | 0 | int16_t lsb; |
485 | 0 | if (aGlyph < hb_codepoint_t(mNumLongVMetrics)) { |
486 | 0 | // Glyph is covered by the first (advance & sidebearing) array |
487 | 0 | lsb = int16_t(metrics->metrics[aGlyph].lsb); |
488 | 0 | } else { |
489 | 0 | // Glyph is covered by the second (sidebearing-only) array |
490 | 0 | const AutoSwap_PRInt16* sidebearings = |
491 | 0 | reinterpret_cast<const AutoSwap_PRInt16*> |
492 | 0 | (&metrics->metrics[mNumLongVMetrics]); |
493 | 0 | lsb = int16_t(sidebearings[aGlyph - mNumLongVMetrics]); |
494 | 0 | } |
495 | 0 | *aY = FloatToFixed(mFont->FUnitsToDevUnitsFactor() * |
496 | 0 | (lsb + int16_t(glyf->yMax))); |
497 | 0 | return; |
498 | 0 | } else { |
499 | 0 | // XXX TODO: not a truetype font; need to get glyph extents |
500 | 0 | // via some other API? |
501 | 0 | // For now, fall through to default code below. |
502 | 0 | } |
503 | 0 | } |
504 | 0 |
|
505 | 0 | if (mDefaultVOrg < 0.0) { |
506 | 0 | // XXX should we consider using OS/2 sTypo* metrics if available? |
507 | 0 |
|
508 | 0 | gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(), |
509 | 0 | TRUETYPE_TAG('h','h','e','a')); |
510 | 0 | if (hheaTable) { |
511 | 0 | uint32_t len; |
512 | 0 | const MetricsHeader* hhea = |
513 | 0 | reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable, |
514 | 0 | &len)); |
515 | 0 | if (len >= sizeof(MetricsHeader)) { |
516 | 0 | // divide up the default advance we're using (1em) in proportion |
517 | 0 | // to ascender:descender from the hhea table |
518 | 0 | int16_t a = int16_t(hhea->ascender); |
519 | 0 | int16_t d = int16_t(hhea->descender); |
520 | 0 | mDefaultVOrg = |
521 | 0 | FloatToFixed(GetFont()->GetAdjustedSize() * a / (a - d)); |
522 | 0 | } |
523 | 0 | } |
524 | 0 |
|
525 | 0 | if (mDefaultVOrg < 0.0) { |
526 | 0 | // Last resort, for non-sfnt fonts: get the horizontal metrics and |
527 | 0 | // compute a default VOrg from their ascent and descent. |
528 | 0 | const gfxFont::Metrics& mtx = mFont->GetHorizontalMetrics(); |
529 | 0 | gfxFloat advance = mFont->GetMetrics(gfxFont::eVertical).aveCharWidth; |
530 | 0 | gfxFloat ascent = mtx.emAscent; |
531 | 0 | gfxFloat height = ascent + mtx.emDescent; |
532 | 0 | // vOrigin that will place the glyph so that its origin is shifted |
533 | 0 | // down most of the way within overall (vertical) advance, in |
534 | 0 | // proportion to the font ascent as a part of the overall font |
535 | 0 | // height. |
536 | 0 | mDefaultVOrg = FloatToFixed(advance * ascent / height); |
537 | 0 | } |
538 | 0 | } |
539 | 0 |
|
540 | 0 | *aY = mDefaultVOrg; |
541 | 0 | } |
542 | | |
543 | | static hb_bool_t |
544 | | HBGetGlyphExtents(hb_font_t *font, void *font_data, |
545 | | hb_codepoint_t glyph, |
546 | | hb_glyph_extents_t *extents, |
547 | | void *user_data) |
548 | 0 | { |
549 | 0 | const gfxHarfBuzzShaper::FontCallbackData *fcd = |
550 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
551 | 0 | return fcd->mShaper->GetGlyphExtents(glyph, extents); |
552 | 0 | } |
553 | | |
554 | | // Find the data for glyph ID |aGlyph| in the 'glyf' table, if present. |
555 | | // Returns null if not found, otherwise pointer to the beginning of the |
556 | | // glyph's data. Sets aEmptyGlyf true if there is no actual data; |
557 | | // otherwise, it's guaranteed that we can read at least the bounding box. |
558 | | const gfxHarfBuzzShaper::Glyf* |
559 | | gfxHarfBuzzShaper::FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const |
560 | 0 | { |
561 | 0 | if (!mLoadedLocaGlyf) { |
562 | 0 | mLoadedLocaGlyf = true; // only try this once; if it fails, this |
563 | 0 | // isn't a truetype font |
564 | 0 | gfxFontEntry *entry = mFont->GetFontEntry(); |
565 | 0 | uint32_t len; |
566 | 0 | gfxFontEntry::AutoTable headTable(entry, |
567 | 0 | TRUETYPE_TAG('h','e','a','d')); |
568 | 0 | if (!headTable) { |
569 | 0 | return nullptr; |
570 | 0 | } |
571 | 0 | const HeadTable* head = |
572 | 0 | reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, |
573 | 0 | &len)); |
574 | 0 | if (len < sizeof(HeadTable)) { |
575 | 0 | return nullptr; |
576 | 0 | } |
577 | 0 | mLocaLongOffsets = int16_t(head->indexToLocFormat) > 0; |
578 | 0 | mLocaTable = entry->GetFontTable(TRUETYPE_TAG('l','o','c','a')); |
579 | 0 | mGlyfTable = entry->GetFontTable(TRUETYPE_TAG('g','l','y','f')); |
580 | 0 | } |
581 | 0 |
|
582 | 0 | if (!mLocaTable || !mGlyfTable) { |
583 | 0 | // it's not a truetype font |
584 | 0 | return nullptr; |
585 | 0 | } |
586 | 0 | |
587 | 0 | uint32_t offset; // offset of glyph record in the 'glyf' table |
588 | 0 | uint32_t len; |
589 | 0 | const char* data = hb_blob_get_data(mLocaTable, &len); |
590 | 0 | if (mLocaLongOffsets) { |
591 | 0 | if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) { |
592 | 0 | return nullptr; |
593 | 0 | } |
594 | 0 | const AutoSwap_PRUint32* offsets = |
595 | 0 | reinterpret_cast<const AutoSwap_PRUint32*>(data); |
596 | 0 | offset = offsets[aGlyph]; |
597 | 0 | *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1])); |
598 | 0 | } else { |
599 | 0 | if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) { |
600 | 0 | return nullptr; |
601 | 0 | } |
602 | 0 | const AutoSwap_PRUint16* offsets = |
603 | 0 | reinterpret_cast<const AutoSwap_PRUint16*>(data); |
604 | 0 | offset = uint16_t(offsets[aGlyph]); |
605 | 0 | *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1])); |
606 | 0 | offset *= 2; |
607 | 0 | } |
608 | 0 |
|
609 | 0 | data = hb_blob_get_data(mGlyfTable, &len); |
610 | 0 | if (offset + sizeof(Glyf) > len) { |
611 | 0 | return nullptr; |
612 | 0 | } |
613 | 0 | |
614 | 0 | return reinterpret_cast<const Glyf*>(data + offset); |
615 | 0 | } |
616 | | |
617 | | hb_bool_t |
618 | | gfxHarfBuzzShaper::GetGlyphExtents(hb_codepoint_t aGlyph, |
619 | | hb_glyph_extents_t *aExtents) const |
620 | 0 | { |
621 | 0 | bool emptyGlyf; |
622 | 0 | const Glyf *glyf = FindGlyf(aGlyph, &emptyGlyf); |
623 | 0 | if (!glyf) { |
624 | 0 | // TODO: for non-truetype fonts, get extents some other way? |
625 | 0 | return false; |
626 | 0 | } |
627 | 0 | |
628 | 0 | if (emptyGlyf) { |
629 | 0 | aExtents->x_bearing = 0; |
630 | 0 | aExtents->y_bearing = 0; |
631 | 0 | aExtents->width = 0; |
632 | 0 | aExtents->height = 0; |
633 | 0 | return true; |
634 | 0 | } |
635 | 0 | |
636 | 0 | double f = mFont->FUnitsToDevUnitsFactor(); |
637 | 0 | aExtents->x_bearing = FloatToFixed(int16_t(glyf->xMin) * f); |
638 | 0 | aExtents->width = |
639 | 0 | FloatToFixed((int16_t(glyf->xMax) - int16_t(glyf->xMin)) * f); |
640 | 0 |
|
641 | 0 | // Our y-coordinates are positive-downwards, whereas harfbuzz assumes |
642 | 0 | // positive-upwards; hence the apparently-reversed subtractions here. |
643 | 0 | aExtents->y_bearing = |
644 | 0 | FloatToFixed(int16_t(glyf->yMax) * f - |
645 | 0 | mFont->GetHorizontalMetrics().emAscent); |
646 | 0 | aExtents->height = |
647 | 0 | FloatToFixed((int16_t(glyf->yMin) - int16_t(glyf->yMax)) * f); |
648 | 0 |
|
649 | 0 | return true; |
650 | 0 | } |
651 | | |
652 | | static hb_bool_t |
653 | | HBGetContourPoint(hb_font_t *font, void *font_data, |
654 | | unsigned int point_index, hb_codepoint_t glyph, |
655 | | hb_position_t *x, hb_position_t *y, |
656 | | void *user_data) |
657 | 0 | { |
658 | 0 | /* not yet implemented - no support for used of hinted contour points |
659 | 0 | to fine-tune anchor positions in GPOS AnchorFormat2 */ |
660 | 0 | return false; |
661 | 0 | } |
662 | | |
663 | | struct KernHeaderFmt0 { |
664 | | AutoSwap_PRUint16 nPairs; |
665 | | AutoSwap_PRUint16 searchRange; |
666 | | AutoSwap_PRUint16 entrySelector; |
667 | | AutoSwap_PRUint16 rangeShift; |
668 | | }; |
669 | | |
670 | | struct KernPair { |
671 | | AutoSwap_PRUint16 left; |
672 | | AutoSwap_PRUint16 right; |
673 | | AutoSwap_PRInt16 value; |
674 | | }; |
675 | | |
676 | | // Find a kern pair in a Format 0 subtable. |
677 | | // The aSubtable parameter points to the subtable itself, NOT its header, |
678 | | // as the header structure differs between Windows and Mac (v0 and v1.0) |
679 | | // versions of the 'kern' table. |
680 | | // aSubtableLen is the length of the subtable EXCLUDING its header. |
681 | | // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is |
682 | | // added to aValue, so that multiple subtables can accumulate a total |
683 | | // kerning value for a given pair. |
684 | | static void |
685 | | GetKernValueFmt0(const void* aSubtable, |
686 | | uint32_t aSubtableLen, |
687 | | uint16_t aFirstGlyph, |
688 | | uint16_t aSecondGlyph, |
689 | | int32_t& aValue, |
690 | | bool aIsOverride = false, |
691 | | bool aIsMinimum = false) |
692 | 0 | { |
693 | 0 | const KernHeaderFmt0* hdr = |
694 | 0 | reinterpret_cast<const KernHeaderFmt0*>(aSubtable); |
695 | 0 |
|
696 | 0 | const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1); |
697 | 0 | const KernPair *hi = lo + uint16_t(hdr->nPairs); |
698 | 0 | const KernPair *limit = hi; |
699 | 0 |
|
700 | 0 | if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen < |
701 | 0 | reinterpret_cast<const char*>(hi)) { |
702 | 0 | // subtable is not large enough to contain the claimed number |
703 | 0 | // of kern pairs, so just ignore it |
704 | 0 | return; |
705 | 0 | } |
706 | 0 | |
707 | 0 | #define KERN_PAIR_KEY(l,r) (uint32_t((uint16_t(l) << 16) + uint16_t(r))) |
708 | 0 | |
709 | 0 | uint32_t key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph); |
710 | 0 | while (lo < hi) { |
711 | 0 | const KernPair *mid = lo + (hi - lo) / 2; |
712 | 0 | if (KERN_PAIR_KEY(mid->left, mid->right) < key) { |
713 | 0 | lo = mid + 1; |
714 | 0 | } else { |
715 | 0 | hi = mid; |
716 | 0 | } |
717 | 0 | } |
718 | 0 |
|
719 | 0 | if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) { |
720 | 0 | if (aIsOverride) { |
721 | 0 | aValue = int16_t(lo->value); |
722 | 0 | } else if (aIsMinimum) { |
723 | 0 | aValue = std::max(aValue, int32_t(lo->value)); |
724 | 0 | } else { |
725 | 0 | aValue += int16_t(lo->value); |
726 | 0 | } |
727 | 0 | } |
728 | 0 | } |
729 | | |
730 | | // Get kerning value from Apple (version 1.0) kern table, |
731 | | // subtable format 2 (simple N x M array of kerning values) |
732 | | |
733 | | // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html |
734 | | // for details of version 1.0 format 2 subtable. |
735 | | |
736 | | struct KernHeaderVersion1Fmt2 { |
737 | | KernTableSubtableHeaderVersion1 header; |
738 | | AutoSwap_PRUint16 rowWidth; |
739 | | AutoSwap_PRUint16 leftOffsetTable; |
740 | | AutoSwap_PRUint16 rightOffsetTable; |
741 | | AutoSwap_PRUint16 array; |
742 | | }; |
743 | | |
744 | | struct KernClassTableHdr { |
745 | | AutoSwap_PRUint16 firstGlyph; |
746 | | AutoSwap_PRUint16 nGlyphs; |
747 | | AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries |
748 | | }; |
749 | | |
750 | | static int16_t |
751 | | GetKernValueVersion1Fmt2(const void* aSubtable, |
752 | | uint32_t aSubtableLen, |
753 | | uint16_t aFirstGlyph, |
754 | | uint16_t aSecondGlyph) |
755 | 0 | { |
756 | 0 | if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) { |
757 | 0 | return 0; |
758 | 0 | } |
759 | 0 | |
760 | 0 | const char* base = reinterpret_cast<const char*>(aSubtable); |
761 | 0 | const char* subtableEnd = base + aSubtableLen; |
762 | 0 |
|
763 | 0 | const KernHeaderVersion1Fmt2* h = |
764 | 0 | reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable); |
765 | 0 | uint32_t offset = h->array; |
766 | 0 |
|
767 | 0 | const KernClassTableHdr* leftClassTable = |
768 | 0 | reinterpret_cast<const KernClassTableHdr*>(base + |
769 | 0 | uint16_t(h->leftOffsetTable)); |
770 | 0 | if (reinterpret_cast<const char*>(leftClassTable) + |
771 | 0 | sizeof(KernClassTableHdr) > subtableEnd) { |
772 | 0 | return 0; |
773 | 0 | } |
774 | 0 | if (aFirstGlyph >= uint16_t(leftClassTable->firstGlyph)) { |
775 | 0 | aFirstGlyph -= uint16_t(leftClassTable->firstGlyph); |
776 | 0 | if (aFirstGlyph < uint16_t(leftClassTable->nGlyphs)) { |
777 | 0 | if (reinterpret_cast<const char*>(leftClassTable) + |
778 | 0 | sizeof(KernClassTableHdr) + |
779 | 0 | aFirstGlyph * sizeof(uint16_t) >= subtableEnd) { |
780 | 0 | return 0; |
781 | 0 | } |
782 | 0 | offset = uint16_t(leftClassTable->offsets[aFirstGlyph]); |
783 | 0 | } |
784 | 0 | } |
785 | 0 |
|
786 | 0 | const KernClassTableHdr* rightClassTable = |
787 | 0 | reinterpret_cast<const KernClassTableHdr*>(base + |
788 | 0 | uint16_t(h->rightOffsetTable)); |
789 | 0 | if (reinterpret_cast<const char*>(rightClassTable) + |
790 | 0 | sizeof(KernClassTableHdr) > subtableEnd) { |
791 | 0 | return 0; |
792 | 0 | } |
793 | 0 | if (aSecondGlyph >= uint16_t(rightClassTable->firstGlyph)) { |
794 | 0 | aSecondGlyph -= uint16_t(rightClassTable->firstGlyph); |
795 | 0 | if (aSecondGlyph < uint16_t(rightClassTable->nGlyphs)) { |
796 | 0 | if (reinterpret_cast<const char*>(rightClassTable) + |
797 | 0 | sizeof(KernClassTableHdr) + |
798 | 0 | aSecondGlyph * sizeof(uint16_t) >= subtableEnd) { |
799 | 0 | return 0; |
800 | 0 | } |
801 | 0 | offset += uint16_t(rightClassTable->offsets[aSecondGlyph]); |
802 | 0 | } |
803 | 0 | } |
804 | 0 |
|
805 | 0 | const AutoSwap_PRInt16* pval = |
806 | 0 | reinterpret_cast<const AutoSwap_PRInt16*>(base + offset); |
807 | 0 | if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) { |
808 | 0 | return 0; |
809 | 0 | } |
810 | 0 | return *pval; |
811 | 0 | } |
812 | | |
813 | | // Get kerning value from Apple (version 1.0) kern table, |
814 | | // subtable format 3 (simple N x M array of kerning values) |
815 | | |
816 | | // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html |
817 | | // for details of version 1.0 format 3 subtable. |
818 | | |
819 | | struct KernHeaderVersion1Fmt3 { |
820 | | KernTableSubtableHeaderVersion1 header; |
821 | | AutoSwap_PRUint16 glyphCount; |
822 | | uint8_t kernValueCount; |
823 | | uint8_t leftClassCount; |
824 | | uint8_t rightClassCount; |
825 | | uint8_t flags; |
826 | | }; |
827 | | |
828 | | static int16_t |
829 | | GetKernValueVersion1Fmt3(const void* aSubtable, |
830 | | uint32_t aSubtableLen, |
831 | | uint16_t aFirstGlyph, |
832 | | uint16_t aSecondGlyph) |
833 | 0 | { |
834 | 0 | // check that we can safely read the header fields |
835 | 0 | if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) { |
836 | 0 | return 0; |
837 | 0 | } |
838 | 0 | |
839 | 0 | const KernHeaderVersion1Fmt3* hdr = |
840 | 0 | reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable); |
841 | 0 | if (hdr->flags != 0) { |
842 | 0 | return 0; |
843 | 0 | } |
844 | 0 | |
845 | 0 | uint16_t glyphCount = hdr->glyphCount; |
846 | 0 |
|
847 | 0 | // check that table is large enough for the arrays |
848 | 0 | if (sizeof(KernHeaderVersion1Fmt3) + |
849 | 0 | hdr->kernValueCount * sizeof(int16_t) + |
850 | 0 | glyphCount + glyphCount + |
851 | 0 | hdr->leftClassCount * hdr->rightClassCount > aSubtableLen) { |
852 | 0 | return 0; |
853 | 0 | } |
854 | 0 | |
855 | 0 | if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) { |
856 | 0 | // glyphs are out of range for the class tables |
857 | 0 | return 0; |
858 | 0 | } |
859 | 0 | |
860 | 0 | // get pointers to the four arrays within the subtable |
861 | 0 | const AutoSwap_PRInt16* kernValue = |
862 | 0 | reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1); |
863 | 0 | const uint8_t* leftClass = |
864 | 0 | reinterpret_cast<const uint8_t*>(kernValue + hdr->kernValueCount); |
865 | 0 | const uint8_t* rightClass = leftClass + glyphCount; |
866 | 0 | const uint8_t* kernIndex = rightClass + glyphCount; |
867 | 0 |
|
868 | 0 | uint8_t lc = leftClass[aFirstGlyph]; |
869 | 0 | uint8_t rc = rightClass[aSecondGlyph]; |
870 | 0 | if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) { |
871 | 0 | return 0; |
872 | 0 | } |
873 | 0 | |
874 | 0 | uint8_t ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount + |
875 | 0 | rightClass[aSecondGlyph]]; |
876 | 0 | if (ki >= hdr->kernValueCount) { |
877 | 0 | return 0; |
878 | 0 | } |
879 | 0 | |
880 | 0 | return kernValue[ki]; |
881 | 0 | } |
882 | | |
883 | 0 | #define KERN0_COVERAGE_HORIZONTAL 0x0001 |
884 | 0 | #define KERN0_COVERAGE_MINIMUM 0x0002 |
885 | 0 | #define KERN0_COVERAGE_CROSS_STREAM 0x0004 |
886 | 0 | #define KERN0_COVERAGE_OVERRIDE 0x0008 |
887 | 0 | #define KERN0_COVERAGE_RESERVED 0x00F0 |
888 | | |
889 | 0 | #define KERN1_COVERAGE_VERTICAL 0x8000 |
890 | 0 | #define KERN1_COVERAGE_CROSS_STREAM 0x4000 |
891 | 0 | #define KERN1_COVERAGE_VARIATION 0x2000 |
892 | 0 | #define KERN1_COVERAGE_RESERVED 0x1F00 |
893 | | |
894 | | hb_position_t |
895 | | gfxHarfBuzzShaper::GetHKerning(uint16_t aFirstGlyph, |
896 | | uint16_t aSecondGlyph) const |
897 | 0 | { |
898 | 0 | // We want to ignore any kern pairs involving <space>, because we are |
899 | 0 | // handling words in isolation, the only space characters seen here are |
900 | 0 | // the ones artificially added by the textRun code. |
901 | 0 | uint32_t spaceGlyph = mFont->GetSpaceGlyph(); |
902 | 0 | if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) { |
903 | 0 | return 0; |
904 | 0 | } |
905 | 0 | |
906 | 0 | if (!mKernTable) { |
907 | 0 | mKernTable = mFont->GetFontEntry()->GetFontTable(TRUETYPE_TAG('k','e','r','n')); |
908 | 0 | if (!mKernTable) { |
909 | 0 | mKernTable = hb_blob_get_empty(); |
910 | 0 | } |
911 | 0 | } |
912 | 0 |
|
913 | 0 | uint32_t len; |
914 | 0 | const char* base = hb_blob_get_data(mKernTable, &len); |
915 | 0 | if (len < sizeof(KernTableVersion0)) { |
916 | 0 | return 0; |
917 | 0 | } |
918 | 0 | int32_t value = 0; |
919 | 0 |
|
920 | 0 | // First try to interpret as "version 0" kern table |
921 | 0 | // (see http://www.microsoft.com/typography/otspec/kern.htm) |
922 | 0 | const KernTableVersion0* kern0 = |
923 | 0 | reinterpret_cast<const KernTableVersion0*>(base); |
924 | 0 | if (uint16_t(kern0->version) == 0) { |
925 | 0 | uint16_t nTables = kern0->nTables; |
926 | 0 | uint32_t offs = sizeof(KernTableVersion0); |
927 | 0 | for (uint16_t i = 0; i < nTables; ++i) { |
928 | 0 | if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) { |
929 | 0 | break; |
930 | 0 | } |
931 | 0 | const KernTableSubtableHeaderVersion0* st0 = |
932 | 0 | reinterpret_cast<const KernTableSubtableHeaderVersion0*> |
933 | 0 | (base + offs); |
934 | 0 | uint16_t subtableLen = uint16_t(st0->length); |
935 | 0 | if (offs + subtableLen > len) { |
936 | 0 | break; |
937 | 0 | } |
938 | 0 | offs += subtableLen; |
939 | 0 | uint16_t coverage = st0->coverage; |
940 | 0 | if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) { |
941 | 0 | // we only care about horizontal kerning (for now) |
942 | 0 | continue; |
943 | 0 | } |
944 | 0 | if (coverage & |
945 | 0 | (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) { |
946 | 0 | // we don't support cross-stream kerning, and |
947 | 0 | // reserved bits should be zero; |
948 | 0 | // ignore the subtable if not |
949 | 0 | continue; |
950 | 0 | } |
951 | 0 | uint8_t format = (coverage >> 8); |
952 | 0 | switch (format) { |
953 | 0 | case 0: |
954 | 0 | GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0), |
955 | 0 | aFirstGlyph, aSecondGlyph, value, |
956 | 0 | (coverage & KERN0_COVERAGE_OVERRIDE) != 0, |
957 | 0 | (coverage & KERN0_COVERAGE_MINIMUM) != 0); |
958 | 0 | break; |
959 | 0 | default: |
960 | 0 | // TODO: implement support for other formats, |
961 | 0 | // if they're ever used in practice |
962 | | #if DEBUG |
963 | | { |
964 | | char buf[1024]; |
965 | | SprintfLiteral(buf, "unknown kern subtable in %s: " |
966 | | "ver 0 format %d\n", |
967 | | mFont->GetName().get(), |
968 | | format); |
969 | | NS_WARNING(buf); |
970 | | } |
971 | | #endif |
972 | | break; |
973 | 0 | } |
974 | 0 | } |
975 | 0 | } else { |
976 | 0 | // It wasn't a "version 0" table; check if it is Apple version 1.0 |
977 | 0 | // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) |
978 | 0 | const KernTableVersion1* kern1 = |
979 | 0 | reinterpret_cast<const KernTableVersion1*>(base); |
980 | 0 | if (uint32_t(kern1->version) == 0x00010000) { |
981 | 0 | uint32_t nTables = kern1->nTables; |
982 | 0 | uint32_t offs = sizeof(KernTableVersion1); |
983 | 0 | for (uint32_t i = 0; i < nTables; ++i) { |
984 | 0 | if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) { |
985 | 0 | break; |
986 | 0 | } |
987 | 0 | const KernTableSubtableHeaderVersion1* st1 = |
988 | 0 | reinterpret_cast<const KernTableSubtableHeaderVersion1*> |
989 | 0 | (base + offs); |
990 | 0 | uint32_t subtableLen = uint32_t(st1->length); |
991 | 0 | offs += subtableLen; |
992 | 0 | uint16_t coverage = st1->coverage; |
993 | 0 | if (coverage & |
994 | 0 | (KERN1_COVERAGE_VERTICAL | |
995 | 0 | KERN1_COVERAGE_CROSS_STREAM | |
996 | 0 | KERN1_COVERAGE_VARIATION | |
997 | 0 | KERN1_COVERAGE_RESERVED)) { |
998 | 0 | // we only care about horizontal kerning (for now), |
999 | 0 | // we don't support cross-stream kerning, |
1000 | 0 | // we don't support variations, |
1001 | 0 | // reserved bits should be zero; |
1002 | 0 | // ignore the subtable if not |
1003 | 0 | continue; |
1004 | 0 | } |
1005 | 0 | uint8_t format = (coverage & 0xff); |
1006 | 0 | switch (format) { |
1007 | 0 | case 0: |
1008 | 0 | GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1), |
1009 | 0 | aFirstGlyph, aSecondGlyph, value); |
1010 | 0 | break; |
1011 | 0 | case 2: |
1012 | 0 | value = GetKernValueVersion1Fmt2(st1, subtableLen, |
1013 | 0 | aFirstGlyph, aSecondGlyph); |
1014 | 0 | break; |
1015 | 0 | case 3: |
1016 | 0 | value = GetKernValueVersion1Fmt3(st1, subtableLen, |
1017 | 0 | aFirstGlyph, aSecondGlyph); |
1018 | 0 | break; |
1019 | 0 | default: |
1020 | 0 | // TODO: implement support for other formats. |
1021 | 0 | // Note that format 1 cannot be supported here, |
1022 | 0 | // as it requires the full glyph array to run the FSM, |
1023 | 0 | // not just the current glyph pair. |
1024 | | #if DEBUG |
1025 | | { |
1026 | | char buf[1024]; |
1027 | | SprintfLiteral(buf, "unknown kern subtable in %s: " |
1028 | | "ver 0 format %d\n", |
1029 | | mFont->GetName().get(), |
1030 | | format); |
1031 | | NS_WARNING(buf); |
1032 | | } |
1033 | | #endif |
1034 | | break; |
1035 | 0 | } |
1036 | 0 | } |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 |
|
1040 | 0 | if (value != 0) { |
1041 | 0 | return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value); |
1042 | 0 | } |
1043 | 0 | return 0; |
1044 | 0 | } |
1045 | | |
1046 | | static hb_position_t |
1047 | | HBGetHKerning(hb_font_t *font, void *font_data, |
1048 | | hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, |
1049 | | void *user_data) |
1050 | 0 | { |
1051 | 0 | const gfxHarfBuzzShaper::FontCallbackData *fcd = |
1052 | 0 | static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data); |
1053 | 0 | return fcd->mShaper->GetHKerning(first_glyph, second_glyph); |
1054 | 0 | } |
1055 | | |
1056 | | /* |
1057 | | * HarfBuzz unicode property callbacks |
1058 | | */ |
1059 | | |
1060 | | static hb_codepoint_t |
1061 | | HBGetMirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, |
1062 | | void *user_data) |
1063 | 0 | { |
1064 | 0 | return GetMirroredChar(aCh); |
1065 | 0 | } |
1066 | | |
1067 | | static hb_unicode_general_category_t |
1068 | | HBGetGeneralCategory(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, |
1069 | | void *user_data) |
1070 | 0 | { |
1071 | 0 | return hb_unicode_general_category_t(GetGeneralCategory(aCh)); |
1072 | 0 | } |
1073 | | |
1074 | | static hb_script_t |
1075 | | HBGetScript(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data) |
1076 | 0 | { |
1077 | 0 | return hb_script_t(GetScriptTagForCode(GetScriptCode(aCh))); |
1078 | 0 | } |
1079 | | |
1080 | | static hb_unicode_combining_class_t |
1081 | | HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, |
1082 | | void *user_data) |
1083 | 0 | { |
1084 | 0 | return hb_unicode_combining_class_t(GetCombiningClass(aCh)); |
1085 | 0 | } |
1086 | | |
1087 | | // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA; |
1088 | | // note that some letters do not have a dagesh presForm encoded |
1089 | | static const char16_t sDageshForms[0x05EA - 0x05D0 + 1] = { |
1090 | | 0xFB30, // ALEF |
1091 | | 0xFB31, // BET |
1092 | | 0xFB32, // GIMEL |
1093 | | 0xFB33, // DALET |
1094 | | 0xFB34, // HE |
1095 | | 0xFB35, // VAV |
1096 | | 0xFB36, // ZAYIN |
1097 | | 0, // HET |
1098 | | 0xFB38, // TET |
1099 | | 0xFB39, // YOD |
1100 | | 0xFB3A, // FINAL KAF |
1101 | | 0xFB3B, // KAF |
1102 | | 0xFB3C, // LAMED |
1103 | | 0, // FINAL MEM |
1104 | | 0xFB3E, // MEM |
1105 | | 0, // FINAL NUN |
1106 | | 0xFB40, // NUN |
1107 | | 0xFB41, // SAMEKH |
1108 | | 0, // AYIN |
1109 | | 0xFB43, // FINAL PE |
1110 | | 0xFB44, // PE |
1111 | | 0, // FINAL TSADI |
1112 | | 0xFB46, // TSADI |
1113 | | 0xFB47, // QOF |
1114 | | 0xFB48, // RESH |
1115 | | 0xFB49, // SHIN |
1116 | | 0xFB4A // TAV |
1117 | | }; |
1118 | | |
1119 | | static hb_bool_t |
1120 | | HBUnicodeCompose(hb_unicode_funcs_t *ufuncs, |
1121 | | hb_codepoint_t a, |
1122 | | hb_codepoint_t b, |
1123 | | hb_codepoint_t *ab, |
1124 | | void *user_data) |
1125 | 0 | { |
1126 | 0 | if (sNormalizer) { |
1127 | 0 | UChar32 ch = unorm2_composePair(sNormalizer, a, b); |
1128 | 0 | if (ch >= 0) { |
1129 | 0 | *ab = ch; |
1130 | 0 | return true; |
1131 | 0 | } |
1132 | 0 | } |
1133 | 0 | |
1134 | 0 | return false; |
1135 | 0 | } |
1136 | | |
1137 | | static hb_bool_t |
1138 | | HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs, |
1139 | | hb_codepoint_t ab, |
1140 | | hb_codepoint_t *a, |
1141 | | hb_codepoint_t *b, |
1142 | | void *user_data) |
1143 | 0 | { |
1144 | | #ifdef MOZ_WIDGET_ANDROID |
1145 | | // Hack for the SamsungDevanagari font, bug 1012365: |
1146 | | // support U+0972 by decomposing it. |
1147 | | if (ab == 0x0972) { |
1148 | | *a = 0x0905; |
1149 | | *b = 0x0945; |
1150 | | return true; |
1151 | | } |
1152 | | #endif |
1153 | |
|
1154 | 0 | if (!sNormalizer) { |
1155 | 0 | return false; |
1156 | 0 | } |
1157 | 0 | |
1158 | 0 | // Canonical decompositions are never more than two characters, |
1159 | 0 | // or a maximum of 4 utf-16 code units. |
1160 | 0 | const unsigned MAX_DECOMP_LENGTH = 4; |
1161 | 0 |
|
1162 | 0 | UErrorCode error = U_ZERO_ERROR; |
1163 | 0 | UChar decomp[MAX_DECOMP_LENGTH]; |
1164 | 0 | int32_t len = unorm2_getRawDecomposition(sNormalizer, ab, decomp, |
1165 | 0 | MAX_DECOMP_LENGTH, &error); |
1166 | 0 | if (U_FAILURE(error) || len < 0) { |
1167 | 0 | return false; |
1168 | 0 | } |
1169 | 0 | |
1170 | 0 | UText text = UTEXT_INITIALIZER; |
1171 | 0 | utext_openUChars(&text, decomp, len, &error); |
1172 | 0 | NS_ASSERTION(U_SUCCESS(error), "UText failure?"); |
1173 | 0 |
|
1174 | 0 | UChar32 ch = UTEXT_NEXT32(&text); |
1175 | 0 | if (ch != U_SENTINEL) { |
1176 | 0 | *a = ch; |
1177 | 0 | } |
1178 | 0 | ch = UTEXT_NEXT32(&text); |
1179 | 0 | if (ch != U_SENTINEL) { |
1180 | 0 | *b = ch; |
1181 | 0 | } |
1182 | 0 | utext_close(&text); |
1183 | 0 |
|
1184 | 0 | return *b != 0 || *a != ab; |
1185 | 0 | } |
1186 | | |
1187 | | static void |
1188 | | AddOpenTypeFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg) |
1189 | 0 | { |
1190 | 0 | nsTArray<hb_feature_t>* features = static_cast<nsTArray<hb_feature_t>*> (aUserArg); |
1191 | 0 |
|
1192 | 0 | hb_feature_t feat = { 0, 0, 0, UINT_MAX }; |
1193 | 0 | feat.tag = aTag; |
1194 | 0 | feat.value = aValue; |
1195 | 0 | features->AppendElement(feat); |
1196 | 0 | } |
1197 | | |
1198 | | /* |
1199 | | * gfxFontShaper override to initialize the text run using HarfBuzz |
1200 | | */ |
1201 | | |
1202 | | static hb_font_funcs_t * sHBFontFuncs = nullptr; |
1203 | | static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr; |
1204 | | static const hb_script_t sMathScript = |
1205 | | hb_ot_tag_to_script(HB_TAG('m','a','t','h')); |
1206 | | |
1207 | | bool |
1208 | | gfxHarfBuzzShaper::Initialize() |
1209 | 0 | { |
1210 | 0 | if (mInitialized) { |
1211 | 0 | return mHBFont != nullptr; |
1212 | 0 | } |
1213 | 0 | mInitialized = true; |
1214 | 0 | mCallbackData.mShaper = this; |
1215 | 0 |
|
1216 | 0 | if (!sHBFontFuncs) { |
1217 | 0 | // static function callback pointers, initialized by the first |
1218 | 0 | // harfbuzz shaper used |
1219 | 0 | sHBFontFuncs = hb_font_funcs_create(); |
1220 | 0 | hb_font_funcs_set_nominal_glyph_func(sHBFontFuncs, |
1221 | 0 | HBGetNominalGlyph, |
1222 | 0 | nullptr, nullptr); |
1223 | 0 | hb_font_funcs_set_variation_glyph_func(sHBFontFuncs, |
1224 | 0 | HBGetVariationGlyph, |
1225 | 0 | nullptr, nullptr); |
1226 | 0 | hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs, |
1227 | 0 | HBGetGlyphHAdvance, |
1228 | 0 | nullptr, nullptr); |
1229 | 0 | hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs, |
1230 | 0 | HBGetGlyphVAdvance, |
1231 | 0 | nullptr, nullptr); |
1232 | 0 | hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs, |
1233 | 0 | HBGetGlyphVOrigin, |
1234 | 0 | nullptr, nullptr); |
1235 | 0 | hb_font_funcs_set_glyph_extents_func(sHBFontFuncs, |
1236 | 0 | HBGetGlyphExtents, |
1237 | 0 | nullptr, nullptr); |
1238 | 0 | hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs, |
1239 | 0 | HBGetContourPoint, |
1240 | 0 | nullptr, nullptr); |
1241 | 0 | hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs, |
1242 | 0 | HBGetHKerning, |
1243 | 0 | nullptr, nullptr); |
1244 | 0 |
|
1245 | 0 | sHBUnicodeFuncs = |
1246 | 0 | hb_unicode_funcs_create(hb_unicode_funcs_get_empty()); |
1247 | 0 | hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs, |
1248 | 0 | HBGetMirroring, |
1249 | 0 | nullptr, nullptr); |
1250 | 0 | hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript, |
1251 | 0 | nullptr, nullptr); |
1252 | 0 | hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs, |
1253 | 0 | HBGetGeneralCategory, |
1254 | 0 | nullptr, nullptr); |
1255 | 0 | hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs, |
1256 | 0 | HBGetCombiningClass, |
1257 | 0 | nullptr, nullptr); |
1258 | 0 | hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs, |
1259 | 0 | HBUnicodeCompose, |
1260 | 0 | nullptr, nullptr); |
1261 | 0 | hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs, |
1262 | 0 | HBUnicodeDecompose, |
1263 | 0 | nullptr, nullptr); |
1264 | 0 |
|
1265 | 0 | UErrorCode error = U_ZERO_ERROR; |
1266 | 0 | sNormalizer = unorm2_getNFCInstance(&error); |
1267 | 0 | MOZ_ASSERT(U_SUCCESS(error), "failed to get ICU normalizer"); |
1268 | 0 | } |
1269 | 0 |
|
1270 | 0 | gfxFontEntry *entry = mFont->GetFontEntry(); |
1271 | 0 | if (!mUseFontGetGlyph) { |
1272 | 0 | // get the cmap table and find offset to our subtable |
1273 | 0 | mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p')); |
1274 | 0 | if (!mCmapTable) { |
1275 | 0 | NS_WARNING("failed to load cmap, glyphs will be missing"); |
1276 | 0 | return false; |
1277 | 0 | } |
1278 | 0 | uint32_t len; |
1279 | 0 | const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len); |
1280 | 0 | mCmapFormat = gfxFontUtils:: |
1281 | 0 | FindPreferredSubtable(data, len, |
1282 | 0 | &mSubtableOffset, &mUVSTableOffset); |
1283 | 0 | if (mCmapFormat <= 0) { |
1284 | 0 | return false; |
1285 | 0 | } |
1286 | 0 | } |
1287 | 0 | |
1288 | 0 | if (!mUseFontGlyphWidths) { |
1289 | 0 | // If font doesn't implement GetGlyphWidth, we will be reading |
1290 | 0 | // the metrics table directly, so make sure we can load it. |
1291 | 0 | if (!LoadHmtxTable()) { |
1292 | 0 | return false; |
1293 | 0 | } |
1294 | 0 | } |
1295 | 0 | |
1296 | 0 | mBuffer = hb_buffer_create(); |
1297 | 0 | hb_buffer_set_unicode_funcs(mBuffer, sHBUnicodeFuncs); |
1298 | 0 | hb_buffer_set_cluster_level(mBuffer, |
1299 | 0 | HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); |
1300 | 0 |
|
1301 | 0 | mHBFont = hb_font_create(mHBFace); |
1302 | 0 | hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr); |
1303 | 0 | hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize()); |
1304 | 0 | uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point |
1305 | 0 | hb_font_set_scale(mHBFont, scale, scale); |
1306 | 0 |
|
1307 | 0 | const auto& vars = mFont->GetStyle()->variationSettings; |
1308 | 0 | size_t len = vars.Length(); |
1309 | 0 | if (len > 0) { |
1310 | 0 | // Fortunately, the hb_variation_t struct is compatible with our |
1311 | 0 | // gfxFontFeature, so we can simply cast here. |
1312 | 0 | auto hbVars = reinterpret_cast<const hb_variation_t*>(vars.Elements()); |
1313 | 0 | hb_font_set_variations(mHBFont, hbVars, len); |
1314 | 0 | } |
1315 | 0 |
|
1316 | 0 | return true; |
1317 | 0 | } |
1318 | | |
1319 | | bool |
1320 | | gfxHarfBuzzShaper::LoadHmtxTable() |
1321 | 0 | { |
1322 | 0 | // Read mNumLongHMetrics from metrics-head table without caching its |
1323 | 0 | // blob, and preload/cache the metrics table. |
1324 | 0 | gfxFontEntry *entry = mFont->GetFontEntry(); |
1325 | 0 | gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a')); |
1326 | 0 | if (hheaTable) { |
1327 | 0 | uint32_t len; |
1328 | 0 | const MetricsHeader* hhea = |
1329 | 0 | reinterpret_cast<const MetricsHeader*> |
1330 | 0 | (hb_blob_get_data(hheaTable, &len)); |
1331 | 0 | if (len >= sizeof(MetricsHeader)) { |
1332 | 0 | mNumLongHMetrics = hhea->numOfLongMetrics; |
1333 | 0 | if (mNumLongHMetrics > 0 && |
1334 | 0 | int16_t(hhea->metricDataFormat) == 0) { |
1335 | 0 | // no point reading metrics if number of entries is zero! |
1336 | 0 | // in that case, we won't be able to use this font |
1337 | 0 | // (this method will return FALSE below if mHmtxTable |
1338 | 0 | // is null) |
1339 | 0 | mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h','m','t','x')); |
1340 | 0 | if (mHmtxTable && hb_blob_get_length(mHmtxTable) < |
1341 | 0 | mNumLongHMetrics * sizeof(LongMetric)) { |
1342 | 0 | // metrics table is not large enough for the claimed |
1343 | 0 | // number of entries: invalid, do not use. |
1344 | 0 | hb_blob_destroy(mHmtxTable); |
1345 | 0 | mHmtxTable = nullptr; |
1346 | 0 | } |
1347 | 0 | } |
1348 | 0 | } |
1349 | 0 | } |
1350 | 0 | if (!mHmtxTable) { |
1351 | 0 | return false; |
1352 | 0 | } |
1353 | 0 | return true; |
1354 | 0 | } |
1355 | | |
1356 | | void |
1357 | | gfxHarfBuzzShaper::InitializeVertical() |
1358 | 0 | { |
1359 | 0 | // We only do this once. If we don't have a mHmtxTable after that, |
1360 | 0 | // we'll be making up fallback metrics. |
1361 | 0 | if (mVerticalInitialized) { |
1362 | 0 | return; |
1363 | 0 | } |
1364 | 0 | mVerticalInitialized = true; |
1365 | 0 |
|
1366 | 0 | if (!mHmtxTable) { |
1367 | 0 | if (!LoadHmtxTable()) { |
1368 | 0 | return; |
1369 | 0 | } |
1370 | 0 | } |
1371 | 0 | |
1372 | 0 | // Load vertical metrics if present in the font; if not, we'll synthesize |
1373 | 0 | // vertical glyph advances based on (horizontal) ascent/descent metrics. |
1374 | 0 | gfxFontEntry *entry = mFont->GetFontEntry(); |
1375 | 0 | gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v','h','e','a')); |
1376 | 0 | if (vheaTable) { |
1377 | 0 | uint32_t len; |
1378 | 0 | const MetricsHeader* vhea = |
1379 | 0 | reinterpret_cast<const MetricsHeader*> |
1380 | 0 | (hb_blob_get_data(vheaTable, &len)); |
1381 | 0 | if (len >= sizeof(MetricsHeader)) { |
1382 | 0 | mNumLongVMetrics = vhea->numOfLongMetrics; |
1383 | 0 | gfxFontEntry::AutoTable |
1384 | 0 | maxpTable(entry, TRUETYPE_TAG('m','a','x','p')); |
1385 | 0 | int numGlyphs = -1; // invalid if we fail to read 'maxp' |
1386 | 0 | if (maxpTable && |
1387 | 0 | hb_blob_get_length(maxpTable) >= sizeof(MaxpTableHeader)) { |
1388 | 0 | const MaxpTableHeader* maxp = |
1389 | 0 | reinterpret_cast<const MaxpTableHeader*> |
1390 | 0 | (hb_blob_get_data(maxpTable, nullptr)); |
1391 | 0 | numGlyphs = uint16_t(maxp->numGlyphs); |
1392 | 0 | } |
1393 | 0 | if (mNumLongVMetrics > 0 && mNumLongVMetrics <= numGlyphs && |
1394 | 0 | int16_t(vhea->metricDataFormat) == 0) { |
1395 | 0 | mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v','m','t','x')); |
1396 | 0 | if (mVmtxTable && hb_blob_get_length(mVmtxTable) < |
1397 | 0 | mNumLongVMetrics * sizeof(LongMetric) + |
1398 | 0 | (numGlyphs - mNumLongVMetrics) * sizeof(int16_t)) { |
1399 | 0 | // metrics table is not large enough for the claimed |
1400 | 0 | // number of entries: invalid, do not use. |
1401 | 0 | hb_blob_destroy(mVmtxTable); |
1402 | 0 | mVmtxTable = nullptr; |
1403 | 0 | } |
1404 | 0 | } |
1405 | 0 | } |
1406 | 0 | } |
1407 | 0 |
|
1408 | 0 | // For CFF fonts only, load a VORG table if present. |
1409 | 0 | if (entry->HasFontTable(TRUETYPE_TAG('C','F','F',' '))) { |
1410 | 0 | mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V','O','R','G')); |
1411 | 0 | if (mVORGTable) { |
1412 | 0 | uint32_t len; |
1413 | 0 | const VORG* vorg = |
1414 | 0 | reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, |
1415 | 0 | &len)); |
1416 | 0 | if (len < sizeof(VORG) || |
1417 | 0 | uint16_t(vorg->majorVersion) != 1 || |
1418 | 0 | uint16_t(vorg->minorVersion) != 0 || |
1419 | 0 | len < sizeof(VORG) + uint16_t(vorg->numVertOriginYMetrics) * |
1420 | 0 | sizeof(VORGrec)) { |
1421 | 0 | // VORG table is an unknown version, or not large enough |
1422 | 0 | // to be valid -- discard it. |
1423 | 0 | NS_WARNING("discarding invalid VORG table"); |
1424 | 0 | hb_blob_destroy(mVORGTable); |
1425 | 0 | mVORGTable = nullptr; |
1426 | 0 | } |
1427 | 0 | } |
1428 | 0 | } |
1429 | 0 | } |
1430 | | |
1431 | | bool |
1432 | | gfxHarfBuzzShaper::ShapeText(DrawTarget *aDrawTarget, |
1433 | | const char16_t *aText, |
1434 | | uint32_t aOffset, |
1435 | | uint32_t aLength, |
1436 | | Script aScript, |
1437 | | bool aVertical, |
1438 | | RoundingFlags aRounding, |
1439 | | gfxShapedText *aShapedText) |
1440 | 0 | { |
1441 | 0 | // some font back-ends require this in order to get proper hinted metrics |
1442 | 0 | if (!mFont->SetupCairoFont(aDrawTarget)) { |
1443 | 0 | return false; |
1444 | 0 | } |
1445 | 0 | |
1446 | 0 | mCallbackData.mDrawTarget = aDrawTarget; |
1447 | 0 | mUseVerticalPresentationForms = false; |
1448 | 0 |
|
1449 | 0 | if (!Initialize()) { |
1450 | 0 | return false; |
1451 | 0 | } |
1452 | 0 | |
1453 | 0 | if (aVertical) { |
1454 | 0 | InitializeVertical(); |
1455 | 0 | if (!mFont->GetFontEntry()-> |
1456 | 0 | SupportsOpenTypeFeature(aScript, HB_TAG('v','e','r','t'))) { |
1457 | 0 | mUseVerticalPresentationForms = true; |
1458 | 0 | } |
1459 | 0 | } |
1460 | 0 |
|
1461 | 0 | const gfxFontStyle *style = mFont->GetStyle(); |
1462 | 0 |
|
1463 | 0 | // determine whether petite-caps falls back to small-caps |
1464 | 0 | bool addSmallCaps = false; |
1465 | 0 | if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) { |
1466 | 0 | switch (style->variantCaps) { |
1467 | 0 | case NS_FONT_VARIANT_CAPS_ALLPETITE: |
1468 | 0 | case NS_FONT_VARIANT_CAPS_PETITECAPS: |
1469 | 0 | bool synLower, synUpper; |
1470 | 0 | mFont->SupportsVariantCaps(aScript, style->variantCaps, |
1471 | 0 | addSmallCaps, synLower, synUpper); |
1472 | 0 | break; |
1473 | 0 | default: |
1474 | 0 | break; |
1475 | 0 | } |
1476 | 0 | } |
1477 | 0 | |
1478 | 0 | gfxFontEntry *entry = mFont->GetFontEntry(); |
1479 | 0 |
|
1480 | 0 | // insert any merged features into hb_feature array |
1481 | 0 | AutoTArray<hb_feature_t,20> features; |
1482 | 0 | MergeFontFeatures(style, |
1483 | 0 | entry->mFeatureSettings, |
1484 | 0 | aShapedText->DisableLigatures(), |
1485 | 0 | entry->FamilyName(), |
1486 | 0 | addSmallCaps, |
1487 | 0 | AddOpenTypeFeature, |
1488 | 0 | &features); |
1489 | 0 |
|
1490 | 0 | bool isRightToLeft = aShapedText->IsRightToLeft(); |
1491 | 0 |
|
1492 | 0 | hb_buffer_set_direction(mBuffer, |
1493 | 0 | aVertical ? HB_DIRECTION_TTB : |
1494 | 0 | (isRightToLeft ? HB_DIRECTION_RTL : |
1495 | 0 | HB_DIRECTION_LTR)); |
1496 | 0 | hb_script_t scriptTag; |
1497 | 0 | if (aShapedText->GetFlags() & gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT) { |
1498 | 0 | scriptTag = sMathScript; |
1499 | 0 | } else { |
1500 | 0 | scriptTag = GetHBScriptUsedForShaping(aScript); |
1501 | 0 | } |
1502 | 0 | hb_buffer_set_script(mBuffer, scriptTag); |
1503 | 0 |
|
1504 | 0 | hb_language_t language; |
1505 | 0 | if (style->languageOverride) { |
1506 | 0 | language = hb_ot_tag_to_language(style->languageOverride); |
1507 | 0 | } else if (entry->mLanguageOverride) { |
1508 | 0 | language = hb_ot_tag_to_language(entry->mLanguageOverride); |
1509 | 0 | } else if (style->explicitLanguage) { |
1510 | 0 | nsCString langString; |
1511 | 0 | style->language->ToUTF8String(langString); |
1512 | 0 | language = |
1513 | 0 | hb_language_from_string(langString.get(), langString.Length()); |
1514 | 0 | } else { |
1515 | 0 | language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE); |
1516 | 0 | } |
1517 | 0 | hb_buffer_set_language(mBuffer, language); |
1518 | 0 |
|
1519 | 0 | uint32_t length = aLength; |
1520 | 0 | hb_buffer_add_utf16(mBuffer, |
1521 | 0 | reinterpret_cast<const uint16_t*>(aText), |
1522 | 0 | length, 0, length); |
1523 | 0 |
|
1524 | 0 | hb_shape(mHBFont, mBuffer, features.Elements(), features.Length()); |
1525 | 0 |
|
1526 | 0 | if (isRightToLeft) { |
1527 | 0 | hb_buffer_reverse(mBuffer); |
1528 | 0 | } |
1529 | 0 |
|
1530 | 0 | nsresult rv = SetGlyphsFromRun(aShapedText, aOffset, aLength, |
1531 | 0 | aText, aVertical, aRounding); |
1532 | 0 |
|
1533 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), |
1534 | 0 | "failed to store glyphs into gfxShapedWord"); |
1535 | 0 | hb_buffer_clear_contents(mBuffer); |
1536 | 0 |
|
1537 | 0 | return NS_SUCCEEDED(rv); |
1538 | 0 | } |
1539 | | |
1540 | | #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs |
1541 | | // will fit without requiring separate allocation |
1542 | | // for charToGlyphArray |
1543 | | |
1544 | | nsresult |
1545 | | gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText *aShapedText, |
1546 | | uint32_t aOffset, |
1547 | | uint32_t aLength, |
1548 | | const char16_t *aText, |
1549 | | bool aVertical, |
1550 | | RoundingFlags aRounding) |
1551 | 0 | { |
1552 | 0 | typedef gfxShapedText::CompressedGlyph CompressedGlyph; |
1553 | 0 |
|
1554 | 0 | uint32_t numGlyphs; |
1555 | 0 | const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs); |
1556 | 0 | if (numGlyphs == 0) { |
1557 | 0 | return NS_OK; |
1558 | 0 | } |
1559 | 0 | |
1560 | 0 | AutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs; |
1561 | 0 |
|
1562 | 0 | uint32_t wordLength = aLength; |
1563 | 0 | static const int32_t NO_GLYPH = -1; |
1564 | 0 | AutoTArray<int32_t,SMALL_GLYPH_RUN> charToGlyphArray; |
1565 | 0 | if (!charToGlyphArray.SetLength(wordLength, fallible)) { |
1566 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1567 | 0 | } |
1568 | 0 | |
1569 | 0 | int32_t *charToGlyph = charToGlyphArray.Elements(); |
1570 | 0 | for (uint32_t offset = 0; offset < wordLength; ++offset) { |
1571 | 0 | charToGlyph[offset] = NO_GLYPH; |
1572 | 0 | } |
1573 | 0 |
|
1574 | 0 | for (uint32_t i = 0; i < numGlyphs; ++i) { |
1575 | 0 | uint32_t loc = ginfo[i].cluster; |
1576 | 0 | if (loc < wordLength) { |
1577 | 0 | charToGlyph[loc] = i; |
1578 | 0 | } |
1579 | 0 | } |
1580 | 0 |
|
1581 | 0 | int32_t glyphStart = 0; // looking for a clump that starts at this glyph |
1582 | 0 | int32_t charStart = 0; // and this char index within the range of the run |
1583 | 0 |
|
1584 | 0 | bool roundI, roundB; |
1585 | 0 | if (aVertical) { |
1586 | 0 | roundI = bool(aRounding & RoundingFlags::kRoundY); |
1587 | 0 | roundB = bool(aRounding & RoundingFlags::kRoundX); |
1588 | 0 | } else { |
1589 | 0 | roundI = bool(aRounding & RoundingFlags::kRoundX); |
1590 | 0 | roundB = bool(aRounding & RoundingFlags::kRoundY); |
1591 | 0 | } |
1592 | 0 |
|
1593 | 0 | int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit(); |
1594 | 0 | CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset; |
1595 | 0 |
|
1596 | 0 | // factor to convert 16.16 fixed-point pixels to app units |
1597 | 0 | // (only used if not rounding) |
1598 | 0 | double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit()); |
1599 | 0 |
|
1600 | 0 | // Residual from rounding of previous advance, for use in rounding the |
1601 | 0 | // subsequent offset or advance appropriately. 16.16 fixed-point |
1602 | 0 | // |
1603 | 0 | // When rounding, the goal is to make the distance between glyphs and |
1604 | 0 | // their base glyph equal to the integral number of pixels closest to that |
1605 | 0 | // suggested by that shaper. |
1606 | 0 | // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset |
1607 | 0 | // |
1608 | 0 | // The value of the residual is the part of the desired distance that has |
1609 | 0 | // not been included in integer offsets. |
1610 | 0 | hb_position_t residual = 0; |
1611 | 0 |
|
1612 | 0 | // keep track of y-position to set glyph offsets if needed |
1613 | 0 | nscoord bPos = 0; |
1614 | 0 |
|
1615 | 0 | const hb_glyph_position_t *posInfo = |
1616 | 0 | hb_buffer_get_glyph_positions(mBuffer, nullptr); |
1617 | 0 |
|
1618 | 0 | while (glyphStart < int32_t(numGlyphs)) { |
1619 | 0 |
|
1620 | 0 | int32_t charEnd = ginfo[glyphStart].cluster; |
1621 | 0 | int32_t glyphEnd = glyphStart; |
1622 | 0 | int32_t charLimit = wordLength; |
1623 | 0 | while (charEnd < charLimit) { |
1624 | 0 | // This is normally executed once for each iteration of the outer loop, |
1625 | 0 | // but in unusual cases where the character/glyph association is complex, |
1626 | 0 | // the initial character range might correspond to a non-contiguous |
1627 | 0 | // glyph range with "holes" in it. If so, we will repeat this loop to |
1628 | 0 | // extend the character range until we have a contiguous glyph sequence. |
1629 | 0 | charEnd += 1; |
1630 | 0 | while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) { |
1631 | 0 | charEnd += 1; |
1632 | 0 | } |
1633 | 0 |
|
1634 | 0 | // find the maximum glyph index covered by the clump so far |
1635 | 0 | for (int32_t i = charStart; i < charEnd; ++i) { |
1636 | 0 | if (charToGlyph[i] != NO_GLYPH) { |
1637 | 0 | glyphEnd = std::max(glyphEnd, charToGlyph[i] + 1); |
1638 | 0 | // update extent of glyph range |
1639 | 0 | } |
1640 | 0 | } |
1641 | 0 |
|
1642 | 0 | if (glyphEnd == glyphStart + 1) { |
1643 | 0 | // for the common case of a single-glyph clump, |
1644 | 0 | // we can skip the following checks |
1645 | 0 | break; |
1646 | 0 | } |
1647 | 0 | |
1648 | 0 | if (glyphEnd == glyphStart) { |
1649 | 0 | // no glyphs, try to extend the clump |
1650 | 0 | continue; |
1651 | 0 | } |
1652 | 0 | |
1653 | 0 | // check whether all glyphs in the range are associated with the characters |
1654 | 0 | // in our clump; if not, we have a discontinuous range, and should extend it |
1655 | 0 | // unless we've reached the end of the text |
1656 | 0 | bool allGlyphsAreWithinCluster = true; |
1657 | 0 | for (int32_t i = glyphStart; i < glyphEnd; ++i) { |
1658 | 0 | int32_t glyphCharIndex = ginfo[i].cluster; |
1659 | 0 | if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) { |
1660 | 0 | allGlyphsAreWithinCluster = false; |
1661 | 0 | break; |
1662 | 0 | } |
1663 | 0 | } |
1664 | 0 | if (allGlyphsAreWithinCluster) { |
1665 | 0 | break; |
1666 | 0 | } |
1667 | 0 | } |
1668 | 0 |
|
1669 | 0 | NS_ASSERTION(glyphStart < glyphEnd, |
1670 | 0 | "character/glyph clump contains no glyphs!"); |
1671 | 0 | NS_ASSERTION(charStart != charEnd, |
1672 | 0 | "character/glyph clump contains no characters!"); |
1673 | 0 |
|
1674 | 0 | // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd; |
1675 | 0 | // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature), |
1676 | 0 | // and endCharIndex to the limit (position beyond the last char), |
1677 | 0 | // adjusting for the offset of the stringRange relative to the textRun. |
1678 | 0 | int32_t baseCharIndex, endCharIndex; |
1679 | 0 | while (charEnd < int32_t(wordLength) && charToGlyph[charEnd] == NO_GLYPH) |
1680 | 0 | charEnd++; |
1681 | 0 | baseCharIndex = charStart; |
1682 | 0 | endCharIndex = charEnd; |
1683 | 0 |
|
1684 | 0 | // Then we check if the clump falls outside our actual string range; |
1685 | 0 | // if so, just go to the next. |
1686 | 0 | if (baseCharIndex >= int32_t(wordLength)) { |
1687 | 0 | glyphStart = glyphEnd; |
1688 | 0 | charStart = charEnd; |
1689 | 0 | continue; |
1690 | 0 | } |
1691 | 0 | // Ensure we won't try to go beyond the valid length of the textRun's text |
1692 | 0 | endCharIndex = std::min<int32_t>(endCharIndex, wordLength); |
1693 | 0 |
|
1694 | 0 | // Now we're ready to set the glyph info in the textRun |
1695 | 0 | int32_t glyphsInClump = glyphEnd - glyphStart; |
1696 | 0 |
|
1697 | 0 | // Check for default-ignorable char that didn't get filtered, combined, |
1698 | 0 | // etc by the shaping process, and remove from the run. |
1699 | 0 | // (This may be done within harfbuzz eventually.) |
1700 | 0 | if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex && |
1701 | 0 | aShapedText->FilterIfIgnorable(aOffset + baseCharIndex, |
1702 | 0 | aText[baseCharIndex])) { |
1703 | 0 | glyphStart = glyphEnd; |
1704 | 0 | charStart = charEnd; |
1705 | 0 | continue; |
1706 | 0 | } |
1707 | 0 | |
1708 | 0 | // HarfBuzz gives us physical x- and y-coordinates, but we will store |
1709 | 0 | // them as logical inline- and block-direction values in the textrun. |
1710 | 0 | |
1711 | 0 | hb_position_t i_offset, i_advance; // inline-direction offset/advance |
1712 | 0 | hb_position_t b_offset, b_advance; // block-direction offset/advance |
1713 | 0 | if (aVertical) { |
1714 | 0 | // our coordinate directions are the opposite of harfbuzz's |
1715 | 0 | // when doing top-to-bottom shaping |
1716 | 0 | i_offset = -posInfo[glyphStart].y_offset; |
1717 | 0 | i_advance = -posInfo[glyphStart].y_advance; |
1718 | 0 | b_offset = -posInfo[glyphStart].x_offset; |
1719 | 0 | b_advance = -posInfo[glyphStart].x_advance; |
1720 | 0 | } else { |
1721 | 0 | i_offset = posInfo[glyphStart].x_offset; |
1722 | 0 | i_advance = posInfo[glyphStart].x_advance; |
1723 | 0 | b_offset = posInfo[glyphStart].y_offset; |
1724 | 0 | b_advance = posInfo[glyphStart].y_advance; |
1725 | 0 | } |
1726 | 0 |
|
1727 | 0 | nscoord iOffset, advance; |
1728 | 0 | if (roundI) { |
1729 | 0 | iOffset = |
1730 | 0 | appUnitsPerDevUnit * FixedToIntRound(i_offset + residual); |
1731 | 0 | // Desired distance from the base glyph to the next reference point. |
1732 | 0 | hb_position_t width = i_advance - i_offset; |
1733 | 0 | int intWidth = FixedToIntRound(width); |
1734 | 0 | residual = width - FloatToFixed(intWidth); |
1735 | 0 | advance = appUnitsPerDevUnit * intWidth + iOffset; |
1736 | 0 | } else { |
1737 | 0 | iOffset = floor(hb2appUnits * i_offset + 0.5); |
1738 | 0 | advance = floor(hb2appUnits * i_advance + 0.5); |
1739 | 0 | } |
1740 | 0 | // Check if it's a simple one-to-one mapping |
1741 | 0 | if (glyphsInClump == 1 && |
1742 | 0 | CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) && |
1743 | 0 | CompressedGlyph::IsSimpleAdvance(advance) && |
1744 | 0 | charGlyphs[baseCharIndex].IsClusterStart() && |
1745 | 0 | iOffset == 0 && b_offset == 0 && |
1746 | 0 | b_advance == 0 && bPos == 0) |
1747 | 0 | { |
1748 | 0 | charGlyphs[baseCharIndex].SetSimpleGlyph(advance, |
1749 | 0 | ginfo[glyphStart].codepoint); |
1750 | 0 | } else { |
1751 | 0 | // Collect all glyphs in a list to be assigned to the first char; |
1752 | 0 | // there must be at least one in the clump, and we already measured |
1753 | 0 | // its advance, hence the placement of the loop-exit test and the |
1754 | 0 | // measurement of the next glyph. |
1755 | 0 | while (1) { |
1756 | 0 | gfxTextRun::DetailedGlyph* details = |
1757 | 0 | detailedGlyphs.AppendElement(); |
1758 | 0 | details->mGlyphID = ginfo[glyphStart].codepoint; |
1759 | 0 |
|
1760 | 0 | details->mAdvance = advance; |
1761 | 0 |
|
1762 | 0 | if (aVertical) { |
1763 | 0 | details->mOffset.x = bPos - |
1764 | 0 | (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset) |
1765 | 0 | : floor(hb2appUnits * b_offset + 0.5)); |
1766 | 0 | details->mOffset.y = iOffset; |
1767 | 0 | } else { |
1768 | 0 | details->mOffset.x = iOffset; |
1769 | 0 | details->mOffset.y = bPos - |
1770 | 0 | (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset) |
1771 | 0 | : floor(hb2appUnits * b_offset + 0.5)); |
1772 | 0 | } |
1773 | 0 |
|
1774 | 0 | if (b_advance != 0) { |
1775 | 0 | bPos -= |
1776 | 0 | roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance) |
1777 | 0 | : floor(hb2appUnits * b_advance + 0.5); |
1778 | 0 | } |
1779 | 0 | if (++glyphStart >= glyphEnd) { |
1780 | 0 | break; |
1781 | 0 | } |
1782 | 0 | |
1783 | 0 | if (aVertical) { |
1784 | 0 | i_offset = -posInfo[glyphStart].y_offset; |
1785 | 0 | i_advance = -posInfo[glyphStart].y_advance; |
1786 | 0 | b_offset = -posInfo[glyphStart].x_offset; |
1787 | 0 | b_advance = -posInfo[glyphStart].x_advance; |
1788 | 0 | } else { |
1789 | 0 | i_offset = posInfo[glyphStart].x_offset; |
1790 | 0 | i_advance = posInfo[glyphStart].x_advance; |
1791 | 0 | b_offset = posInfo[glyphStart].y_offset; |
1792 | 0 | b_advance = posInfo[glyphStart].y_advance; |
1793 | 0 | } |
1794 | 0 |
|
1795 | 0 | if (roundI) { |
1796 | 0 | iOffset = appUnitsPerDevUnit * |
1797 | 0 | FixedToIntRound(i_offset + residual); |
1798 | 0 | // Desired distance to the next reference point. The |
1799 | 0 | // residual is considered here, and includes the residual |
1800 | 0 | // from the base glyph offset and subsequent advances, so |
1801 | 0 | // that the distance from the base glyph is optimized |
1802 | 0 | // rather than the distance from combining marks. |
1803 | 0 | i_advance += residual; |
1804 | 0 | int intAdvance = FixedToIntRound(i_advance); |
1805 | 0 | residual = i_advance - FloatToFixed(intAdvance); |
1806 | 0 | advance = appUnitsPerDevUnit * intAdvance; |
1807 | 0 | } else { |
1808 | 0 | iOffset = floor(hb2appUnits * i_offset + 0.5); |
1809 | 0 | advance = floor(hb2appUnits * i_advance + 0.5); |
1810 | 0 | } |
1811 | 0 | } |
1812 | 0 |
|
1813 | 0 | bool isClusterStart = charGlyphs[baseCharIndex].IsClusterStart(); |
1814 | 0 | aShapedText->SetGlyphs(aOffset + baseCharIndex, |
1815 | 0 | CompressedGlyph::MakeComplex(isClusterStart, |
1816 | 0 | true, |
1817 | 0 | detailedGlyphs.Length()), |
1818 | 0 | detailedGlyphs.Elements()); |
1819 | 0 |
|
1820 | 0 | detailedGlyphs.Clear(); |
1821 | 0 | } |
1822 | 0 |
|
1823 | 0 | // the rest of the chars in the group are ligature continuations, |
1824 | 0 | // no associated glyphs |
1825 | 0 | while (++baseCharIndex != endCharIndex && |
1826 | 0 | baseCharIndex < int32_t(wordLength)) { |
1827 | 0 | CompressedGlyph &g = charGlyphs[baseCharIndex]; |
1828 | 0 | NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph"); |
1829 | 0 | g.SetComplex(g.IsClusterStart(), false, 0); |
1830 | 0 | } |
1831 | 0 |
|
1832 | 0 | glyphStart = glyphEnd; |
1833 | 0 | charStart = charEnd; |
1834 | 0 | } |
1835 | 0 |
|
1836 | 0 | return NS_OK; |
1837 | 0 | } |