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