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 &nbsp;, 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