Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
6 #include "gfxGraphiteShaper.h"
7 #include "nsString.h"
8 #include "gfxContext.h"
10 #include "graphite2/Font.h"
11 #include "graphite2/Segment.h"
13 #include "harfbuzz/hb.h"
15 #define FloatToFixed(f) (65536 * (f))
16 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
17 // Right shifts of negative (signed) integers are undefined, as are overflows
18 // when converting unsigned to negative signed integers.
19 // (If speed were an issue we could make some 2's complement assumptions.)
20 #define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \
21 : -((32767 - (f)) >> 16))
23 using namespace mozilla; // for AutoSwap_* types
25 /*
26 * Creation and destruction; on deletion, release any font tables we're holding
27 */
29 gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont)
30 : gfxFontShaper(aFont),
31 mGrFace(mFont->GetFontEntry()->GetGrFace()),
32 mGrFont(nullptr)
33 {
34 mCallbackData.mFont = aFont;
35 mCallbackData.mShaper = this;
36 }
38 gfxGraphiteShaper::~gfxGraphiteShaper()
39 {
40 if (mGrFont) {
41 gr_font_destroy(mGrFont);
42 }
43 mFont->GetFontEntry()->ReleaseGrFace(mGrFace);
44 }
46 /*static*/ float
47 gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid)
48 {
49 const CallbackData *cb =
50 static_cast<const CallbackData*>(appFontHandle);
51 return FixedToFloat(cb->mFont->GetGlyphWidth(cb->mContext, glyphid));
52 }
54 static inline uint32_t
55 MakeGraphiteLangTag(uint32_t aTag)
56 {
57 uint32_t grLangTag = aTag;
58 // replace trailing space-padding with NULs for graphite
59 uint32_t mask = 0x000000FF;
60 while ((grLangTag & mask) == ' ') {
61 grLangTag &= ~mask;
62 mask <<= 8;
63 }
64 return grLangTag;
65 }
67 struct GrFontFeatures {
68 gr_face *mFace;
69 gr_feature_val *mFeatures;
70 };
72 static PLDHashOperator
73 AddFeature(const uint32_t& aTag, uint32_t& aValue, void *aUserArg)
74 {
75 GrFontFeatures *f = static_cast<GrFontFeatures*>(aUserArg);
77 const gr_feature_ref* fref = gr_face_find_fref(f->mFace, aTag);
78 if (fref) {
79 gr_fref_set_feature_value(fref, aValue, f->mFeatures);
80 }
81 return PL_DHASH_NEXT;
82 }
84 bool
85 gfxGraphiteShaper::ShapeText(gfxContext *aContext,
86 const char16_t *aText,
87 uint32_t aOffset,
88 uint32_t aLength,
89 int32_t aScript,
90 gfxShapedText *aShapedText)
91 {
92 // some font back-ends require this in order to get proper hinted metrics
93 if (!mFont->SetupCairoFont(aContext)) {
94 return false;
95 }
97 mCallbackData.mContext = aContext;
99 if (!mGrFont) {
100 if (!mGrFace) {
101 return false;
102 }
104 if (mFont->ProvidesGlyphWidths()) {
105 gr_font_ops ops = {
106 sizeof(gr_font_ops),
107 &GrGetAdvance,
108 nullptr // vertical text not yet implemented
109 };
110 mGrFont = gr_make_font_with_ops(mFont->GetAdjustedSize(),
111 &mCallbackData, &ops, mGrFace);
112 } else {
113 mGrFont = gr_make_font(mFont->GetAdjustedSize(), mGrFace);
114 }
116 if (!mGrFont) {
117 return false;
118 }
119 }
121 gfxFontEntry *entry = mFont->GetFontEntry();
122 const gfxFontStyle *style = mFont->GetStyle();
123 uint32_t grLang = 0;
124 if (style->languageOverride) {
125 grLang = MakeGraphiteLangTag(style->languageOverride);
126 } else if (entry->mLanguageOverride) {
127 grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
128 } else {
129 nsAutoCString langString;
130 style->language->ToUTF8String(langString);
131 grLang = GetGraphiteTagForLang(langString);
132 }
133 gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
135 nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
137 // if style contains font-specific features
138 if (MergeFontFeatures(style,
139 mFont->GetFontEntry()->mFeatureSettings,
140 aShapedText->DisableLigatures(),
141 mFont->GetFontEntry()->FamilyName(),
142 mergedFeatures))
143 {
144 // enumerate result and insert into Graphite feature list
145 GrFontFeatures f = {mGrFace, grFeatures};
146 mergedFeatures.Enumerate(AddFeature, &f);
147 }
149 size_t numChars = gr_count_unicode_characters(gr_utf16,
150 aText, aText + aLength,
151 nullptr);
152 gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
153 gr_utf16, aText, numChars,
154 aShapedText->IsRightToLeft());
156 gr_featureval_destroy(grFeatures);
158 if (!seg) {
159 return false;
160 }
162 nsresult rv = SetGlyphsFromSegment(aContext, aShapedText, aOffset, aLength,
163 aText, seg);
165 gr_seg_destroy(seg);
167 return NS_SUCCEEDED(rv);
168 }
170 #define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
171 // for short (typical) runs up to this length
173 struct Cluster {
174 uint32_t baseChar; // in UTF16 code units, not Unicode character indices
175 uint32_t baseGlyph;
176 uint32_t nChars; // UTF16 code units
177 uint32_t nGlyphs;
178 Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
179 };
181 nsresult
182 gfxGraphiteShaper::SetGlyphsFromSegment(gfxContext *aContext,
183 gfxShapedText *aShapedText,
184 uint32_t aOffset,
185 uint32_t aLength,
186 const char16_t *aText,
187 gr_segment *aSegment)
188 {
189 int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
190 bool rtl = aShapedText->IsRightToLeft();
192 uint32_t glyphCount = gr_seg_n_slots(aSegment);
194 // identify clusters; graphite may have reordered/expanded/ligated glyphs.
195 AutoFallibleTArray<Cluster,SMALL_GLYPH_RUN> clusters;
196 AutoFallibleTArray<uint16_t,SMALL_GLYPH_RUN> gids;
197 AutoFallibleTArray<float,SMALL_GLYPH_RUN> xLocs;
198 AutoFallibleTArray<float,SMALL_GLYPH_RUN> yLocs;
200 if (!clusters.SetLength(aLength) ||
201 !gids.SetLength(glyphCount) ||
202 !xLocs.SetLength(glyphCount) ||
203 !yLocs.SetLength(glyphCount))
204 {
205 return NS_ERROR_OUT_OF_MEMORY;
206 }
208 // walk through the glyph slots and check which original character
209 // each is associated with
210 uint32_t gIndex = 0; // glyph slot index
211 uint32_t cIndex = 0; // current cluster index
212 for (const gr_slot *slot = gr_seg_first_slot(aSegment);
213 slot != nullptr;
214 slot = gr_slot_next_in_segment(slot), gIndex++)
215 {
216 uint32_t before =
217 gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot)));
218 uint32_t after =
219 gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot)));
220 gids[gIndex] = gr_slot_gid(slot);
221 xLocs[gIndex] = gr_slot_origin_X(slot);
222 yLocs[gIndex] = gr_slot_origin_Y(slot);
224 // if this glyph has a "before" character index that precedes the
225 // current cluster's char index, we need to merge preceding
226 // clusters until it gets included
227 while (before < clusters[cIndex].baseChar && cIndex > 0) {
228 clusters[cIndex-1].nChars += clusters[cIndex].nChars;
229 clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs;
230 --cIndex;
231 }
233 // if there's a gap between the current cluster's base character and
234 // this glyph's, extend the cluster to include the intervening chars
235 if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
236 before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
237 {
238 NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word");
239 Cluster& c = clusters[cIndex + 1];
240 c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
241 c.nChars = before - c.baseChar;
242 c.baseGlyph = gIndex;
243 c.nGlyphs = 0;
244 ++cIndex;
245 }
247 // increment cluster's glyph count to include current slot
248 NS_ASSERTION(cIndex < aLength, "cIndex beyond word length");
249 ++clusters[cIndex].nGlyphs;
251 // extend cluster if necessary to reach the glyph's "after" index
252 if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
253 clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
254 }
255 }
257 bool roundX;
258 bool roundY;
259 aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
261 gfxShapedText::CompressedGlyph *charGlyphs =
262 aShapedText->GetCharacterGlyphs() + aOffset;
264 // now put glyphs into the textrun, one cluster at a time
265 for (uint32_t i = 0; i <= cIndex; ++i) {
266 const Cluster& c = clusters[i];
268 float adv; // total advance of the cluster
269 if (rtl) {
270 if (i == 0) {
271 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
272 } else {
273 adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph];
274 }
275 } else {
276 if (i == cIndex) {
277 adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
278 } else {
279 adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
280 }
281 }
283 // Check for default-ignorable char that didn't get filtered, combined,
284 // etc by the shaping process, and skip it.
285 uint32_t offs = c.baseChar;
286 NS_ASSERTION(offs < aLength, "unexpected offset");
287 if (c.nGlyphs == 1 && c.nChars == 1 &&
288 aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
289 continue;
290 }
292 uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits :
293 NSToIntRound(adv * dev2appUnits);
294 if (c.nGlyphs == 1 &&
295 gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
296 gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
297 charGlyphs[offs].IsClusterStart() &&
298 yLocs[c.baseGlyph] == 0)
299 {
300 charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
301 } else {
302 // not a one-to-one mapping with simple metrics: use DetailedGlyph
303 nsAutoTArray<gfxShapedText::DetailedGlyph,8> details;
304 float clusterLoc;
305 for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
306 gfxShapedText::DetailedGlyph* d = details.AppendElement();
307 d->mGlyphID = gids[j];
308 d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
309 -yLocs[j] * dev2appUnits;
310 if (j == c.baseGlyph) {
311 d->mXOffset = 0;
312 d->mAdvance = appAdvance;
313 clusterLoc = xLocs[j];
314 } else {
315 float dx = rtl ? (xLocs[j] - clusterLoc) :
316 (xLocs[j] - clusterLoc - adv);
317 d->mXOffset = roundX ? NSToIntRound(dx) * dev2appUnits :
318 dx * dev2appUnits;
319 d->mAdvance = 0;
320 }
321 }
322 gfxShapedText::CompressedGlyph g;
323 g.SetComplex(charGlyphs[offs].IsClusterStart(),
324 true, details.Length());
325 aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
326 }
328 for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
329 NS_ASSERTION(j < aLength, "unexpected offset");
330 gfxShapedText::CompressedGlyph &g = charGlyphs[j];
331 NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
332 g.SetComplex(g.IsClusterStart(), false, 0);
333 }
334 }
336 return NS_OK;
337 }
339 #undef SMALL_GLYPH_RUN
341 // for language tag validation - include list of tags from the IANA registry
342 #include "gfxLanguageTagList.cpp"
344 nsTHashtable<nsUint32HashKey> *gfxGraphiteShaper::sLanguageTags;
346 /*static*/ uint32_t
347 gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang)
348 {
349 int len = aLang.Length();
350 if (len < 2) {
351 return 0;
352 }
354 // convert primary language subtag to a left-packed, NUL-padded integer
355 // for the Graphite API
356 uint32_t grLang = 0;
357 for (int i = 0; i < 4; ++i) {
358 grLang <<= 8;
359 if (i < len) {
360 uint8_t ch = aLang[i];
361 if (ch == '-') {
362 // found end of primary language subtag, truncate here
363 len = i;
364 continue;
365 }
366 if (ch < 'a' || ch > 'z') {
367 // invalid character in tag, so ignore it completely
368 return 0;
369 }
370 grLang += ch;
371 }
372 }
374 // valid tags must have length = 2 or 3
375 if (len < 2 || len > 3) {
376 return 0;
377 }
379 if (!sLanguageTags) {
380 // store the registered IANA tags in a hash for convenient validation
381 sLanguageTags = new nsTHashtable<nsUint32HashKey>(ArrayLength(sLanguageTagList));
382 for (const uint32_t *tag = sLanguageTagList; *tag != 0; ++tag) {
383 sLanguageTags->PutEntry(*tag);
384 }
385 }
387 // only accept tags known in the IANA registry
388 if (sLanguageTags->GetEntry(grLang)) {
389 return grLang;
390 }
392 return 0;
393 }
395 /*static*/ void
396 gfxGraphiteShaper::Shutdown()
397 {
398 #ifdef NS_FREE_PERMANENT_DATA
399 if (sLanguageTags) {
400 sLanguageTags->Clear();
401 delete sLanguageTags;
402 sLanguageTags = nullptr;
403 }
404 #endif
405 }