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