1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2009-2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonathan Kew <jfkthame@gmail.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "prtypes.h"
40 : #include "nsAlgorithm.h"
41 : #include "prmem.h"
42 : #include "nsString.h"
43 : #include "nsBidiUtils.h"
44 : #include "nsMathUtils.h"
45 :
46 : #include "gfxTypes.h"
47 :
48 : #include "gfxContext.h"
49 : #include "gfxPlatform.h"
50 : #include "gfxHarfBuzzShaper.h"
51 : #include "gfxFontUtils.h"
52 : #include "nsUnicodeProperties.h"
53 : #include "nsUnicodeScriptCodes.h"
54 : #include "nsUnicodeNormalizer.h"
55 :
56 : #include "harfbuzz/hb-unicode.h"
57 : #include "harfbuzz/hb-ot.h"
58 :
59 : #include "cairo.h"
60 :
61 : #include "nsCRT.h"
62 :
63 : #if defined(XP_WIN)
64 : #include "gfxWindowsPlatform.h"
65 : #endif
66 :
67 : #define FloatToFixed(f) (65536 * (f))
68 : #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
69 : // Right shifts of negative (signed) integers are undefined, as are overflows
70 : // when converting unsigned to negative signed integers.
71 : // (If speed were an issue we could make some 2's complement assumptions.)
72 : #define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \
73 : : -((32767 - (f)) >> 16))
74 :
75 : using namespace mozilla; // for AutoSwap_* types
76 : using namespace mozilla::unicode; // for Unicode property lookup
77 :
78 : /*
79 : * Creation and destruction; on deletion, release any font tables we're holding
80 : */
81 :
82 0 : gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
83 : : gfxFontShaper(aFont),
84 : mHBFace(nsnull),
85 : mKernTable(nsnull),
86 : mHmtxTable(nsnull),
87 : mNumLongMetrics(0),
88 : mCmapTable(nsnull),
89 : mCmapFormat(-1),
90 : mSubtableOffset(0),
91 : mUVSTableOffset(0),
92 0 : mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
93 0 : mUseFontGlyphWidths(false)
94 : {
95 0 : }
96 :
97 0 : gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
98 : {
99 0 : hb_blob_destroy(mCmapTable);
100 0 : hb_blob_destroy(mHmtxTable);
101 0 : hb_blob_destroy(mKernTable);
102 0 : hb_face_destroy(mHBFace);
103 0 : }
104 :
105 : /*
106 : * HarfBuzz callback access to font table data
107 : */
108 :
109 : // callback for HarfBuzz to get a font table (in hb_blob_t form)
110 : // from the shaper (passed as aUserData)
111 : static hb_blob_t *
112 0 : HBGetTable(hb_face_t *face, hb_tag_t aTag, void *aUserData)
113 : {
114 0 : gfxHarfBuzzShaper *shaper = static_cast<gfxHarfBuzzShaper*>(aUserData);
115 0 : gfxFont *font = shaper->GetFont();
116 :
117 : // bug 589682 - ignore the GDEF table in buggy fonts (applies to
118 : // Italic and BoldItalic faces of Times New Roman)
119 0 : if (aTag == TRUETYPE_TAG('G','D','E','F') &&
120 0 : font->GetFontEntry()->IgnoreGDEF()) {
121 0 : return nsnull;
122 : }
123 :
124 : // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
125 : // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
126 0 : if (aTag == TRUETYPE_TAG('G','S','U','B') &&
127 0 : font->GetFontEntry()->IgnoreGSUB()) {
128 0 : return nsnull;
129 : }
130 :
131 0 : return font->GetFontTable(aTag);
132 : }
133 :
134 : /*
135 : * HarfBuzz font callback functions; font_data is a ptr to a
136 : * FontCallbackData struct
137 : */
138 :
139 : struct FontCallbackData {
140 0 : FontCallbackData(gfxHarfBuzzShaper *aShaper, gfxContext *aContext)
141 0 : : mShaper(aShaper), mContext(aContext)
142 0 : { }
143 :
144 : gfxHarfBuzzShaper *mShaper;
145 : gfxContext *mContext;
146 : };
147 :
148 : #define UNICODE_BMP_LIMIT 0x10000
149 :
150 : hb_codepoint_t
151 0 : gfxHarfBuzzShaper::GetGlyph(hb_codepoint_t unicode,
152 : hb_codepoint_t variation_selector) const
153 : {
154 0 : if (mUseFontGetGlyph) {
155 0 : return mFont->GetGlyph(unicode, variation_selector);
156 : }
157 :
158 : // we only instantiate a harfbuzz shaper if there's a cmap available
159 0 : NS_ASSERTION(mFont->GetFontEntry()->HasCmapTable(),
160 : "we cannot be using this font!");
161 :
162 0 : NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
163 : "cmap data not correctly set up, expect disaster");
164 :
165 0 : const PRUint8* data = (const PRUint8*)hb_blob_get_data(mCmapTable, nsnull);
166 :
167 : hb_codepoint_t gid;
168 0 : switch (mCmapFormat) {
169 : case 4:
170 : gid = unicode < UNICODE_BMP_LIMIT ?
171 0 : gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset, unicode) : 0;
172 0 : break;
173 : case 12:
174 0 : gid = gfxFontUtils::MapCharToGlyphFormat12(data + mSubtableOffset, unicode);
175 0 : break;
176 : default:
177 0 : NS_WARNING("unsupported cmap format, glyphs will be missing");
178 0 : gid = 0;
179 0 : break;
180 : }
181 :
182 0 : if (gid && variation_selector && mUVSTableOffset) {
183 : hb_codepoint_t varGID =
184 : gfxFontUtils::MapUVSToGlyphFormat14(data + mUVSTableOffset,
185 0 : unicode, variation_selector);
186 0 : if (varGID) {
187 0 : gid = varGID;
188 : }
189 : // else the variation sequence was not supported, use default mapping
190 : // of the character code alone
191 : }
192 :
193 0 : return gid;
194 : }
195 :
196 : static hb_bool_t
197 0 : HBGetGlyph(hb_font_t *font, void *font_data,
198 : hb_codepoint_t unicode, hb_codepoint_t variation_selector,
199 : hb_codepoint_t *glyph,
200 : void *user_data)
201 : {
202 : const FontCallbackData *fcd =
203 0 : static_cast<const FontCallbackData*>(font_data);
204 0 : *glyph = fcd->mShaper->GetGlyph(unicode, variation_selector);
205 0 : return true;
206 : }
207 :
208 : struct HMetricsHeader {
209 : AutoSwap_PRUint32 tableVersionNumber;
210 : AutoSwap_PRInt16 ascender;
211 : AutoSwap_PRInt16 descender;
212 : AutoSwap_PRInt16 lineGap;
213 : AutoSwap_PRUint16 advanceWidthMax;
214 : AutoSwap_PRInt16 minLeftSideBearing;
215 : AutoSwap_PRInt16 minRightSideBearing;
216 : AutoSwap_PRInt16 xMaxExtent;
217 : AutoSwap_PRInt16 caretSlopeRise;
218 : AutoSwap_PRInt16 caretSlopeRun;
219 : AutoSwap_PRInt16 caretOffset;
220 : AutoSwap_PRInt16 reserved[4];
221 : AutoSwap_PRInt16 metricDataFormat;
222 : AutoSwap_PRUint16 numberOfHMetrics;
223 : };
224 :
225 : struct HLongMetric {
226 : AutoSwap_PRUint16 advanceWidth;
227 : AutoSwap_PRInt16 lsb;
228 : };
229 :
230 : struct HMetrics {
231 : HLongMetric metrics[1]; // actually numberOfHMetrics
232 : // the variable-length metrics[] array is immediately followed by:
233 : // AutoSwap_PRUint16 leftSideBearing[];
234 : };
235 :
236 : hb_position_t
237 0 : gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
238 : hb_codepoint_t glyph) const
239 : {
240 0 : if (mUseFontGlyphWidths) {
241 0 : return mFont->GetGlyphWidth(aContext, glyph);
242 : }
243 :
244 : // font did not implement GetHintedGlyphWidth, so get an unhinted value
245 : // directly from the font tables
246 :
247 0 : NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nsnull,
248 : "font is lacking metrics, we shouldn't be here");
249 :
250 0 : if (glyph >= PRUint32(mNumLongMetrics)) {
251 0 : glyph = mNumLongMetrics - 1;
252 : }
253 :
254 : // glyph must be valid now, because we checked during initialization
255 : // that mNumLongMetrics is > 0, and that the hmtx table is large enough
256 : // to contain mNumLongMetrics records
257 : const HMetrics* hmtx =
258 0 : reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nsnull));
259 0 : return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
260 : PRUint16(hmtx->metrics[glyph].advanceWidth));
261 : }
262 :
263 : static hb_position_t
264 0 : HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
265 : hb_codepoint_t glyph, void *user_data)
266 : {
267 : const FontCallbackData *fcd =
268 0 : static_cast<const FontCallbackData*>(font_data);
269 0 : return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
270 : }
271 :
272 : static hb_bool_t
273 0 : HBGetContourPoint(hb_font_t *font, void *font_data,
274 : unsigned int point_index, hb_codepoint_t glyph,
275 : hb_position_t *x, hb_position_t *y,
276 : void *user_data)
277 : {
278 : /* not yet implemented - no support for used of hinted contour points
279 : to fine-tune anchor positions in GPOS AnchorFormat2 */
280 0 : return false;
281 : }
282 :
283 : struct KernHeaderFmt0 {
284 : AutoSwap_PRUint16 nPairs;
285 : AutoSwap_PRUint16 searchRange;
286 : AutoSwap_PRUint16 entrySelector;
287 : AutoSwap_PRUint16 rangeShift;
288 : };
289 :
290 : struct KernPair {
291 : AutoSwap_PRUint16 left;
292 : AutoSwap_PRUint16 right;
293 : AutoSwap_PRInt16 value;
294 : };
295 :
296 : // Find a kern pair in a Format 0 subtable.
297 : // The aSubtable parameter points to the subtable itself, NOT its header,
298 : // as the header structure differs between Windows and Mac (v0 and v1.0)
299 : // versions of the 'kern' table.
300 : // aSubtableLen is the length of the subtable EXCLUDING its header.
301 : // If the pair <aFirstGlyph,aSecondGlyph> is found, the kerning value is
302 : // added to aValue, so that multiple subtables can accumulate a total
303 : // kerning value for a given pair.
304 : static void
305 0 : GetKernValueFmt0(const void* aSubtable,
306 : PRUint32 aSubtableLen,
307 : PRUint16 aFirstGlyph,
308 : PRUint16 aSecondGlyph,
309 : PRInt32& aValue,
310 : bool aIsOverride = false,
311 : bool aIsMinimum = false)
312 : {
313 : const KernHeaderFmt0* hdr =
314 0 : reinterpret_cast<const KernHeaderFmt0*>(aSubtable);
315 :
316 0 : const KernPair *lo = reinterpret_cast<const KernPair*>(hdr + 1);
317 0 : const KernPair *hi = lo + PRUint16(hdr->nPairs);
318 0 : const KernPair *limit = hi;
319 :
320 0 : if (reinterpret_cast<const char*>(aSubtable) + aSubtableLen <
321 : reinterpret_cast<const char*>(hi)) {
322 : // subtable is not large enough to contain the claimed number
323 : // of kern pairs, so just ignore it
324 0 : return;
325 : }
326 :
327 : #define KERN_PAIR_KEY(l,r) (PRUint32((PRUint16(l) << 16) + PRUint16(r)))
328 :
329 0 : PRUint32 key = KERN_PAIR_KEY(aFirstGlyph, aSecondGlyph);
330 0 : while (lo < hi) {
331 0 : const KernPair *mid = lo + (hi - lo) / 2;
332 0 : if (KERN_PAIR_KEY(mid->left, mid->right) < key) {
333 0 : lo = mid + 1;
334 : } else {
335 0 : hi = mid;
336 : }
337 : }
338 :
339 0 : if (lo < limit && KERN_PAIR_KEY(lo->left, lo->right) == key) {
340 0 : if (aIsOverride) {
341 0 : aValue = PRInt16(lo->value);
342 0 : } else if (aIsMinimum) {
343 0 : aValue = NS_MAX(aValue, PRInt32(lo->value));
344 : } else {
345 0 : aValue += PRInt16(lo->value);
346 : }
347 : }
348 : }
349 :
350 : // Get kerning value from Apple (version 1.0) kern table,
351 : // subtable format 2 (simple N x M array of kerning values)
352 :
353 : // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
354 : // for details of version 1.0 format 2 subtable.
355 :
356 : struct KernHeaderVersion1Fmt2 {
357 : KernTableSubtableHeaderVersion1 header;
358 : AutoSwap_PRUint16 rowWidth;
359 : AutoSwap_PRUint16 leftOffsetTable;
360 : AutoSwap_PRUint16 rightOffsetTable;
361 : AutoSwap_PRUint16 array;
362 : };
363 :
364 : struct KernClassTableHdr {
365 : AutoSwap_PRUint16 firstGlyph;
366 : AutoSwap_PRUint16 nGlyphs;
367 : AutoSwap_PRUint16 offsets[1]; // actually an array of nGlyphs entries
368 : };
369 :
370 : static PRInt16
371 0 : GetKernValueVersion1Fmt2(const void* aSubtable,
372 : PRUint32 aSubtableLen,
373 : PRUint16 aFirstGlyph,
374 : PRUint16 aSecondGlyph)
375 : {
376 0 : if (aSubtableLen < sizeof(KernHeaderVersion1Fmt2)) {
377 0 : return 0;
378 : }
379 :
380 0 : const char* base = reinterpret_cast<const char*>(aSubtable);
381 0 : const char* subtableEnd = base + aSubtableLen;
382 :
383 : const KernHeaderVersion1Fmt2* h =
384 0 : reinterpret_cast<const KernHeaderVersion1Fmt2*>(aSubtable);
385 0 : PRUint32 offset = h->array;
386 :
387 : const KernClassTableHdr* leftClassTable =
388 : reinterpret_cast<const KernClassTableHdr*>(base +
389 0 : PRUint16(h->leftOffsetTable));
390 0 : if (reinterpret_cast<const char*>(leftClassTable) +
391 0 : sizeof(KernClassTableHdr) > subtableEnd) {
392 0 : return 0;
393 : }
394 0 : if (aFirstGlyph >= PRUint16(leftClassTable->firstGlyph)) {
395 0 : aFirstGlyph -= PRUint16(leftClassTable->firstGlyph);
396 0 : if (aFirstGlyph < PRUint16(leftClassTable->nGlyphs)) {
397 0 : if (reinterpret_cast<const char*>(leftClassTable) +
398 : sizeof(KernClassTableHdr) +
399 0 : aFirstGlyph * sizeof(PRUint16) >= subtableEnd) {
400 0 : return 0;
401 : }
402 0 : offset = PRUint16(leftClassTable->offsets[aFirstGlyph]);
403 : }
404 : }
405 :
406 : const KernClassTableHdr* rightClassTable =
407 : reinterpret_cast<const KernClassTableHdr*>(base +
408 0 : PRUint16(h->rightOffsetTable));
409 0 : if (reinterpret_cast<const char*>(rightClassTable) +
410 0 : sizeof(KernClassTableHdr) > subtableEnd) {
411 0 : return 0;
412 : }
413 0 : if (aSecondGlyph >= PRUint16(rightClassTable->firstGlyph)) {
414 0 : aSecondGlyph -= PRUint16(rightClassTable->firstGlyph);
415 0 : if (aSecondGlyph < PRUint16(rightClassTable->nGlyphs)) {
416 0 : if (reinterpret_cast<const char*>(rightClassTable) +
417 : sizeof(KernClassTableHdr) +
418 0 : aSecondGlyph * sizeof(PRUint16) >= subtableEnd) {
419 0 : return 0;
420 : }
421 0 : offset += PRUint16(rightClassTable->offsets[aSecondGlyph]);
422 : }
423 : }
424 :
425 : const AutoSwap_PRInt16* pval =
426 0 : reinterpret_cast<const AutoSwap_PRInt16*>(base + offset);
427 0 : if (reinterpret_cast<const char*>(pval + 1) >= subtableEnd) {
428 0 : return 0;
429 : }
430 0 : return *pval;
431 : }
432 :
433 : // Get kerning value from Apple (version 1.0) kern table,
434 : // subtable format 3 (simple N x M array of kerning values)
435 :
436 : // See http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html
437 : // for details of version 1.0 format 3 subtable.
438 :
439 : struct KernHeaderVersion1Fmt3 {
440 : KernTableSubtableHeaderVersion1 header;
441 : AutoSwap_PRUint16 glyphCount;
442 : PRUint8 kernValueCount;
443 : PRUint8 leftClassCount;
444 : PRUint8 rightClassCount;
445 : PRUint8 flags;
446 : };
447 :
448 : static PRInt16
449 0 : GetKernValueVersion1Fmt3(const void* aSubtable,
450 : PRUint32 aSubtableLen,
451 : PRUint16 aFirstGlyph,
452 : PRUint16 aSecondGlyph)
453 : {
454 : // check that we can safely read the header fields
455 0 : if (aSubtableLen < sizeof(KernHeaderVersion1Fmt3)) {
456 0 : return 0;
457 : }
458 :
459 : const KernHeaderVersion1Fmt3* hdr =
460 0 : reinterpret_cast<const KernHeaderVersion1Fmt3*>(aSubtable);
461 0 : if (hdr->flags != 0) {
462 0 : return 0;
463 : }
464 :
465 0 : PRUint16 glyphCount = hdr->glyphCount;
466 :
467 : // check that table is large enough for the arrays
468 0 : if (sizeof(KernHeaderVersion1Fmt3) +
469 : hdr->kernValueCount * sizeof(PRInt16) +
470 : glyphCount + glyphCount +
471 : hdr->leftClassCount * hdr->rightClassCount > aSubtableLen) {
472 0 : return 0;
473 : }
474 :
475 0 : if (aFirstGlyph >= glyphCount || aSecondGlyph >= glyphCount) {
476 : // glyphs are out of range for the class tables
477 0 : return 0;
478 : }
479 :
480 : // get pointers to the four arrays within the subtable
481 : const AutoSwap_PRInt16* kernValue =
482 0 : reinterpret_cast<const AutoSwap_PRInt16*>(hdr + 1);
483 : const PRUint8* leftClass =
484 0 : reinterpret_cast<const PRUint8*>(kernValue + hdr->kernValueCount);
485 0 : const PRUint8* rightClass = leftClass + glyphCount;
486 0 : const PRUint8* kernIndex = rightClass + glyphCount;
487 :
488 0 : PRUint8 lc = leftClass[aFirstGlyph];
489 0 : PRUint8 rc = rightClass[aSecondGlyph];
490 0 : if (lc >= hdr->leftClassCount || rc >= hdr->rightClassCount) {
491 0 : return 0;
492 : }
493 :
494 0 : PRUint8 ki = kernIndex[leftClass[aFirstGlyph] * hdr->rightClassCount +
495 0 : rightClass[aSecondGlyph]];
496 0 : if (ki >= hdr->kernValueCount) {
497 0 : return 0;
498 : }
499 :
500 0 : return kernValue[ki];
501 : }
502 :
503 : #define KERN0_COVERAGE_HORIZONTAL 0x0001
504 : #define KERN0_COVERAGE_MINIMUM 0x0002
505 : #define KERN0_COVERAGE_CROSS_STREAM 0x0004
506 : #define KERN0_COVERAGE_OVERRIDE 0x0008
507 : #define KERN0_COVERAGE_RESERVED 0x00F0
508 :
509 : #define KERN1_COVERAGE_VERTICAL 0x8000
510 : #define KERN1_COVERAGE_CROSS_STREAM 0x4000
511 : #define KERN1_COVERAGE_VARIATION 0x2000
512 : #define KERN1_COVERAGE_RESERVED 0x1F00
513 :
514 : hb_position_t
515 0 : gfxHarfBuzzShaper::GetHKerning(PRUint16 aFirstGlyph,
516 : PRUint16 aSecondGlyph) const
517 : {
518 : // We want to ignore any kern pairs involving <space>, because we are
519 : // handling words in isolation, the only space characters seen here are
520 : // the ones artificially added by the textRun code.
521 0 : PRUint32 spaceGlyph = mFont->GetSpaceGlyph();
522 0 : if (aFirstGlyph == spaceGlyph || aSecondGlyph == spaceGlyph) {
523 0 : return 0;
524 : }
525 :
526 0 : if (!mKernTable) {
527 0 : mKernTable = mFont->GetFontTable(TRUETYPE_TAG('k','e','r','n'));
528 0 : if (!mKernTable) {
529 0 : mKernTable = hb_blob_get_empty();
530 : }
531 : }
532 :
533 : PRUint32 len;
534 0 : const char* base = hb_blob_get_data(mKernTable, &len);
535 0 : if (len < sizeof(KernTableVersion0)) {
536 0 : return 0;
537 : }
538 0 : PRInt32 value = 0;
539 :
540 : // First try to interpret as "version 0" kern table
541 : // (see http://www.microsoft.com/typography/otspec/kern.htm)
542 : const KernTableVersion0* kern0 =
543 0 : reinterpret_cast<const KernTableVersion0*>(base);
544 0 : if (PRUint16(kern0->version) == 0) {
545 0 : PRUint16 nTables = kern0->nTables;
546 0 : PRUint32 offs = sizeof(KernTableVersion0);
547 0 : for (PRUint16 i = 0; i < nTables; ++i) {
548 0 : if (offs + sizeof(KernTableSubtableHeaderVersion0) > len) {
549 0 : break;
550 : }
551 : const KernTableSubtableHeaderVersion0* st0 =
552 : reinterpret_cast<const KernTableSubtableHeaderVersion0*>
553 0 : (base + offs);
554 0 : PRUint16 subtableLen = PRUint16(st0->length);
555 0 : if (offs + subtableLen > len) {
556 0 : break;
557 : }
558 0 : offs += subtableLen;
559 0 : PRUint16 coverage = st0->coverage;
560 0 : if (!(coverage & KERN0_COVERAGE_HORIZONTAL)) {
561 : // we only care about horizontal kerning (for now)
562 0 : continue;
563 : }
564 0 : if (coverage &
565 : (KERN0_COVERAGE_CROSS_STREAM | KERN0_COVERAGE_RESERVED)) {
566 : // we don't support cross-stream kerning, and
567 : // reserved bits should be zero;
568 : // ignore the subtable if not
569 0 : continue;
570 : }
571 0 : PRUint8 format = (coverage >> 8);
572 0 : switch (format) {
573 : case 0:
574 0 : GetKernValueFmt0(st0 + 1, subtableLen - sizeof(*st0),
575 : aFirstGlyph, aSecondGlyph, value,
576 : (coverage & KERN0_COVERAGE_OVERRIDE) != 0,
577 0 : (coverage & KERN0_COVERAGE_MINIMUM) != 0);
578 0 : break;
579 : default:
580 : // TODO: implement support for other formats,
581 : // if they're ever used in practice
582 : #if DEBUG
583 : {
584 : char buf[1024];
585 : sprintf(buf, "unknown kern subtable in %s: "
586 : "ver 0 format %d\n",
587 0 : NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
588 0 : format);
589 0 : NS_WARNING(buf);
590 : }
591 : #endif
592 0 : break;
593 : }
594 : }
595 : } else {
596 : // It wasn't a "version 0" table; check if it is Apple version 1.0
597 : // (see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html)
598 : const KernTableVersion1* kern1 =
599 0 : reinterpret_cast<const KernTableVersion1*>(base);
600 0 : if (PRUint32(kern1->version) == 0x00010000) {
601 0 : PRUint32 nTables = kern1->nTables;
602 0 : PRUint32 offs = sizeof(KernTableVersion1);
603 0 : for (PRUint32 i = 0; i < nTables; ++i) {
604 0 : if (offs + sizeof(KernTableSubtableHeaderVersion1) > len) {
605 0 : break;
606 : }
607 : const KernTableSubtableHeaderVersion1* st1 =
608 : reinterpret_cast<const KernTableSubtableHeaderVersion1*>
609 0 : (base + offs);
610 0 : PRUint32 subtableLen = PRUint32(st1->length);
611 0 : offs += subtableLen;
612 0 : PRUint16 coverage = st1->coverage;
613 0 : if (coverage &
614 : (KERN1_COVERAGE_VERTICAL |
615 : KERN1_COVERAGE_CROSS_STREAM |
616 : KERN1_COVERAGE_VARIATION |
617 : KERN1_COVERAGE_RESERVED)) {
618 : // we only care about horizontal kerning (for now),
619 : // we don't support cross-stream kerning,
620 : // we don't support variations,
621 : // reserved bits should be zero;
622 : // ignore the subtable if not
623 0 : continue;
624 : }
625 0 : PRUint8 format = (coverage & 0xff);
626 0 : switch (format) {
627 : case 0:
628 0 : GetKernValueFmt0(st1 + 1, subtableLen - sizeof(*st1),
629 0 : aFirstGlyph, aSecondGlyph, value);
630 0 : break;
631 : case 2:
632 : value = GetKernValueVersion1Fmt2(st1, subtableLen,
633 0 : aFirstGlyph, aSecondGlyph);
634 0 : break;
635 : case 3:
636 : value = GetKernValueVersion1Fmt3(st1, subtableLen,
637 0 : aFirstGlyph, aSecondGlyph);
638 0 : break;
639 : default:
640 : // TODO: implement support for other formats.
641 : // Note that format 1 cannot be supported here,
642 : // as it requires the full glyph array to run the FSM,
643 : // not just the current glyph pair.
644 : #if DEBUG
645 : {
646 : char buf[1024];
647 : sprintf(buf, "unknown kern subtable in %s: "
648 : "ver 0 format %d\n",
649 0 : NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
650 0 : format);
651 0 : NS_WARNING(buf);
652 : }
653 : #endif
654 0 : break;
655 : }
656 : }
657 : }
658 : }
659 :
660 0 : if (value != 0) {
661 0 : return FloatToFixed(mFont->FUnitsToDevUnitsFactor() * value);
662 : }
663 0 : return 0;
664 : }
665 :
666 : static hb_position_t
667 0 : HBGetHKerning(hb_font_t *font, void *font_data,
668 : hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
669 : void *user_data)
670 : {
671 : const FontCallbackData *fcd =
672 0 : static_cast<const FontCallbackData*>(font_data);
673 0 : return fcd->mShaper->GetHKerning(first_glyph, second_glyph);
674 : }
675 :
676 : /*
677 : * HarfBuzz unicode property callbacks
678 : */
679 :
680 : static hb_codepoint_t
681 0 : HBGetMirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
682 : {
683 0 : return GetMirroredChar(aCh);
684 : }
685 :
686 : static hb_unicode_general_category_t
687 0 : HBGetGeneralCategory(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
688 : {
689 0 : return hb_unicode_general_category_t(GetGeneralCategory(aCh));
690 : }
691 :
692 : static hb_script_t
693 0 : HBGetScript(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
694 : {
695 : return hb_script_t(GetScriptTagForCode
696 0 : (GetScriptCode(aCh)));
697 : }
698 :
699 : static unsigned int
700 0 : HBGetCombiningClass(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
701 : {
702 0 : return GetCombiningClass(aCh);
703 : }
704 :
705 : static unsigned int
706 0 : HBGetEastAsianWidth(hb_unicode_funcs_t *ufuncs, hb_codepoint_t aCh, void *user_data)
707 : {
708 0 : return GetEastAsianWidth(aCh);
709 : }
710 :
711 : // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
712 : // note that some letters do not have a dagesh presForm encoded
713 : static const PRUnichar sDageshForms[0x05EA - 0x05D0 + 1] = {
714 : 0xFB30, // ALEF
715 : 0xFB31, // BET
716 : 0xFB32, // GIMEL
717 : 0xFB33, // DALET
718 : 0xFB34, // HE
719 : 0xFB35, // VAV
720 : 0xFB36, // ZAYIN
721 : 0, // HET
722 : 0xFB38, // TET
723 : 0xFB39, // YOD
724 : 0xFB3A, // FINAL KAF
725 : 0xFB3B, // KAF
726 : 0xFB3C, // LAMED
727 : 0, // FINAL MEM
728 : 0xFB3E, // MEM
729 : 0, // FINAL NUN
730 : 0xFB40, // NUN
731 : 0xFB41, // SAMEKH
732 : 0, // AYIN
733 : 0xFB43, // FINAL PE
734 : 0xFB44, // PE
735 : 0, // FINAL TSADI
736 : 0xFB46, // TSADI
737 : 0xFB47, // QOF
738 : 0xFB48, // RESH
739 : 0xFB49, // SHIN
740 : 0xFB4A // TAV
741 : };
742 :
743 : static hb_bool_t
744 0 : HBUnicodeCompose(hb_unicode_funcs_t *ufuncs,
745 : hb_codepoint_t a,
746 : hb_codepoint_t b,
747 : hb_codepoint_t *ab,
748 : void *user_data)
749 : {
750 0 : hb_bool_t found = nsUnicodeNormalizer::Compose(a, b, ab);
751 :
752 0 : if (!found && (b & 0x1fff80) == 0x0580) {
753 : // special-case Hebrew presentation forms that are excluded from
754 : // standard normalization, but wanted for old fonts
755 0 : switch (b) {
756 : case 0x05B4: // HIRIQ
757 0 : if (a == 0x05D9) { // YOD
758 0 : *ab = 0xFB1D;
759 0 : found = true;
760 : }
761 0 : break;
762 : case 0x05B7: // patah
763 0 : if (a == 0x05F2) { // YIDDISH YOD YOD
764 0 : *ab = 0xFB1F;
765 0 : found = true;
766 0 : } else if (a == 0x05D0) { // ALEF
767 0 : *ab = 0xFB2E;
768 0 : found = true;
769 : }
770 0 : break;
771 : case 0x05B8: // QAMATS
772 0 : if (a == 0x05D0) { // ALEF
773 0 : *ab = 0xFB2F;
774 0 : found = true;
775 : }
776 0 : break;
777 : case 0x05B9: // HOLAM
778 0 : if (a == 0x05D5) { // VAV
779 0 : *ab = 0xFB4B;
780 0 : found = true;
781 : }
782 0 : break;
783 : case 0x05BC: // DAGESH
784 0 : if (a >= 0x05D0 && a <= 0x05EA) {
785 0 : *ab = sDageshForms[a - 0x05D0];
786 0 : found = (*ab != 0);
787 0 : } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
788 0 : *ab = 0xFB2C;
789 0 : found = true;
790 0 : } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
791 0 : *ab = 0xFB2D;
792 0 : found = true;
793 : }
794 0 : break;
795 : case 0x05BF: // RAFE
796 0 : switch (a) {
797 : case 0x05D1: // BET
798 0 : *ab = 0xFB4C;
799 0 : found = true;
800 0 : break;
801 : case 0x05DB: // KAF
802 0 : *ab = 0xFB4D;
803 0 : found = true;
804 0 : break;
805 : case 0x05E4: // PE
806 0 : *ab = 0xFB4E;
807 0 : found = true;
808 0 : break;
809 : }
810 0 : break;
811 : case 0x05C1: // SHIN DOT
812 0 : if (a == 0x05E9) { // SHIN
813 0 : *ab = 0xFB2A;
814 0 : found = true;
815 0 : } else if (a == 0xFB49) { // SHIN WITH DAGESH
816 0 : *ab = 0xFB2C;
817 0 : found = true;
818 : }
819 0 : break;
820 : case 0x05C2: // SIN DOT
821 0 : if (a == 0x05E9) { // SHIN
822 0 : *ab = 0xFB2B;
823 0 : found = true;
824 0 : } else if (a == 0xFB49) { // SHIN WITH DAGESH
825 0 : *ab = 0xFB2D;
826 0 : found = true;
827 : }
828 0 : break;
829 : }
830 : }
831 :
832 0 : return found;
833 : }
834 :
835 : static hb_bool_t
836 0 : HBUnicodeDecompose(hb_unicode_funcs_t *ufuncs,
837 : hb_codepoint_t ab,
838 : hb_codepoint_t *a,
839 : hb_codepoint_t *b,
840 : void *user_data)
841 : {
842 0 : return nsUnicodeNormalizer::DecomposeNonRecursively(ab, a, b);
843 : }
844 :
845 : /*
846 : * gfxFontShaper override to initialize the text run using HarfBuzz
847 : */
848 :
849 : static hb_font_funcs_t * sHBFontFuncs = nsnull;
850 : static hb_unicode_funcs_t * sHBUnicodeFuncs = nsnull;
851 :
852 : bool
853 0 : gfxHarfBuzzShaper::ShapeWord(gfxContext *aContext,
854 : gfxShapedWord *aShapedWord,
855 : const PRUnichar *aText)
856 : {
857 : // some font back-ends require this in order to get proper hinted metrics
858 0 : mFont->SetupCairoFont(aContext);
859 :
860 0 : if (!mHBFace) {
861 :
862 0 : mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
863 :
864 : // set up the harfbuzz face etc the first time we use the font
865 :
866 0 : if (!sHBFontFuncs) {
867 : // static function callback pointers, initialized by the first
868 : // harfbuzz shaper used
869 0 : sHBFontFuncs = hb_font_funcs_create();
870 : hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
871 0 : nsnull, nsnull);
872 : hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
873 : HBGetGlyphHAdvance,
874 0 : nsnull, nsnull);
875 : hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
876 : HBGetContourPoint,
877 0 : nsnull, nsnull);
878 : hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
879 : HBGetHKerning,
880 0 : nsnull, nsnull);
881 :
882 : sHBUnicodeFuncs =
883 0 : hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
884 : hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
885 : HBGetMirroring,
886 0 : nsnull, nsnull);
887 : hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
888 0 : nsnull, nsnull);
889 : hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
890 : HBGetGeneralCategory,
891 0 : nsnull, nsnull);
892 : hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
893 : HBGetCombiningClass,
894 0 : nsnull, nsnull);
895 : hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
896 : HBGetEastAsianWidth,
897 0 : nsnull, nsnull);
898 : hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
899 : HBUnicodeCompose,
900 0 : nsnull, nsnull);
901 : hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
902 : HBUnicodeDecompose,
903 0 : nsnull, nsnull);
904 : }
905 :
906 0 : mHBFace = hb_face_create_for_tables(HBGetTable, this, nsnull);
907 :
908 0 : if (!mUseFontGetGlyph) {
909 : // get the cmap table and find offset to our subtable
910 0 : mCmapTable = mFont->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
911 0 : if (!mCmapTable) {
912 0 : NS_WARNING("failed to load cmap, glyphs will be missing");
913 0 : return false;
914 : }
915 : PRUint32 len;
916 0 : const PRUint8* data = (const PRUint8*)hb_blob_get_data(mCmapTable, &len);
917 : bool symbol;
918 : mCmapFormat = gfxFontUtils::
919 : FindPreferredSubtable(data, len,
920 : &mSubtableOffset, &mUVSTableOffset,
921 0 : &symbol);
922 : }
923 :
924 0 : if (!mUseFontGlyphWidths) {
925 : // if font doesn't implement GetGlyphWidth, we will be reading
926 : // the hmtx table directly;
927 : // read mNumLongMetrics from hhea table without caching its blob,
928 : // and preload/cache the hmtx table
929 : hb_blob_t *hheaTable =
930 0 : mFont->GetFontTable(TRUETYPE_TAG('h','h','e','a'));
931 0 : if (hheaTable) {
932 : PRUint32 len;
933 : const HMetricsHeader* hhea =
934 : reinterpret_cast<const HMetricsHeader*>
935 0 : (hb_blob_get_data(hheaTable, &len));
936 0 : if (len >= sizeof(HMetricsHeader)) {
937 0 : mNumLongMetrics = hhea->numberOfHMetrics;
938 0 : if (mNumLongMetrics > 0 &&
939 0 : PRInt16(hhea->metricDataFormat) == 0) {
940 : // no point reading hmtx if number of entries is zero!
941 : // in that case, we won't be able to use this font
942 : // (this method will return FALSE below if mHmtx is null)
943 : mHmtxTable =
944 0 : mFont->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
945 0 : if (hb_blob_get_length(mHmtxTable) <
946 : mNumLongMetrics * sizeof(HLongMetric)) {
947 : // hmtx table is not large enough for the claimed
948 : // number of entries: invalid, do not use.
949 0 : hb_blob_destroy(mHmtxTable);
950 0 : mHmtxTable = nsnull;
951 : }
952 : }
953 : }
954 : }
955 0 : hb_blob_destroy(hheaTable);
956 : }
957 : }
958 :
959 0 : if ((!mUseFontGetGlyph && mCmapFormat <= 0) ||
960 0 : (!mUseFontGlyphWidths && !mHmtxTable)) {
961 : // unable to shape with this font
962 0 : return false;
963 : }
964 :
965 0 : FontCallbackData fcd(this, aContext);
966 0 : hb_font_t *font = hb_font_create(mHBFace);
967 0 : hb_font_set_funcs(font, sHBFontFuncs, &fcd, nsnull);
968 0 : hb_font_set_ppem(font, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
969 0 : PRUint32 scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
970 0 : hb_font_set_scale(font, scale, scale);
971 :
972 0 : nsAutoTArray<hb_feature_t,20> features;
973 :
974 : // Ligature features are enabled by default in the generic shaper,
975 : // so we explicitly turn them off if necessary (for letter-spacing)
976 0 : if (aShapedWord->DisableLigatures()) {
977 0 : hb_feature_t ligaOff = { HB_TAG('l','i','g','a'), 0, 0, UINT_MAX };
978 0 : hb_feature_t cligOff = { HB_TAG('c','l','i','g'), 0, 0, UINT_MAX };
979 0 : features.AppendElement(ligaOff);
980 0 : features.AppendElement(cligOff);
981 : }
982 :
983 : // css features need to be merged with the existing ones, if any
984 0 : gfxFontEntry *entry = mFont->GetFontEntry();
985 0 : const gfxFontStyle *style = mFont->GetStyle();
986 0 : const nsTArray<gfxFontFeature> *cssFeatures = &style->featureSettings;
987 0 : if (cssFeatures->IsEmpty()) {
988 0 : cssFeatures = &entry->mFeatureSettings;
989 : }
990 0 : for (PRUint32 i = 0; i < cssFeatures->Length(); ++i) {
991 : PRUint32 j;
992 0 : for (j = 0; j < features.Length(); ++j) {
993 0 : if (cssFeatures->ElementAt(i).mTag == features[j].tag) {
994 0 : features[j].value = cssFeatures->ElementAt(i).mValue;
995 0 : break;
996 : }
997 : }
998 0 : if (j == features.Length()) {
999 0 : const gfxFontFeature& f = cssFeatures->ElementAt(i);
1000 0 : hb_feature_t hbf = { f.mTag, f.mValue, 0, UINT_MAX };
1001 0 : features.AppendElement(hbf);
1002 : }
1003 : }
1004 :
1005 0 : bool isRightToLeft = aShapedWord->IsRightToLeft();
1006 0 : hb_buffer_t *buffer = hb_buffer_create();
1007 0 : hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
1008 : hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
1009 0 : HB_DIRECTION_LTR);
1010 : // For unresolved "common" or "inherited" runs, default to Latin for now.
1011 : // (Should we somehow use the language or locale to try and infer
1012 : // a better default?)
1013 0 : PRInt32 scriptCode = aShapedWord->Script();
1014 : hb_script_t scriptTag = (scriptCode <= MOZ_SCRIPT_INHERITED) ?
1015 : HB_SCRIPT_LATIN :
1016 0 : hb_script_t(GetScriptTagForCode(scriptCode));
1017 0 : hb_buffer_set_script(buffer, scriptTag);
1018 :
1019 : hb_language_t language;
1020 0 : if (style->languageOverride) {
1021 0 : language = hb_ot_tag_to_language(style->languageOverride);
1022 0 : } else if (entry->mLanguageOverride) {
1023 0 : language = hb_ot_tag_to_language(entry->mLanguageOverride);
1024 : } else {
1025 0 : nsCString langString;
1026 0 : style->language->ToUTF8String(langString);
1027 : language =
1028 0 : hb_language_from_string(langString.get(), langString.Length());
1029 : }
1030 0 : hb_buffer_set_language(buffer, language);
1031 :
1032 0 : PRUint32 length = aShapedWord->Length();
1033 : hb_buffer_add_utf16(buffer,
1034 : reinterpret_cast<const uint16_t*>(aText),
1035 0 : length, 0, length);
1036 :
1037 0 : hb_shape(font, buffer, features.Elements(), features.Length());
1038 :
1039 0 : if (isRightToLeft) {
1040 0 : hb_buffer_reverse(buffer);
1041 : }
1042 :
1043 0 : nsresult rv = SetGlyphsFromRun(aContext, aShapedWord, buffer);
1044 :
1045 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
1046 0 : hb_buffer_destroy(buffer);
1047 0 : hb_font_destroy(font);
1048 :
1049 0 : return NS_SUCCEEDED(rv);
1050 : }
1051 :
1052 : /**
1053 : * Work out whether cairo will snap inter-glyph spacing to pixels.
1054 : *
1055 : * Layout does not align text to pixel boundaries, so, with font drawing
1056 : * backends that snap glyph positions to pixels, it is important that
1057 : * inter-glyph spacing within words is always an integer number of pixels.
1058 : * This ensures that the drawing backend snaps all of the word's glyphs in the
1059 : * same direction and so inter-glyph spacing remains the same.
1060 : */
1061 : static void
1062 0 : GetRoundOffsetsToPixels(gfxContext *aContext,
1063 : bool *aRoundX, bool *aRoundY)
1064 : {
1065 0 : *aRoundX = false;
1066 : // Could do something fancy here for ScaleFactors of
1067 : // AxisAlignedTransforms, but we leave things simple.
1068 : // Not much point rounding if a matrix will mess things up anyway.
1069 0 : if (aContext->CurrentMatrix().HasNonTranslation()) {
1070 0 : *aRoundY = false;
1071 0 : return;
1072 : }
1073 :
1074 : // All raster backends snap glyphs to pixels vertically.
1075 : // Print backends set CAIRO_HINT_METRICS_OFF.
1076 0 : *aRoundY = true;
1077 :
1078 0 : cairo_t *cr = aContext->GetCairo();
1079 0 : cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
1080 : // Sometimes hint metrics gets set for us, most notably for printing.
1081 0 : cairo_font_options_t *font_options = cairo_font_options_create();
1082 0 : cairo_scaled_font_get_font_options(scaled_font, font_options);
1083 : cairo_hint_metrics_t hint_metrics =
1084 0 : cairo_font_options_get_hint_metrics(font_options);
1085 0 : cairo_font_options_destroy(font_options);
1086 :
1087 0 : switch (hint_metrics) {
1088 : case CAIRO_HINT_METRICS_OFF:
1089 0 : *aRoundY = false;
1090 0 : return;
1091 : case CAIRO_HINT_METRICS_DEFAULT:
1092 : // Here we mimic what cairo surface/font backends do. Printing
1093 : // surfaces have already been handled by hint_metrics. The
1094 : // fallback show_glyphs implementation composites pixel-aligned
1095 : // glyph surfaces, so we just pick surface/font combinations that
1096 : // override this.
1097 0 : switch (cairo_scaled_font_get_type(scaled_font)) {
1098 : #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
1099 : case CAIRO_FONT_TYPE_DWRITE:
1100 : // show_glyphs is implemented on the font and so is used for
1101 : // all surface types; however, it may pixel-snap depending on
1102 : // the dwrite rendering mode
1103 : if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
1104 : gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
1105 : DWRITE_MEASURING_MODE_NATURAL) {
1106 : return;
1107 : }
1108 : #endif
1109 : case CAIRO_FONT_TYPE_QUARTZ:
1110 : // Quartz surfaces implement show_glyphs for Quartz fonts
1111 0 : if (cairo_surface_get_type(cairo_get_target(cr)) ==
1112 : CAIRO_SURFACE_TYPE_QUARTZ) {
1113 0 : return;
1114 : }
1115 : default:
1116 0 : break;
1117 : }
1118 : // fall through:
1119 : case CAIRO_HINT_METRICS_ON:
1120 0 : break;
1121 : }
1122 0 : *aRoundX = true;
1123 0 : return;
1124 : }
1125 :
1126 : #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
1127 : // will fit without requiring separate allocation
1128 : // for charToGlyphArray
1129 :
1130 : nsresult
1131 0 : gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
1132 : gfxShapedWord *aShapedWord,
1133 : hb_buffer_t *aBuffer)
1134 : {
1135 : PRUint32 numGlyphs;
1136 0 : const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
1137 0 : if (numGlyphs == 0) {
1138 0 : return NS_OK;
1139 : }
1140 :
1141 0 : nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
1142 :
1143 0 : PRUint32 wordLength = aShapedWord->Length();
1144 : static const PRInt32 NO_GLYPH = -1;
1145 0 : nsAutoTArray<PRInt32,SMALL_GLYPH_RUN> charToGlyphArray;
1146 0 : if (!charToGlyphArray.SetLength(wordLength)) {
1147 0 : return NS_ERROR_OUT_OF_MEMORY;
1148 : }
1149 :
1150 0 : PRInt32 *charToGlyph = charToGlyphArray.Elements();
1151 0 : for (PRUint32 offset = 0; offset < wordLength; ++offset) {
1152 0 : charToGlyph[offset] = NO_GLYPH;
1153 : }
1154 :
1155 0 : for (PRUint32 i = 0; i < numGlyphs; ++i) {
1156 0 : PRUint32 loc = ginfo[i].cluster;
1157 0 : if (loc < wordLength) {
1158 0 : charToGlyph[loc] = i;
1159 : }
1160 : }
1161 :
1162 0 : PRInt32 glyphStart = 0; // looking for a clump that starts at this glyph
1163 0 : PRInt32 charStart = 0; // and this char index within the range of the run
1164 :
1165 : bool roundX;
1166 : bool roundY;
1167 0 : GetRoundOffsetsToPixels(aContext, &roundX, &roundY);
1168 :
1169 0 : PRInt32 appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
1170 :
1171 : // factor to convert 16.16 fixed-point pixels to app units
1172 : // (only used if not rounding)
1173 0 : double hb2appUnits = FixedToFloat(aShapedWord->AppUnitsPerDevUnit());
1174 :
1175 : // Residual from rounding of previous advance, for use in rounding the
1176 : // subsequent offset or advance appropriately. 16.16 fixed-point
1177 : //
1178 : // When rounding, the goal is to make the distance between glyphs and
1179 : // their base glyph equal to the integral number of pixels closest to that
1180 : // suggested by that shaper.
1181 : // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
1182 : //
1183 : // The value of the residual is the part of the desired distance that has
1184 : // not been included in integer offsets.
1185 0 : hb_position_t x_residual = 0;
1186 :
1187 : // keep track of y-position to set glyph offsets if needed
1188 0 : nscoord yPos = 0;
1189 :
1190 : const hb_glyph_position_t *posInfo =
1191 0 : hb_buffer_get_glyph_positions(aBuffer, nsnull);
1192 :
1193 0 : while (glyphStart < PRInt32(numGlyphs)) {
1194 :
1195 0 : bool inOrder = true;
1196 0 : PRInt32 charEnd = ginfo[glyphStart].cluster;
1197 0 : PRInt32 glyphEnd = glyphStart;
1198 0 : PRInt32 charLimit = wordLength;
1199 0 : while (charEnd < charLimit) {
1200 : // This is normally executed once for each iteration of the outer loop,
1201 : // but in unusual cases where the character/glyph association is complex,
1202 : // the initial character range might correspond to a non-contiguous
1203 : // glyph range with "holes" in it. If so, we will repeat this loop to
1204 : // extend the character range until we have a contiguous glyph sequence.
1205 0 : charEnd += 1;
1206 0 : while (charEnd != charLimit && charToGlyph[charEnd] == NO_GLYPH) {
1207 0 : charEnd += 1;
1208 : }
1209 :
1210 : // find the maximum glyph index covered by the clump so far
1211 0 : for (PRInt32 i = charStart; i < charEnd; ++i) {
1212 0 : if (charToGlyph[i] != NO_GLYPH) {
1213 0 : glyphEnd = NS_MAX(glyphEnd, charToGlyph[i] + 1);
1214 : // update extent of glyph range
1215 : }
1216 : }
1217 :
1218 0 : if (glyphEnd == glyphStart + 1) {
1219 : // for the common case of a single-glyph clump,
1220 : // we can skip the following checks
1221 0 : break;
1222 : }
1223 :
1224 0 : if (glyphEnd == glyphStart) {
1225 : // no glyphs, try to extend the clump
1226 0 : continue;
1227 : }
1228 :
1229 : // check whether all glyphs in the range are associated with the characters
1230 : // in our clump; if not, we have a discontinuous range, and should extend it
1231 : // unless we've reached the end of the text
1232 0 : bool allGlyphsAreWithinCluster = true;
1233 0 : PRInt32 prevGlyphCharIndex = charStart - 1;
1234 0 : for (PRInt32 i = glyphStart; i < glyphEnd; ++i) {
1235 0 : PRInt32 glyphCharIndex = ginfo[i].cluster;
1236 0 : if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
1237 0 : allGlyphsAreWithinCluster = false;
1238 0 : break;
1239 : }
1240 0 : if (glyphCharIndex <= prevGlyphCharIndex) {
1241 0 : inOrder = false;
1242 : }
1243 0 : prevGlyphCharIndex = glyphCharIndex;
1244 : }
1245 0 : if (allGlyphsAreWithinCluster) {
1246 0 : break;
1247 : }
1248 : }
1249 :
1250 0 : NS_ASSERTION(glyphStart < glyphEnd,
1251 : "character/glyph clump contains no glyphs!");
1252 0 : NS_ASSERTION(charStart != charEnd,
1253 : "character/glyph clump contains no characters!");
1254 :
1255 : // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
1256 : // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
1257 : // and endCharIndex to the limit (position beyond the last char),
1258 : // adjusting for the offset of the stringRange relative to the textRun.
1259 : PRInt32 baseCharIndex, endCharIndex;
1260 0 : while (charEnd < PRInt32(wordLength) && charToGlyph[charEnd] == NO_GLYPH)
1261 0 : charEnd++;
1262 0 : baseCharIndex = charStart;
1263 0 : endCharIndex = charEnd;
1264 :
1265 : // Then we check if the clump falls outside our actual string range;
1266 : // if so, just go to the next.
1267 0 : if (baseCharIndex >= PRInt32(wordLength)) {
1268 0 : glyphStart = glyphEnd;
1269 0 : charStart = charEnd;
1270 0 : continue;
1271 : }
1272 : // Ensure we won't try to go beyond the valid length of the textRun's text
1273 0 : endCharIndex = NS_MIN<PRInt32>(endCharIndex, wordLength);
1274 :
1275 : // Now we're ready to set the glyph info in the textRun
1276 0 : PRInt32 glyphsInClump = glyphEnd - glyphStart;
1277 :
1278 : // Check for default-ignorable char that didn't get filtered, combined,
1279 : // etc by the shaping process, and remove from the run.
1280 : // (This may be done within harfbuzz eventually.)
1281 0 : if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
1282 0 : aShapedWord->FilterIfIgnorable(baseCharIndex)) {
1283 0 : glyphStart = glyphEnd;
1284 0 : charStart = charEnd;
1285 0 : continue;
1286 : }
1287 :
1288 0 : hb_position_t x_offset = posInfo[glyphStart].x_offset;
1289 0 : hb_position_t x_advance = posInfo[glyphStart].x_advance;
1290 : nscoord xOffset, advance;
1291 0 : if (roundX) {
1292 : xOffset =
1293 0 : appUnitsPerDevUnit * FixedToIntRound(x_offset + x_residual);
1294 : // Desired distance from the base glyph to the next reference point.
1295 0 : hb_position_t width = x_advance - x_offset;
1296 0 : int intWidth = FixedToIntRound(width);
1297 0 : x_residual = width - FloatToFixed(intWidth);
1298 0 : advance = appUnitsPerDevUnit * intWidth + xOffset;
1299 : } else {
1300 0 : xOffset = floor(hb2appUnits * x_offset + 0.5);
1301 0 : advance = floor(hb2appUnits * x_advance + 0.5);
1302 : }
1303 : // Check if it's a simple one-to-one mapping
1304 0 : if (glyphsInClump == 1 &&
1305 0 : gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
1306 0 : gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
1307 0 : aShapedWord->IsClusterStart(baseCharIndex) &&
1308 : xOffset == 0 &&
1309 0 : posInfo[glyphStart].y_offset == 0 && yPos == 0)
1310 : {
1311 0 : gfxTextRun::CompressedGlyph g;
1312 : aShapedWord->SetSimpleGlyph(baseCharIndex,
1313 : g.SetSimpleGlyph(advance,
1314 0 : ginfo[glyphStart].codepoint));
1315 : } else {
1316 : // collect all glyphs in a list to be assigned to the first char;
1317 : // there must be at least one in the clump, and we already measured
1318 : // its advance, hence the placement of the loop-exit test and the
1319 : // measurement of the next glyph
1320 0 : while (1) {
1321 : gfxTextRun::DetailedGlyph* details =
1322 0 : detailedGlyphs.AppendElement();
1323 0 : details->mGlyphID = ginfo[glyphStart].codepoint;
1324 :
1325 0 : details->mXOffset = xOffset;
1326 0 : details->mAdvance = advance;
1327 :
1328 0 : hb_position_t y_offset = posInfo[glyphStart].y_offset;
1329 : details->mYOffset = yPos -
1330 : (roundY ? appUnitsPerDevUnit * FixedToIntRound(y_offset)
1331 0 : : floor(hb2appUnits * y_offset + 0.5));
1332 :
1333 0 : hb_position_t y_advance = posInfo[glyphStart].y_advance;
1334 0 : if (y_advance != 0) {
1335 : yPos -=
1336 : roundY ? appUnitsPerDevUnit * FixedToIntRound(y_advance)
1337 0 : : floor(hb2appUnits * y_advance + 0.5);
1338 : }
1339 0 : if (++glyphStart >= glyphEnd) {
1340 : break;
1341 : }
1342 :
1343 0 : x_offset = posInfo[glyphStart].x_offset;
1344 0 : x_advance = posInfo[glyphStart].x_advance;
1345 0 : if (roundX) {
1346 : xOffset = appUnitsPerDevUnit *
1347 0 : FixedToIntRound(x_offset + x_residual);
1348 : // Desired distance to the next reference point. The
1349 : // residual is considered here, and includes the residual
1350 : // from the base glyph offset and subsequent advances, so
1351 : // that the distance from the base glyph is optimized
1352 : // rather than the distance from combining marks.
1353 0 : x_advance += x_residual;
1354 0 : int intAdvance = FixedToIntRound(x_advance);
1355 0 : x_residual = x_advance - FloatToFixed(intAdvance);
1356 0 : advance = appUnitsPerDevUnit * intAdvance;
1357 : } else {
1358 0 : xOffset = floor(hb2appUnits * x_offset + 0.5);
1359 0 : advance = floor(hb2appUnits * x_advance + 0.5);
1360 : }
1361 : }
1362 :
1363 0 : gfxTextRun::CompressedGlyph g;
1364 0 : g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
1365 0 : true, detailedGlyphs.Length());
1366 : aShapedWord->SetGlyphs(baseCharIndex,
1367 0 : g, detailedGlyphs.Elements());
1368 :
1369 0 : detailedGlyphs.Clear();
1370 : }
1371 :
1372 : // the rest of the chars in the group are ligature continuations,
1373 : // no associated glyphs
1374 0 : while (++baseCharIndex != endCharIndex &&
1375 : baseCharIndex < PRInt32(wordLength)) {
1376 0 : gfxTextRun::CompressedGlyph g;
1377 : g.SetComplex(inOrder &&
1378 0 : aShapedWord->IsClusterStart(baseCharIndex),
1379 0 : false, 0);
1380 0 : aShapedWord->SetGlyphs(baseCharIndex, g, nsnull);
1381 : }
1382 :
1383 0 : glyphStart = glyphEnd;
1384 0 : charStart = charEnd;
1385 : }
1386 :
1387 0 : return NS_OK;
1388 : }
|