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