Bug 435739 Poor performance of Firefox 3 with no X RENDER extension
[wine-gecko.git] / gfx / thebes / src / gfxPangoFonts.cpp
blobab691337a425a4f0fdfd2c8b07b67c03a0e1e3ed
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
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/
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.
15 * The Original Code is Mozilla Foundation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Vladimir Vukicevic <vladimir@mozilla.com>
23 * Masayuki Nakano <masayuki@d-toybox.com>
24 * Behdad Esfahbod <behdad@gnome.org>
25 * Mats Palmgren <mats.palmgren@bredband.net>
26 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
28 * based on nsFontMetricsPango.cpp by
29 * Christopher Blizzard <blizzard@mozilla.org>
31 * Alternatively, the contents of this file may be used under the terms of
32 * either the GNU General Public License Version 2 or later (the "GPL"), or
33 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 * in which case the provisions of the GPL or the LGPL are applicable instead
35 * of those above. If you wish to allow use of your version of this file only
36 * under the terms of either the GPL or the LGPL, and not to allow others to
37 * use your version of this file under the terms of the MPL, indicate your
38 * decision by deleting the provisions above and replace them with the notice
39 * and other provisions required by the GPL or the LGPL. If you do not delete
40 * the provisions above, a recipient may use your version of this file under
41 * the terms of any one of the MPL, the GPL or the LGPL.
43 * ***** END LICENSE BLOCK ***** */
45 #define PANGO_ENABLE_BACKEND
47 #include "prtypes.h"
48 #include "prlink.h"
49 #include "gfxTypes.h"
51 #include "nsUnicodeRange.h"
53 #include "nsIPref.h"
54 #include "nsIPrefBranch.h"
55 #include "nsIPrefService.h"
56 #include "nsServiceManagerUtils.h"
57 #include "nsMathUtils.h"
59 #include "nsVoidArray.h"
60 #include "nsPromiseFlatString.h"
62 #include "gfxContext.h"
63 #include "gfxPlatformGtk.h"
64 #include "gfxPangoFonts.h"
66 #include "nsCRT.h"
68 #include <locale.h>
69 #include <freetype/tttables.h>
71 #include <cairo.h>
72 #include <cairo-ft.h>
74 #include <pango/pango.h>
75 #include <pango/pangocairo.h>
76 #include <pango/pangofc-fontmap.h>
78 #include <gdk/gdkpango.h>
80 #include <math.h>
82 #define FLOAT_PANGO_SCALE ((gfxFloat)PANGO_SCALE)
84 #ifndef PANGO_VERSION_CHECK
85 #define PANGO_VERSION_CHECK(x,y,z) 0
86 #endif
87 #ifndef PANGO_GLYPH_UNKNOWN_FLAG
88 #define PANGO_GLYPH_UNKNOWN_FLAG ((PangoGlyph)0x10000000)
89 #endif
90 #ifndef PANGO_GLYPH_EMPTY
91 #define PANGO_GLYPH_EMPTY ((PangoGlyph)0)
92 #endif
93 // For g a PangoGlyph,
94 #define IS_MISSING_GLYPH(g) ((g) & PANGO_GLYPH_UNKNOWN_FLAG)
95 #define IS_EMPTY_GLYPH(g) ((g) == PANGO_GLYPH_EMPTY)
97 static PangoLanguage *GetPangoLanguage(const nsACString& aLangGroup);
99 /* static */ gfxPangoFontCache* gfxPangoFontCache::sPangoFontCache = nsnull;
102 * gfxPangoFontset: An implementation of a PangoFontset for gfxPangoFontMap
105 #define GFX_TYPE_PANGO_FONTSET (gfx_pango_fontset_get_type())
106 #define GFX_PANGO_FONTSET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONTSET, gfxPangoFontset))
107 #define GFX_IS_PANGO_FONTSET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONTSET))
109 /* static */
110 GType gfx_pango_fontset_get_type (void);
112 #define GFX_PANGO_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
113 #define GFX_IS_PANGO_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONTSET))
114 #define GFX_PANGO_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
116 // This struct is POD so that it can be used as a GObject.
117 struct gfxPangoFontset {
118 PangoFontset parent_instance;
120 PangoContext *mContext;
121 PangoFontDescription *mFontDesc;
122 PangoLanguage *mLanguage;
123 PangoFont *mBaseFont;
124 PangoFontMap *mFontMap;
125 PangoFontset *mChildFontset;
127 static PangoFontset *
128 NewFontset(PangoContext *aContext,
129 const PangoFontDescription *aFontDesc,
130 PangoLanguage *aLanguage,
131 PangoFont *aBaseFont, PangoFontMap *aFontMap)
133 gfxPangoFontset *fontset = static_cast<gfxPangoFontset *>
134 (g_object_new(GFX_TYPE_PANGO_FONTSET, NULL));
136 fontset->mContext = aContext;
137 g_object_ref(aContext);
139 fontset->mFontDesc = pango_font_description_copy(aFontDesc);
140 fontset->mLanguage = aLanguage;
142 fontset->mBaseFont = aBaseFont;
143 if(aBaseFont)
144 g_object_ref(aBaseFont);
146 fontset->mFontMap = aFontMap;
147 g_object_ref(aFontMap);
149 return PANGO_FONTSET(fontset);
153 struct gfxPangoFontsetClass {
154 PangoFontsetClass parent_class;
157 G_DEFINE_TYPE (gfxPangoFontset, gfx_pango_fontset, PANGO_TYPE_FONTSET)
159 static void
160 gfx_pango_fontset_init(gfxPangoFontset *fontset)
162 fontset->mContext = NULL;
163 fontset->mFontDesc = NULL;
164 fontset->mLanguage = NULL;
165 fontset->mBaseFont = NULL;
166 fontset->mFontMap = NULL;
167 fontset->mChildFontset = NULL;
171 static void
172 gfx_pango_fontset_finalize(GObject *object)
174 gfxPangoFontset *self = GFX_PANGO_FONTSET(object);
176 if (self->mContext)
177 g_object_unref(self->mContext);
178 if (self->mFontDesc)
179 pango_font_description_free(self->mFontDesc);
180 if (self->mBaseFont)
181 g_object_unref(self->mBaseFont);
182 if (self->mFontMap)
183 g_object_unref(self->mFontMap);
184 if (self->mChildFontset)
185 g_object_unref(self->mChildFontset);
187 G_OBJECT_CLASS(gfx_pango_fontset_parent_class)->finalize(object);
190 static PangoLanguage *
191 gfx_pango_fontset_get_language(PangoFontset *fontset)
193 gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
194 return self->mLanguage;
197 struct ForeachExceptBaseData {
198 PangoFont *mBaseFont;
199 PangoFontset *mFontset;
200 PangoFontsetForeachFunc mFunc;
201 gpointer mData;
204 static gboolean
205 foreach_except_base_cb(PangoFontset *fontset, PangoFont *font, gpointer data)
207 ForeachExceptBaseData *baseData =
208 static_cast<ForeachExceptBaseData *>(data);
210 // returning false means continue with the other fonts in the set
211 return font != baseData->mBaseFont &&
212 (*baseData->mFunc)(baseData->mFontset, font, baseData->mData);
215 static PangoFontset *
216 EnsureChildFontset(gfxPangoFontset *self)
218 if (!self->mChildFontset) {
219 // To consider:
221 // * If this is happening often (e.g. Chinese/Japanese pagess where a
222 // Latin font is specified first), and Pango's 64-entry pattern
223 // cache is not large enough, then a fontset cache here could be
224 // helpful. Ideally we'd only cache the fonts that are actually
225 // accessed rather than all the fonts from the FcFontSort.
227 // * Mozilla's langGroup font prefs could be used to specify preferred
228 // fallback fonts for the script of the characters (as indicated by
229 // Pango in mLanguage), by doing the conversion from gfxFontGroup
230 // "families" to PangoFcFontMap "family" here.
231 self->mChildFontset =
232 pango_font_map_load_fontset(self->mFontMap, self->mContext,
233 self->mFontDesc, self->mLanguage);
235 return self->mChildFontset;
238 static void
239 gfx_pango_fontset_foreach(PangoFontset *fontset, PangoFontsetForeachFunc func,
240 gpointer data)
242 gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
244 if (self->mBaseFont && (*func)(fontset, self->mBaseFont, data))
245 return;
247 // Falling back to secondary fonts
248 PangoFontset *childFontset = EnsureChildFontset(self);
249 ForeachExceptBaseData baseData = { self->mBaseFont, fontset, func, data };
250 pango_fontset_foreach(childFontset, foreach_except_base_cb, &baseData);
253 static PangoFont *
254 gfx_pango_fontset_get_font(PangoFontset *fontset, guint wc)
256 gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
258 PangoCoverageLevel baseLevel = PANGO_COVERAGE_NONE;
259 if (self->mBaseFont) {
260 // PangoFcFontMap caches this:
261 PangoCoverage *coverage =
262 pango_font_get_coverage(self->mBaseFont, self->mLanguage);
263 if (coverage) {
264 baseLevel = pango_coverage_get(coverage, wc);
265 pango_coverage_unref(coverage);
269 if (baseLevel != PANGO_COVERAGE_EXACT) {
270 PangoFontset *childFontset = EnsureChildFontset(self);
271 PangoFont *childFont = pango_fontset_get_font(childFontset, wc);
272 if (!self->mBaseFont || childFont == self->mBaseFont)
273 return childFont;
275 if (childFont) {
276 PangoCoverage *coverage =
277 pango_font_get_coverage(childFont, self->mLanguage);
278 if (coverage) {
279 PangoCoverageLevel childLevel =
280 pango_coverage_get(coverage, wc);
281 pango_coverage_unref(coverage);
283 // Only use the child font if better than the base font.
284 if (childLevel > baseLevel)
285 return childFont;
287 g_object_unref(childFont);
291 g_object_ref(self->mBaseFont);
292 return self->mBaseFont;
295 static void
296 gfx_pango_fontset_class_init (gfxPangoFontsetClass *klass)
298 GObjectClass *object_class = G_OBJECT_CLASS (klass);
299 PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (klass);
301 object_class->finalize = gfx_pango_fontset_finalize;
302 fontset_class->get_font = gfx_pango_fontset_get_font;
303 // inherit fontset_class->get_metrics (which won't be used anyway)
304 fontset_class->get_language = gfx_pango_fontset_get_language;
305 fontset_class->foreach = gfx_pango_fontset_foreach;
309 * gfxPangoFontMap: An implementation of a PangoFontMap.
311 * This allows the primary (base) font to be specified. There are two
312 * advantages to this:
314 * 1. Always using the same base font irrespective of the language that Pango
315 * chooses for the script means that PANGO_SCRIPT_COMMON characters are
316 * consistently rendered with the same font. (Bug 339513 and bug 416725)
318 * 2. We normally have the base font from the gfxFont cache so this saves a
319 * FcFontSort when the entry has expired from Pango's much smaller pattern
320 * cache.
322 * This object references a child font map rather than deriving so that
323 * the cache of the child font map is shared.
326 #define GFX_TYPE_PANGO_FONT_MAP (gfx_pango_font_map_get_type())
327 #define GFX_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMap))
328 #define GFX_IS_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONT_MAP))
330 GType gfx_pango_font_map_get_type (void);
332 #define GFX_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
333 #define GFX_IS_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONT_MAP))
334 #define GFX_PANGO_FONT_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
336 // Do not instantiate this class directly, but use NewFontMap.
337 // This struct is POD so that it can be used as a GObject.
338 struct gfxPangoFontMap {
339 PangoFontMap parent_instance;
341 PangoFontMap *mChildFontMap;
342 PangoFont *mBaseFont;
344 static PangoFontMap *
345 NewFontMap(PangoFontMap *aChildFontMap, PangoFont *aBaseFont)
347 NS_ASSERTION(strcmp(pango_font_map_get_shape_engine_type(aChildFontMap),
348 PANGO_RENDER_TYPE_FC) == 0,
349 "Unexpected child PangoFontMap shape engine type");
351 gfxPangoFontMap *fontmap = static_cast<gfxPangoFontMap *>
352 (g_object_new(GFX_TYPE_PANGO_FONT_MAP, NULL));
354 fontmap->mChildFontMap = aChildFontMap;
355 g_object_ref(aChildFontMap);
357 fontmap->mBaseFont = aBaseFont;
358 if(aBaseFont)
359 g_object_ref(aBaseFont);
361 return PANGO_FONT_MAP(fontmap);
364 void
365 SetBaseFont(PangoFont *aBaseFont)
367 if (mBaseFont)
368 g_object_unref(mBaseFont);
370 mBaseFont = aBaseFont;
372 if (aBaseFont)
373 g_object_ref(aBaseFont);
377 struct gfxPangoFontMapClass {
378 PangoFontMapClass parent_class;
381 G_DEFINE_TYPE (gfxPangoFontMap, gfx_pango_font_map, PANGO_TYPE_FONT_MAP)
383 static void
384 gfx_pango_font_map_init(gfxPangoFontMap *fontset)
386 fontset->mChildFontMap = NULL;
387 fontset->mBaseFont = NULL;
390 static void
391 gfx_pango_font_map_finalize(GObject *object)
393 gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(object);
395 if (self->mChildFontMap)
396 g_object_unref(self->mChildFontMap);
397 if (self->mBaseFont)
398 g_object_unref(self->mBaseFont);
400 G_OBJECT_CLASS(gfx_pango_font_map_parent_class)->finalize(object);
403 static PangoFont *
404 gfx_pango_font_map_load_font(PangoFontMap *fontmap, PangoContext *context,
405 const PangoFontDescription *description)
407 gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(fontmap);
408 if (self->mBaseFont) {
409 g_object_ref(self->mBaseFont);
410 return self->mBaseFont;
413 return pango_font_map_load_font(self->mChildFontMap, context, description);
416 static PangoFontset *
417 gfx_pango_font_map_load_fontset(PangoFontMap *fontmap, PangoContext *context,
418 const PangoFontDescription *desc,
419 PangoLanguage *language)
421 gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(fontmap);
422 return gfxPangoFontset::NewFontset(context, desc, language,
423 self->mBaseFont, self->mChildFontMap);
426 static void
427 gfx_pango_font_map_list_families(PangoFontMap *fontmap,
428 PangoFontFamily ***families, int *n_families)
430 gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(fontmap);
431 pango_font_map_list_families(self->mChildFontMap, families, n_families);
434 static void
435 gfx_pango_font_map_class_init(gfxPangoFontMapClass *klass)
437 GObjectClass *object_class = G_OBJECT_CLASS (klass);
438 PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (klass);
440 object_class->finalize = gfx_pango_font_map_finalize;
441 fontmap_class->load_font = gfx_pango_font_map_load_font;
442 fontmap_class->load_fontset = gfx_pango_font_map_load_fontset;
443 fontmap_class->list_families = gfx_pango_font_map_list_families;
444 fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_FC;
448 ** gfxPangoFontGroup
451 static int
452 FFRECountHyphens (const nsAString &aFFREName)
454 int h = 0;
455 PRInt32 hyphen = 0;
456 while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) {
457 ++h;
458 ++hyphen;
460 return h;
463 PRBool
464 gfxPangoFontGroup::FontCallback (const nsAString& fontName,
465 const nsACString& genericName,
466 void *closure)
468 nsStringArray *sa = static_cast<nsStringArray*>(closure);
470 // We ignore prefs that have three hypens since they are X style prefs.
471 if (genericName.Length() && FFRECountHyphens(fontName) >= 3)
472 return PR_TRUE;
474 if (sa->IndexOf(fontName) < 0) {
475 sa->AppendString(fontName);
478 return PR_TRUE;
482 * Look up the font in the gfxFont cache. If we don't find it, create one.
483 * In either case, add a ref, append it to the aFonts array, and return it ---
484 * except for OOM in which case we do nothing and return null.
486 static already_AddRefed<gfxPangoFont>
487 GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle)
489 nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(aName, aStyle);
490 if (!font) {
491 font = new gfxPangoFont(aName, aStyle);
492 if (!font)
493 return nsnull;
494 gfxFontCache::GetCache()->AddNew(font);
496 gfxFont *f = nsnull;
497 font.swap(f);
498 return static_cast<gfxPangoFont *>(f);
501 gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families,
502 const gfxFontStyle *aStyle)
503 : gfxFontGroup(families, aStyle)
505 g_type_init();
507 nsStringArray familyArray;
509 // Leave non-existing fonts in the list so that fontconfig can get the
510 // best match.
511 ForEachFontInternal(families, aStyle->langGroup, PR_TRUE, PR_FALSE,
512 FontCallback, &familyArray);
514 // Construct a string suitable for fontconfig
515 nsAutoString fcFamilies;
516 if (familyArray.Count()) {
517 int i = 0;
518 while (1) {
519 fcFamilies.Append(*familyArray[i]);
520 ++i;
521 if (i >= familyArray.Count())
522 break;
523 fcFamilies.Append(NS_LITERAL_STRING(","));
526 else {
527 // XXX If there are no fonts, we should use dummy family.
528 // Pango will resolve from this.
529 // behdad: yep, looks good.
530 // printf("%s(%s)\n", NS_ConvertUTF16toUTF8(families).get(),
531 // aStyle->langGroup.get());
532 fcFamilies.Append(NS_LITERAL_STRING("sans-serif"));
535 nsRefPtr<gfxPangoFont> font = GetOrMakeFont(fcFamilies, &mStyle);
536 if (font) {
537 mFonts.AppendElement(font);
541 gfxPangoFontGroup::~gfxPangoFontGroup()
545 gfxFontGroup *
546 gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
548 return new gfxPangoFontGroup(mFamilies, aStyle);
552 ** gfxPangoFont
555 gfxPangoFont::gfxPangoFont(const nsAString &aName,
556 const gfxFontStyle *aFontStyle)
557 : gfxFont(aName, aFontStyle),
558 mPangoFont(nsnull), mCairoFont(nsnull),
559 mHasMetrics(PR_FALSE), mAdjustedSize(0)
563 // key for locating a gfxPangoFont corresponding to a PangoFont
564 static GQuark GetFontQuark()
566 // Not using g_quark_from_static_string() because this module may be
567 // unloaded (which would leave a dangling pointer). Using
568 // g_quark_from_string() instead, which creates a small shutdown leak.
569 static GQuark quark = g_quark_from_string("moz-gfxFont");
570 return quark;
573 gfxPangoFont::gfxPangoFont(PangoFont *aPangoFont, const nsAString &aName,
574 const gfxFontStyle *aFontStyle)
575 : gfxFont(aName, aFontStyle),
576 mPangoFont(aPangoFont), mCairoFont(nsnull),
577 mHasMetrics(PR_FALSE), mAdjustedSize(aFontStyle->size)
579 g_object_ref(mPangoFont);
580 g_object_set_qdata(G_OBJECT(mPangoFont), GetFontQuark(), this);
583 gfxPangoFont::~gfxPangoFont()
585 if (mPangoFont) {
586 if (g_object_get_qdata(G_OBJECT(mPangoFont), GetFontQuark()) == this)
587 g_object_set_qdata(G_OBJECT(mPangoFont), GetFontQuark(), NULL);
588 g_object_unref(mPangoFont);
591 if (mCairoFont)
592 cairo_scaled_font_destroy(mCairoFont);
595 /* static */ void
596 gfxPangoFont::Shutdown()
598 gfxPangoFontCache::Shutdown();
600 // This just cleans up memory used by Pango's caches and may cause an
601 // assert and crash in cairo (Bug 399556), so only do this when we care
602 // about cleaning up memory on shutdown.
603 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) || defined(NS_TRACE_MALLOC)
604 PangoFontMap *fontmap = pango_cairo_font_map_get_default ();
605 if (PANGO_IS_FC_FONT_MAP (fontmap))
606 pango_fc_font_map_shutdown (PANGO_FC_FONT_MAP (fontmap));
607 #endif
610 static PangoStyle
611 ThebesStyleToPangoStyle (const gfxFontStyle *fs)
613 if (fs->style == FONT_STYLE_ITALIC)
614 return PANGO_STYLE_ITALIC;
615 if (fs->style == FONT_STYLE_OBLIQUE)
616 return PANGO_STYLE_OBLIQUE;
618 return PANGO_STYLE_NORMAL;
621 static PRUint8
622 PangoStyleToThebesStyle (PangoStyle aPangoStyle)
624 if (aPangoStyle == PANGO_STYLE_ITALIC)
625 return FONT_STYLE_ITALIC;
626 if (aPangoStyle == FONT_STYLE_OBLIQUE)
627 return FONT_STYLE_OBLIQUE;
629 return FONT_STYLE_NORMAL;
632 static PangoWeight
633 ThebesStyleToPangoWeight (const gfxFontStyle *fs)
635 PRInt32 w = fs->weight;
638 * weights come in two parts crammed into one
639 * integer -- the "base" weight is weight / 100,
640 * the rest of the value is the "offset" from that
641 * weight -- the number of steps to move to adjust
642 * the weight in the list of supported font weights,
643 * this value can be negative or positive.
645 PRInt32 baseWeight = (w + 50) / 100;
646 PRInt32 offset = w - baseWeight * 100;
648 /* clip weights to range 0 to 9 */
649 if (baseWeight < 0)
650 baseWeight = 0;
651 if (baseWeight > 9)
652 baseWeight = 9;
654 /* Map from weight value to fcWeights index */
655 static const int fcWeightLookup[10] = {
656 0, 0, 0, 0, 1, 1, 2, 3, 3, 4,
659 PRInt32 fcWeight = fcWeightLookup[baseWeight];
662 * adjust by the offset value, make sure we stay inside the
663 * fcWeights table
665 fcWeight += offset;
667 if (fcWeight < 0)
668 fcWeight = 0;
669 if (fcWeight > 4)
670 fcWeight = 4;
672 /* Map to final PANGO_WEIGHT value */
673 static const int fcWeights[5] = {
674 349,
675 449,
676 649,
677 749,
681 return (PangoWeight)fcWeights[fcWeight];
684 /* Note this doesn't check sizeAdjust */
685 static PangoFontDescription *
686 NewPangoFontDescription(const nsAString &aName, const gfxFontStyle *aFontStyle)
688 PangoFontDescription *fontDesc = pango_font_description_new();
690 pango_font_description_set_family(fontDesc,
691 NS_ConvertUTF16toUTF8(aName).get());
692 pango_font_description_set_absolute_size(fontDesc,
693 aFontStyle->size * PANGO_SCALE);
694 pango_font_description_set_style(fontDesc,
695 ThebesStyleToPangoStyle(aFontStyle));
696 pango_font_description_set_weight(fontDesc,
697 ThebesStyleToPangoWeight(aFontStyle));
698 return fontDesc;
702 * The following gfxPangoFonts are accessed from the PangoFont, not from the
703 * gfxFontCache hash table. The gfxFontCache hash table is keyed by desired
704 * family and style, whereas here we only know actual family and style. There
705 * may be more than one of these fonts with the same family and style, but
706 * different PangoFont and actual font face.
708 * The point of this is to record the exact font face for gfxTextRun glyph
709 * indices. The style of this font does not necessarily represent the exact
710 * gfxFontStyle used to build the text run. Notably, the language is not
711 * recorded, but is used for GetMetrics().aveCharWidth. However, the font
712 * that should be used for aveCharWidth is gfxPangoFontGroup::GetFontAt(0),
713 * which is not constructed here.
716 /* static */
717 already_AddRefed<gfxPangoFont>
718 gfxPangoFont::GetOrMakeFont(PangoFont *aPangoFont)
720 gfxPangoFont *font = static_cast<gfxPangoFont*>
721 (g_object_get_qdata(G_OBJECT(aPangoFont), GetFontQuark()));
723 if (!font) {
724 // pango_font_describe_with_absolute_size requires Pango-1.14
725 PangoFontDescription *desc = pango_font_describe(aPangoFont);
727 PangoFcFont *fcfont = PANGO_FC_FONT(aPangoFont);
728 double size;
729 if (FcPatternGetDouble(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size)
730 != FcResultMatch)
731 size = pango_font_description_get_size(desc) / FLOAT_PANGO_SCALE;
733 // Shouldn't actually need to take too much care about the correct
734 // family or style, as size is the only thing expected to be
735 // important.
736 PRUint8 style =
737 PangoStyleToThebesStyle(pango_font_description_get_style(desc));
738 PRUint16 weight = pango_font_description_get_weight(desc);
739 NS_NAMED_LITERAL_CSTRING(langGroup, "x-unicode");
740 gfxFontStyle fontStyle(style, weight, size, langGroup, 0.0,
741 PR_TRUE, PR_FALSE);
743 // (The PangoFontDescription owns the family string)
744 const char *family = pango_font_description_get_family(desc);
745 font = new gfxPangoFont(aPangoFont,
746 NS_ConvertUTF8toUTF16(family), &fontStyle);
748 pango_font_description_free(desc);
749 if (!font)
750 return nsnull;
752 // Do not add this font to the gfxFontCache hash table as this may not
753 // be the PangoFont that fontconfig chooses for this style.
755 NS_ADDREF(font);
756 return font;
759 static PangoFont*
760 LoadPangoFont(PangoContext *aPangoCtx, const PangoFontDescription *aPangoFontDesc)
762 gfxPangoFontCache *cache = gfxPangoFontCache::GetPangoFontCache();
763 if (!cache)
764 return nsnull; // Error
765 PangoFont* pangoFont = cache->Get(aPangoFontDesc);
766 if (!pangoFont) {
767 pangoFont = pango_context_load_font(aPangoCtx, aPangoFontDesc);
768 if (pangoFont) {
769 cache->Put(aPangoFontDesc, pangoFont);
772 return pangoFont;
775 void
776 gfxPangoFont::RealizePangoFont()
778 // already realized?
779 if (mPangoFont)
780 return;
782 PangoFontDescription *pangoFontDesc =
783 NewPangoFontDescription(mName, GetStyle());
785 PangoContext *pangoCtx = gdk_pango_context_get();
787 if (!GetStyle()->langGroup.IsEmpty()) {
788 PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup);
789 if (lang)
790 pango_context_set_language(pangoCtx, lang);
793 mPangoFont = LoadPangoFont(pangoCtx, pangoFontDesc);
795 gfxFloat size = GetStyle()->size;
796 // Checking mPangoFont to avoid infinite recursion through GetCharSize
797 if (size != 0.0 && GetStyle()->sizeAdjust != 0.0 && mPangoFont) {
798 // Could try xHeight from TrueType/OpenType fonts.
799 gfxSize isz, lsz;
800 GetCharSize('x', isz, lsz);
801 if (isz.height != 0.0) {
802 gfxFloat aspect = isz.height / size;
803 size = GetStyle()->GetAdjustedSize(aspect);
805 pango_font_description_set_absolute_size(pangoFontDesc,
806 size * PANGO_SCALE);
807 g_object_unref(mPangoFont);
808 mPangoFont = LoadPangoFont(pangoCtx, pangoFontDesc);
812 NS_ASSERTION(mHasMetrics == PR_FALSE, "metrics will be invalid...");
813 mAdjustedSize = size;
814 if (!g_object_get_qdata(G_OBJECT(mPangoFont), GetFontQuark()))
815 g_object_set_qdata(G_OBJECT(mPangoFont), GetFontQuark(), this);
817 if (pangoFontDesc)
818 pango_font_description_free(pangoFontDesc);
819 if (pangoCtx)
820 g_object_unref(pangoCtx);
823 void
824 gfxPangoFont::GetCharSize(char aChar, gfxSize& aInkSize, gfxSize& aLogSize,
825 PRUint32 *aGlyphID)
827 if (NS_UNLIKELY(GetStyle()->size == 0.0)) {
828 if (aGlyphID)
829 *aGlyphID = 0;
830 aInkSize.SizeTo(0.0, 0.0);
831 aLogSize.SizeTo(0.0, 0.0);
832 return;
835 // XXXkt: Why not use pango_font_get_glyph_extents? This function isn't
836 // currently being used for characters likely to involve glyph clusters.
837 // I don't think pango_shape will fallback to other fonts.
838 PangoAnalysis analysis;
839 // Initialize new fields, gravity and flags in pango 1.16
840 // (or padding in 1.14).
841 // Use memset instead of { 0 } aggregate initialization or placement new
842 // default initialization so that padding (which may have meaning in other
843 // versions) is initialized.
844 memset(&analysis, 0, sizeof(analysis));
845 analysis.font = GetPangoFont();
846 analysis.language = pango_language_from_string("en");
847 analysis.shape_engine = pango_font_find_shaper(analysis.font, analysis.language, aChar);
849 PangoGlyphString *glstr = pango_glyph_string_new();
850 pango_shape (&aChar, 1, &analysis, glstr);
852 if (aGlyphID) {
853 *aGlyphID = 0;
854 if (glstr->num_glyphs == 1) {
855 PangoGlyph glyph = glstr->glyphs[0].glyph;
856 if (!IS_MISSING_GLYPH(glyph) && !IS_EMPTY_GLYPH(glyph)) {
857 *aGlyphID = glyph;
862 PangoRectangle ink_rect, log_rect;
863 pango_glyph_string_extents(glstr, analysis.font, &ink_rect, &log_rect);
865 aInkSize.width = ink_rect.width / FLOAT_PANGO_SCALE;
866 aInkSize.height = ink_rect.height / FLOAT_PANGO_SCALE;
868 aLogSize.width = log_rect.width / FLOAT_PANGO_SCALE;
869 aLogSize.height = log_rect.height / FLOAT_PANGO_SCALE;
871 pango_glyph_string_free(glstr);
874 // rounding and truncation functions for a Freetype fixed point number
875 // (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
876 // part and low 6 bits for the fractional part.
877 #define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1
878 #define MOZ_FT_TRUNC(x) ((x) >> 6)
879 #define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \
880 MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s))))
882 const gfxFont::Metrics&
883 gfxPangoFont::GetMetrics()
885 if (mHasMetrics)
886 return mMetrics;
888 /* pango_cairo case; try to get all the metrics from pango itself */
889 PangoFont *font;
890 PangoFontMetrics *pfm;
891 if (NS_LIKELY(GetStyle()->size > 0.0)) {
892 font = GetPangoFont(); // RealizePangoFont is called here.
893 PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup);
894 // If lang is NULL, Pango will measure a string of many languages,
895 // which will require many FcFontSorts, but we don't want to go to
896 // that much trouble.
897 // pango_language_get_default() is available from Pango-1.16.
898 if (!lang)
899 lang = pango_language_from_string(setlocale(LC_CTYPE, NULL));
901 pfm = pango_font_get_metrics(font, lang);
902 } else {
903 // Don't ask pango when the font-size is zero since it causes
904 // some versions of libpango to crash (bug 404112).
905 font = NULL;
906 pfm = NULL;
909 if (NS_LIKELY(pfm)) {
910 mMetrics.maxAscent =
911 pango_font_metrics_get_ascent(pfm) / FLOAT_PANGO_SCALE;
913 mMetrics.maxDescent =
914 pango_font_metrics_get_descent(pfm) / FLOAT_PANGO_SCALE;
916 // This is used for the width of text input elements so be liberal
917 // rather than conservative in the estimate.
918 mMetrics.aveCharWidth =
919 PR_MAX(pango_font_metrics_get_approximate_char_width(pfm),
920 pango_font_metrics_get_approximate_digit_width(pfm))
921 / FLOAT_PANGO_SCALE;
923 mMetrics.underlineOffset =
924 pango_font_metrics_get_underline_position(pfm) / FLOAT_PANGO_SCALE;
926 mMetrics.underlineSize =
927 pango_font_metrics_get_underline_thickness(pfm) / FLOAT_PANGO_SCALE;
929 mMetrics.strikeoutOffset =
930 pango_font_metrics_get_strikethrough_position(pfm) / FLOAT_PANGO_SCALE;
932 mMetrics.strikeoutSize =
933 pango_font_metrics_get_strikethrough_thickness(pfm) / FLOAT_PANGO_SCALE;
935 // We're going to overwrite this below if we have a FT_Face
936 // (which we normally should have...).
937 mMetrics.maxAdvance = mMetrics.aveCharWidth;
938 } else {
939 mMetrics.maxAscent = 0.0;
940 mMetrics.maxDescent = 0.0;
941 mMetrics.aveCharWidth = 0.0;
942 mMetrics.underlineOffset = -1.0;
943 mMetrics.underlineSize = 0.0;
944 mMetrics.strikeoutOffset = 0.0;
945 mMetrics.strikeoutSize = 0.0;
946 mMetrics.maxAdvance = 0.0;
949 // ??
950 mMetrics.emHeight = mAdjustedSize;
952 gfxFloat lineHeight = mMetrics.maxAscent + mMetrics.maxDescent;
953 if (lineHeight > mMetrics.emHeight)
954 mMetrics.externalLeading = lineHeight - mMetrics.emHeight;
955 else
956 mMetrics.externalLeading = 0;
957 mMetrics.internalLeading = 0;
959 mMetrics.maxHeight = lineHeight;
961 mMetrics.emAscent = lineHeight > 0.0 ?
962 mMetrics.maxAscent * mMetrics.emHeight / lineHeight : 0.0;
963 mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
965 gfxSize isz, lsz;
966 GetCharSize(' ', isz, lsz, &mSpaceGlyph);
967 mMetrics.spaceWidth = lsz.width;
968 GetCharSize('x', isz, lsz);
969 mMetrics.xHeight = isz.height;
971 FT_Face face = NULL;
972 if (pfm && PANGO_IS_FC_FONT(font))
973 face = pango_fc_font_lock_face(PANGO_FC_FONT(font));
975 if (face) {
976 mMetrics.maxAdvance = face->size->metrics.max_advance / 64.0; // 26.6
978 float val;
980 TT_OS2 *os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
982 if (os2 && os2->ySuperscriptYOffset) {
983 val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySuperscriptYOffset,
984 face->size->metrics.y_scale);
985 mMetrics.superscriptOffset = PR_MAX(1, val);
986 } else {
987 mMetrics.superscriptOffset = mMetrics.xHeight;
990 if (os2 && os2->ySubscriptYOffset) {
991 val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySubscriptYOffset,
992 face->size->metrics.y_scale);
993 // some fonts have the incorrect sign.
994 val = (val < 0) ? -val : val;
995 mMetrics.subscriptOffset = PR_MAX(1, val);
996 } else {
997 mMetrics.subscriptOffset = mMetrics.xHeight;
1000 pango_fc_font_unlock_face(PANGO_FC_FONT(font));
1001 } else {
1002 mMetrics.superscriptOffset = mMetrics.xHeight;
1003 mMetrics.subscriptOffset = mMetrics.xHeight;
1006 SanitizeMetrics(&mMetrics, PR_FALSE);
1008 #if 0
1009 // printf("font name: %s %f %f\n", NS_ConvertUTF16toUTF8(mName).get(), GetStyle()->size, mAdjustedSize);
1010 // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
1012 fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(mName).get());
1013 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
1014 fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
1015 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
1016 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
1017 fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f suOff: %f suSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset);
1018 #endif
1020 if (pfm)
1021 pango_font_metrics_unref(pfm);
1023 mHasMetrics = PR_TRUE;
1024 return mMetrics;
1027 PRUint32
1028 gfxPangoFont::GetGlyph(const PRUint32 aChar)
1030 // Ensure that null character should be missing.
1031 if (aChar == 0)
1032 return 0;
1033 return pango_fc_font_get_glyph(PANGO_FC_FONT(GetPangoFont()), aChar);
1036 nsString
1037 gfxPangoFont::GetUniqueName()
1039 PangoFont *font = GetPangoFont();
1040 PangoFontDescription *desc = pango_font_describe(font);
1041 pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
1042 char *str = pango_font_description_to_string(desc);
1043 pango_font_description_free (desc);
1045 nsString result;
1046 CopyUTF8toUTF16(str, result);
1047 g_free(str);
1048 return result;
1052 ** gfxTextRun
1054 * Some serious problems:
1056 * -- We draw with a font that's hinted for the CTM, but we measure with a font
1057 * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
1059 * -- CreateScaledFont doesn't necessarily give us the font that the Pango
1060 * metrics assume.
1065 * We use this to append an LTR or RTL Override character to the start of the
1066 * string. This forces Pango to honour our direction even if there are neutral characters
1067 * in the string.
1069 static PRInt32 AppendDirectionalIndicatorUTF8(PRBool aIsRTL, nsACString& aString)
1071 static const PRUnichar overrides[2][2] =
1072 { { 0x202d, 0 }, { 0x202e, 0 }}; // LRO, RLO
1073 AppendUTF16toUTF8(overrides[aIsRTL], aString);
1074 return 3; // both overrides map to 3 bytes in UTF8
1077 gfxTextRun *
1078 gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
1079 const Parameters *aParams, PRUint32 aFlags)
1081 NS_ASSERTION(aFlags & TEXT_IS_8BIT, "8bit should have been set");
1082 gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
1083 if (!run)
1084 return nsnull;
1086 PRBool isRTL = run->IsRightToLeft();
1087 if ((aFlags & TEXT_IS_ASCII) && !isRTL) {
1088 // We don't need to send an override character here, the characters must be all LTR
1089 const gchar *utf8Chars = reinterpret_cast<const gchar*>(aString);
1090 InitTextRun(run, utf8Chars, aLength, 0, PR_TRUE);
1091 } else {
1092 // this is really gross...
1093 const char *chars = reinterpret_cast<const char*>(aString);
1094 NS_ConvertASCIItoUTF16 unicodeString(chars, aLength);
1095 nsCAutoString utf8;
1096 PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8);
1097 AppendUTF16toUTF8(unicodeString, utf8);
1098 InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE);
1100 run->FetchGlyphExtents(aParams->mContext);
1101 return run;
1104 #if defined(ENABLE_FAST_PATH_8BIT)
1105 PRBool
1106 gfxPangoFontGroup::CanTakeFastPath(PRUint32 aFlags)
1108 // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't.
1109 // We need to always use Pango for RTL text, in case glyph mirroring is
1110 // required.
1111 PRBool speed = aFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
1112 PRBool isRTL = aFlags & gfxTextRunFactory::TEXT_IS_RTL;
1113 return speed && !isRTL && PANGO_IS_FC_FONT(GetFontAt(0)->GetPangoFont());
1115 #endif
1117 gfxTextRun *
1118 gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
1119 const Parameters *aParams, PRUint32 aFlags)
1121 gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
1122 if (!run)
1123 return nsnull;
1125 run->RecordSurrogates(aString);
1127 nsCAutoString utf8;
1128 PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8);
1129 AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8);
1130 PRBool is8Bit = PR_FALSE;
1132 #if defined(ENABLE_FAST_PATH_8BIT)
1133 if (CanTakeFastPath(aFlags)) {
1134 PRUint32 allBits = 0;
1135 PRUint32 i;
1136 for (i = 0; i < aLength; ++i) {
1137 allBits |= aString[i];
1139 is8Bit = (allBits & 0xFF00) == 0;
1141 #endif
1142 InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit);
1143 run->FetchGlyphExtents(aParams->mContext);
1144 return run;
1147 void
1148 gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
1149 PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
1150 PRBool aTake8BitPath)
1152 #if defined(ENABLE_FAST_PATH_ALWAYS)
1153 CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
1154 #else
1155 #if defined(ENABLE_FAST_PATH_8BIT)
1156 if (aTake8BitPath && CanTakeFastPath(aTextRun->GetFlags())) {
1157 nsresult rv = CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
1158 if (NS_SUCCEEDED(rv))
1159 return;
1161 #endif
1163 CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength);
1164 #endif
1167 static cairo_scaled_font_t*
1168 CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont)
1170 // XXX this needs to also check that we're using system cairo
1171 // otherwise this causes bad problems.
1172 #if 0
1173 //#if PANGO_VERSION_CHECK(1,17,5)
1174 // Lets just use pango_cairo_font_get_scaled_font() for now. it's only
1175 // available in pango 1.17.x though :(
1176 return cairo_scaled_font_reference (pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (aPangoFont)));
1177 #else
1178 // XXX is this safe really? We should probably check the font type or something.
1179 // XXX does this really create the same font that Pango used for measurement?
1180 // We probably need to work harder here. We should pay particular attention
1181 // to the font options.
1182 PangoFcFont *fcfont = PANGO_FC_FONT(aPangoFont);
1183 cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern(fcfont->font_pattern);
1184 double size;
1185 if (FcPatternGetDouble(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
1186 size = 12.0;
1187 cairo_matrix_t fontMatrix;
1188 FcMatrix *fcMatrix;
1189 if (FcPatternGetMatrix(fcfont->font_pattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
1190 cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
1191 else
1192 cairo_matrix_init_identity(&fontMatrix);
1193 cairo_matrix_scale(&fontMatrix, size, size);
1194 cairo_font_options_t *fontOptions = cairo_font_options_create();
1195 cairo_get_font_options(aCR, fontOptions);
1196 cairo_scaled_font_t *scaledFont =
1197 cairo_scaled_font_create(face, &fontMatrix, aCTM, fontOptions);
1198 cairo_font_options_destroy(fontOptions);
1199 cairo_font_face_destroy(face);
1200 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
1201 "Failed to create scaled font");
1202 return scaledFont;
1203 #endif
1206 PRBool
1207 gfxPangoFont::SetupCairoFont(gfxContext *aContext)
1209 cairo_t *cr = aContext->GetCairo();
1210 cairo_matrix_t currentCTM;
1211 cairo_get_matrix(cr, &currentCTM);
1213 if (mCairoFont) {
1214 // Need to validate that its CTM is OK
1215 cairo_matrix_t fontCTM;
1216 cairo_scaled_font_get_ctm(mCairoFont, &fontCTM);
1217 if (fontCTM.xx != currentCTM.xx || fontCTM.yy != currentCTM.yy ||
1218 fontCTM.xy != currentCTM.xy || fontCTM.yx != currentCTM.yx) {
1219 // Just recreate it from scratch, simplest way
1220 cairo_scaled_font_destroy(mCairoFont);
1221 mCairoFont = nsnull;
1224 if (!mCairoFont) {
1225 mCairoFont = CreateScaledFont(cr, &currentCTM, GetPangoFont());
1227 if (cairo_scaled_font_status(mCairoFont) != CAIRO_STATUS_SUCCESS) {
1228 // Don't cairo_set_scaled_font as that would propagate the error to
1229 // the cairo_t, precluding any further drawing.
1230 return PR_FALSE;
1232 cairo_set_scaled_font(cr, mCairoFont);
1233 return PR_TRUE;
1236 static void
1237 SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8Length,
1238 PRUint32 aUTF16Offset, PangoAnalysis *aAnalysis)
1240 if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) {
1241 // 8-bit text doesn't have clusters.
1242 // XXX is this true in all languages???
1243 // behdad: don't think so. Czech for example IIRC has a
1244 // 'ch' grapheme.
1245 return;
1248 // Pango says "the array of PangoLogAttr passed in must have at least N+1
1249 // elements, if there are N characters in the text being broken".
1250 // Could use g_utf8_strlen(aUTF8, aUTF8Length) + 1 but the memory savings
1251 // may not be worth the call.
1252 nsAutoTArray<PangoLogAttr,2000> buffer;
1253 if (!buffer.AppendElements(aUTF8Length + 1))
1254 return;
1256 pango_break(aUTF8, aUTF8Length, aAnalysis,
1257 buffer.Elements(), buffer.Length());
1259 const gchar *p = aUTF8;
1260 const gchar *end = aUTF8 + aUTF8Length;
1261 const PangoLogAttr *attr = buffer.Elements();
1262 gfxTextRun::CompressedGlyph g;
1263 while (p < end) {
1264 if (!attr->is_cursor_position) {
1265 aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
1267 ++aUTF16Offset;
1269 gunichar ch = g_utf8_get_char(p);
1270 NS_ASSERTION(ch != 0, "Shouldn't have NUL in pango_break");
1271 NS_ASSERTION(!IS_SURROGATE(ch), "Shouldn't have surrogates in UTF8");
1272 if (ch >= 0x10000) {
1273 ++aUTF16Offset;
1275 // We produced this utf8 so we don't need to worry about malformed stuff
1276 p = g_utf8_next_char(p);
1277 ++attr;
1281 static PRInt32
1282 ConvertPangoToAppUnits(PRInt32 aCoordinate, PRUint32 aAppUnitsPerDevUnit)
1284 PRInt64 v = (PRInt64(aCoordinate)*aAppUnitsPerDevUnit + PANGO_SCALE/2)/PANGO_SCALE;
1285 return PRInt32(v);
1289 * Given a run of Pango glyphs that should be treated as a single
1290 * cluster/ligature, store them in the textrun at the appropriate character
1291 * and set the other characters involved to be ligature/cluster continuations
1292 * as appropriate.
1294 static nsresult
1295 SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
1296 gfxTextRun *aTextRun,
1297 const gchar *aUTF8, PRUint32 aUTF8Length,
1298 PRUint32 *aUTF16Offset,
1299 PangoGlyphUnit aOverrideSpaceWidth)
1301 PRUint32 utf16Offset = *aUTF16Offset;
1302 PRUint32 textRunLength = aTextRun->GetLength();
1303 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
1304 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
1306 // Override the width of a space, but only for spaces that aren't
1307 // clustered with something else (like a freestanding diacritical mark)
1308 PangoGlyphUnit width = aGlyphs[0].geometry.width;
1309 if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
1310 (utf16Offset + 1 == textRunLength ||
1311 charGlyphs[utf16Offset].IsClusterStart())) {
1312 width = aOverrideSpaceWidth;
1314 PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
1316 gfxTextRun::CompressedGlyph g;
1317 PRBool atClusterStart = aTextRun->IsClusterStart(utf16Offset);
1318 // See if we fit in the compressed area.
1319 if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
1320 aGlyphs[0].geometry.x_offset == 0 &&
1321 aGlyphs[0].geometry.y_offset == 0 &&
1322 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
1323 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
1324 aTextRun->SetSimpleGlyph(utf16Offset,
1325 g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
1326 } else {
1327 nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
1328 if (!detailedGlyphs.AppendElements(aGlyphCount))
1329 return NS_ERROR_OUT_OF_MEMORY;
1331 PRUint32 i;
1332 for (i = 0; i < aGlyphCount; ++i) {
1333 gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
1334 PRUint32 j = (aTextRun->IsRightToLeft()) ? aGlyphCount - 1 - i : i;
1335 const PangoGlyphInfo &glyph = aGlyphs[j];
1336 details->mGlyphID = glyph.glyph;
1337 NS_ASSERTION(details->mGlyphID == glyph.glyph,
1338 "Seriously weird glyph ID detected!");
1339 details->mAdvance =
1340 ConvertPangoToAppUnits(glyph.geometry.width,
1341 appUnitsPerDevUnit);
1342 details->mXOffset =
1343 float(glyph.geometry.x_offset)*appUnitsPerDevUnit/PANGO_SCALE;
1344 details->mYOffset =
1345 float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
1347 g.SetComplex(atClusterStart, PR_TRUE, aGlyphCount);
1348 aTextRun->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
1351 // Check for ligatures and set *aUTF16Offset.
1352 const gchar *p = aUTF8;
1353 const gchar *end = aUTF8 + aUTF8Length;
1354 while (1) {
1355 // Skip the CompressedGlyph that we have added, but check if the
1356 // character was supposed to be ignored. If it's supposed to be ignored,
1357 // overwrite the textrun entry with an invisible missing-glyph.
1358 gunichar ch = g_utf8_get_char(p);
1359 NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
1360 if (ch >= 0x10000) {
1361 // Skip surrogate
1362 ++utf16Offset;
1364 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(PRUnichar(ch)),
1365 "Invalid character detected");
1366 ++utf16Offset;
1368 // We produced this UTF8 so we don't need to worry about malformed stuff
1369 p = g_utf8_next_char(p);
1370 if (p >= end)
1371 break;
1373 if (utf16Offset >= textRunLength) {
1374 NS_ERROR("Someone has added too many glyphs!");
1375 return NS_ERROR_FAILURE;
1378 g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_FALSE, 0);
1379 aTextRun->SetGlyphs(utf16Offset, g, nsnull);
1381 *aUTF16Offset = utf16Offset;
1382 return NS_OK;
1385 nsresult
1386 gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont,
1387 const gchar *aUTF8, PRUint32 aUTF8Length,
1388 PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
1389 PangoGlyphUnit aOverrideSpaceWidth,
1390 PRBool aAbortOnMissingGlyph)
1392 gint numGlyphs = aGlyphs->num_glyphs;
1393 PangoGlyphInfo *glyphs = aGlyphs->glyphs;
1394 const gint *logClusters = aGlyphs->log_clusters;
1395 // We cannot make any assumptions about the order of glyph clusters
1396 // provided by pango_shape (see 375864), so we work through the UTF8 text
1397 // and process the glyph clusters in logical order.
1399 // logGlyphs is like an inverse of logClusters. For each UTF8 byte:
1400 // >= 0 indicates that the byte is first in a cluster and
1401 // gives the position of the starting glyph for the cluster.
1402 // -1 indicates that the byte does not start a cluster.
1403 nsAutoTArray<gint,2000> logGlyphs;
1404 if (!logGlyphs.AppendElements(aUTF8Length + 1))
1405 return NS_ERROR_OUT_OF_MEMORY;
1406 PRUint32 utf8Index = 0;
1407 for(; utf8Index < aUTF8Length; ++utf8Index)
1408 logGlyphs[utf8Index] = -1;
1409 logGlyphs[aUTF8Length] = numGlyphs;
1411 gint lastCluster = -1; // != utf8Index
1412 for (gint glyphIndex = 0; glyphIndex < numGlyphs; ++glyphIndex) {
1413 gint thisCluster = logClusters[glyphIndex];
1414 if (thisCluster != lastCluster) {
1415 lastCluster = thisCluster;
1416 NS_ASSERTION(0 <= thisCluster && thisCluster < gint(aUTF8Length),
1417 "garbage from pango_shape - this is bad");
1418 logGlyphs[thisCluster] = glyphIndex;
1422 PRUint32 utf16Offset = *aUTF16Offset;
1423 PRUint32 textRunLength = aTextRun->GetLength();
1424 utf8Index = 0;
1425 // The next glyph cluster in logical order.
1426 gint nextGlyphClusterStart = logGlyphs[utf8Index];
1427 NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
1428 while (utf8Index < aUTF8Length) {
1429 if (utf16Offset >= textRunLength) {
1430 NS_ERROR("Someone has added too many glyphs!");
1431 return NS_ERROR_FAILURE;
1433 gint glyphClusterStart = nextGlyphClusterStart;
1434 // Find the utf8 text associated with this glyph cluster.
1435 PRUint32 clusterUTF8Start = utf8Index;
1436 // Check we are consistent with pango_break data.
1437 NS_ASSERTION(aTextRun->GetCharacterGlyphs()->IsClusterStart(),
1438 "Glyph cluster not aligned on character cluster.");
1439 do {
1440 ++utf8Index;
1441 nextGlyphClusterStart = logGlyphs[utf8Index];
1442 } while (nextGlyphClusterStart < 0);
1443 const gchar *clusterUTF8 = &aUTF8[clusterUTF8Start];
1444 PRUint32 clusterUTF8Length = utf8Index - clusterUTF8Start;
1446 PRBool haveMissingGlyph = PR_FALSE;
1447 gint glyphIndex = glyphClusterStart;
1449 // It's now unncecessary to do NUL handling here.
1450 do {
1451 if (IS_EMPTY_GLYPH(glyphs[glyphIndex].glyph)) {
1452 // The zero width characters return empty glyph ID at
1453 // shaping, we should override it.
1454 glyphs[glyphIndex].glyph = aFont->GetGlyph(' ');
1455 glyphs[glyphIndex].geometry.width = 0;
1456 } else if (IS_MISSING_GLYPH(glyphs[glyphIndex].glyph)) {
1457 // Does pango ever provide more than one glyph in the
1458 // cluster if there is a missing glyph?
1459 // behdad: yes
1460 haveMissingGlyph = PR_TRUE;
1462 glyphIndex++;
1463 } while (glyphIndex < numGlyphs &&
1464 logClusters[glyphIndex] == gint(clusterUTF8Start));
1466 if (haveMissingGlyph && aAbortOnMissingGlyph)
1467 return NS_ERROR_FAILURE;
1469 nsresult rv;
1470 if (haveMissingGlyph) {
1471 rv = SetMissingGlyphs(aTextRun, clusterUTF8, clusterUTF8Length,
1472 &utf16Offset);
1473 } else {
1474 rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
1475 glyphIndex - glyphClusterStart,
1476 aTextRun,
1477 clusterUTF8, clusterUTF8Length,
1478 &utf16Offset, aOverrideSpaceWidth);
1480 NS_ENSURE_SUCCESS(rv,rv);
1482 *aUTF16Offset = utf16Offset;
1483 return NS_OK;
1486 nsresult
1487 gfxPangoFontGroup::SetMissingGlyphs(gfxTextRun *aTextRun,
1488 const gchar *aUTF8, PRUint32 aUTF8Length,
1489 PRUint32 *aUTF16Offset)
1491 PRUint32 utf16Offset = *aUTF16Offset;
1492 PRUint32 textRunLength = aTextRun->GetLength();
1493 for (PRUint32 index = 0; index < aUTF8Length;) {
1494 if (utf16Offset >= textRunLength) {
1495 NS_ERROR("Someone has added too many glyphs!");
1496 break;
1498 gunichar ch = g_utf8_get_char(aUTF8 + index);
1499 aTextRun->SetMissingGlyph(utf16Offset, ch);
1501 ++utf16Offset;
1502 NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
1503 if (ch >= 0x10000)
1504 ++utf16Offset;
1505 // We produced this UTF8 so we don't need to worry about malformed stuff
1506 index = g_utf8_next_char(aUTF8 + index) - aUTF8;
1509 *aUTF16Offset = utf16Offset;
1510 return NS_OK;
1513 #if defined(ENABLE_FAST_PATH_8BIT) || defined(ENABLE_FAST_PATH_ALWAYS)
1514 nsresult
1515 gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
1516 const gchar *aUTF8, PRUint32 aUTF8Length)
1518 const gchar *p = aUTF8;
1519 gfxPangoFont *font = GetFontAt(0);
1520 PangoFont *pangofont = font->GetPangoFont();
1521 PangoFcFont *fcfont = PANGO_FC_FONT (pangofont);
1522 PRUint32 utf16Offset = 0;
1523 gfxTextRun::CompressedGlyph g;
1524 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
1526 aTextRun->AddGlyphRun(font, 0);
1528 while (p < aUTF8 + aUTF8Length) {
1529 // glib-2.12.9: "If p does not point to a valid UTF-8 encoded
1530 // character, results are undefined." so it is not easy to assert that
1531 // aUTF8 in fact points to UTF8 data but asserting
1532 // g_unichar_validate(ch) may be mildly useful.
1533 gunichar ch = g_utf8_get_char(p);
1534 p = g_utf8_next_char(p);
1536 if (ch == 0) {
1537 // treat this null byte as a missing glyph. Pango
1538 // doesn't create glyphs for these, not even missing-glyphs.
1539 aTextRun->SetMissingGlyph(utf16Offset, 0);
1540 } else {
1541 NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected");
1542 FT_UInt glyph = pango_fc_font_get_glyph (fcfont, ch);
1543 if (!glyph) // character not in font,
1544 return NS_ERROR_FAILURE; // fallback to CreateGlyphRunsItemizing
1546 PangoRectangle rect;
1547 pango_font_get_glyph_extents (pangofont, glyph, NULL, &rect);
1549 PRInt32 advance = PANGO_PIXELS (rect.width * appUnitsPerDevUnit);
1550 if (advance >= 0 &&
1551 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
1552 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
1553 aTextRun->SetSimpleGlyph(utf16Offset,
1554 g.SetSimpleGlyph(advance, glyph));
1555 } else {
1556 gfxTextRun::DetailedGlyph details;
1557 details.mGlyphID = glyph;
1558 NS_ASSERTION(details.mGlyphID == glyph,
1559 "Seriously weird glyph ID detected!");
1560 details.mAdvance = advance;
1561 details.mXOffset = 0;
1562 details.mYOffset = 0;
1563 g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_TRUE, 1);
1564 aTextRun->SetGlyphs(utf16Offset, g, &details);
1567 NS_ASSERTION(!IS_SURROGATE(ch), "Surrogates shouldn't appear in UTF8");
1568 if (ch >= 0x10000) {
1569 // This character is a surrogate pair in UTF16
1570 ++utf16Offset;
1574 ++utf16Offset;
1576 return NS_OK;
1578 #endif
1580 static void
1581 SetBaseFont(PangoContext *aContext, PangoFont *aBaseFont)
1583 PangoFontMap *fontmap = pango_context_get_font_map(aContext);
1584 if (GFX_IS_PANGO_FONT_MAP(fontmap)) {
1585 // Update the base font in the gfxPangoFontMap
1586 GFX_PANGO_FONT_MAP(fontmap)->SetBaseFont(aBaseFont);
1588 else if (aBaseFont) {
1589 // Change the font map to record and activate the base font
1590 fontmap = gfxPangoFontMap::NewFontMap(fontmap, aBaseFont);
1591 pango_context_set_font_map(aContext, fontmap);
1592 g_object_unref(fontmap);
1596 void
1597 gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
1598 const gchar *aUTF8, PRUint32 aUTF8Length,
1599 PRUint32 aUTF8HeaderLen)
1602 PangoContext *context = gdk_pango_context_get();
1604 PangoFontDescription *fontDesc =
1605 NewPangoFontDescription(GetFontAt(0)->GetName(), GetStyle());
1606 if (GetStyle()->sizeAdjust != 0.0) {
1607 gfxFloat size =
1608 static_cast<gfxPangoFont*>(GetFontAt(0))->GetAdjustedSize();
1609 pango_font_description_set_absolute_size(fontDesc, size * PANGO_SCALE);
1612 pango_context_set_font_description(context, fontDesc);
1613 pango_font_description_free(fontDesc);
1615 PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup);
1617 // we should set this to null if we don't have a text language from the page...
1618 // except that we almost always have something...
1619 pango_context_set_language(context, lang);
1621 // Set the primary font for consistent font selection for common
1622 // characters, but use the default Pango behavior
1623 // (selecting generic fonts from the script of the characters)
1624 // in two situations:
1625 // 1. When we don't have a language to make a good choice for the
1626 // primary font.
1627 // 2. For system fonts, use the default Pango behavior
1628 // to give consistency with other apps.
1629 if (lang && !GetStyle()->systemFont) {
1630 SetBaseFont(context, GetFontAt(0)->GetPangoFont());
1633 PangoDirection dir = aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
1634 GList *items = pango_itemize_with_base_dir(context, dir, aUTF8, 0, aUTF8Length, nsnull, nsnull);
1636 PRUint32 utf16Offset = 0;
1637 PRBool isRTL = aTextRun->IsRightToLeft();
1638 GList *pos = items;
1639 PangoGlyphString *glyphString = pango_glyph_string_new();
1640 if (!glyphString)
1641 goto out; // OOM
1643 for (; pos && pos->data; pos = pos->next) {
1644 PangoItem *item = (PangoItem *)pos->data;
1645 NS_ASSERTION(isRTL == item->analysis.level % 2, "RTL assumption mismatch");
1647 PRUint32 offset = item->offset;
1648 PRUint32 length = item->length;
1649 if (offset < aUTF8HeaderLen) {
1650 if (offset + length <= aUTF8HeaderLen)
1651 continue;
1653 length -= aUTF8HeaderLen - offset;
1654 offset = aUTF8HeaderLen;
1657 /* look up the gfxPangoFont from the PangoFont */
1658 nsRefPtr<gfxPangoFont> font =
1659 gfxPangoFont::GetOrMakeFont(item->analysis.font);
1661 nsresult rv = aTextRun->AddGlyphRun(font, utf16Offset, PR_TRUE);
1662 if (NS_FAILED(rv)) {
1663 NS_ERROR("AddGlyphRun Failed");
1664 goto out;
1667 PRUint32 spaceWidth = NS_lround(font->GetMetrics().spaceWidth * FLOAT_PANGO_SCALE);
1669 const gchar *p = aUTF8 + offset;
1670 const gchar *end = p + length;
1671 while (p < end) {
1672 if (*p == 0) {
1673 aTextRun->SetMissingGlyph(utf16Offset, 0);
1674 ++p;
1675 ++utf16Offset;
1676 continue;
1679 // It's necessary to loop over pango_shape as it treats
1680 // NULs as string terminators
1681 const gchar *text = p;
1682 do {
1683 ++p;
1684 } while(p < end && *p != 0);
1685 gint len = p - text;
1687 pango_shape(text, len, &item->analysis, glyphString);
1688 SetupClusterBoundaries(aTextRun, text, len, utf16Offset, &item->analysis);
1689 SetGlyphs(aTextRun, font, text, len, &utf16Offset, glyphString, spaceWidth, PR_FALSE);
1693 aTextRun->SortGlyphRuns();
1695 out:
1696 if (glyphString)
1697 pango_glyph_string_free(glyphString);
1699 for (pos = items; pos; pos = pos->next)
1700 pango_item_free((PangoItem *)pos->data);
1702 if (items)
1703 g_list_free(items);
1705 g_object_unref(context);
1711 ** language group helpers
1714 struct MozPangoLangGroup {
1715 const char *mozLangGroup;
1716 const char *PangoLang;
1719 static const MozPangoLangGroup MozPangoLangGroups[] = {
1720 { "x-western", "en" },
1721 { "x-central-euro", "pl" },
1722 { "ja", "ja" },
1723 { "zh-TW", "zh-tw" },
1724 { "zh-CN", "zh-cn" },
1725 { "zh-HK", "zh-hk" },
1726 { "ko", "ko" },
1727 { "x-cyrillic", "ru" },
1728 { "x-baltic", "lv" },
1729 { "el", "el" },
1730 { "tr", "tr" },
1731 { "th", "th" },
1732 { "he", "he" },
1733 { "ar", "ar" },
1734 { "x-devanagari", "hi" },
1735 { "x-tamil", "ta" },
1736 { "x-armn", "ar" },
1737 { "x-beng", "bn" },
1738 { "x-ethi", "et" },
1739 { "x-geor", "ka" },
1740 { "x-gujr", "gu" },
1741 { "x-guru", "pa" },
1742 { "x-khmr", "km" },
1743 { "x-mlym", "ml" },
1744 { "x-cans", "iu" },
1745 { "x-unicode", 0 },
1746 { "x-user-def", 0 },
1749 #define NUM_PANGO_LANG_GROUPS (sizeof (MozPangoLangGroups) / \
1750 sizeof (MozPangoLangGroups[0]))
1752 /* static */
1753 PangoLanguage *
1754 GetPangoLanguage(const nsACString& cname)
1756 // see if the lang group needs to be translated from mozilla's
1757 // internal mapping into fontconfig's
1758 const struct MozPangoLangGroup *langGroup = nsnull;
1760 for (unsigned int i=0; i < NUM_PANGO_LANG_GROUPS; ++i) {
1761 if (cname.Equals(MozPangoLangGroups[i].mozLangGroup,
1762 nsCaseInsensitiveCStringComparator())) {
1763 langGroup = &MozPangoLangGroups[i];
1764 break;
1768 // if there's no lang group, just use the lang group as it was
1769 // passed to us
1771 // we're casting away the const here for the strings - should be
1772 // safe.
1773 if (!langGroup)
1774 return pango_language_from_string(nsPromiseFlatCString(cname).get());
1775 else if (langGroup->PangoLang)
1776 return pango_language_from_string(langGroup->PangoLang);
1778 return nsnull;
1781 // See pango-script-lang-table.h in pango.
1782 static const MozPangoLangGroup PangoAllLangGroup[] = {
1783 { "x-western", "aa" },
1784 { "x-cyrillic", "ab" },
1785 { "x-western", "af" },
1786 { "x-ethi", "am" },
1787 { "ar", "ar" },
1788 { "x-western", "ast" },
1789 { "x-cyrillic", "ava" },
1790 { "x-western", "ay" },
1791 { "x-western", "az" },
1792 { "x-cyrillic", "ba" },
1793 { "x-western", "bam" },
1794 { "x-cyrillic", "be" },
1795 { "x-cyrillic", "bg" },
1796 { "x-devanagari", "bh" },
1797 { "x-devanagari", "bho" },
1798 { "x-western", "bi" },
1799 { "x-western", "bin" },
1800 { "x-beng", "bn" },
1801 { 0, "bo" }, // PANGO_SCRIPT_TIBETAN
1802 { "x-western", "br" },
1803 { "x-western", "bs" },
1804 { "x-cyrillic", "bua" },
1805 { "x-western", "ca" },
1806 { "x-cyrillic", "ce" },
1807 { "x-western", "ch" },
1808 { "x-cyrillic", "chm" },
1809 { 0, "chr" }, // PANGO_SCRIPT_CHEROKEE
1810 { "x-western", "co" },
1811 { "x-central-euro", "cs" }, // PANGO_SCRIPT_LATIN
1812 { "x-cyrillic", "cu" },
1813 { "x-cyrillic", "cv" },
1814 { "x-western", "cy" },
1815 { "x-western", "da" },
1816 { "x-central-euro", "de" }, // PANGO_SCRIPT_LATIN
1817 { 0, "dz" }, // PANGO_SCRIPT_TIBETAN
1818 { "el", "el" },
1819 { "x-western", "en" },
1820 { "x-western", "eo" },
1821 { "x-western", "es" },
1822 { "x-western", "et" },
1823 { "x-western", "eu" },
1824 { "ar", "fa" },
1825 { "x-western", "fi" },
1826 { "x-western", "fj" },
1827 { "x-western", "fo" },
1828 { "x-western", "fr" },
1829 { "x-western", "ful" },
1830 { "x-western", "fur" },
1831 { "x-western", "fy" },
1832 { "x-western", "ga" },
1833 { "x-western", "gd" },
1834 { "x-ethi", "gez" },
1835 { "x-western", "gl" },
1836 { "x-western", "gn" },
1837 { "x-gujr", "gu" },
1838 { "x-western", "gv" },
1839 { "x-western", "ha" },
1840 { "x-western", "haw" },
1841 { "he", "he" },
1842 { "x-devanagari", "hi" },
1843 { "x-western", "ho" },
1844 { "x-central-euro", "hr" }, // PANGO_SCRIPT_LATIN
1845 { "x-western", "hu" },
1846 { "x-armn", "hy" },
1847 { "x-western", "ia" },
1848 { "x-western", "ibo" },
1849 { "x-western", "id" },
1850 { "x-western", "ie" },
1851 { "x-cyrillic", "ik" },
1852 { "x-western", "io" },
1853 { "x-western", "is" },
1854 { "x-western", "it" },
1855 { "x-cans", "iu" },
1856 { "ja", "ja" },
1857 { "x-geor", "ka" },
1858 { "x-cyrillic", "kaa" },
1859 { "x-western", "ki" },
1860 { "x-cyrillic", "kk" },
1861 { "x-western", "kl" },
1862 { "x-khmr", "km" },
1863 { 0, "kn" }, // PANGO_SCRIPT_KANNADA
1864 { "ko", "ko" },
1865 { "x-devanagari", "kok" },
1866 { "x-devanagari", "ks" },
1867 { "x-cyrillic", "ku" },
1868 { "x-cyrillic", "kum" },
1869 { "x-cyrillic", "kv" },
1870 { "x-western", "kw" },
1871 { "x-cyrillic", "ky" },
1872 { "x-western", "la" },
1873 { "x-western", "lb" },
1874 { "x-cyrillic", "lez" },
1875 { 0, "lo" }, // PANGO_SCRIPT_LAO
1876 { "x-western", "lt" },
1877 { "x-western", "lv" },
1878 { "x-western", "mg" },
1879 { "x-western", "mh" },
1880 { "x-western", "mi" },
1881 { "x-cyrillic", "mk" },
1882 { "x-mlym", "ml" },
1883 { 0, "mn" }, // PANGO_SCRIPT_MONGOLIAN
1884 { "x-western", "mo" },
1885 { "x-devanagari", "mr" },
1886 { "x-western", "mt" },
1887 { 0, "my" }, // PANGO_SCRIPT_MYANMAR
1888 { "x-western", "nb" },
1889 { "x-devanagari", "ne" },
1890 { "x-western", "nl" },
1891 { "x-western", "nn" },
1892 { "x-western", "no" },
1893 { "x-western", "ny" },
1894 { "x-western", "oc" },
1895 { "x-western", "om" },
1896 { 0, "or" }, // PANGO_SCRIPT_ORIYA
1897 { "x-cyrillic", "os" },
1898 { "x-central-euro", "pl" }, // PANGO_SCRIPT_LATIN
1899 { "x-western", "pt" },
1900 { "x-western", "rm" },
1901 { "x-western", "ro" },
1902 { "x-cyrillic", "ru" },
1903 { "x-devanagari", "sa" },
1904 { "x-cyrillic", "sah" },
1905 { "x-western", "sco" },
1906 { "x-western", "se" },
1907 { "x-cyrillic", "sel" },
1908 { "x-cyrillic", "sh" },
1909 { 0, "si" }, // PANGO_SCRIPT_SINHALA
1910 { "x-central-euro", "sk" }, // PANGO_SCRIPT_LATIN
1911 { "x-central-euro", "sl" }, // PANGO_SCRIPT_LATIN
1912 { "x-western", "sm" },
1913 { "x-western", "sma" },
1914 { "x-western", "smj" },
1915 { "x-western", "smn" },
1916 { "x-western", "sms" },
1917 { "x-western", "so" },
1918 { "x-western", "sq" },
1919 { "x-cyrillic", "sr" },
1920 { "x-western", "sv" },
1921 { "x-western", "sw" },
1922 { 0, "syr" }, // PANGO_SCRIPT_SYRIAC
1923 { "x-tamil", "ta" },
1924 { 0, "te" }, // PANGO_SCRIPT_TELUGU
1925 { "x-cyrillic", "tg" },
1926 { "th", "th" },
1927 { "x-ethi", "ti-er" },
1928 { "x-ethi", "ti-et" },
1929 { "x-ethi", "tig" },
1930 { "x-cyrillic", "tk" },
1931 { 0, "tl" }, // PANGO_SCRIPT_TAGALOG
1932 { "x-western", "tn" },
1933 { "x-western", "to" },
1934 { "x-western", "tr" },
1935 { "x-western", "ts" },
1936 { "x-cyrillic", "tt" },
1937 { "x-western", "tw" },
1938 { "x-cyrillic", "tyv" },
1939 { "ar", "ug" },
1940 { "x-cyrillic", "uk" },
1941 { "ar", "ur" },
1942 { "x-cyrillic", "uz" },
1943 { "x-western", "ven" },
1944 { "x-western", "vi" },
1945 { "x-western", "vo" },
1946 { "x-western", "vot" },
1947 { "x-western", "wa" },
1948 { "x-western", "wen" },
1949 { "x-western", "wo" },
1950 { "x-western", "xh" },
1951 { "x-western", "yap" },
1952 { "he", "yi" },
1953 { "x-western", "yo" },
1954 { "zh-CN", "zh-cn" },
1955 { "zh-HK", "zh-hk" },
1956 { "zh-HK", "zh-mo" },
1957 { "zh-CN", "zh-sg" },
1958 { "zh-TW", "zh-tw" },
1959 { "x-western", "zu" },
1962 #define NUM_PANGO_ALL_LANG_GROUPS (G_N_ELEMENTS (PangoAllLangGroup))
1964 gfxPangoFontCache::gfxPangoFontCache()
1966 mPangoFonts.Init(500);
1969 gfxPangoFontCache::~gfxPangoFontCache()
1973 void
1974 gfxPangoFontCache::Put(const PangoFontDescription *aFontDesc, PangoFont *aPangoFont)
1976 if (mPangoFonts.Count() > 5000)
1977 mPangoFonts.Clear();
1978 PRUint32 key = pango_font_description_hash(aFontDesc);
1979 gfxPangoFontWrapper *value = new gfxPangoFontWrapper(aPangoFont);
1980 if (!value)
1981 return;
1982 mPangoFonts.Put(key, value);
1985 PangoFont*
1986 gfxPangoFontCache::Get(const PangoFontDescription *aFontDesc)
1988 PRUint32 key = pango_font_description_hash(aFontDesc);
1989 gfxPangoFontWrapper *value;
1990 if (!mPangoFonts.Get(key, &value))
1991 return nsnull;
1992 PangoFont *font = value->Get();
1993 g_object_ref(font);
1994 return font;