Coverage Report

Created: 2018-09-25 14:53

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