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 , 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