Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / gfx / thebes / src / gfxPangoFonts.cpp
blob41ceb8edcb4ca11bf072ec7422be5757b9c1c77b
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 "nsMathUtils.h"
52 #include "nsServiceManagerUtils.h"
53 #include "nsILanguageAtomService.h"
55 #include "gfxContext.h"
56 #include "gfxPlatformGtk.h"
57 #include "gfxPangoFonts.h"
58 #include "gfxFontconfigUtils.h"
59 #include "gfxUserFontSet.h"
61 #include <freetype/tttables.h>
63 #include <cairo.h>
64 #include <cairo-ft.h>
66 #include <fontconfig/fcfreetype.h>
67 #include <pango/pango.h>
68 #include <pango/pangofc-fontmap.h>
70 #ifdef MOZ_WIDGET_GTK2
71 #include <gdk/gdkscreen.h>
72 #endif
74 #include <math.h>
76 #define FLOAT_PANGO_SCALE ((gfxFloat)PANGO_SCALE)
78 #ifndef PANGO_VERSION_CHECK
79 #define PANGO_VERSION_CHECK(x,y,z) 0
80 #endif
81 #ifndef PANGO_GLYPH_UNKNOWN_FLAG
82 #define PANGO_GLYPH_UNKNOWN_FLAG ((PangoGlyph)0x10000000)
83 #endif
84 #ifndef PANGO_GLYPH_EMPTY
85 #define PANGO_GLYPH_EMPTY ((PangoGlyph)0)
86 #endif
87 // For g a PangoGlyph,
88 #define IS_MISSING_GLYPH(g) ((g) & PANGO_GLYPH_UNKNOWN_FLAG)
89 #define IS_EMPTY_GLYPH(g) ((g) == PANGO_GLYPH_EMPTY)
91 // Same as pango_units_from_double from Pango 1.16 (but not in older versions)
92 int moz_pango_units_from_double(double d) {
93 return NS_lround(d * FLOAT_PANGO_SCALE);
96 static PangoLanguage *GuessPangoLanguage(const nsACString& aLangGroup);
98 static cairo_scaled_font_t *CreateScaledFont(FcPattern *aPattern);
100 static PangoFontMap *gPangoFontMap;
101 static PangoFontMap *GetPangoFontMap();
103 static FT_Library gFTLibrary;
104 static nsILanguageAtomService* gLangService;
106 NS_SPECIALIZE_TEMPLATE
107 class nsAutoRefTraits<PangoFont> : public gfxGObjectRefTraits<PangoFont> { };
109 NS_SPECIALIZE_TEMPLATE
110 class nsAutoRefTraits<PangoCoverage>
111 : public nsPointerRefTraits<PangoCoverage> {
112 public:
113 static void Release(PangoCoverage *aPtr) { pango_coverage_unref(aPtr); }
114 static void AddRef(PangoCoverage *aPtr) { pango_coverage_ref(aPtr); }
118 // FC_FAMILYLANG and FC_FULLNAME were introduced in fontconfig-2.2.97
119 // and so fontconfig-2.3.0 (2005).
120 #ifndef FC_FAMILYLANG
121 #define FC_FAMILYLANG "familylang"
122 #endif
123 #ifndef FC_FULLNAME
124 #define FC_FULLNAME "fullname"
125 #endif
127 // Rounding and truncation functions for a FreeType fixed point number
128 // (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
129 // part and low 6 bits for the fractional part.
130 #define FLOAT_FROM_26_6(x) ((x) / 64.0)
131 #define FLOAT_FROM_16_16(x) ((x) / 65536.0)
132 #define ROUND_26_6_TO_INT(x) ((x) >= 0 ? ((32 + (x)) >> 6) \
133 : -((32 - (x)) >> 6))
134 // aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
135 static inline FT_Long
136 ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
138 FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
139 return ROUND_26_6_TO_INT(fixed26dot6);
142 // A namespace for @font-face family names in FcPatterns so that fontconfig
143 // aliases do not pick up families from @font-face rules and so that
144 // fontconfig rules can distinguish between web fonts and platform fonts.
145 // http://lists.freedesktop.org/archives/fontconfig/2008-November/003037.html
146 #define FONT_FACE_FAMILY_PREFIX "@font-face:"
149 * gfxFcFontEntry:
151 * An abstract class for objects in a gfxUserFontSet that can provide an
152 * FcPattern* handle to a font face.
154 * Separate implementations of this class support local fonts from src:local()
155 * and web fonts from src:url().
158 class gfxFcFontEntry : public gfxFontEntry {
159 public:
160 FcPattern *GetPattern()
162 if (!mPattern) {
163 InitPattern();
165 return mPattern;
168 protected:
169 gfxFcFontEntry(const gfxProxyFontEntry &aProxyEntry)
170 // store the family name
171 : gfxFontEntry(aProxyEntry.mFamily->Name())
173 mItalic = aProxyEntry.mItalic;
174 mWeight = aProxyEntry.mWeight;
175 mStretch = aProxyEntry.mStretch;
178 // Initializes mPattern.
179 virtual void InitPattern() = 0;
181 // Helper function to be called from InitPattern() to change the pattern
182 // so that it matches the CSS style descriptors and so gets properly
183 // sorted in font selection. This also avoids synthetic style effects
184 // being added by the renderer when the style of the font itself does not
185 // match the descriptor provided by the author.
186 void AdjustPatternToCSS();
188 nsCountedRef<FcPattern> mPattern;
191 void
192 gfxFcFontEntry::AdjustPatternToCSS()
194 int fontWeight = -1;
195 FcPatternGetInteger(mPattern, FC_WEIGHT, 0, &fontWeight);
196 int cssWeight = gfxFontconfigUtils::FcWeightForBaseWeight(mWeight);
197 if (cssWeight != fontWeight) {
198 FcPatternDel(mPattern, FC_WEIGHT);
199 FcPatternAddInteger(mPattern, FC_WEIGHT, cssWeight);
202 int fontSlant;
203 FcResult res = FcPatternGetInteger(mPattern, FC_SLANT, 0, &fontSlant);
204 // gfxFontEntry doesn't understand the difference between oblique
205 // and italic.
206 if (res != FcResultMatch ||
207 IsItalic() != (fontSlant != FC_SLANT_ROMAN)) {
208 FcPatternDel(mPattern, FC_SLANT);
209 FcPatternAddInteger(mPattern, FC_SLANT,
210 IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN);
213 // Ensure that there is a fullname property (if there is a family
214 // property) so that fontconfig rules can identify the real name of the
215 // font, because the family property will be replaced.
216 FcChar8 *fullname;
217 FcChar8 *fontFamily;
218 if (FcPatternGetString(mPattern,
219 FC_FULLNAME, 0, &fullname) == FcResultNoMatch &&
220 FcPatternGetString(mPattern,
221 FC_FAMILY, 0, &fontFamily) == FcResultMatch) {
222 // Construct fullname from family and style
223 nsCAutoString fullname(gfxFontconfigUtils::ToCString(fontFamily));
224 FcChar8 *fontStyle;
225 if (FcPatternGetString(mPattern,
226 FC_STYLE, 0, &fontStyle) == FcResultMatch) {
227 const char *style = gfxFontconfigUtils::ToCString(fontStyle);
228 if (strcmp(style, "Regular") != 0) {
229 fullname.Append(' ');
230 fullname.Append(style);
234 FcPatternAddString(mPattern, FC_FULLNAME,
235 gfxFontconfigUtils::ToFcChar8(fullname.get()));
238 nsCAutoString family;
239 family.Append(FONT_FACE_FAMILY_PREFIX);
240 AppendUTF16toUTF8(Name(), family);
242 FcPatternDel(mPattern, FC_FAMILY);
243 FcPatternDel(mPattern, FC_FAMILYLANG);
244 FcPatternAddString(mPattern, FC_FAMILY,
245 gfxFontconfigUtils::ToFcChar8(family.get()));
249 * gfxDownloadedFcFontEntry:
251 * An implementation of gfxFcFontEntry for web fonts from src:url().
254 class gfxDownloadedFcFontEntry : public gfxFcFontEntry {
255 public:
256 // This takes ownership of the face.
257 gfxDownloadedFcFontEntry(const gfxProxyFontEntry &aProxyEntry,
258 nsISupports *aLoader, FT_Face aFace)
259 : gfxFcFontEntry(aProxyEntry), mLoader(aLoader), mFace(aFace)
261 NS_PRECONDITION(aFace != NULL, "aFace is NULL!");
264 virtual ~gfxDownloadedFcFontEntry();
266 // Returns a PangoCoverage owned by the FontEntry. The caller must add a
267 // reference if it wishes to keep the PangoCoverage longer than the
268 // lifetime of the FontEntry.
269 PangoCoverage *GetPangoCoverage();
271 protected:
272 virtual void InitPattern();
274 // mLoader holds a reference to memory used by mFace.
275 nsCOMPtr<nsISupports> mLoader;
276 FT_Face mFace;
277 // mPangoCoverage is the charset property of mPattern translated to a
278 // format that Pango understands. A reference is kept here so that it can
279 // be shared by multiple PangoFonts (of different sizes).
280 nsAutoRef<PangoCoverage> mPangoCoverage;
283 // A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
284 static const char *kFontEntryFcProp = "-moz-font-entry";
286 static FcBool AddDownloadedFontEntry(FcPattern *aPattern,
287 gfxDownloadedFcFontEntry *aFontEntry)
289 FcValue value;
290 value.type = FcTypeFTFace; // void* field of union
291 value.u.f = aFontEntry;
293 return FcPatternAdd(aPattern, kFontEntryFcProp, value, FcFalse);
296 static FcBool DelDownloadedFontEntry(FcPattern *aPattern)
298 return FcPatternDel(aPattern, kFontEntryFcProp);
301 static gfxDownloadedFcFontEntry *GetDownloadedFontEntry(FcPattern *aPattern)
303 FcValue value;
304 if (FcPatternGet(aPattern, kFontEntryFcProp, 0, &value) != FcResultMatch)
305 return nsnull;
307 if (value.type != FcTypeFTFace) {
308 NS_NOTREACHED("Wrong type for -moz-font-entry font property");
309 return nsnull;
312 return static_cast<gfxDownloadedFcFontEntry*>(value.u.f);
315 gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry()
317 if (mPattern) {
318 // Remove back reference to this font entry and the face in case
319 // anyone holds a reference to the pattern.
320 DelDownloadedFontEntry(mPattern);
321 FcPatternDel(mPattern, FC_FT_FACE);
323 FT_Done_Face(mFace);
326 typedef FcPattern* (*QueryFaceFunction)(const FT_Face face,
327 const FcChar8 *file, int id,
328 FcBlanks *blanks);
330 static QueryFaceFunction
331 GetFcFreeTypeQueryFace()
333 PRLibrary *lib = nsnull;
334 PRFuncPtr result =
335 PR_FindFunctionSymbolAndLibrary("FcFreeTypeQueryFace", &lib);
336 if (lib) {
337 PR_UnloadLibrary(lib);
340 return reinterpret_cast<QueryFaceFunction>(result);
343 void
344 gfxDownloadedFcFontEntry::InitPattern()
346 static QueryFaceFunction sQueryFacePtr = GetFcFreeTypeQueryFace();
348 // FcFreeTypeQueryFace is the same function used to construct patterns for
349 // system fonts and so is the preferred function to use for this purpose.
350 // This will set up the langset property, which helps with sorting, and
351 // the foundry, fullname, and fontversion properties, which properly
352 // identify the font to fontconfig rules. However, FcFreeTypeQueryFace is
353 // available only from fontconfig-2.4.2 (December 2006). (CentOS 5.0 has
354 // fontconfig-2.4.1.)
355 if (sQueryFacePtr) {
356 // The "file" argument cannot be NULL (in fontconfig-2.6.0 at least).
357 // The dummy file passed here is removed below.
359 // When fontconfig scans the system fonts, FcConfigGetBlanks(NULL) is
360 // passed as the "blanks" argument, which provides that unexpectedly
361 // blank glyphs are elided. Here, however, we pass NULL for "blanks",
362 // effectively assuming that, if the font has a blank glyph, then the
363 // author intends any associated character to be rendered blank.
364 mPattern.own((*sQueryFacePtr)(mFace,
365 gfxFontconfigUtils::ToFcChar8(""), 0,
366 NULL));
367 if (!mPattern)
368 // Either OOM, or fontconfig chose to skip this font because it
369 // has "no encoded characters", which I think means "BDF and PCF
370 // fonts which are not in Unicode (or the effectively equivalent
371 // ISO Latin-1) encoding".
372 return;
374 // These properties don't make sense for this face without a file.
375 FcPatternDel(mPattern, FC_FILE);
376 FcPatternDel(mPattern, FC_INDEX);
378 } else {
379 // Do the minimum necessary to construct a pattern for sorting.
381 // FC_CHARSET is vital to determine which characters are supported.
382 nsAutoRef<FcCharSet> charset(FcFreeTypeCharSet(mFace, NULL));
383 // If there are no characters then assume we don't know how to read
384 // this font and leave mPattern NULL.
385 if (!charset || FcCharSetCount(charset) == 0)
386 return;
388 mPattern.own(FcPatternCreate());
389 FcPatternAddCharSet(mPattern, FC_CHARSET, charset);
391 // FC_PIXEL_SIZE can be important for font selection of fixed-size
392 // fonts.
393 if (!(mFace->face_flags & FT_FACE_FLAG_SCALABLE)) {
394 for (FT_Int i = 0; i < mFace->num_fixed_sizes; ++i) {
395 #if HAVE_FT_BITMAP_SIZE_Y_PPEM
396 double size = FLOAT_FROM_26_6(mFace->available_sizes[i].y_ppem);
397 #else
398 double size = mFace->available_sizes[i].height;
399 #endif
400 FcPatternAddDouble (mPattern, FC_PIXEL_SIZE, size);
403 // Not sure whether this is important;
404 // imitating FcFreeTypeQueryFace:
405 FcPatternAddBool (mPattern, FC_ANTIALIAS, FcFalse);
408 // Setting up the FC_LANGSET property is very difficult with the APIs
409 // available prior to FcFreeTypeQueryFace. Having no FC_LANGSET
410 // property seems better than having a property with an empty LangSet.
411 // With no FC_LANGSET property, fontconfig sort functions will
412 // consider this face to have the same priority as (otherwise equal)
413 // faces that have support for the primary requested language, but
414 // will not consider any language to have been satisfied (and so will
415 // continue to look for a face with language support in fallback
416 // fonts).
419 FcPatternAddFTFace(mPattern, FC_FT_FACE, mFace);
420 AddDownloadedFontEntry(mPattern, this);
422 AdjustPatternToCSS();
425 static PangoCoverage *NewPangoCoverage(FcPattern *aFont)
427 // This uses g_slice_alloc which will abort on OOM rather than return NULL.
428 PangoCoverage *coverage = pango_coverage_new();
430 FcCharSet *charset;
431 if (FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset) != FcResultMatch)
432 return coverage; // empty
434 FcChar32 base;
435 FcChar32 map[FC_CHARSET_MAP_SIZE];
436 FcChar32 next;
437 for (base = FcCharSetFirstPage(charset, map, &next);
438 base != FC_CHARSET_DONE;
439 base = FcCharSetNextPage(charset, map, &next)) {
440 for (PRUint32 i = 0; i < FC_CHARSET_MAP_SIZE; ++i) {
441 PRUint32 offset = 0;
442 FcChar32 bitmap = map[i];
443 for (; bitmap; bitmap >>= 1) {
444 if (bitmap & 1) {
445 pango_coverage_set(coverage, base + offset,
446 PANGO_COVERAGE_EXACT);
448 ++offset;
450 base += 32;
453 return coverage;
456 PangoCoverage *
457 gfxDownloadedFcFontEntry::GetPangoCoverage()
459 if (!mPangoCoverage) {
460 mPangoCoverage.own(NewPangoCoverage(mPattern));
462 return mPangoCoverage;
466 * gfxFcFont
468 * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
469 * cairo_scaled_font created from an FcPattern.
472 class gfxFcFont : public gfxFont {
473 public:
474 virtual ~gfxFcFont ();
475 static already_AddRefed<gfxFcFont> GetOrMakeFont(FcPattern *aPattern);
477 virtual const gfxFont::Metrics& GetMetrics();
479 virtual nsString GetUniqueName();
481 // Get the glyphID of a space
482 virtual PRUint32 GetSpaceGlyph() {
483 NS_ASSERTION(GetStyle()->size != 0,
484 "forgot to short-circuit a text run with zero-sized font?");
485 GetMetrics();
486 return mSpaceGlyph;
489 cairo_scaled_font_t *CairoScaledFont() { return mCairoFont; }
490 void GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents);
492 protected:
493 cairo_scaled_font_t *mCairoFont;
495 PRUint32 mSpaceGlyph;
496 Metrics mMetrics;
497 PRPackedBool mHasMetrics;
499 gfxFcFont(cairo_scaled_font_t *aCairoFont,
500 gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle);
502 virtual PRBool SetupCairoFont(gfxContext *aContext);
504 // key for locating a gfxFcFont corresponding to a cairo_scaled_font
505 static cairo_user_data_key_t sGfxFontKey;
508 class LockedFTFace {
509 public:
510 LockedFTFace(gfxFcFont *aFont)
511 : mGfxFont(aFont),
512 mFace(cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont()))
516 ~LockedFTFace()
518 if (mFace) {
519 cairo_ft_scaled_font_unlock_face(mGfxFont->CairoScaledFont());
523 FT_Face get()
525 return mFace;
529 * Get extents for a simple character representable by a single glyph.
530 * The return value is the glyph id of that glyph or zero if no such glyph
531 * exists. aExtents is only set when this returns a non-zero glyph id.
533 PRUint32 GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
535 void GetMetrics(gfxFont::Metrics* aMetrics, PRUint32* aSpaceGlyph);
537 private:
538 nsRefPtr<gfxFcFont> mGfxFont;
539 FT_Face mFace;
543 * gfxPangoFcFont:
545 * An implementation of PangoFcFont that wraps a gfxFont so that it can be
546 * passed to PangoRenderFc shapers.
548 * Many of these will be created for pango_itemize, but most will only be
549 * tested for coverage of individual characters (and sometimes not even that).
550 * Therefore the gfxFont is only constructed if and when needed.
553 #define GFX_TYPE_PANGO_FC_FONT (gfx_pango_fc_font_get_type())
554 #define GFX_PANGO_FC_FONT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFont))
555 #define GFX_IS_PANGO_FC_FONT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FC_FONT))
557 /* static */
558 GType gfx_pango_fc_font_get_type (void);
560 #define GFX_PANGO_FC_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFontClass))
561 #define GFX_IS_PANGO_FC_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FC_FONT))
562 #define GFX_PANGO_FC_FONT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFontClass))
564 // This struct is POD so that it can be used as a GObject.
565 struct gfxPangoFcFont {
566 PangoFcFont parent_instance;
568 FcPattern *mRequestedPattern;
569 PangoCoverage *mCoverage;
570 gfxFcFont *mGfxFont;
572 static nsReturnRef<PangoFont>
573 NewFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern)
575 // A pattern is needed for pango_fc_font_finalize.
577 // Adding a ref to the requested pattern and one of fontconfig's
578 // patterns uses much less memory than using the fully resolved
579 // pattern here, and saves calling FcFontRenderPrepare when the
580 // PangoFont is only tested for character coverage.
582 // Normally the is_hinted field of the PangoFcFont is set based on the
583 // FC_HINTING property on the pattern at construction, but this
584 // property is not known until after RenderPrepare. is_hinted is used
585 // by pango_fc_font_kern_glyphs, which is sometimes used by
586 // pango_ot_buffer_output. is_hinted will be set when the gfxFont is
587 // constructed for PangoFcFont::lock_face.
588 gfxPangoFcFont *font = static_cast<gfxPangoFcFont*>
589 (g_object_new(GFX_TYPE_PANGO_FC_FONT,
590 "pattern", aFontPattern, NULL));
592 // Save the requested pattern for FcFontRenderPrepare.
593 FcPatternReference(aRequestedPattern);
594 font->mRequestedPattern = aRequestedPattern;
596 // PangoFcFont::get_coverage wants an FcFontMap. (PangoFcFontMap
597 // usually sets this after calling PangoFcFontMap::create_font().)
598 PangoFcFont *fc_font = &font->parent_instance;
599 fc_font->fontmap = GetPangoFontMap();
600 g_object_ref(fc_font->fontmap);
602 return nsReturnRef<PangoFont>(PANGO_FONT(font));
605 static gfxFcFont *GfxFont(gfxPangoFcFont *self)
607 if (!self->mGfxFont) {
608 PangoFcFont *fc_font = &self->parent_instance;
610 if (NS_LIKELY(self->mRequestedPattern)) {
611 // Created with gfxPangoFcFont::NewFont()
612 nsAutoRef<FcPattern> renderPattern
613 (FcFontRenderPrepare(NULL, self->mRequestedPattern,
614 fc_font->font_pattern));
615 if (!renderPattern)
616 return nsnull;
618 FcBool hinting = FcTrue;
619 FcPatternGetBool(renderPattern, FC_HINTING, 0, &hinting);
620 fc_font->is_hinted = hinting;
622 // is_transformed does not appear to be used anywhere but looks
623 // like it should be set.
624 FcMatrix *matrix;
625 FcResult result = FcPatternGetMatrix(renderPattern,
626 FC_MATRIX, 0, &matrix);
627 fc_font->is_transformed =
628 result == FcResultMatch &&
629 (matrix->xy != 0.0 || matrix->yx != 0.0 ||
630 matrix->xx != 1.0 || matrix->yy != 1.0);
632 self->mGfxFont = gfxFcFont::GetOrMakeFont(renderPattern).get();
633 if (self->mGfxFont) {
634 // Finished with the requested pattern
635 FcPatternDestroy(self->mRequestedPattern);
636 self->mRequestedPattern = NULL;
639 } else {
640 // Created with gfxPangoFontMap::create_font()
641 self->mGfxFont =
642 gfxFcFont::GetOrMakeFont(fc_font->font_pattern).get();
645 return self->mGfxFont;
648 static cairo_scaled_font_t *CairoFont(gfxPangoFcFont *self)
650 return gfxPangoFcFont::GfxFont(self)->CairoScaledFont();
654 struct gfxPangoFcFontClass {
655 PangoFcFontClass parent_class;
658 G_DEFINE_TYPE (gfxPangoFcFont, gfx_pango_fc_font, PANGO_TYPE_FC_FONT)
660 static void
661 gfx_pango_fc_font_init(gfxPangoFcFont *font)
666 static void
667 gfx_pango_fc_font_finalize(GObject *object)
669 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(object);
671 if (self->mRequestedPattern)
672 FcPatternDestroy(self->mRequestedPattern);
673 if (self->mCoverage)
674 pango_coverage_unref(self->mCoverage);
675 NS_IF_RELEASE(self->mGfxFont);
677 // The parent class removes the reference to parent_instance->fontmap.
679 G_OBJECT_CLASS(gfx_pango_fc_font_parent_class)->finalize(object);
682 static PangoCoverage *
683 gfx_pango_fc_font_get_coverage(PangoFont *font, PangoLanguage *lang)
685 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
687 // The coverage is requested often enough that it is worth holding a
688 // reference on the font.
689 if (!self->mCoverage) {
690 FcPattern *pattern = self->parent_instance.font_pattern;
691 gfxDownloadedFcFontEntry *downloadedFontEntry =
692 GetDownloadedFontEntry(pattern);
693 // The parent class implementation requires the font pattern to have
694 // a file and caches results against that filename. This is not
695 // suitable for web fonts.
696 if (!downloadedFontEntry) {
697 self->mCoverage =
698 PANGO_FONT_CLASS(gfx_pango_fc_font_parent_class)->
699 get_coverage(font, lang);
700 } else {
701 self->mCoverage =
702 pango_coverage_ref(downloadedFontEntry->GetPangoCoverage());
706 return pango_coverage_ref(self->mCoverage);
709 static PangoFontDescription *
710 gfx_pango_fc_font_describe(PangoFont *font)
712 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
713 PangoFcFont *fcFont = &self->parent_instance;
714 PangoFontDescription *result =
715 pango_font_description_copy(fcFont->description);
717 gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
718 if (gfxFont) {
719 double pixelsize = gfxFont->GetStyle()->size;
720 double dpi = gfxPlatformGtk::DPI();
721 gint size = moz_pango_units_from_double(pixelsize * dpi / 72.0);
722 pango_font_description_set_size(result, size);
724 return result;
727 static PangoFontDescription *
728 gfx_pango_fc_font_describe_absolute(PangoFont *font)
730 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
731 PangoFcFont *fcFont = &self->parent_instance;
732 PangoFontDescription *result =
733 pango_font_description_copy(fcFont->description);
735 gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
736 if (gfxFont) {
737 double size = gfxFont->GetStyle()->size * PANGO_SCALE;
738 pango_font_description_set_absolute_size(result, size);
740 return result;
743 static void
744 gfx_pango_fc_font_get_glyph_extents(PangoFont *font, PangoGlyph glyph,
745 PangoRectangle *ink_rect,
746 PangoRectangle *logical_rect)
748 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
749 gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
751 if (IS_MISSING_GLYPH(glyph)) {
752 const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
754 PangoRectangle rect;
755 rect.x = 0;
756 rect.y = moz_pango_units_from_double(-metrics.maxAscent);
757 rect.width = moz_pango_units_from_double(metrics.aveCharWidth);
758 rect.height = moz_pango_units_from_double(metrics.maxHeight);
759 if (ink_rect) {
760 *ink_rect = rect;
762 if (logical_rect) {
763 *logical_rect = rect;
765 return;
768 if (logical_rect) {
769 // logical_rect.width is possibly used by pango_ot_buffer_output (used
770 // by many shapers) and used by fallback_engine_shape (possibly used
771 // by pango_shape and pango_itemize when no glyphs are found). I
772 // doubt the other fields will be used but we won't have any way to
773 // detecting if they are so we'd better set them.
774 const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
775 logical_rect->y = moz_pango_units_from_double(-metrics.maxAscent);
776 logical_rect->height = moz_pango_units_from_double(metrics.maxHeight);
779 cairo_text_extents_t extents;
780 if (IS_EMPTY_GLYPH(glyph)) {
781 new (&extents) cairo_text_extents_t(); // zero
782 } else {
783 gfxFont->GetGlyphExtents(glyph, &extents);
786 if (ink_rect) {
787 ink_rect->x = moz_pango_units_from_double(extents.x_bearing);
788 ink_rect->y = moz_pango_units_from_double(extents.y_bearing);
789 ink_rect->width = moz_pango_units_from_double(extents.width);
790 ink_rect->height = moz_pango_units_from_double(extents.height);
792 if (logical_rect) {
793 logical_rect->x = 0;
794 logical_rect->width = moz_pango_units_from_double(extents.x_advance);
798 static PangoFontMetrics *
799 gfx_pango_fc_font_get_metrics(PangoFont *font, PangoLanguage *language)
801 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
803 // This uses g_slice_alloc which will abort on OOM rather than return NULL.
804 PangoFontMetrics *result = pango_font_metrics_new();
806 gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(self);
807 if (gfxFont) {
808 const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
810 result->ascent = moz_pango_units_from_double(metrics.maxAscent);
811 result->descent = moz_pango_units_from_double(metrics.maxDescent);
812 result->approximate_char_width =
813 moz_pango_units_from_double(metrics.aveCharWidth);
814 result->approximate_digit_width =
815 moz_pango_units_from_double(metrics.zeroOrAveCharWidth);
816 result->underline_position =
817 moz_pango_units_from_double(metrics.underlineOffset);
818 result->underline_thickness =
819 moz_pango_units_from_double(metrics.underlineSize);
820 result->strikethrough_position =
821 moz_pango_units_from_double(metrics.strikeoutOffset);
822 result->strikethrough_thickness =
823 moz_pango_units_from_double(metrics.strikeoutSize);
825 return result;
828 static FT_Face
829 gfx_pango_fc_font_lock_face(PangoFcFont *font)
831 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
832 return cairo_ft_scaled_font_lock_face(gfxPangoFcFont::CairoFont(self));
835 static void
836 gfx_pango_fc_font_unlock_face(PangoFcFont *font)
838 gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
839 cairo_ft_scaled_font_unlock_face(gfxPangoFcFont::CairoFont(self));
842 static void
843 gfx_pango_fc_font_class_init (gfxPangoFcFontClass *klass)
845 GObjectClass *object_class = G_OBJECT_CLASS (klass);
846 PangoFontClass *font_class = PANGO_FONT_CLASS (klass);
847 PangoFcFontClass *fc_font_class = PANGO_FC_FONT_CLASS (klass);
849 object_class->finalize = gfx_pango_fc_font_finalize;
851 font_class->get_coverage = gfx_pango_fc_font_get_coverage;
852 // describe is called on errors in pango_shape.
853 font_class->describe = gfx_pango_fc_font_describe;
854 font_class->get_glyph_extents = gfx_pango_fc_font_get_glyph_extents;
855 // get_metrics and describe_absolute are not likely to be used but
856 // implemented because the class makes them available.
857 font_class->get_metrics = gfx_pango_fc_font_get_metrics;
858 font_class->describe_absolute = gfx_pango_fc_font_describe_absolute;
859 // font_class->find_shaper,get_font_map are inherited from PangoFcFontClass
861 // fc_font_class->has_char,get_glyph are inherited
862 fc_font_class->lock_face = gfx_pango_fc_font_lock_face;
863 fc_font_class->unlock_face = gfx_pango_fc_font_unlock_face;
867 * Recording a gfxPangoFontGroup on a PangoContext
870 static GQuark GetFontGroupQuark()
872 // Not using g_quark_from_static_string() because this module may be
873 // unloaded (which would leave a dangling pointer). Using
874 // g_quark_from_string() instead, which creates a small shutdown leak.
875 static GQuark quark = g_quark_from_string("moz-font-group");
876 return quark;
879 static void
880 gfxFontGroup_unref(gpointer data)
882 gfxPangoFontGroup *fontGroup = static_cast<gfxPangoFontGroup*>(data);
883 NS_RELEASE(fontGroup);
886 static void
887 SetFontGroup(PangoContext *aContext, gfxPangoFontGroup *aFontGroup)
889 NS_ADDREF(aFontGroup);
890 g_object_set_qdata_full(G_OBJECT(aContext), GetFontGroupQuark(),
891 aFontGroup, gfxFontGroup_unref);
894 static gfxPangoFontGroup *
895 GetFontGroup(PangoContext *aContext)
897 return static_cast<gfxPangoFontGroup*>
898 (g_object_get_qdata(G_OBJECT(aContext), GetFontGroupQuark()));
902 * gfxFcPangoFontSet:
904 * Translation from a desired FcPattern to a sorted set of font references
905 * (fontconfig cache data) and (when needed) PangoFonts.
908 class gfxFcPangoFontSet {
909 public:
910 THEBES_INLINE_DECL_REFCOUNTING(gfxFcPangoFontSet)
912 explicit gfxFcPangoFontSet(FcPattern *aPattern,
913 gfxUserFontSet *aUserFontSet)
914 : mSortPattern(aPattern), mUserFontSet(aUserFontSet),
915 mFcFontSet(SortPreferredFonts()), mFcFontsTrimmed(0),
916 mHaveFallbackFonts(PR_FALSE)
920 // A reference is held by the FontSet.
921 // The caller may add a ref to keep the font alive longer than the FontSet.
922 PangoFont *GetFontAt(PRUint32 i)
924 if (i >= mFonts.Length() || !mFonts[i].mFont) {
925 // GetFontPatternAt sets up mFonts
926 FcPattern *fontPattern = GetFontPatternAt(i);
927 if (!fontPattern)
928 return NULL;
930 mFonts[i].mFont =
931 gfxPangoFcFont::NewFont(mSortPattern, fontPattern);
933 return mFonts[i].mFont;
936 FcPattern *GetFontPatternAt(PRUint32 i);
938 private:
939 nsReturnRef<FcFontSet> SortPreferredFonts();
940 nsReturnRef<FcFontSet> SortFallbackFonts();
942 struct FontEntry {
943 explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {}
944 nsCountedRef<FcPattern> mPattern;
945 nsCountedRef<PangoFont> mFont;
948 struct LangSupportEntry {
949 LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) :
950 mLang(aLang), mBestSupport(aSupport) {}
951 FcChar8 *mLang;
952 FcLangResult mBestSupport;
955 public:
956 // public for nsTArray
957 class LangComparator {
958 public:
959 PRBool Equals(const LangSupportEntry& a, const FcChar8 *b) const
961 return FcStrCmpIgnoreCase(a.mLang, b) == 0;
965 private:
966 // The requested pattern
967 nsCountedRef<FcPattern> mSortPattern;
968 // Fonts from @font-face rules
969 nsRefPtr<gfxUserFontSet> mUserFontSet;
970 // A (trimmed) list of font patterns and PangoFonts that is built up as
971 // required.
972 nsTArray<FontEntry> mFonts;
973 // Holds a list of font patterns that will be trimmed. This is first set
974 // to a list of preferred fonts. Then, if/when all the preferred fonts
975 // have been trimmed and added to mFonts, this is set to a list of
976 // fallback fonts.
977 nsAutoRef<FcFontSet> mFcFontSet;
978 // The set of characters supported by the fonts in mFonts.
979 nsAutoRef<FcCharSet> mCharSet;
980 // The index of the next font in mFcFontSet that has not yet been
981 // considered for mFonts.
982 int mFcFontsTrimmed;
983 // True iff fallback fonts are either stored in mFcFontSet or have been
984 // trimmed and added to mFonts (so that mFcFontSet is NULL).
985 PRPackedBool mHaveFallbackFonts;
988 // Find the FcPattern for an @font-face font suitable for CSS family |aFamily|
989 // and style |aStyle| properties.
990 static FcPattern *
991 FindFontPattern(gfxUserFontSet *mUserFontSet,
992 const nsACString &aFamily, PRUint8 aStyle, PRUint16 aWeight)
994 // Convert to UTF16
995 NS_ConvertUTF8toUTF16 utf16Family(aFamily);
997 // needsBold is not used here. Instead synthetic bold is enabled through
998 // FcFontRenderPrepare when the weight in the requested pattern is
999 // compared against the weight in the font pattern.
1000 PRBool needsBold;
1002 gfxFontStyle style;
1003 style.style = aStyle;
1004 style.weight = aWeight;
1006 gfxFcFontEntry *fontEntry = static_cast<gfxFcFontEntry*>
1007 (mUserFontSet->FindFontEntry(utf16Family, style, needsBold));
1009 // Accept synthetic oblique for italic and oblique.
1010 if (!fontEntry && aStyle != FONT_STYLE_NORMAL) {
1011 style.style = FONT_STYLE_NORMAL;
1012 fontEntry = static_cast<gfxFcFontEntry*>
1013 (mUserFontSet->FindFontEntry(utf16Family, style, needsBold));
1016 if (!fontEntry)
1017 return NULL;
1019 return fontEntry->GetPattern();
1022 typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object,
1023 int id);
1025 static FcPatternRemoveFunction
1026 GetFcPatternRemove()
1028 PRLibrary *lib = nsnull;
1029 PRFuncPtr result =
1030 PR_FindFunctionSymbolAndLibrary("FcPatternRemove", &lib);
1031 if (lib) {
1032 PR_UnloadLibrary(lib);
1035 return reinterpret_cast<FcPatternRemoveFunction>(result);
1038 // FcPatternRemove is available in fontconfig-2.3.0 (2005)
1039 static FcBool
1040 moz_FcPatternRemove(FcPattern *p, const char *object, int id)
1042 static FcPatternRemoveFunction sFcPatternRemovePtr = GetFcPatternRemove();
1044 if (!sFcPatternRemovePtr)
1045 return FcFalse;
1047 return (*sFcPatternRemovePtr)(p, object, id);
1050 // fontconfig always prefers a matching family to a matching slant, but CSS
1051 // mostly prioritizes slant. The logic here is from CSS 2.1.
1052 static PRBool
1053 SlantIsAcceptable(FcPattern *aFont, int aRequestedSlant)
1055 // CSS accepts (possibly synthetic) oblique for italic.
1056 if (aRequestedSlant == FC_SLANT_ITALIC)
1057 return PR_TRUE;
1059 int slant;
1060 FcResult result = FcPatternGetInteger(aFont, FC_SLANT, 0, &slant);
1061 // Not having a value would be strange.
1062 // fontconfig sort and match functions would consider no value a match.
1063 if (result != FcResultMatch)
1064 return PR_TRUE;
1066 switch (aRequestedSlant) {
1067 case FC_SLANT_ROMAN:
1068 // CSS requires an exact match
1069 return slant == aRequestedSlant;
1070 case FC_SLANT_OBLIQUE:
1071 // Accept synthetic oblique from Roman,
1072 // but CSS doesn't accept italic.
1073 return slant != FC_SLANT_ITALIC;
1076 return PR_TRUE;
1079 // fontconfig prefers a matching family or lang to pixelsize of bitmap
1080 // fonts. CSS suggests a tolerance of 20% on pixelsize.
1081 static PRBool
1082 SizeIsAcceptable(FcPattern *aFont, double aRequestedSize)
1084 double size;
1085 int v = 0;
1086 while (FcPatternGetDouble(aFont,
1087 FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
1088 ++v;
1089 if (5.0 * fabs(size - aRequestedSize) < aRequestedSize)
1090 return PR_TRUE;
1093 // No size means scalable
1094 return v == 0;
1097 // Sorting only the preferred fonts first usually saves having to sort through
1098 // every font on the system.
1099 nsReturnRef<FcFontSet>
1100 gfxFcPangoFontSet::SortPreferredFonts()
1102 gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
1103 if (!utils)
1104 return nsReturnRef<FcFontSet>();
1106 // The list of families in mSortPattern has values with both weak and
1107 // strong bindings. Values with strong bindings should be preferred.
1108 // Values with weak bindings are default fonts that should be considered
1109 // only when the font provides the best support for a requested language
1110 // or after other fonts have satisfied all the requested languages.
1112 // There are no direct fontconfig APIs to get the binding type. The
1113 // binding only takes effect in the sort and match functions.
1115 // |requiredLangs| is a list of requested languages that have not yet been
1116 // satisfied. gfxFontconfigUtils only sets one FC_LANG property value,
1117 // but FcConfigSubstitute may add more values (e.g. prepending "en" to
1118 // "ja" will use western fonts to render Latin/Arabic numerals in Japanese
1119 // text.)
1120 nsAutoTArray<LangSupportEntry,10> requiredLangs;
1121 for (int v = 0; ; ++v) {
1122 FcChar8 *lang;
1123 FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang);
1124 if (result != FcResultMatch) {
1125 // No need to check FcPatternGetLangSet() because
1126 // gfxFontconfigUtils sets only a string value for FC_LANG and
1127 // FcConfigSubstitute cannot add LangSets.
1128 NS_ASSERTION(result != FcResultTypeMismatch,
1129 "Expected a string for FC_LANG");
1130 break;
1133 if (!requiredLangs.Contains(lang, LangComparator())) {
1134 FcLangResult bestLangSupport = utils->GetBestLangSupport(lang);
1135 if (bestLangSupport != FcLangDifferentLang) {
1136 requiredLangs.
1137 AppendElement(LangSupportEntry(lang, bestLangSupport));
1142 nsAutoRef<FcFontSet> fontSet(FcFontSetCreate());
1143 if (!fontSet)
1144 return fontSet.out();
1146 // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever
1147 // doesn't happen, Roman will be used.
1148 int requestedSlant = FC_SLANT_ROMAN;
1149 FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant);
1150 double requestedSize = -1.0;
1151 FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize);
1153 nsTHashtable<gfxFontconfigUtils::DepFcStrEntry> existingFamilies;
1154 existingFamilies.Init(50);
1155 FcChar8 *family;
1156 for (int v = 0;
1157 FcPatternGetString(mSortPattern,
1158 FC_FAMILY, v, &family) == FcResultMatch; ++v) {
1159 nsAutoTArray<nsCountedRef<FcPattern>,1> userFont;
1160 const nsTArray< nsCountedRef<FcPattern> > *familyFonts = nsnull;
1162 if (mUserFontSet) {
1163 // Have some @font-face definitions
1165 nsDependentCString cFamily(gfxFontconfigUtils::ToCString(family));
1166 NS_NAMED_LITERAL_CSTRING(userPrefix, FONT_FACE_FAMILY_PREFIX);
1168 if (StringBeginsWith(cFamily, userPrefix)) {
1169 // This is an @font-face family.
1170 familyFonts = &userFont;
1172 // Trim off the prefix
1173 nsDependentCSubstring cssFamily(cFamily, userPrefix.Length());
1175 PRUint8 thebesStyle =
1176 gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant);
1177 PRUint16 thebesWeight =
1178 gfxFontconfigUtils::GetThebesWeight(mSortPattern);
1180 FcPattern *fontPattern =
1181 FindFontPattern(mUserFontSet, cssFamily,
1182 thebesStyle, thebesWeight);
1184 if (fontPattern) {
1185 userFont.AppendElement(fontPattern);
1190 if (!familyFonts) {
1191 familyFonts = &utils->GetFontsForFamily(family);
1194 if (familyFonts->Length() == 0) {
1195 // There are no fonts matching this family, so there is not point
1196 // in searching for this family in the FontSort.
1198 // Perhaps the original pattern should be retained for
1199 // FcFontRenderPrepare. However, the only a useful config
1200 // substitution test against missing families that i can imagine
1201 // would only be interested in the preferred family
1202 // (qual="first"), so always keep the first family and use the
1203 // same pattern for Sort and RenderPrepare.
1204 if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) {
1205 --v;
1207 continue;
1210 // Aliases seem to often end up occurring more than once, but
1211 // duplicate families can't be removed from the sort pattern without
1212 // knowing whether duplicates have the same binding.
1213 gfxFontconfigUtils::DepFcStrEntry *entry =
1214 existingFamilies.PutEntry(family);
1215 if (entry) {
1216 if (entry->mKey) // old entry
1217 continue;
1219 entry->mKey = family; // initialize new entry
1222 for (PRUint32 f = 0; f < familyFonts->Length(); ++f) {
1223 FcPattern *font = familyFonts->ElementAt(f);
1225 // User fonts are already filtered by slant (but not size) in
1226 // mUserFontSet->FindFontEntry().
1227 if (familyFonts != &userFont &&
1228 !SlantIsAcceptable(font, requestedSlant))
1229 continue;
1230 if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
1231 continue;
1233 for (PRUint32 r = 0; r < requiredLangs.Length(); ++r) {
1234 const LangSupportEntry& entry = requiredLangs[r];
1235 FcLangResult support =
1236 gfxFontconfigUtils::GetLangSupport(font, entry.mLang);
1237 if (support <= entry.mBestSupport) { // lower is better
1238 requiredLangs.RemoveElementAt(r);
1239 --r;
1243 // FcFontSetDestroy will remove a reference but FcFontSetAdd
1244 // does _not_ take a reference!
1245 if (FcFontSetAdd(fontSet, font)) {
1246 FcPatternReference(font);
1251 FcPattern *truncateMarker = NULL;
1252 for (PRUint32 r = 0; r < requiredLangs.Length(); ++r) {
1253 const nsTArray< nsCountedRef<FcPattern> >& langFonts =
1254 utils->GetFontsForLang(requiredLangs[r].mLang);
1256 PRBool haveLangFont = PR_FALSE;
1257 for (PRUint32 f = 0; f < langFonts.Length(); ++f) {
1258 FcPattern *font = langFonts[f];
1259 if (!SlantIsAcceptable(font, requestedSlant))
1260 continue;
1261 if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
1262 continue;
1264 haveLangFont = PR_TRUE;
1265 if (FcFontSetAdd(fontSet, font)) {
1266 FcPatternReference(font);
1270 if (!haveLangFont && langFonts.Length() > 0) {
1271 // There is a font that supports this language but it didn't pass
1272 // the slant and size criteria. Weak default font families should
1273 // not be considered until the language has been satisfied.
1275 // Insert a font that supports the language so that it will mark
1276 // the position of fonts from weak families in the sorted set and
1277 // they can be removed. The language and weak families will be
1278 // considered in the fallback fonts, which use fontconfig's
1279 // algorithm.
1281 // Of the fonts that don't meet slant and size criteria, strong
1282 // default font families should be considered before (other) fonts
1283 // for this language, so this marker font will be removed (as well
1284 // as the fonts from weak families), and strong families will be
1285 // reconsidered in the fallback fonts.
1286 FcPattern *font = langFonts[0];
1287 if (FcFontSetAdd(fontSet, font)) {
1288 FcPatternReference(font);
1289 truncateMarker = font;
1291 break;
1295 FcFontSet *sets[1] = { fontSet };
1296 FcResult result;
1297 fontSet.own(FcFontSetSort(NULL, sets, 1, mSortPattern,
1298 FcFalse, NULL, &result));
1300 if (truncateMarker != NULL && fontSet) {
1301 nsAutoRef<FcFontSet> truncatedSet(FcFontSetCreate());
1303 for (int f = 0; f < fontSet->nfont; ++f) {
1304 FcPattern *font = fontSet->fonts[f];
1305 if (font == truncateMarker)
1306 break;
1308 if (FcFontSetAdd(truncatedSet, font)) {
1309 FcPatternReference(font);
1313 fontSet.steal(truncatedSet);
1316 return fontSet.out();
1319 nsReturnRef<FcFontSet>
1320 gfxFcPangoFontSet::SortFallbackFonts()
1322 // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet,
1323 // but would take much longer due to comparing all the character sets.
1325 // The references to fonts in this FcFontSet are almost free
1326 // as they are pointers into mmaped cache files.
1328 // GetFontPatternAt() will trim lazily if and as needed, which will also
1329 // remove duplicates of preferred fonts.
1330 FcResult result;
1331 return nsReturnRef<FcFontSet>(FcFontSort(NULL, mSortPattern,
1332 FcFalse, NULL, &result));
1335 // GetFontAt relies on this setting up all patterns up to |i|.
1336 FcPattern *
1337 gfxFcPangoFontSet::GetFontPatternAt(PRUint32 i)
1339 while (i >= mFonts.Length()) {
1340 while (!mFcFontSet) {
1341 if (mHaveFallbackFonts)
1342 return nsnull;
1344 mFcFontSet = SortFallbackFonts();
1345 mHaveFallbackFonts = PR_TRUE;
1346 mFcFontsTrimmed = 0;
1347 // Loop to test that mFcFontSet is non-NULL.
1350 while (mFcFontsTrimmed < mFcFontSet->nfont) {
1351 FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed];
1352 ++mFcFontsTrimmed;
1354 if (mFonts.Length() != 0) {
1355 // See if the next font provides support for any extra
1356 // characters. Most often the next font is not going to
1357 // support more characters so check for a SubSet first before
1358 // allocating a new CharSet with Union.
1359 FcCharSet *supportedChars = mCharSet;
1360 if (!supportedChars) {
1361 FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern,
1362 FC_CHARSET, 0, &supportedChars);
1365 if (supportedChars) {
1366 FcCharSet *newChars = NULL;
1367 FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars);
1368 if (newChars) {
1369 if (FcCharSetIsSubset(newChars, supportedChars))
1370 continue;
1372 mCharSet.own(FcCharSetUnion(supportedChars, newChars));
1373 } else if (!mCharSet) {
1374 mCharSet.own(FcCharSetCopy(supportedChars));
1379 mFonts.AppendElement(font);
1380 if (mFonts.Length() >= i)
1381 break;
1384 if (mFcFontsTrimmed == mFcFontSet->nfont) {
1385 // finished with this font set
1386 mFcFontSet.reset();
1390 return mFonts[i].mPattern;
1394 * gfxPangoFontset: An implementation of a PangoFontset for gfxPangoFontMap
1397 #define GFX_TYPE_PANGO_FONTSET (gfx_pango_fontset_get_type())
1398 #define GFX_PANGO_FONTSET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONTSET, gfxPangoFontset))
1399 #define GFX_IS_PANGO_FONTSET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONTSET))
1401 /* static */
1402 GType gfx_pango_fontset_get_type (void);
1404 #define GFX_PANGO_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
1405 #define GFX_IS_PANGO_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONTSET))
1406 #define GFX_PANGO_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
1408 // This struct is POD so that it can be used as a GObject.
1409 struct gfxPangoFontset {
1410 PangoFontset parent_instance;
1412 PangoLanguage *mLanguage;
1413 gfxFcPangoFontSet *mGfxFontSet;
1414 PangoFont *mBaseFont;
1415 gfxPangoFontGroup *mFontGroup;
1417 static PangoFontset *
1418 NewFontset(gfxPangoFontGroup *aFontGroup,
1419 PangoLanguage *aLanguage)
1421 gfxPangoFontset *fontset = static_cast<gfxPangoFontset *>
1422 (g_object_new(GFX_TYPE_PANGO_FONTSET, NULL));
1424 fontset->mLanguage = aLanguage;
1426 // Use the font group's fontset if the language matches
1427 if (aFontGroup->GetPangoLanguage() == aLanguage) {
1428 fontset->mGfxFontSet = aFontGroup->GetFontSet();
1429 NS_IF_ADDREF(fontset->mGfxFontSet);
1431 } else {
1432 // Otherwise, fallback fonts depend on the language so get
1433 // another font-set for the language if/when the base font is
1434 // not suitable. Save the font group for this.
1435 fontset->mFontGroup = aFontGroup;
1436 NS_ADDREF(fontset->mFontGroup);
1438 // Using the same base font irrespective of the language that
1439 // Pango chooses for the script means that PANGO_SCRIPT_COMMON
1440 // characters are consistently rendered with the same font.
1441 // (Bug 339513 and bug 416725).
1443 // However, use the default Pango behavior (selecting generic
1444 // fonts from the script of the characters) in two situations:
1446 // 1. When we don't have a language to make a good choice for
1447 // the primary font.
1449 // 2. For system fonts, use the default Pango behavior to give
1450 // consistency with other apps. (This probably wouldn't be
1451 // necessary but for bug 91190.)
1452 if (aFontGroup->GetPangoLanguage() &&
1453 !aFontGroup->GetStyle()->systemFont) {
1454 fontset->mBaseFont = aFontGroup->GetBasePangoFont();
1455 if (fontset->mBaseFont)
1456 g_object_ref(fontset->mBaseFont);
1460 return PANGO_FONTSET(fontset);
1464 struct gfxPangoFontsetClass {
1465 PangoFontsetClass parent_class;
1468 G_DEFINE_TYPE (gfxPangoFontset, gfx_pango_fontset, PANGO_TYPE_FONTSET)
1470 static void
1471 gfx_pango_fontset_init(gfxPangoFontset *fontset)
1475 static void
1476 gfx_pango_fontset_finalize(GObject *object)
1478 gfxPangoFontset *self = GFX_PANGO_FONTSET(object);
1480 if (self->mBaseFont)
1481 g_object_unref(self->mBaseFont);
1482 NS_IF_RELEASE(self->mGfxFontSet);
1483 NS_IF_RELEASE(self->mFontGroup);
1485 G_OBJECT_CLASS(gfx_pango_fontset_parent_class)->finalize(object);
1488 static PangoLanguage *
1489 gfx_pango_fontset_get_language(PangoFontset *fontset)
1491 gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
1492 return self->mLanguage;
1495 static gfxFcPangoFontSet *
1496 GetGfxFontSet(gfxPangoFontset *self)
1498 if (!self->mGfxFontSet && self->mFontGroup) {
1499 self->mGfxFontSet = self->mFontGroup->GetFontSet(self->mLanguage);
1500 // Finished with the font group
1501 NS_RELEASE(self->mFontGroup);
1503 if (!self->mGfxFontSet)
1504 return nsnull;
1506 NS_ADDREF(self->mGfxFontSet);
1508 return self->mGfxFontSet;
1511 static void
1512 gfx_pango_fontset_foreach(PangoFontset *fontset, PangoFontsetForeachFunc func,
1513 gpointer data)
1515 gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
1517 FcPattern *baseFontPattern = NULL;
1518 if (self->mBaseFont) {
1519 if ((*func)(fontset, self->mBaseFont, data))
1520 return;
1522 baseFontPattern = PANGO_FC_FONT(self->mBaseFont)->font_pattern;
1525 // Falling back to secondary fonts
1526 gfxFcPangoFontSet *gfxFontSet = GetGfxFontSet(self);
1527 if (!gfxFontSet)
1528 return;
1530 for (PRUint32 i = 0;
1531 FcPattern *pattern = gfxFontSet->GetFontPatternAt(i);
1532 ++i) {
1533 // Skip this font if it is the same face as the base font
1534 if (pattern == baseFontPattern) {
1535 continue;
1537 PangoFont *font = gfxFontSet->GetFontAt(i);
1538 if (font) {
1539 if ((*func)(fontset, font, data))
1540 return;
1545 static PRBool HasChar(FcPattern *aFont, FcChar32 wc)
1547 FcCharSet *charset = NULL;
1548 FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
1550 return charset && FcCharSetHasChar(charset, wc);
1553 static PangoFont *
1554 gfx_pango_fontset_get_font(PangoFontset *fontset, guint wc)
1556 gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
1558 PangoFont *result = NULL;
1560 FcPattern *baseFontPattern = NULL;
1561 if (self->mBaseFont) {
1562 baseFontPattern = PANGO_FC_FONT(self->mBaseFont)->font_pattern;
1564 if (HasChar(baseFontPattern, wc)) {
1565 result = self->mBaseFont;
1569 if (!result) {
1570 // Falling back to secondary fonts
1571 gfxFcPangoFontSet *gfxFontSet = GetGfxFontSet(self);
1573 if (gfxFontSet) {
1574 for (PRUint32 i = 0;
1575 FcPattern *pattern = gfxFontSet->GetFontPatternAt(i);
1576 ++i) {
1577 // Skip this font if it is the same face as the base font
1578 if (pattern == baseFontPattern) {
1579 continue;
1582 if (HasChar(pattern, wc)) {
1583 result = gfxFontSet->GetFontAt(i);
1584 break;
1589 if (!result) {
1590 // Nothing found. Return the first font.
1591 if (self->mBaseFont) {
1592 result = self->mBaseFont;
1593 } else if (gfxFontSet) {
1594 result = gfxFontSet->GetFontAt(0);
1599 if (!result)
1600 return NULL;
1602 g_object_ref(result);
1603 return result;
1606 static void
1607 gfx_pango_fontset_class_init (gfxPangoFontsetClass *klass)
1609 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1610 PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (klass);
1612 object_class->finalize = gfx_pango_fontset_finalize;
1613 // get_font is not likely to be used but implemented because the class
1614 // makes it available.
1615 fontset_class->get_font = gfx_pango_fontset_get_font;
1616 // inherit fontset_class->get_metrics (which is not likely to be used)
1617 fontset_class->get_language = gfx_pango_fontset_get_language;
1618 fontset_class->foreach = gfx_pango_fontset_foreach;
1622 * gfxPangoFontMap: An implementation of a PangoFontMap.
1624 * This is passed to pango_itemize() through the PangoContext parameter, and
1625 * provides font selection through the gfxPangoFontGroup.
1627 * It is intended that the font group is recorded on the PangoContext with
1628 * SetFontGroup(). The font group is then queried for fonts, with
1629 * gfxFcPangoFontSet doing the font selection.
1632 #define GFX_TYPE_PANGO_FONT_MAP (gfx_pango_font_map_get_type())
1633 #define GFX_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMap))
1634 #define GFX_IS_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONT_MAP))
1636 GType gfx_pango_font_map_get_type (void);
1638 #define GFX_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
1639 #define GFX_IS_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONT_MAP))
1640 #define GFX_PANGO_FONT_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
1642 // Do not instantiate this class directly, but use NewFontMap.
1643 // This struct is POD so that it can be used as a GObject.
1644 struct gfxPangoFontMap {
1645 PangoFcFontMap parent_instance;
1647 static PangoFontMap *
1648 NewFontMap()
1650 gfxPangoFontMap *fontmap = static_cast<gfxPangoFontMap *>
1651 (g_object_new(GFX_TYPE_PANGO_FONT_MAP, NULL));
1653 return PANGO_FONT_MAP(fontmap);
1657 struct gfxPangoFontMapClass {
1658 PangoFcFontMapClass parent_class;
1661 G_DEFINE_TYPE (gfxPangoFontMap, gfx_pango_font_map, PANGO_TYPE_FC_FONT_MAP)
1663 static void
1664 gfx_pango_font_map_init(gfxPangoFontMap *fontset)
1668 static PangoFont *
1669 gfx_pango_font_map_load_font(PangoFontMap *fontmap, PangoContext *context,
1670 const PangoFontDescription *description)
1672 gfxPangoFontGroup *fontGroup = GetFontGroup(context);
1673 if (NS_UNLIKELY(!fontGroup)) {
1674 return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
1675 load_font(fontmap, context, description);
1678 PangoFont *baseFont = fontGroup->GetBasePangoFont();
1679 if (NS_LIKELY(baseFont)) {
1680 g_object_ref(baseFont);
1682 return baseFont;
1685 static PangoFontset *
1686 gfx_pango_font_map_load_fontset(PangoFontMap *fontmap, PangoContext *context,
1687 const PangoFontDescription *desc,
1688 PangoLanguage *language)
1690 gfxPangoFontGroup *fontGroup = GetFontGroup(context);
1691 if (NS_UNLIKELY(!fontGroup)) {
1692 return PANGO_FONT_MAP_CLASS(gfx_pango_font_map_parent_class)->
1693 load_fontset(fontmap, context, desc, language);
1696 return gfxPangoFontset::NewFontset(fontGroup, language);
1699 static double
1700 gfx_pango_font_map_get_resolution(PangoFcFontMap *fcfontmap,
1701 PangoContext *context)
1703 // This merely enables the FC_SIZE field of the pattern to be accurate.
1704 // We use gfxPlatformGtk::DPI() much of the time...
1705 return gfxPlatformGtk::DPI();
1708 #ifdef MOZ_WIDGET_GTK2
1709 static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
1710 #endif
1712 // Apply user settings and defaults to pattern in preparation for matching.
1713 static void
1714 PrepareSortPattern(FcPattern *aPattern, double aFallbackSize,
1715 double aSizeAdjustFactor, PRBool aIsPrinterFont)
1717 FcConfigSubstitute(NULL, aPattern, FcMatchPattern);
1719 // This gets cairo_font_options_t for the Screen. We should have
1720 // different font options for printing (no hinting) but we are not told
1721 // what we are measuring for.
1723 // If cairo adds support for lcd_filter, gdk will not provide the default
1724 // setting for that option. We could get the default setting by creating
1725 // an xlib surface once, recording its font_options, and then merging the
1726 // gdk options.
1728 // Using an xlib surface would also be an option to get Screen font
1729 // options for non-GTK X11 toolkits, but less efficient than using GDK to
1730 // pick up dynamic changes.
1731 if(aIsPrinterFont) {
1732 cairo_font_options_t *options = cairo_font_options_create();
1733 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
1734 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
1735 cairo_ft_font_options_substitute(options, aPattern);
1736 cairo_font_options_destroy(options);
1738 #ifdef MOZ_WIDGET_GTK2
1739 else {
1740 ApplyGdkScreenFontOptions(aPattern);
1742 #endif
1744 // Protect against any fontconfig settings that may have incorrectly
1745 // modified the pixelsize, and consider aSizeAdjustFactor.
1746 double size = aFallbackSize;
1747 if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
1748 || aSizeAdjustFactor != 1.0) {
1749 FcPatternDel(aPattern, FC_PIXEL_SIZE);
1750 FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
1753 FcDefaultSubstitute(aPattern);
1756 static void
1757 gfx_pango_font_map_context_substitute(PangoFcFontMap *fontmap,
1758 PangoContext *context,
1759 FcPattern *pattern)
1761 // owned by the context
1762 PangoFontDescription *desc = pango_context_get_font_description(context);
1763 double size = pango_font_description_get_size(desc) / FLOAT_PANGO_SCALE;
1764 gfxPangoFontGroup *fontGroup = GetFontGroup(context);
1765 PRBool usePrinterFont = fontGroup && fontGroup->GetStyle()->printerFont;
1766 PrepareSortPattern(pattern, size, 1.0, usePrinterFont);
1769 static PangoFcFont *
1770 gfx_pango_font_map_create_font(PangoFcFontMap *fontmap,
1771 PangoContext *context,
1772 const PangoFontDescription *desc,
1773 FcPattern *pattern)
1775 return PANGO_FC_FONT(g_object_new(GFX_TYPE_PANGO_FC_FONT,
1776 "pattern", pattern, NULL));
1779 static void
1780 gfx_pango_font_map_class_init(gfxPangoFontMapClass *klass)
1782 // inherit GObjectClass::finalize from parent as this class adds no data.
1784 PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (klass);
1785 fontmap_class->load_font = gfx_pango_font_map_load_font;
1786 // inherit fontmap_class->list_families (which is not likely to be used)
1787 // from PangoFcFontMap
1788 fontmap_class->load_fontset = gfx_pango_font_map_load_fontset;
1789 // inherit fontmap_class->shape_engine_type from PangoFcFontMap
1791 PangoFcFontMapClass *fcfontmap_class = PANGO_FC_FONT_MAP_CLASS (klass);
1792 fcfontmap_class->get_resolution = gfx_pango_font_map_get_resolution;
1793 // context_key_* virtual functions are only necessary if we want to
1794 // dynamically respond to changes in the screen cairo_font_options_t.
1796 // context_substitute and get_font are not likely to be used but
1797 // implemented because the class makes them available.
1798 fcfontmap_class->context_substitute = gfx_pango_font_map_context_substitute;
1799 fcfontmap_class->create_font = gfx_pango_font_map_create_font;
1803 ** gfxPangoFontGroup
1806 struct FamilyCallbackData {
1807 FamilyCallbackData(nsTArray<nsString> *aFcFamilyList,
1808 gfxUserFontSet *aUserFontSet)
1809 : mFcFamilyList(aFcFamilyList), mUserFontSet(aUserFontSet)
1812 nsTArray<nsString> *mFcFamilyList;
1813 const gfxUserFontSet *mUserFontSet;
1816 static int
1817 FFRECountHyphens (const nsAString &aFFREName)
1819 int h = 0;
1820 PRInt32 hyphen = 0;
1821 while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) {
1822 ++h;
1823 ++hyphen;
1825 return h;
1828 static PRBool
1829 FamilyCallback (const nsAString& fontName, const nsACString& genericName,
1830 void *closure)
1832 FamilyCallbackData *data = static_cast<FamilyCallbackData*>(closure);
1833 nsTArray<nsString> *list = data->mFcFamilyList;
1835 // We ignore prefs that have three hypens since they are X style prefs.
1836 if (genericName.Length() && FFRECountHyphens(fontName) >= 3)
1837 return PR_TRUE;
1839 if (!list->Contains(fontName)) {
1840 // The family properties of FcPatterns for @font-face fonts have a
1841 // namespace to identify them among system fonts. (see
1842 // FONT_FACE_FAMILY_PREFIX.) The CSS family name can match either the
1843 // @font-face family or the system font family so both names are added
1844 // here.
1846 // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802 required
1847 // looking for locally-installed fonts matching requested properties
1848 // before checking the src descriptor in @font-face rules.
1849 // http://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#algorithm
1850 // also only checks src descriptors if there is no local font matching
1851 // the requested properties.
1853 // Similarly "Editor's Draft 27 June 2008"
1854 // http://dev.w3.org/csswg/css3-fonts/#font-matching says "The user
1855 // agent attempts to find the family name among fonts available on the
1856 // system and then among fonts defined via @font-face rules."
1857 // However, this is contradicted by "if [the name from the font-family
1858 // descriptor] is the same as a font family available in a given
1859 // user's environment, it effectively hides the underlying font for
1860 // documents that use the stylesheet."
1862 // Windows and Mac code currently prioritizes fonts from @font-face
1863 // rules. The order of families here reflects the priorities on those
1864 // platforms.
1865 const gfxUserFontSet *userFontSet = data->mUserFontSet;
1866 if (genericName.Length() == 0 &&
1867 userFontSet && userFontSet->HasFamily(fontName)) {
1868 nsAutoString userFontName =
1869 NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
1870 list->AppendElement(userFontName);
1873 list->AppendElement(fontName);
1876 return PR_TRUE;
1879 gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families,
1880 const gfxFontStyle *aStyle,
1881 gfxUserFontSet *aUserFontSet)
1882 : gfxFontGroup(families, aStyle, aUserFontSet),
1883 mPangoLanguage(GuessPangoLanguage(aStyle->langGroup))
1885 mFonts.AppendElements(1);
1888 gfxPangoFontGroup::~gfxPangoFontGroup()
1892 gfxFontGroup *
1893 gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
1895 return new gfxPangoFontGroup(mFamilies, aStyle, mUserFontSet);
1898 // An array of family names suitable for fontconfig
1899 void
1900 gfxPangoFontGroup::GetFcFamilies(nsTArray<nsString> *aFcFamilyList,
1901 const nsACString& aLangGroup)
1903 FamilyCallbackData data(aFcFamilyList, mUserFontSet);
1904 // Leave non-existing fonts in the list so that fontconfig can get the
1905 // best match.
1906 ForEachFontInternal(mFamilies, aLangGroup, PR_TRUE, PR_FALSE,
1907 FamilyCallback, &data);
1910 PangoFont *
1911 gfxPangoFontGroup::GetBasePangoFont()
1913 return GetBaseFontSet()->GetFontAt(0);
1916 gfxFont *
1917 gfxPangoFontGroup::GetFontAt(PRInt32 i) {
1918 // If it turns out to be hard for all clients that cache font
1919 // groups to call UpdateFontList at appropriate times, we could
1920 // instead consider just calling UpdateFontList from someplace
1921 // more central (such as here).
1922 NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
1923 "Whoever was caching this font group should have "
1924 "called UpdateFontList on it");
1926 NS_PRECONDITION(i == 0, "Only have one font");
1928 if (!mFonts[0]) {
1929 PangoFont *pangoFont = GetBasePangoFont();
1930 mFonts[0] = gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(pangoFont));
1933 return mFonts[0];
1936 void
1937 gfxPangoFontGroup::UpdateFontList()
1939 if (!mUserFontSet)
1940 return;
1942 PRUint64 newGeneration = mUserFontSet->GetGeneration();
1943 if (newGeneration == mCurrGeneration)
1944 return;
1946 mFonts[0] = NULL;
1947 mFontSets.Clear();
1948 mCurrGeneration = newGeneration;
1951 already_AddRefed<gfxFcPangoFontSet>
1952 gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
1953 nsAutoRef<FcPattern> *aMatchPattern)
1955 const char *lang = pango_language_to_string(aLang);
1957 const char *langGroup = nsnull;
1958 if (aLang != mPangoLanguage) {
1959 // Set up langGroup for Mozilla's font prefs.
1960 if (!gLangService) {
1961 CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
1963 if (gLangService) {
1964 nsIAtom *atom =
1965 gLangService->LookupLanguage(NS_ConvertUTF8toUTF16(lang));
1966 if (atom) {
1967 atom->GetUTF8String(&langGroup);
1972 nsAutoTArray<nsString, 20> fcFamilyList;
1973 GetFcFamilies(&fcFamilyList,
1974 langGroup ? nsDependentCString(langGroup) : mStyle.langGroup);
1976 // To consider: A fontset cache here could be helpful.
1978 // Get a pattern suitable for matching.
1979 nsAutoRef<FcPattern> pattern
1980 (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
1982 PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont);
1984 nsRefPtr<gfxFcPangoFontSet> fontset =
1985 new gfxFcPangoFontSet(pattern, mUserFontSet);
1987 if (aMatchPattern)
1988 aMatchPattern->steal(pattern);
1990 return fontset.forget();
1993 gfxPangoFontGroup::
1994 FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
1995 gfxFcPangoFontSet *aFontSet)
1996 : mLang(aLang), mFontSet(aFontSet)
2000 gfxFcPangoFontSet *
2001 gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
2003 GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
2005 if (!aLang)
2006 return mFontSets[0].mFontSet;
2008 for (PRUint32 i = 0; i < mFontSets.Length(); ++i) {
2009 if (mFontSets[i].mLang == aLang)
2010 return mFontSets[i].mFontSet;
2013 nsRefPtr<gfxFcPangoFontSet> fontSet =
2014 MakeFontSet(aLang, mSizeAdjustFactor);
2015 mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
2017 return fontSet;
2021 ** gfxFcFont
2024 cairo_user_data_key_t gfxFcFont::sGfxFontKey;
2026 gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
2027 gfxFontEntry *aFontEntry,
2028 const gfxFontStyle *aFontStyle)
2029 : gfxFont(aFontEntry, aFontStyle),
2030 mCairoFont(aCairoFont),
2031 mHasMetrics(PR_FALSE)
2033 cairo_scaled_font_reference(mCairoFont);
2034 cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, this, NULL);
2037 gfxFcFont::~gfxFcFont()
2039 cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, NULL, NULL);
2040 cairo_scaled_font_destroy(mCairoFont);
2043 /* static */ void
2044 gfxPangoFontGroup::Shutdown()
2046 if (gPangoFontMap) {
2047 if (PANGO_IS_FC_FONT_MAP (gPangoFontMap)) {
2048 // This clears circular references from the fontmap to itself
2049 // through its fonts.
2050 pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(gPangoFontMap));
2052 g_object_unref(gPangoFontMap);
2053 gPangoFontMap = NULL;
2056 // Resetting gFTLibrary in case this is wanted again after a
2057 // cairo_debug_reset_static_data.
2058 gFTLibrary = NULL;
2060 NS_IF_RELEASE(gLangService);
2063 static FT_Library
2064 GetFTLibrary()
2066 if (!gFTLibrary) {
2067 // Use cairo's FT_Library so that cairo takes care of shutdown of the
2068 // FT_Library after it has destroyed its font_faces, and FT_Done_Face
2069 // has been called on each FT_Face, at least until this bug is fixed:
2070 // https://bugs.freedesktop.org/show_bug.cgi?id=18857
2072 // Cairo's FT_Library can be obtained from any cairo_scaled_font. The
2073 // font properties requested here are chosen to get an FT_Face that is
2074 // likely to be also used elsewhere.
2075 gfxFontStyle style;
2076 nsRefPtr<gfxPangoFontGroup> fontGroup =
2077 new gfxPangoFontGroup(NS_LITERAL_STRING("sans-serif"),
2078 &style, nsnull);
2080 gfxFcFont *font = static_cast<gfxFcFont*>(fontGroup->GetFontAt(0));
2081 if (!font)
2082 return NULL;
2084 LockedFTFace face(font);
2085 if (!face.get())
2086 return NULL;
2088 gFTLibrary = face.get()->glyph->library;
2091 return gFTLibrary;
2094 /* static */ gfxFontEntry *
2095 gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
2096 nsISupports *aLoader,
2097 const PRUint8 *aFontData, PRUint32 aLength)
2099 // Using face_index = 0 for the first face in the font, as we have no
2100 // other information. FT_New_Memory_Face checks for a NULL FT_Library.
2101 FT_Face face;
2102 FT_Error error =
2103 FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
2104 if (error != 0)
2105 return nsnull;
2107 return new gfxDownloadedFcFontEntry(aProxyEntry, aLoader, face);
2111 static double
2112 GetPixelSize(FcPattern *aPattern)
2114 double size;
2115 if (FcPatternGetDouble(aPattern,
2116 FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
2117 return size;
2119 NS_NOTREACHED("No size on pattern");
2120 return 0.0;
2124 * The following gfxPangoFonts are accessed from the PangoFont, not from the
2125 * gfxFontCache hash table. The gfxFontCache hash table is keyed by desired
2126 * family and style, whereas here we only know actual family and style. There
2127 * may be more than one of these fonts with the same family and style, but
2128 * different PangoFont and actual font face.
2130 * The point of this is to record the exact font face for gfxTextRun glyph
2131 * indices. The style of this font does not necessarily represent the exact
2132 * gfxFontStyle used to build the text run. Notably, the language is not
2133 * recorded.
2136 /* static */
2137 already_AddRefed<gfxFcFont>
2138 gfxFcFont::GetOrMakeFont(FcPattern *aPattern)
2140 cairo_scaled_font_t *cairoFont = CreateScaledFont(aPattern);
2142 nsRefPtr<gfxFcFont> font = static_cast<gfxFcFont*>
2143 (cairo_scaled_font_get_user_data(cairoFont, &sGfxFontKey));
2145 if (!font) {
2146 gfxFloat size = GetPixelSize(aPattern);
2148 // Shouldn't actually need to take too much care about the correct
2149 // name or style, as size is the only thing expected to be important.
2150 PRUint8 style = gfxFontconfigUtils::GetThebesStyle(aPattern);
2151 PRUint16 weight = gfxFontconfigUtils::GetThebesWeight(aPattern);
2153 // The LangSet in the FcPattern does not have an order so there is no
2154 // one particular language to choose and converting the set to a
2155 // string through FcNameUnparse() is more trouble than it's worth.
2156 NS_NAMED_LITERAL_CSTRING(langGroup, "x-unicode");
2157 gfxFontStyle fontStyle(style, weight, size, langGroup, 0.0,
2158 PR_TRUE, PR_FALSE, PR_FALSE);
2160 nsRefPtr<gfxFontEntry> fe;
2161 FcChar8 *fc_file;
2162 if (FcPatternGetString(aPattern,
2163 FC_FILE, 0, &fc_file) == FcResultMatch) {
2164 int index;
2165 if (FcPatternGetInteger(aPattern,
2166 FC_INDEX, 0, &index) != FcResultMatch) {
2167 // cairo won't know what to do with this pattern.
2168 NS_NOTREACHED("No index in pattern for font face from file");
2169 index = 0;
2172 // Get a unique name for the font face data from the file and id.
2173 nsAutoString name;
2174 AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name);
2175 if (index != 0) {
2176 name.AppendLiteral("/");
2177 name.AppendInt(index);
2180 fe = new gfxFontEntry(name);
2181 } else {
2182 fe = GetDownloadedFontEntry(aPattern);
2183 if (!fe) {
2184 // cairo won't know which font to open without a file.
2185 // (We don't create fonts from an FT_Face.)
2186 NS_NOTREACHED("Fonts without a file is not a web font!?");
2187 fe = new gfxFontEntry(nsString());
2191 // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
2192 // not necessarily enough to provide a key that will describe a unique
2193 // font. cairoFont contains information from aPattern, which is a
2194 // fully resolved pattern from FcFontRenderPrepare.
2195 // FcFontRenderPrepare takes the requested pattern and the face
2196 // pattern as input and can modify elements of the resulting pattern
2197 // that affect rendering but are not included in the gfxFontStyle.
2198 font = new gfxFcFont(cairoFont, fe, &fontStyle);
2201 cairo_scaled_font_destroy(cairoFont);
2202 return font.forget();
2205 static PangoFontMap *
2206 GetPangoFontMap()
2208 if (!gPangoFontMap) {
2209 gPangoFontMap = gfxPangoFontMap::NewFontMap();
2211 return gPangoFontMap;
2214 static PangoContext *
2215 GetPangoContext()
2217 PangoContext *context = pango_context_new();
2218 pango_context_set_font_map(context, GetPangoFontMap());
2219 return context;
2222 gfxFcPangoFontSet *
2223 gfxPangoFontGroup::GetBaseFontSet()
2225 if (mFontSets.Length() > 0)
2226 return mFontSets[0].mFontSet;
2228 mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
2229 nsAutoRef<FcPattern> pattern;
2230 nsRefPtr<gfxFcPangoFontSet> fontSet =
2231 MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
2233 double size = GetPixelSize(pattern);
2234 if (size != 0.0 && mStyle.sizeAdjust != 0.0) {
2235 gfxFcFont *font =
2236 gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(fontSet->GetFontAt(0)));
2237 if (font) {
2238 const gfxFont::Metrics& metrics = font->GetMetrics();
2240 // The factor of 0.1 ensures that xHeight is sane so fonts don't
2241 // become huge. Strictly ">" ensures that xHeight and emHeight are
2242 // not both zero.
2243 if (metrics.xHeight > 0.1 * metrics.emHeight) {
2244 mSizeAdjustFactor =
2245 mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
2247 size *= mSizeAdjustFactor;
2248 FcPatternDel(pattern, FC_PIXEL_SIZE);
2249 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
2251 fontSet = new gfxFcPangoFontSet(pattern, mUserFontSet);
2256 PangoLanguage *pangoLang = mPangoLanguage;
2257 FcChar8 *fcLang;
2258 if (!pangoLang &&
2259 FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
2260 pangoLang =
2261 pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
2264 mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
2266 return fontSet;
2269 void
2270 gfxFcFont::GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents)
2272 NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
2274 cairo_glyph_t glyphs[1];
2275 glyphs[0].index = aGlyph;
2276 glyphs[0].x = 0.0;
2277 glyphs[0].y = 0.0;
2278 // cairo does some caching for us here but perhaps a small gain could be
2279 // made by caching more. It is usually only the advance that is needed,
2280 // so caching only the advance could allow many requests to be cached with
2281 // little memory use. Ideally this cache would be merged with
2282 // gfxGlyphExtents.
2283 cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
2286 PRUint32
2287 LockedFTFace::GetCharExtents(char aChar,
2288 cairo_text_extents_t* aExtents)
2290 NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
2292 if (!mFace)
2293 return 0;
2295 // pango_fc_font_real_get_glyph uses FcFreeTypeCharIndex which may change
2296 // the charmap currently selected on the FT_Face, so, while
2297 // pango_fc_font_real_get_glyph might be used, we should use the same
2298 // function so as to search the charmaps.
2300 // Unfortunately this considers the mac/roman cmap even when there is a
2301 // unicode cmap, which will be bad for symbol fonts, so we should do this
2302 // ourselves, perhaps with a lightweight cache like
2303 // pango_fc_font_real_get_glyph uses.
2304 FT_UInt gid = FcFreeTypeCharIndex(mFace, aChar); // glyph id
2305 if (gid) {
2306 mGfxFont->GetGlyphExtents(gid, aExtents);
2309 return gid;
2312 // Snap a line to pixels while keeping the center and size of the line as
2313 // close to the original position as possible.
2315 // Pango does similar snapping for underline and strikethrough when fonts are
2316 // hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
2317 // top and size of lines. Optimizing the distance between the line and
2318 // baseline is probably good for the gap between text and underline, but
2319 // optimizing the center of the line is better for positioning strikethough.
2320 static void
2321 SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
2323 gfxFloat snappedSize = PR_MAX(NS_floor(aSize + 0.5), 1.0);
2324 // Correct offset for change in size
2325 gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
2326 // Snap offset
2327 aOffset = NS_floor(offset + 0.5);
2328 aSize = snappedSize;
2331 void
2332 LockedFTFace::GetMetrics(gfxFont::Metrics* aMetrics, PRUint32* aSpaceGlyph)
2334 NS_PRECONDITION(aMetrics != NULL, "aMetrics must not be NULL");
2335 NS_PRECONDITION(aSpaceGlyph != NULL, "aSpaceGlyph must not be NULL");
2337 if (NS_UNLIKELY(!mFace)) {
2338 // No face. This unfortunate situation might happen if the font
2339 // file is (re)moved at the wrong time.
2340 aMetrics->emHeight = mGfxFont->GetStyle()->size;
2341 aMetrics->emAscent = 0.8 * aMetrics->emHeight;
2342 aMetrics->emDescent = 0.2 * aMetrics->emHeight;
2343 aMetrics->maxAscent = aMetrics->emAscent;
2344 aMetrics->maxDescent = aMetrics->maxDescent;
2345 aMetrics->maxHeight = aMetrics->emHeight;
2346 aMetrics->internalLeading = 0.0;
2347 aMetrics->externalLeading = 0.2 * aMetrics->emHeight;
2348 aSpaceGlyph = 0;
2349 aMetrics->spaceWidth = 0.5 * aMetrics->emHeight;
2350 aMetrics->maxAdvance = aMetrics->spaceWidth;
2351 aMetrics->aveCharWidth = aMetrics->spaceWidth;
2352 aMetrics->zeroOrAveCharWidth = aMetrics->spaceWidth;
2353 aMetrics->xHeight = 0.5 * aMetrics->emHeight;
2354 aMetrics->underlineSize = aMetrics->emHeight / 14.0;
2355 aMetrics->underlineOffset = -aMetrics->underlineSize;
2356 aMetrics->strikeoutOffset = 0.25 * aMetrics->emHeight;
2357 aMetrics->strikeoutSize = aMetrics->underlineSize;
2358 aMetrics->superscriptOffset = aMetrics->xHeight;
2359 aMetrics->subscriptOffset = aMetrics->xHeight;
2361 return;
2364 const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
2366 gfxFloat emHeight;
2367 // Scale for vertical design metric conversion: pixels per design unit.
2368 gfxFloat yScale;
2369 if (FT_IS_SCALABLE(mFace)) {
2370 // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
2371 // have subpixel accuracy.
2373 // FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
2374 // (fractional) value is a factor that converts vertical metrics from
2375 // design units to units of 1/64 pixels, so that the result may be
2376 // interpreted as pixels in 26.6 fixed point format.
2377 yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
2378 emHeight = mFace->units_per_EM * yScale;
2379 } else { // Not scalable.
2380 // FT_Size_Metrics doc says x_scale is "only relevant for scalable
2381 // font formats".
2382 gfxFloat emUnit = mFace->units_per_EM;
2383 emHeight = ftMetrics.y_ppem;
2384 yScale = emHeight / emUnit;
2387 TT_OS2 *os2 =
2388 static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
2390 aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
2391 aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
2392 aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
2394 gfxFloat lineHeight;
2395 if (os2 && os2->sTypoAscender) {
2396 aMetrics->emAscent = os2->sTypoAscender * yScale;
2397 aMetrics->emDescent = -os2->sTypoDescender * yScale;
2398 FT_Short typoHeight =
2399 os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
2400 lineHeight = typoHeight * yScale;
2402 // maxAscent/maxDescent get used for frame heights, and some fonts
2403 // don't have the HHEA table ascent/descent set (bug 279032).
2404 if (aMetrics->emAscent > aMetrics->maxAscent)
2405 aMetrics->maxAscent = aMetrics->emAscent;
2406 if (aMetrics->emDescent > aMetrics->maxDescent)
2407 aMetrics->maxDescent = aMetrics->emDescent;
2408 } else {
2409 aMetrics->emAscent = aMetrics->maxAscent;
2410 aMetrics->emDescent = aMetrics->maxDescent;
2411 lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
2414 cairo_text_extents_t extents;
2415 *aSpaceGlyph = GetCharExtents(' ', &extents);
2416 if (*aSpaceGlyph) {
2417 aMetrics->spaceWidth = extents.x_advance;
2418 } else {
2419 aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
2422 aMetrics->zeroOrAveCharWidth = 0.0;
2423 if (GetCharExtents('0', &extents)) {
2424 aMetrics->zeroOrAveCharWidth = extents.x_advance;
2427 // Prefering a measured x over sxHeight because sxHeight doesn't consider
2428 // hinting, but maybe the x extents are not quite right in some fancy
2429 // script fonts. CSS 2.1 suggests possibly using the height of an "o",
2430 // which would have a more consistent glyph across fonts.
2431 if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
2432 aMetrics->xHeight = -extents.y_bearing;
2433 aMetrics->aveCharWidth = extents.x_advance;
2434 } else {
2435 if (os2 && os2->sxHeight) {
2436 aMetrics->xHeight = os2->sxHeight * yScale;
2437 } else {
2438 // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
2439 // impossible or impractical to determine the x-height, a value of
2440 // 0.5em should be used."
2441 aMetrics->xHeight = 0.5 * emHeight;
2443 aMetrics->aveCharWidth = 0.0; // updated below
2445 // aveCharWidth is used for the width of text input elements so be
2446 // liberal rather than conservative in the estimate.
2447 if (os2 && os2->xAvgCharWidth) {
2448 // Round to pixels as this is compared with maxAdvance to guess
2449 // whether this is a fixed width font.
2450 gfxFloat avgCharWidth =
2451 ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
2452 aMetrics->aveCharWidth =
2453 PR_MAX(aMetrics->aveCharWidth, avgCharWidth);
2455 aMetrics->aveCharWidth =
2456 PR_MAX(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
2457 if (aMetrics->aveCharWidth == 0.0) {
2458 aMetrics->aveCharWidth = aMetrics->spaceWidth;
2460 if (aMetrics->zeroOrAveCharWidth == 0.0) {
2461 aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
2463 // Apparently hinting can mean that max_advance is not always accurate.
2464 aMetrics->maxAdvance =
2465 PR_MAX(aMetrics->maxAdvance, aMetrics->aveCharWidth);
2467 // gfxFont::Metrics::underlineOffset is the position of the top of the
2468 // underline.
2470 // FT_FaceRec documentation describes underline_position as "the
2471 // center of the underlining stem". This was the original definition
2472 // of the PostScript metric, but in the PostScript table of OpenType
2473 // fonts the metric is "the top of the underline"
2474 // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
2475 // (up to version 2.3.7) doesn't make any adjustment.
2477 // Therefore get the underline position directly from the table
2478 // ourselves when this table exists. Use FreeType's metrics for
2479 // other (including older PostScript) fonts.
2480 if (mFace->underline_position && mFace->underline_thickness) {
2481 aMetrics->underlineSize = mFace->underline_thickness * yScale;
2482 TT_Postscript *post = static_cast<TT_Postscript*>
2483 (FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
2484 if (post && post->underlinePosition) {
2485 aMetrics->underlineOffset = post->underlinePosition * yScale;
2486 } else {
2487 aMetrics->underlineOffset = mFace->underline_position * yScale
2488 + 0.5 * aMetrics->underlineSize;
2490 } else { // No underline info.
2491 // Imitate Pango.
2492 aMetrics->underlineSize = emHeight / 14.0;
2493 aMetrics->underlineOffset = -aMetrics->underlineSize;
2496 if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition) {
2497 aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
2498 aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
2499 } else { // No strikeout info.
2500 aMetrics->strikeoutSize = aMetrics->underlineSize;
2501 // Use OpenType spec's suggested position for Roman font.
2502 aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
2503 + 0.5 * aMetrics->strikeoutSize;
2505 SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
2507 if (os2 && os2->ySuperscriptYOffset) {
2508 gfxFloat val = ScaleRoundDesignUnits(os2->ySuperscriptYOffset,
2509 ftMetrics.y_scale);
2510 aMetrics->superscriptOffset = PR_MAX(1.0, val);
2511 } else {
2512 aMetrics->superscriptOffset = aMetrics->xHeight;
2515 if (os2 && os2->ySubscriptYOffset) {
2516 gfxFloat val = ScaleRoundDesignUnits(os2->ySubscriptYOffset,
2517 ftMetrics.y_scale);
2518 // some fonts have the incorrect sign.
2519 val = fabs(val);
2520 aMetrics->subscriptOffset = PR_MAX(1.0, val);
2521 } else {
2522 aMetrics->subscriptOffset = aMetrics->xHeight;
2525 aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
2527 // Make the line height an integer number of pixels so that lines will be
2528 // equally spaced (rather than just being snapped to pixels, some up and
2529 // some down). Layout calculates line height from the emHeight +
2530 // internalLeading + externalLeading, but first each of these is rounded
2531 // to layout units. To ensure that the result is an integer number of
2532 // pixels, round each of the components to pixels.
2533 aMetrics->emHeight = NS_floor(emHeight + 0.5);
2535 // maxHeight will normally be an integer, but round anyway in case
2536 // FreeType is configured differently.
2537 aMetrics->internalLeading =
2538 NS_floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
2540 // Text input boxes currently don't work well with lineHeight
2541 // significantly less than maxHeight (with Verdana, for example).
2542 lineHeight = NS_floor(PR_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
2543 aMetrics->externalLeading =
2544 lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
2546 // Ensure emAscent + emDescent == emHeight
2547 gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
2548 aMetrics->emAscent = sum > 0.0 ?
2549 aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
2550 aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
2553 const gfxFont::Metrics&
2554 gfxFcFont::GetMetrics()
2556 if (mHasMetrics)
2557 return mMetrics;
2559 if (NS_UNLIKELY(GetStyle()->size <= 0.0)) {
2560 new(&mMetrics) gfxFont::Metrics(); // zero initialize
2561 mSpaceGlyph = 0;
2562 } else {
2563 LockedFTFace(this).GetMetrics(&mMetrics, &mSpaceGlyph);
2566 SanitizeMetrics(&mMetrics, PR_FALSE);
2568 #if 0
2569 // printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
2570 // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
2572 fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
2573 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
2574 fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
2575 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
2576 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
2577 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);
2578 #endif
2580 mHasMetrics = PR_TRUE;
2581 return mMetrics;
2584 nsString
2585 gfxFcFont::GetUniqueName()
2587 return GetName();
2591 ** gfxTextRun
2593 * A serious problem:
2595 * -- We draw with a font that's hinted for the CTM, but we measure with a font
2596 * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
2601 * We use this to append an LTR or RTL Override character to the start of the
2602 * string. This forces Pango to honour our direction even if there are neutral characters
2603 * in the string.
2605 static PRInt32 AppendDirectionalIndicatorUTF8(PRBool aIsRTL, nsACString& aString)
2607 static const PRUnichar overrides[2][2] =
2608 { { 0x202d, 0 }, { 0x202e, 0 }}; // LRO, RLO
2609 AppendUTF16toUTF8(overrides[aIsRTL], aString);
2610 return 3; // both overrides map to 3 bytes in UTF8
2613 gfxTextRun *
2614 gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
2615 const Parameters *aParams, PRUint32 aFlags)
2617 NS_ASSERTION(aFlags & TEXT_IS_8BIT, "8bit should have been set");
2618 gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
2619 if (!run)
2620 return nsnull;
2622 PRBool isRTL = run->IsRightToLeft();
2623 if ((aFlags & TEXT_IS_ASCII) && !isRTL) {
2624 // We don't need to send an override character here, the characters must be all LTR
2625 const gchar *utf8Chars = reinterpret_cast<const gchar*>(aString);
2626 InitTextRun(run, utf8Chars, aLength, 0, PR_TRUE);
2627 } else {
2628 // this is really gross...
2629 const char *chars = reinterpret_cast<const char*>(aString);
2630 NS_ConvertASCIItoUTF16 unicodeString(chars, aLength);
2631 nsCAutoString utf8;
2632 PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8);
2633 AppendUTF16toUTF8(unicodeString, utf8);
2634 InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE);
2636 run->FetchGlyphExtents(aParams->mContext);
2637 return run;
2640 #if defined(ENABLE_FAST_PATH_8BIT)
2641 PRBool
2642 gfxPangoFontGroup::CanTakeFastPath(PRUint32 aFlags)
2644 // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't.
2645 // We need to always use Pango for RTL text, in case glyph mirroring is
2646 // required.
2647 PRBool speed = aFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
2648 PRBool isRTL = aFlags & gfxTextRunFactory::TEXT_IS_RTL;
2649 return speed && !isRTL && PANGO_IS_FC_FONT(GetBasePangoFont());
2651 #endif
2653 gfxTextRun *
2654 gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
2655 const Parameters *aParams, PRUint32 aFlags)
2657 gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
2658 if (!run)
2659 return nsnull;
2661 run->RecordSurrogates(aString);
2663 nsCAutoString utf8;
2664 PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8);
2665 AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8);
2666 PRBool is8Bit = PR_FALSE;
2668 #if defined(ENABLE_FAST_PATH_8BIT)
2669 if (CanTakeFastPath(aFlags)) {
2670 PRUint32 allBits = 0;
2671 PRUint32 i;
2672 for (i = 0; i < aLength; ++i) {
2673 allBits |= aString[i];
2675 is8Bit = (allBits & 0xFF00) == 0;
2677 #endif
2678 InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit);
2679 run->FetchGlyphExtents(aParams->mContext);
2680 return run;
2683 void
2684 gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
2685 PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
2686 PRBool aTake8BitPath)
2688 #if defined(ENABLE_FAST_PATH_ALWAYS)
2689 CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
2690 #else
2691 #if defined(ENABLE_FAST_PATH_8BIT)
2692 if (aTake8BitPath && CanTakeFastPath(aTextRun->GetFlags())) {
2693 nsresult rv = CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
2694 if (NS_SUCCEEDED(rv))
2695 return;
2697 #endif
2699 CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength);
2700 #endif
2703 static void ReleaseDownloadedFontEntry(void *data)
2705 gfxDownloadedFcFontEntry *downloadedFontEntry =
2706 static_cast<gfxDownloadedFcFontEntry*>(data);
2707 NS_RELEASE(downloadedFontEntry);
2710 // This will fetch an existing scaled_font if one exists.
2711 static cairo_scaled_font_t *
2712 CreateScaledFont(FcPattern *aPattern)
2714 cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern(aPattern);
2716 // If the face is created from a web font entry, hold a reference to the
2717 // font entry to keep the font face data.
2718 gfxDownloadedFcFontEntry *downloadedFontEntry =
2719 GetDownloadedFontEntry(aPattern);
2720 if (downloadedFontEntry &&
2721 cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) {
2722 static cairo_user_data_key_t sFontEntryKey;
2724 // Check whether this is a new cairo face
2725 void *currentEntry =
2726 cairo_font_face_get_user_data(face, &sFontEntryKey);
2727 if (!currentEntry) {
2728 NS_ADDREF(downloadedFontEntry);
2729 cairo_font_face_set_user_data(face, &sFontEntryKey,
2730 downloadedFontEntry,
2731 ReleaseDownloadedFontEntry);
2732 } else {
2733 NS_ASSERTION(currentEntry == downloadedFontEntry,
2734 "Unexpected cairo font face!");
2738 double size = GetPixelSize(aPattern);
2740 cairo_matrix_t fontMatrix;
2741 FcMatrix *fcMatrix;
2742 if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
2743 cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
2744 else
2745 cairo_matrix_init_identity(&fontMatrix);
2746 cairo_matrix_scale(&fontMatrix, size, size);
2748 // The cairo_scaled_font is created with a unit ctm so that metrics and
2749 // positions are in user space, but this means that hinting effects will
2750 // not be estimated accurately for non-unit transformations.
2751 cairo_matrix_t identityMatrix;
2752 cairo_matrix_init_identity(&identityMatrix);
2754 // Font options are set explicitly here to improve cairo's caching
2755 // behavior and to record the relevant parts of the pattern for
2756 // SetupCairoFont (so that the pattern can be released).
2758 // Most font_options have already been set as defaults on the FcPattern
2759 // with cairo_ft_font_options_substitute(), then user and system
2760 // fontconfig configurations were applied. The resulting font_options
2761 // have been recorded on the face during
2762 // cairo_ft_font_face_create_for_pattern().
2764 // None of the settings here cause this scaled_font to behave any
2765 // differently from how it would behave if it were created from the same
2766 // face with default font_options.
2768 // We set options explicitly so that the same scaled_font will be found in
2769 // the cairo_scaled_font_map when cairo loads glyphs from a context with
2770 // the same font_face, font_matrix, ctm, and surface font_options.
2772 // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
2773 // font_options on the cairo_ft_font_face, and doesn't consider default
2774 // option values to not match any explicit values.
2776 // Even after cairo_set_scaled_font is used to set font_options for the
2777 // cairo context, when cairo looks for a scaled_font for the context, it
2778 // will look for a font with some option values from the target surface if
2779 // any values are left default on the context font_options. If this
2780 // scaled_font is created with default font_options, cairo will not find
2781 // it.
2782 cairo_font_options_t *fontOptions = cairo_font_options_create();
2784 // The one option not recorded in the pattern is hint_metrics, which will
2785 // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
2786 // We should be considering the font_options of the surface on which this
2787 // font will be used, but currently we don't have different gfxFonts for
2788 // different surface font_options, so we'll create a font suitable for the
2789 // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
2790 cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_ON);
2792 // The remaining options have been recorded on the pattern and the face.
2793 // _cairo_ft_options_merge has some logic to decide which options from the
2794 // scaled_font or from the cairo_ft_font_face take priority in the way the
2795 // font behaves.
2797 // In the majority of cases, _cairo_ft_options_merge uses the options from
2798 // the cairo_ft_font_face, so sometimes it is not so important which
2799 // values are set here so long as they are not defaults, but we'll set
2800 // them to the exact values that we expect from the font, to be consistent
2801 // and to protect against changes in cairo.
2803 // In some cases, _cairo_ft_options_merge uses some options from the
2804 // scaled_font's font_options rather than options on the
2805 // cairo_ft_font_face (from fontconfig).
2806 // https://bugs.freedesktop.org/show_bug.cgi?id=11838
2808 // Surface font options were set on the pattern in
2809 // cairo_ft_font_options_substitute. If fontconfig has changed the
2810 // hint_style then that is what the user (or distribution) wants, so we
2811 // use the setting from the FcPattern.
2813 // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
2814 FcBool hinting;
2815 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
2816 hinting = FcTrue;
2818 cairo_hint_style_t hint_style;
2819 if (!hinting) {
2820 hint_style = CAIRO_HINT_STYLE_NONE;
2821 } else {
2822 #ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91.
2823 int fc_hintstyle;
2824 if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
2825 0, &fc_hintstyle ) != FcResultMatch) {
2826 fc_hintstyle = FC_HINT_FULL;
2828 switch (fc_hintstyle) {
2829 case FC_HINT_NONE:
2830 hint_style = CAIRO_HINT_STYLE_NONE;
2831 break;
2832 case FC_HINT_SLIGHT:
2833 hint_style = CAIRO_HINT_STYLE_SLIGHT;
2834 break;
2835 case FC_HINT_MEDIUM:
2836 default: // This fallback mirrors _get_pattern_ft_options in cairo.
2837 hint_style = CAIRO_HINT_STYLE_MEDIUM;
2838 break;
2839 case FC_HINT_FULL:
2840 hint_style = CAIRO_HINT_STYLE_FULL;
2841 break;
2843 #else // no FC_HINT_STYLE
2844 hint_style = CAIRO_HINT_STYLE_FULL;
2845 #endif
2847 cairo_font_options_set_hint_style(fontOptions, hint_style);
2849 int rgba;
2850 if (FcPatternGetInteger(aPattern,
2851 FC_RGBA, 0, &rgba) != FcResultMatch) {
2852 rgba = FC_RGBA_UNKNOWN;
2854 cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
2855 switch (rgba) {
2856 case FC_RGBA_UNKNOWN:
2857 case FC_RGBA_NONE:
2858 default:
2859 // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
2860 // is disabled through cairo_antialias_t.
2861 rgba = FC_RGBA_NONE;
2862 // subpixel_order won't be used by the font as we won't use
2863 // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
2864 // caching reasons described above. Fall through:
2865 case FC_RGBA_RGB:
2866 subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
2867 break;
2868 case FC_RGBA_BGR:
2869 subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
2870 break;
2871 case FC_RGBA_VRGB:
2872 subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
2873 break;
2874 case FC_RGBA_VBGR:
2875 subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
2876 break;
2878 cairo_font_options_set_subpixel_order(fontOptions, subpixel_order);
2880 FcBool fc_antialias;
2881 if (FcPatternGetBool(aPattern,
2882 FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
2883 fc_antialias = FcTrue;
2885 cairo_antialias_t antialias;
2886 if (!fc_antialias) {
2887 antialias = CAIRO_ANTIALIAS_NONE;
2888 } else if (rgba == FC_RGBA_NONE) {
2889 antialias = CAIRO_ANTIALIAS_GRAY;
2890 } else {
2891 antialias = CAIRO_ANTIALIAS_SUBPIXEL;
2893 cairo_font_options_set_antialias(fontOptions, antialias);
2895 cairo_scaled_font_t *scaledFont =
2896 cairo_scaled_font_create(face, &fontMatrix, &identityMatrix,
2897 fontOptions);
2899 cairo_font_options_destroy(fontOptions);
2900 cairo_font_face_destroy(face);
2902 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
2903 "Failed to create scaled font");
2904 return scaledFont;
2907 PRBool
2908 gfxFcFont::SetupCairoFont(gfxContext *aContext)
2910 cairo_t *cr = aContext->GetCairo();
2912 // The scaled font ctm is not relevant right here because
2913 // cairo_set_scaled_font does not record the scaled font itself, but
2914 // merely the font_face, font_matrix, font_options. The scaled_font used
2915 // for the target can be different from the scaled_font passed to
2916 // cairo_set_scaled_font. (Unfortunately we have measured only for an
2917 // identity ctm.)
2918 cairo_scaled_font_t *cairoFont = CairoScaledFont();
2920 if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
2921 // Don't cairo_set_scaled_font as that would propagate the error to
2922 // the cairo_t, precluding any further drawing.
2923 return PR_FALSE;
2925 // Thoughts on which font_options to set on the context:
2927 // cairoFont has been created for screen rendering.
2929 // When the context is being used for screen rendering, we should set
2930 // font_options such that the same scaled_font gets used (when the ctm is
2931 // the same). The use of explicit font_options recorded in
2932 // CreateScaledFont ensures that this will happen.
2934 // XXXkt: For pdf and ps surfaces, I don't know whether it's better to
2935 // remove surface-specific options, or try to draw with the same
2936 // scaled_font that was used to measure. As the same font_face is being
2937 // used, its font_options will often override some values anyway (unless
2938 // perhaps we remove those from the FcPattern at face creation).
2940 // I can't see any significant difference in printing, irrespective of
2941 // what is set here. It's too late to change things here as measuring has
2942 // already taken place. We should really be measuring with a different
2943 // font for pdf and ps surfaces (bug 403513).
2944 cairo_set_scaled_font(cr, cairoFont);
2945 return PR_TRUE;
2948 static void
2949 SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8Length,
2950 PRUint32 aUTF16Offset, PangoAnalysis *aAnalysis)
2952 if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) {
2953 // 8-bit text doesn't have clusters.
2954 // XXX is this true in all languages???
2955 // behdad: don't think so. Czech for example IIRC has a
2956 // 'ch' grapheme.
2957 return;
2960 // Pango says "the array of PangoLogAttr passed in must have at least N+1
2961 // elements, if there are N characters in the text being broken".
2962 // Could use g_utf8_strlen(aUTF8, aUTF8Length) + 1 but the memory savings
2963 // may not be worth the call.
2964 nsAutoTArray<PangoLogAttr,2000> buffer;
2965 if (!buffer.AppendElements(aUTF8Length + 1))
2966 return;
2968 pango_break(aUTF8, aUTF8Length, aAnalysis,
2969 buffer.Elements(), buffer.Length());
2971 const gchar *p = aUTF8;
2972 const gchar *end = aUTF8 + aUTF8Length;
2973 const PangoLogAttr *attr = buffer.Elements();
2974 gfxTextRun::CompressedGlyph g;
2975 while (p < end) {
2976 if (!attr->is_cursor_position) {
2977 aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
2979 ++aUTF16Offset;
2981 gunichar ch = g_utf8_get_char(p);
2982 NS_ASSERTION(ch != 0, "Shouldn't have NUL in pango_break");
2983 NS_ASSERTION(!IS_SURROGATE(ch), "Shouldn't have surrogates in UTF8");
2984 if (ch >= 0x10000) {
2985 // set glyph info for the UTF-16 low surrogate
2986 aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_FALSE, 0), nsnull);
2987 ++aUTF16Offset;
2989 // We produced this utf8 so we don't need to worry about malformed stuff
2990 p = g_utf8_next_char(p);
2991 ++attr;
2995 static PRInt32
2996 ConvertPangoToAppUnits(PRInt32 aCoordinate, PRUint32 aAppUnitsPerDevUnit)
2998 PRInt64 v = (PRInt64(aCoordinate)*aAppUnitsPerDevUnit + PANGO_SCALE/2)/PANGO_SCALE;
2999 return PRInt32(v);
3003 * Given a run of Pango glyphs that should be treated as a single
3004 * cluster/ligature, store them in the textrun at the appropriate character
3005 * and set the other characters involved to be ligature/cluster continuations
3006 * as appropriate.
3008 static nsresult
3009 SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
3010 gfxTextRun *aTextRun,
3011 const gchar *aUTF8, PRUint32 aUTF8Length,
3012 PRUint32 *aUTF16Offset,
3013 PangoGlyphUnit aOverrideSpaceWidth)
3015 PRUint32 utf16Offset = *aUTF16Offset;
3016 PRUint32 textRunLength = aTextRun->GetLength();
3017 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
3018 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
3020 // Override the width of a space, but only for spaces that aren't
3021 // clustered with something else (like a freestanding diacritical mark)
3022 PangoGlyphUnit width = aGlyphs[0].geometry.width;
3023 if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
3024 (utf16Offset + 1 == textRunLength ||
3025 charGlyphs[utf16Offset].IsClusterStart())) {
3026 width = aOverrideSpaceWidth;
3028 PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
3030 gfxTextRun::CompressedGlyph g;
3031 PRBool atClusterStart = aTextRun->IsClusterStart(utf16Offset);
3032 // See if we fit in the compressed area.
3033 if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
3034 aGlyphs[0].geometry.x_offset == 0 &&
3035 aGlyphs[0].geometry.y_offset == 0 &&
3036 !IS_EMPTY_GLYPH(aGlyphs[0].glyph) &&
3037 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
3038 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
3039 aTextRun->SetSimpleGlyph(utf16Offset,
3040 g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
3041 } else {
3042 nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
3043 if (!detailedGlyphs.AppendElements(aGlyphCount))
3044 return NS_ERROR_OUT_OF_MEMORY;
3046 PRInt32 direction = aTextRun->IsRightToLeft() ? -1 : 1;
3047 PRUint32 pangoIndex = direction > 0 ? 0 : aGlyphCount - 1;
3048 PRUint32 detailedIndex = 0;
3049 for (PRUint32 i = 0; i < aGlyphCount; ++i) {
3050 const PangoGlyphInfo &glyph = aGlyphs[pangoIndex];
3051 pangoIndex += direction;
3052 // The zero width characters return empty glyph ID at
3053 // shaping; we should skip these.
3054 if (IS_EMPTY_GLYPH(glyph.glyph))
3055 continue;
3057 gfxTextRun::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
3058 ++detailedIndex;
3060 details->mGlyphID = glyph.glyph;
3061 NS_ASSERTION(details->mGlyphID == glyph.glyph,
3062 "Seriously weird glyph ID detected!");
3063 details->mAdvance =
3064 ConvertPangoToAppUnits(glyph.geometry.width,
3065 appUnitsPerDevUnit);
3066 details->mXOffset =
3067 float(glyph.geometry.x_offset)*appUnitsPerDevUnit/PANGO_SCALE;
3068 details->mYOffset =
3069 float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
3071 g.SetComplex(atClusterStart, PR_TRUE, detailedIndex);
3072 aTextRun->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
3075 // Check for ligatures and set *aUTF16Offset.
3076 const gchar *p = aUTF8;
3077 const gchar *end = aUTF8 + aUTF8Length;
3078 while (1) {
3079 // Skip the CompressedGlyph that we have added, but check if the
3080 // character was supposed to be ignored. If it's supposed to be ignored,
3081 // overwrite the textrun entry with an invisible missing-glyph.
3082 gunichar ch = g_utf8_get_char(p);
3083 NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
3084 if (ch >= 0x10000) {
3085 // Skip surrogate
3086 ++utf16Offset;
3088 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(PRUnichar(ch)),
3089 "Invalid character detected");
3090 ++utf16Offset;
3092 // We produced this UTF8 so we don't need to worry about malformed stuff
3093 p = g_utf8_next_char(p);
3094 if (p >= end)
3095 break;
3097 if (utf16Offset >= textRunLength) {
3098 NS_ERROR("Someone has added too many glyphs!");
3099 return NS_ERROR_FAILURE;
3102 g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_FALSE, 0);
3103 aTextRun->SetGlyphs(utf16Offset, g, nsnull);
3105 *aUTF16Offset = utf16Offset;
3106 return NS_OK;
3109 nsresult
3110 gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
3111 const gchar *aUTF8, PRUint32 aUTF8Length,
3112 PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
3113 PangoGlyphUnit aOverrideSpaceWidth,
3114 PRBool aAbortOnMissingGlyph)
3116 gint numGlyphs = aGlyphs->num_glyphs;
3117 PangoGlyphInfo *glyphs = aGlyphs->glyphs;
3118 const gint *logClusters = aGlyphs->log_clusters;
3119 // We cannot make any assumptions about the order of glyph clusters
3120 // provided by pango_shape (see 375864), so we work through the UTF8 text
3121 // and process the glyph clusters in logical order.
3123 // logGlyphs is like an inverse of logClusters. For each UTF8 byte:
3124 // >= 0 indicates that the byte is first in a cluster and
3125 // gives the position of the starting glyph for the cluster.
3126 // -1 indicates that the byte does not start a cluster.
3127 nsAutoTArray<gint,2000> logGlyphs;
3128 if (!logGlyphs.AppendElements(aUTF8Length + 1))
3129 return NS_ERROR_OUT_OF_MEMORY;
3130 PRUint32 utf8Index = 0;
3131 for(; utf8Index < aUTF8Length; ++utf8Index)
3132 logGlyphs[utf8Index] = -1;
3133 logGlyphs[aUTF8Length] = numGlyphs;
3135 gint lastCluster = -1; // != utf8Index
3136 for (gint glyphIndex = 0; glyphIndex < numGlyphs; ++glyphIndex) {
3137 gint thisCluster = logClusters[glyphIndex];
3138 if (thisCluster != lastCluster) {
3139 lastCluster = thisCluster;
3140 NS_ASSERTION(0 <= thisCluster && thisCluster < gint(aUTF8Length),
3141 "garbage from pango_shape - this is bad");
3142 logGlyphs[thisCluster] = glyphIndex;
3146 PRUint32 utf16Offset = *aUTF16Offset;
3147 PRUint32 textRunLength = aTextRun->GetLength();
3148 utf8Index = 0;
3149 // The next glyph cluster in logical order.
3150 gint nextGlyphClusterStart = logGlyphs[utf8Index];
3151 NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
3152 while (utf8Index < aUTF8Length) {
3153 if (utf16Offset >= textRunLength) {
3154 NS_ERROR("Someone has added too many glyphs!");
3155 return NS_ERROR_FAILURE;
3157 gint glyphClusterStart = nextGlyphClusterStart;
3158 // Find the utf8 text associated with this glyph cluster.
3159 PRUint32 clusterUTF8Start = utf8Index;
3160 // Check we are consistent with pango_break data.
3161 NS_ASSERTION(aTextRun->GetCharacterGlyphs()->IsClusterStart(),
3162 "Glyph cluster not aligned on character cluster.");
3163 do {
3164 ++utf8Index;
3165 nextGlyphClusterStart = logGlyphs[utf8Index];
3166 } while (nextGlyphClusterStart < 0);
3167 const gchar *clusterUTF8 = &aUTF8[clusterUTF8Start];
3168 PRUint32 clusterUTF8Length = utf8Index - clusterUTF8Start;
3170 PRBool haveMissingGlyph = PR_FALSE;
3171 gint glyphIndex = glyphClusterStart;
3173 // It's now unncecessary to do NUL handling here.
3174 do {
3175 if (IS_MISSING_GLYPH(glyphs[glyphIndex].glyph)) {
3176 // Does pango ever provide more than one glyph in the
3177 // cluster if there is a missing glyph?
3178 // behdad: yes
3179 haveMissingGlyph = PR_TRUE;
3181 glyphIndex++;
3182 } while (glyphIndex < numGlyphs &&
3183 logClusters[glyphIndex] == gint(clusterUTF8Start));
3185 if (haveMissingGlyph && aAbortOnMissingGlyph)
3186 return NS_ERROR_FAILURE;
3188 nsresult rv;
3189 if (haveMissingGlyph) {
3190 rv = SetMissingGlyphs(aTextRun, clusterUTF8, clusterUTF8Length,
3191 &utf16Offset);
3192 } else {
3193 rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
3194 glyphIndex - glyphClusterStart,
3195 aTextRun,
3196 clusterUTF8, clusterUTF8Length,
3197 &utf16Offset, aOverrideSpaceWidth);
3199 NS_ENSURE_SUCCESS(rv,rv);
3201 *aUTF16Offset = utf16Offset;
3202 return NS_OK;
3205 nsresult
3206 gfxPangoFontGroup::SetMissingGlyphs(gfxTextRun *aTextRun,
3207 const gchar *aUTF8, PRUint32 aUTF8Length,
3208 PRUint32 *aUTF16Offset)
3210 PRUint32 utf16Offset = *aUTF16Offset;
3211 PRUint32 textRunLength = aTextRun->GetLength();
3212 for (PRUint32 index = 0; index < aUTF8Length;) {
3213 if (utf16Offset >= textRunLength) {
3214 NS_ERROR("Someone has added too many glyphs!");
3215 break;
3217 gunichar ch = g_utf8_get_char(aUTF8 + index);
3218 aTextRun->SetMissingGlyph(utf16Offset, ch);
3220 ++utf16Offset;
3221 NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
3222 if (ch >= 0x10000)
3223 ++utf16Offset;
3224 // We produced this UTF8 so we don't need to worry about malformed stuff
3225 index = g_utf8_next_char(aUTF8 + index) - aUTF8;
3228 *aUTF16Offset = utf16Offset;
3229 return NS_OK;
3232 #if defined(ENABLE_FAST_PATH_8BIT) || defined(ENABLE_FAST_PATH_ALWAYS)
3233 nsresult
3234 gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
3235 const gchar *aUTF8, PRUint32 aUTF8Length)
3237 const gchar *p = aUTF8;
3238 PangoFont *pangofont = GetBasePangoFont();
3239 PangoFcFont *fcfont = PANGO_FC_FONT (pangofont);
3240 gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(pangofont));
3241 PRUint32 utf16Offset = 0;
3242 gfxTextRun::CompressedGlyph g;
3243 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
3245 aTextRun->AddGlyphRun(gfxFont, 0);
3247 while (p < aUTF8 + aUTF8Length) {
3248 // glib-2.12.9: "If p does not point to a valid UTF-8 encoded
3249 // character, results are undefined." so it is not easy to assert that
3250 // aUTF8 in fact points to UTF8 data but asserting
3251 // g_unichar_validate(ch) may be mildly useful.
3252 gunichar ch = g_utf8_get_char(p);
3253 p = g_utf8_next_char(p);
3255 if (ch == 0) {
3256 // treat this null byte as a missing glyph. Pango
3257 // doesn't create glyphs for these, not even missing-glyphs.
3258 aTextRun->SetMissingGlyph(utf16Offset, 0);
3259 } else {
3260 NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected");
3261 FT_UInt glyph = pango_fc_font_get_glyph (fcfont, ch);
3262 if (!glyph) // character not in font,
3263 return NS_ERROR_FAILURE; // fallback to CreateGlyphRunsItemizing
3265 cairo_text_extents_t extents;
3266 gfxFont->GetGlyphExtents(glyph, &extents);
3268 PRInt32 advance = NS_lround(extents.x_advance * appUnitsPerDevUnit);
3269 if (advance >= 0 &&
3270 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
3271 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
3272 aTextRun->SetSimpleGlyph(utf16Offset,
3273 g.SetSimpleGlyph(advance, glyph));
3274 } else {
3275 gfxTextRun::DetailedGlyph details;
3276 details.mGlyphID = glyph;
3277 NS_ASSERTION(details.mGlyphID == glyph,
3278 "Seriously weird glyph ID detected!");
3279 details.mAdvance = advance;
3280 details.mXOffset = 0;
3281 details.mYOffset = 0;
3282 g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_TRUE, 1);
3283 aTextRun->SetGlyphs(utf16Offset, g, &details);
3286 NS_ASSERTION(!IS_SURROGATE(ch), "Surrogates shouldn't appear in UTF8");
3287 if (ch >= 0x10000) {
3288 // This character is a surrogate pair in UTF16
3289 ++utf16Offset;
3293 ++utf16Offset;
3295 return NS_OK;
3297 #endif
3299 void
3300 gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
3301 const gchar *aUTF8, PRUint32 aUTF8Length,
3302 PRUint32 aUTF8HeaderLen)
3304 // This font group and gfxPangoFontMap are recorded on the PangoContext
3305 // passed to pango_itemize_with_base_dir().
3307 // pango_itemize_with_base_dir() divides the string into substrings for
3308 // each language, and queries gfxPangoFontMap::load_fontset() to provide
3309 // ordered lists of fonts for each language. gfxPangoFontMap passes the
3310 // request back to this font group, which returns a gfxFcPangoFontSet
3311 // handling the font sorting/selection.
3313 // For each character, pango_itemize_with_base_dir searches through these
3314 // lists of fonts for a font with support for the character. The
3315 // PangoItems returned represent substrings (or runs) of consectutive
3316 // characters to be shaped with the same PangoFont and having the same
3317 // script.
3319 // The PangoFonts in the PangoItems are from the gfxPangoFontMap and so
3320 // each have a gfxFont. This gfxFont represents the same face as the
3321 // PangoFont and so can render the same glyphs in the same way as
3322 // pango_shape measures.
3324 PangoContext *context = GetPangoContext();
3325 // we should set this to null if we don't have a text language from the page...
3326 // except that we almost always have something...
3327 pango_context_set_language(context, mPangoLanguage);
3328 SetFontGroup(context, this);
3330 PangoDirection dir = aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
3331 GList *items = pango_itemize_with_base_dir(context, dir, aUTF8, 0, aUTF8Length, nsnull, nsnull);
3333 PRUint32 utf16Offset = 0;
3334 #ifdef DEBUG
3335 PRBool isRTL = aTextRun->IsRightToLeft();
3336 #endif
3337 GList *pos = items;
3338 PangoGlyphString *glyphString = pango_glyph_string_new();
3339 if (!glyphString)
3340 goto out; // OOM
3342 for (; pos && pos->data; pos = pos->next) {
3343 PangoItem *item = (PangoItem *)pos->data;
3344 NS_ASSERTION(isRTL == item->analysis.level % 2, "RTL assumption mismatch");
3346 PRUint32 offset = item->offset;
3347 PRUint32 length = item->length;
3348 if (offset < aUTF8HeaderLen) {
3349 if (offset + length <= aUTF8HeaderLen)
3350 continue;
3352 length -= aUTF8HeaderLen - offset;
3353 offset = aUTF8HeaderLen;
3356 gfxFcFont *font =
3357 gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(item->analysis.font));
3359 nsresult rv = aTextRun->AddGlyphRun(font, utf16Offset);
3360 if (NS_FAILED(rv)) {
3361 NS_ERROR("AddGlyphRun Failed");
3362 goto out;
3365 PRUint32 spaceWidth =
3366 moz_pango_units_from_double(font->GetMetrics().spaceWidth);
3368 const gchar *p = aUTF8 + offset;
3369 const gchar *end = p + length;
3370 while (p < end) {
3371 if (*p == 0) {
3372 aTextRun->SetMissingGlyph(utf16Offset, 0);
3373 ++p;
3374 ++utf16Offset;
3375 continue;
3378 // It's necessary to loop over pango_shape as it treats
3379 // NULs as string terminators
3380 const gchar *text = p;
3381 do {
3382 ++p;
3383 } while(p < end && *p != 0);
3384 gint len = p - text;
3386 pango_shape(text, len, &item->analysis, glyphString);
3387 SetupClusterBoundaries(aTextRun, text, len, utf16Offset, &item->analysis);
3388 SetGlyphs(aTextRun, text, len, &utf16Offset, glyphString, spaceWidth, PR_FALSE);
3392 out:
3393 if (glyphString)
3394 pango_glyph_string_free(glyphString);
3396 for (pos = items; pos; pos = pos->next)
3397 pango_item_free((PangoItem *)pos->data);
3399 if (items)
3400 g_list_free(items);
3402 g_object_unref(context);
3405 /* static */
3406 PangoLanguage *
3407 GuessPangoLanguage(const nsACString& aLangGroup)
3409 // See if the lang group needs to be translated from Mozilla's
3410 // internal mapping into fontconfig's
3411 nsCAutoString lang;
3412 gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
3414 if (lang.IsEmpty())
3415 return NULL;
3417 return pango_language_from_string(lang.get());
3420 #ifdef MOZ_WIDGET_GTK2
3421 /***************************************************************************
3423 * This function must be last in the file because it uses the system cairo
3424 * library. Above this point the cairo library used is the tree cairo if
3425 * MOZ_TREE_CAIRO.
3428 #if MOZ_TREE_CAIRO
3429 // Tree cairo symbols have different names. Disable their activation through
3430 // preprocessor macros.
3431 #undef cairo_ft_font_options_substitute
3433 // The system cairo functions are not declared because the include paths cause
3434 // the gdk headers to pick up the tree cairo.h.
3435 extern "C" {
3436 NS_VISIBILITY_DEFAULT void
3437 cairo_ft_font_options_substitute (const cairo_font_options_t *options,
3438 FcPattern *pattern);
3440 #endif
3442 static void
3443 ApplyGdkScreenFontOptions(FcPattern *aPattern)
3445 const cairo_font_options_t *options =
3446 gdk_screen_get_font_options(gdk_screen_get_default());
3448 cairo_ft_font_options_substitute(options, aPattern);
3451 #endif // MOZ_WIDGET_GTK2