Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / gfx / thebes / src / gfxPangoFonts.cpp
blobbfafb65b8bb09effd50ab7672196ab3e4a9bf62e
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)
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 #ifdef MOZ_WIDGET_GTK2
1732 ApplyGdkScreenFontOptions(aPattern);
1733 #endif
1735 // Protect against any fontconfig settings that may have incorrectly
1736 // modified the pixelsize, and consider aSizeAdjustFactor.
1737 double size = aFallbackSize;
1738 if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
1739 || aSizeAdjustFactor != 1.0) {
1740 FcPatternDel(aPattern, FC_PIXEL_SIZE);
1741 FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
1744 FcDefaultSubstitute(aPattern);
1747 static void
1748 gfx_pango_font_map_context_substitute(PangoFcFontMap *fontmap,
1749 PangoContext *context,
1750 FcPattern *pattern)
1752 // owned by the context
1753 PangoFontDescription *desc = pango_context_get_font_description(context);
1754 double size = pango_font_description_get_size(desc) / FLOAT_PANGO_SCALE;
1755 PrepareSortPattern(pattern, size, 1.0);
1758 static PangoFcFont *
1759 gfx_pango_font_map_create_font(PangoFcFontMap *fontmap,
1760 PangoContext *context,
1761 const PangoFontDescription *desc,
1762 FcPattern *pattern)
1764 return PANGO_FC_FONT(g_object_new(GFX_TYPE_PANGO_FC_FONT,
1765 "pattern", pattern, NULL));
1768 static void
1769 gfx_pango_font_map_class_init(gfxPangoFontMapClass *klass)
1771 // inherit GObjectClass::finalize from parent as this class adds no data.
1773 PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (klass);
1774 fontmap_class->load_font = gfx_pango_font_map_load_font;
1775 // inherit fontmap_class->list_families (which is not likely to be used)
1776 // from PangoFcFontMap
1777 fontmap_class->load_fontset = gfx_pango_font_map_load_fontset;
1778 // inherit fontmap_class->shape_engine_type from PangoFcFontMap
1780 PangoFcFontMapClass *fcfontmap_class = PANGO_FC_FONT_MAP_CLASS (klass);
1781 fcfontmap_class->get_resolution = gfx_pango_font_map_get_resolution;
1782 // context_key_* virtual functions are only necessary if we want to
1783 // dynamically respond to changes in the screen cairo_font_options_t.
1785 // context_substitute and get_font are not likely to be used but
1786 // implemented because the class makes them available.
1787 fcfontmap_class->context_substitute = gfx_pango_font_map_context_substitute;
1788 fcfontmap_class->create_font = gfx_pango_font_map_create_font;
1792 ** gfxPangoFontGroup
1795 struct FamilyCallbackData {
1796 FamilyCallbackData(nsTArray<nsString> *aFcFamilyList,
1797 gfxUserFontSet *aUserFontSet)
1798 : mFcFamilyList(aFcFamilyList), mUserFontSet(aUserFontSet)
1801 nsTArray<nsString> *mFcFamilyList;
1802 const gfxUserFontSet *mUserFontSet;
1805 static int
1806 FFRECountHyphens (const nsAString &aFFREName)
1808 int h = 0;
1809 PRInt32 hyphen = 0;
1810 while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) {
1811 ++h;
1812 ++hyphen;
1814 return h;
1817 static PRBool
1818 FamilyCallback (const nsAString& fontName, const nsACString& genericName,
1819 void *closure)
1821 FamilyCallbackData *data = static_cast<FamilyCallbackData*>(closure);
1822 nsTArray<nsString> *list = data->mFcFamilyList;
1824 // We ignore prefs that have three hypens since they are X style prefs.
1825 if (genericName.Length() && FFRECountHyphens(fontName) >= 3)
1826 return PR_TRUE;
1828 if (!list->Contains(fontName)) {
1829 // The family properties of FcPatterns for @font-face fonts have a
1830 // namespace to identify them among system fonts. (see
1831 // FONT_FACE_FAMILY_PREFIX.) The CSS family name can match either the
1832 // @font-face family or the system font family so both names are added
1833 // here.
1835 // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802 required
1836 // looking for locally-installed fonts matching requested properties
1837 // before checking the src descriptor in @font-face rules.
1838 // http://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#algorithm
1839 // also only checks src descriptors if there is no local font matching
1840 // the requested properties.
1842 // Similarly "Editor's Draft 27 June 2008"
1843 // http://dev.w3.org/csswg/css3-fonts/#font-matching says "The user
1844 // agent attempts to find the family name among fonts available on the
1845 // system and then among fonts defined via @font-face rules."
1846 // However, this is contradicted by "if [the name from the font-family
1847 // descriptor] is the same as a font family available in a given
1848 // user's environment, it effectively hides the underlying font for
1849 // documents that use the stylesheet."
1851 // Windows and Mac code currently prioritizes fonts from @font-face
1852 // rules. The order of families here reflects the priorities on those
1853 // platforms.
1854 const gfxUserFontSet *userFontSet = data->mUserFontSet;
1855 if (genericName.Length() == 0 &&
1856 userFontSet && userFontSet->HasFamily(fontName)) {
1857 nsAutoString userFontName =
1858 NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
1859 list->AppendElement(userFontName);
1862 list->AppendElement(fontName);
1865 return PR_TRUE;
1868 gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families,
1869 const gfxFontStyle *aStyle,
1870 gfxUserFontSet *aUserFontSet)
1871 : gfxFontGroup(families, aStyle, aUserFontSet),
1872 mPangoLanguage(GuessPangoLanguage(aStyle->langGroup))
1874 mFonts.AppendElements(1);
1877 gfxPangoFontGroup::~gfxPangoFontGroup()
1881 gfxFontGroup *
1882 gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
1884 return new gfxPangoFontGroup(mFamilies, aStyle, mUserFontSet);
1887 // An array of family names suitable for fontconfig
1888 void
1889 gfxPangoFontGroup::GetFcFamilies(nsTArray<nsString> *aFcFamilyList,
1890 const nsACString& aLangGroup)
1892 FamilyCallbackData data(aFcFamilyList, mUserFontSet);
1893 // Leave non-existing fonts in the list so that fontconfig can get the
1894 // best match.
1895 ForEachFontInternal(mFamilies, aLangGroup, PR_TRUE, PR_FALSE,
1896 FamilyCallback, &data);
1899 PangoFont *
1900 gfxPangoFontGroup::GetBasePangoFont()
1902 return GetBaseFontSet()->GetFontAt(0);
1905 gfxFont *
1906 gfxPangoFontGroup::GetFontAt(PRInt32 i) {
1907 // If it turns out to be hard for all clients that cache font
1908 // groups to call UpdateFontList at appropriate times, we could
1909 // instead consider just calling UpdateFontList from someplace
1910 // more central (such as here).
1911 NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
1912 "Whoever was caching this font group should have "
1913 "called UpdateFontList on it");
1915 NS_PRECONDITION(i == 0, "Only have one font");
1917 if (!mFonts[0]) {
1918 PangoFont *pangoFont = GetBasePangoFont();
1919 mFonts[0] = gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(pangoFont));
1922 return mFonts[0];
1925 void
1926 gfxPangoFontGroup::UpdateFontList()
1928 if (!mUserFontSet)
1929 return;
1931 PRUint64 newGeneration = mUserFontSet->GetGeneration();
1932 if (newGeneration == mCurrGeneration)
1933 return;
1935 mFonts[0] = NULL;
1936 mFontSets.Clear();
1937 mCurrGeneration = newGeneration;
1940 already_AddRefed<gfxFcPangoFontSet>
1941 gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
1942 nsAutoRef<FcPattern> *aMatchPattern)
1944 const char *lang = pango_language_to_string(aLang);
1946 const char *langGroup = nsnull;
1947 if (aLang != mPangoLanguage) {
1948 // Set up langGroup for Mozilla's font prefs.
1949 if (!gLangService) {
1950 CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
1952 if (gLangService) {
1953 nsIAtom *atom =
1954 gLangService->LookupLanguage(NS_ConvertUTF8toUTF16(lang));
1955 if (atom) {
1956 atom->GetUTF8String(&langGroup);
1961 nsAutoTArray<nsString, 20> fcFamilyList;
1962 GetFcFamilies(&fcFamilyList,
1963 langGroup ? nsDependentCString(langGroup) : mStyle.langGroup);
1965 // To consider: A fontset cache here could be helpful.
1967 // Get a pattern suitable for matching.
1968 nsAutoRef<FcPattern> pattern
1969 (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
1971 PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor);
1973 nsRefPtr<gfxFcPangoFontSet> fontset =
1974 new gfxFcPangoFontSet(pattern, mUserFontSet);
1976 if (aMatchPattern)
1977 aMatchPattern->steal(pattern);
1979 return fontset.forget();
1982 gfxPangoFontGroup::
1983 FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
1984 gfxFcPangoFontSet *aFontSet)
1985 : mLang(aLang), mFontSet(aFontSet)
1989 gfxFcPangoFontSet *
1990 gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
1992 GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
1994 if (!aLang)
1995 return mFontSets[0].mFontSet;
1997 for (PRUint32 i = 0; i < mFontSets.Length(); ++i) {
1998 if (mFontSets[i].mLang == aLang)
1999 return mFontSets[i].mFontSet;
2002 nsRefPtr<gfxFcPangoFontSet> fontSet =
2003 MakeFontSet(aLang, mSizeAdjustFactor);
2004 mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
2006 return fontSet;
2010 ** gfxFcFont
2013 cairo_user_data_key_t gfxFcFont::sGfxFontKey;
2015 gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
2016 gfxFontEntry *aFontEntry,
2017 const gfxFontStyle *aFontStyle)
2018 : gfxFont(aFontEntry, aFontStyle),
2019 mCairoFont(aCairoFont),
2020 mHasMetrics(PR_FALSE)
2022 cairo_scaled_font_reference(mCairoFont);
2023 cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, this, NULL);
2026 gfxFcFont::~gfxFcFont()
2028 cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, NULL, NULL);
2029 cairo_scaled_font_destroy(mCairoFont);
2032 /* static */ void
2033 gfxPangoFontGroup::Shutdown()
2035 if (gPangoFontMap) {
2036 if (PANGO_IS_FC_FONT_MAP (gPangoFontMap)) {
2037 // This clears circular references from the fontmap to itself
2038 // through its fonts.
2039 pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(gPangoFontMap));
2041 g_object_unref(gPangoFontMap);
2042 gPangoFontMap = NULL;
2045 // Resetting gFTLibrary in case this is wanted again after a
2046 // cairo_debug_reset_static_data.
2047 gFTLibrary = NULL;
2049 NS_IF_RELEASE(gLangService);
2052 static FT_Library
2053 GetFTLibrary()
2055 if (!gFTLibrary) {
2056 // Use cairo's FT_Library so that cairo takes care of shutdown of the
2057 // FT_Library after it has destroyed its font_faces, and FT_Done_Face
2058 // has been called on each FT_Face, at least until this bug is fixed:
2059 // https://bugs.freedesktop.org/show_bug.cgi?id=18857
2061 // Cairo's FT_Library can be obtained from any cairo_scaled_font. The
2062 // font properties requested here are chosen to get an FT_Face that is
2063 // likely to be also used elsewhere.
2064 gfxFontStyle style;
2065 nsRefPtr<gfxPangoFontGroup> fontGroup =
2066 new gfxPangoFontGroup(NS_LITERAL_STRING("sans-serif"),
2067 &style, nsnull);
2069 gfxFcFont *font = static_cast<gfxFcFont*>(fontGroup->GetFontAt(0));
2070 if (!font)
2071 return NULL;
2073 LockedFTFace face(font);
2074 if (!face.get())
2075 return NULL;
2077 gFTLibrary = face.get()->glyph->library;
2080 return gFTLibrary;
2083 /* static */ gfxFontEntry *
2084 gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
2085 nsISupports *aLoader,
2086 const PRUint8 *aFontData, PRUint32 aLength)
2088 // Using face_index = 0 for the first face in the font, as we have no
2089 // other information. FT_New_Memory_Face checks for a NULL FT_Library.
2090 FT_Face face;
2091 FT_Error error =
2092 FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
2093 if (error != 0)
2094 return nsnull;
2096 return new gfxDownloadedFcFontEntry(aProxyEntry, aLoader, face);
2100 static double
2101 GetPixelSize(FcPattern *aPattern)
2103 double size;
2104 if (FcPatternGetDouble(aPattern,
2105 FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
2106 return size;
2108 NS_NOTREACHED("No size on pattern");
2109 return 0.0;
2113 * The following gfxPangoFonts are accessed from the PangoFont, not from the
2114 * gfxFontCache hash table. The gfxFontCache hash table is keyed by desired
2115 * family and style, whereas here we only know actual family and style. There
2116 * may be more than one of these fonts with the same family and style, but
2117 * different PangoFont and actual font face.
2119 * The point of this is to record the exact font face for gfxTextRun glyph
2120 * indices. The style of this font does not necessarily represent the exact
2121 * gfxFontStyle used to build the text run. Notably, the language is not
2122 * recorded.
2125 /* static */
2126 already_AddRefed<gfxFcFont>
2127 gfxFcFont::GetOrMakeFont(FcPattern *aPattern)
2129 cairo_scaled_font_t *cairoFont = CreateScaledFont(aPattern);
2131 nsRefPtr<gfxFcFont> font = static_cast<gfxFcFont*>
2132 (cairo_scaled_font_get_user_data(cairoFont, &sGfxFontKey));
2134 if (!font) {
2135 gfxFloat size = GetPixelSize(aPattern);
2137 // Shouldn't actually need to take too much care about the correct
2138 // name or style, as size is the only thing expected to be important.
2139 PRUint8 style = gfxFontconfigUtils::GetThebesStyle(aPattern);
2140 PRUint16 weight = gfxFontconfigUtils::GetThebesWeight(aPattern);
2142 // The LangSet in the FcPattern does not have an order so there is no
2143 // one particular language to choose and converting the set to a
2144 // string through FcNameUnparse() is more trouble than it's worth.
2145 NS_NAMED_LITERAL_CSTRING(langGroup, "x-unicode");
2146 gfxFontStyle fontStyle(style, weight, size, langGroup, 0.0,
2147 PR_TRUE, PR_FALSE);
2149 nsRefPtr<gfxFontEntry> fe;
2150 FcChar8 *fc_file;
2151 if (FcPatternGetString(aPattern,
2152 FC_FILE, 0, &fc_file) == FcResultMatch) {
2153 int index;
2154 if (FcPatternGetInteger(aPattern,
2155 FC_INDEX, 0, &index) != FcResultMatch) {
2156 // cairo won't know what to do with this pattern.
2157 NS_NOTREACHED("No index in pattern for font face from file");
2158 index = 0;
2161 // Get a unique name for the font face data from the file and id.
2162 nsAutoString name;
2163 AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name);
2164 if (index != 0) {
2165 name.AppendLiteral("/");
2166 name.AppendInt(index);
2169 fe = new gfxFontEntry(name);
2170 } else {
2171 fe = GetDownloadedFontEntry(aPattern);
2172 if (!fe) {
2173 // cairo won't know which font to open without a file.
2174 // (We don't create fonts from an FT_Face.)
2175 NS_NOTREACHED("Fonts without a file is not a web font!?");
2176 fe = new gfxFontEntry(nsString());
2180 // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
2181 // not necessarily enough to provide a key that will describe a unique
2182 // font. cairoFont contains information from aPattern, which is a
2183 // fully resolved pattern from FcFontRenderPrepare.
2184 // FcFontRenderPrepare takes the requested pattern and the face
2185 // pattern as input and can modify elements of the resulting pattern
2186 // that affect rendering but are not included in the gfxFontStyle.
2187 font = new gfxFcFont(cairoFont, fe, &fontStyle);
2190 cairo_scaled_font_destroy(cairoFont);
2191 return font.forget();
2194 static PangoFontMap *
2195 GetPangoFontMap()
2197 if (!gPangoFontMap) {
2198 gPangoFontMap = gfxPangoFontMap::NewFontMap();
2200 return gPangoFontMap;
2203 static PangoContext *
2204 GetPangoContext()
2206 PangoContext *context = pango_context_new();
2207 pango_context_set_font_map(context, GetPangoFontMap());
2208 return context;
2211 gfxFcPangoFontSet *
2212 gfxPangoFontGroup::GetBaseFontSet()
2214 if (mFontSets.Length() > 0)
2215 return mFontSets[0].mFontSet;
2217 mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
2218 nsAutoRef<FcPattern> pattern;
2219 nsRefPtr<gfxFcPangoFontSet> fontSet =
2220 MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
2222 double size = GetPixelSize(pattern);
2223 if (size != 0.0 && mStyle.sizeAdjust != 0.0) {
2224 gfxFcFont *font =
2225 gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(fontSet->GetFontAt(0)));
2226 if (font) {
2227 const gfxFont::Metrics& metrics = font->GetMetrics();
2229 // The factor of 0.1 ensures that xHeight is sane so fonts don't
2230 // become huge. Strictly ">" ensures that xHeight and emHeight are
2231 // not both zero.
2232 if (metrics.xHeight > 0.1 * metrics.emHeight) {
2233 mSizeAdjustFactor =
2234 mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
2236 size *= mSizeAdjustFactor;
2237 FcPatternDel(pattern, FC_PIXEL_SIZE);
2238 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
2240 fontSet = new gfxFcPangoFontSet(pattern, mUserFontSet);
2245 PangoLanguage *pangoLang = mPangoLanguage;
2246 FcChar8 *fcLang;
2247 if (!pangoLang &&
2248 FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
2249 pangoLang =
2250 pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
2253 mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
2255 return fontSet;
2258 void
2259 gfxFcFont::GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents)
2261 NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
2263 cairo_glyph_t glyphs[1];
2264 glyphs[0].index = aGlyph;
2265 glyphs[0].x = 0.0;
2266 glyphs[0].y = 0.0;
2267 // cairo does some caching for us here but perhaps a small gain could be
2268 // made by caching more. It is usually only the advance that is needed,
2269 // so caching only the advance could allow many requests to be cached with
2270 // little memory use. Ideally this cache would be merged with
2271 // gfxGlyphExtents.
2272 cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
2275 PRUint32
2276 LockedFTFace::GetCharExtents(char aChar,
2277 cairo_text_extents_t* aExtents)
2279 NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
2281 if (!mFace)
2282 return 0;
2284 // pango_fc_font_real_get_glyph uses FcFreeTypeCharIndex which may change
2285 // the charmap currently selected on the FT_Face, so, while
2286 // pango_fc_font_real_get_glyph might be used, we should use the same
2287 // function so as to search the charmaps.
2289 // Unfortunately this considers the mac/roman cmap even when there is a
2290 // unicode cmap, which will be bad for symbol fonts, so we should do this
2291 // ourselves, perhaps with a lightweight cache like
2292 // pango_fc_font_real_get_glyph uses.
2293 FT_UInt gid = FcFreeTypeCharIndex(mFace, aChar); // glyph id
2294 if (gid) {
2295 mGfxFont->GetGlyphExtents(gid, aExtents);
2298 return gid;
2301 // Snap a line to pixels while keeping the center and size of the line as
2302 // close to the original position as possible.
2304 // Pango does similar snapping for underline and strikethrough when fonts are
2305 // hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
2306 // top and size of lines. Optimizing the distance between the line and
2307 // baseline is probably good for the gap between text and underline, but
2308 // optimizing the center of the line is better for positioning strikethough.
2309 static void
2310 SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
2312 gfxFloat snappedSize = PR_MAX(NS_floor(aSize + 0.5), 1.0);
2313 // Correct offset for change in size
2314 gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
2315 // Snap offset
2316 aOffset = NS_floor(offset + 0.5);
2317 aSize = snappedSize;
2320 void
2321 LockedFTFace::GetMetrics(gfxFont::Metrics* aMetrics, PRUint32* aSpaceGlyph)
2323 NS_PRECONDITION(aMetrics != NULL, "aMetrics must not be NULL");
2324 NS_PRECONDITION(aSpaceGlyph != NULL, "aSpaceGlyph must not be NULL");
2326 if (NS_UNLIKELY(!mFace)) {
2327 // No face. This unfortunate situation might happen if the font
2328 // file is (re)moved at the wrong time.
2329 aMetrics->emHeight = mGfxFont->GetStyle()->size;
2330 aMetrics->emAscent = 0.8 * aMetrics->emHeight;
2331 aMetrics->emDescent = 0.2 * aMetrics->emHeight;
2332 aMetrics->maxAscent = aMetrics->emAscent;
2333 aMetrics->maxDescent = aMetrics->maxDescent;
2334 aMetrics->maxHeight = aMetrics->emHeight;
2335 aMetrics->internalLeading = 0.0;
2336 aMetrics->externalLeading = 0.2 * aMetrics->emHeight;
2337 aSpaceGlyph = 0;
2338 aMetrics->spaceWidth = 0.5 * aMetrics->emHeight;
2339 aMetrics->maxAdvance = aMetrics->spaceWidth;
2340 aMetrics->aveCharWidth = aMetrics->spaceWidth;
2341 aMetrics->zeroOrAveCharWidth = aMetrics->spaceWidth;
2342 aMetrics->xHeight = 0.5 * aMetrics->emHeight;
2343 aMetrics->underlineSize = aMetrics->emHeight / 14.0;
2344 aMetrics->underlineOffset = -aMetrics->underlineSize;
2345 aMetrics->strikeoutOffset = 0.25 * aMetrics->emHeight;
2346 aMetrics->strikeoutSize = aMetrics->underlineSize;
2347 aMetrics->superscriptOffset = aMetrics->xHeight;
2348 aMetrics->subscriptOffset = aMetrics->xHeight;
2350 return;
2353 const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
2355 gfxFloat emHeight;
2356 // Scale for vertical design metric conversion: pixels per design unit.
2357 gfxFloat yScale;
2358 if (FT_IS_SCALABLE(mFace)) {
2359 // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
2360 // have subpixel accuracy.
2362 // FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
2363 // (fractional) value is a factor that converts vertical metrics from
2364 // design units to units of 1/64 pixels, so that the result may be
2365 // interpreted as pixels in 26.6 fixed point format.
2366 yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
2367 emHeight = mFace->units_per_EM * yScale;
2368 } else { // Not scalable.
2369 // FT_Size_Metrics doc says x_scale is "only relevant for scalable
2370 // font formats".
2371 gfxFloat emUnit = mFace->units_per_EM;
2372 emHeight = ftMetrics.y_ppem;
2373 yScale = emHeight / emUnit;
2376 TT_OS2 *os2 =
2377 static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
2379 aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
2380 aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
2381 aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
2383 gfxFloat lineHeight;
2384 if (os2 && os2->sTypoAscender) {
2385 aMetrics->emAscent = os2->sTypoAscender * yScale;
2386 aMetrics->emDescent = -os2->sTypoDescender * yScale;
2387 FT_Short typoHeight =
2388 os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
2389 lineHeight = typoHeight * yScale;
2391 // maxAscent/maxDescent get used for frame heights, and some fonts
2392 // don't have the HHEA table ascent/descent set (bug 279032).
2393 if (aMetrics->emAscent > aMetrics->maxAscent)
2394 aMetrics->maxAscent = aMetrics->emAscent;
2395 if (aMetrics->emDescent > aMetrics->maxDescent)
2396 aMetrics->maxDescent = aMetrics->emDescent;
2397 } else {
2398 aMetrics->emAscent = aMetrics->maxAscent;
2399 aMetrics->emDescent = aMetrics->maxDescent;
2400 lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
2403 cairo_text_extents_t extents;
2404 *aSpaceGlyph = GetCharExtents(' ', &extents);
2405 if (*aSpaceGlyph) {
2406 aMetrics->spaceWidth = extents.x_advance;
2407 } else {
2408 aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
2411 aMetrics->zeroOrAveCharWidth = 0.0;
2412 if (GetCharExtents('0', &extents)) {
2413 aMetrics->zeroOrAveCharWidth = extents.x_advance;
2416 // Prefering a measured x over sxHeight because sxHeight doesn't consider
2417 // hinting, but maybe the x extents are not quite right in some fancy
2418 // script fonts. CSS 2.1 suggests possibly using the height of an "o",
2419 // which would have a more consistent glyph across fonts.
2420 if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
2421 aMetrics->xHeight = -extents.y_bearing;
2422 aMetrics->aveCharWidth = extents.x_advance;
2423 } else {
2424 if (os2 && os2->sxHeight) {
2425 aMetrics->xHeight = os2->sxHeight * yScale;
2426 } else {
2427 // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
2428 // impossible or impractical to determine the x-height, a value of
2429 // 0.5em should be used."
2430 aMetrics->xHeight = 0.5 * emHeight;
2432 aMetrics->aveCharWidth = 0.0; // updated below
2434 // aveCharWidth is used for the width of text input elements so be
2435 // liberal rather than conservative in the estimate.
2436 if (os2 && os2->xAvgCharWidth) {
2437 // Round to pixels as this is compared with maxAdvance to guess
2438 // whether this is a fixed width font.
2439 gfxFloat avgCharWidth =
2440 ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
2441 aMetrics->aveCharWidth =
2442 PR_MAX(aMetrics->aveCharWidth, avgCharWidth);
2444 aMetrics->aveCharWidth =
2445 PR_MAX(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
2446 if (aMetrics->aveCharWidth == 0.0) {
2447 aMetrics->aveCharWidth = aMetrics->spaceWidth;
2449 if (aMetrics->zeroOrAveCharWidth == 0.0) {
2450 aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
2452 // Apparently hinting can mean that max_advance is not always accurate.
2453 aMetrics->maxAdvance =
2454 PR_MAX(aMetrics->maxAdvance, aMetrics->aveCharWidth);
2456 // gfxFont::Metrics::underlineOffset is the position of the top of the
2457 // underline.
2459 // FT_FaceRec documentation describes underline_position as "the
2460 // center of the underlining stem". This was the original definition
2461 // of the PostScript metric, but in the PostScript table of OpenType
2462 // fonts the metric is "the top of the underline"
2463 // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
2464 // (up to version 2.3.7) doesn't make any adjustment.
2466 // Therefore get the underline position directly from the table
2467 // ourselves when this table exists. Use FreeType's metrics for
2468 // other (including older PostScript) fonts.
2469 if (mFace->underline_position && mFace->underline_thickness) {
2470 aMetrics->underlineSize = mFace->underline_thickness * yScale;
2471 TT_Postscript *post = static_cast<TT_Postscript*>
2472 (FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
2473 if (post && post->underlinePosition) {
2474 aMetrics->underlineOffset = post->underlinePosition * yScale;
2475 } else {
2476 aMetrics->underlineOffset = mFace->underline_position * yScale
2477 + 0.5 * aMetrics->underlineSize;
2479 } else { // No underline info.
2480 // Imitate Pango.
2481 aMetrics->underlineSize = emHeight / 14.0;
2482 aMetrics->underlineOffset = -aMetrics->underlineSize;
2485 if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition) {
2486 aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
2487 aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
2488 } else { // No strikeout info.
2489 aMetrics->strikeoutSize = aMetrics->underlineSize;
2490 // Use OpenType spec's suggested position for Roman font.
2491 aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
2492 + 0.5 * aMetrics->strikeoutSize;
2494 SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
2496 if (os2 && os2->ySuperscriptYOffset) {
2497 gfxFloat val = ScaleRoundDesignUnits(os2->ySuperscriptYOffset,
2498 ftMetrics.y_scale);
2499 aMetrics->superscriptOffset = PR_MAX(1.0, val);
2500 } else {
2501 aMetrics->superscriptOffset = aMetrics->xHeight;
2504 if (os2 && os2->ySubscriptYOffset) {
2505 gfxFloat val = ScaleRoundDesignUnits(os2->ySubscriptYOffset,
2506 ftMetrics.y_scale);
2507 // some fonts have the incorrect sign.
2508 val = fabs(val);
2509 aMetrics->subscriptOffset = PR_MAX(1.0, val);
2510 } else {
2511 aMetrics->subscriptOffset = aMetrics->xHeight;
2514 aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
2516 // Make the line height an integer number of pixels so that lines will be
2517 // equally spaced (rather than just being snapped to pixels, some up and
2518 // some down). Layout calculates line height from the emHeight +
2519 // internalLeading + externalLeading, but first each of these is rounded
2520 // to layout units. To ensure that the result is an integer number of
2521 // pixels, round each of the components to pixels.
2522 aMetrics->emHeight = NS_floor(emHeight + 0.5);
2524 // maxHeight will normally be an integer, but round anyway in case
2525 // FreeType is configured differently.
2526 aMetrics->internalLeading =
2527 NS_floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
2529 // Text input boxes currently don't work well with lineHeight
2530 // significantly less than maxHeight (with Verdana, for example).
2531 lineHeight = NS_floor(PR_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
2532 aMetrics->externalLeading =
2533 lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
2535 // Ensure emAscent + emDescent == emHeight
2536 gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
2537 aMetrics->emAscent = sum > 0.0 ?
2538 aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
2539 aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
2542 const gfxFont::Metrics&
2543 gfxFcFont::GetMetrics()
2545 if (mHasMetrics)
2546 return mMetrics;
2548 if (NS_UNLIKELY(GetStyle()->size <= 0.0)) {
2549 new(&mMetrics) gfxFont::Metrics(); // zero initialize
2550 mSpaceGlyph = 0;
2551 } else {
2552 LockedFTFace(this).GetMetrics(&mMetrics, &mSpaceGlyph);
2555 SanitizeMetrics(&mMetrics, PR_FALSE);
2557 #if 0
2558 // printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
2559 // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
2561 fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
2562 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
2563 fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
2564 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
2565 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
2566 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);
2567 #endif
2569 mHasMetrics = PR_TRUE;
2570 return mMetrics;
2573 nsString
2574 gfxFcFont::GetUniqueName()
2576 return GetName();
2580 ** gfxTextRun
2582 * A serious problem:
2584 * -- We draw with a font that's hinted for the CTM, but we measure with a font
2585 * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
2590 * We use this to append an LTR or RTL Override character to the start of the
2591 * string. This forces Pango to honour our direction even if there are neutral characters
2592 * in the string.
2594 static PRInt32 AppendDirectionalIndicatorUTF8(PRBool aIsRTL, nsACString& aString)
2596 static const PRUnichar overrides[2][2] =
2597 { { 0x202d, 0 }, { 0x202e, 0 }}; // LRO, RLO
2598 AppendUTF16toUTF8(overrides[aIsRTL], aString);
2599 return 3; // both overrides map to 3 bytes in UTF8
2602 gfxTextRun *
2603 gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
2604 const Parameters *aParams, PRUint32 aFlags)
2606 NS_ASSERTION(aFlags & TEXT_IS_8BIT, "8bit should have been set");
2607 gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
2608 if (!run)
2609 return nsnull;
2611 PRBool isRTL = run->IsRightToLeft();
2612 if ((aFlags & TEXT_IS_ASCII) && !isRTL) {
2613 // We don't need to send an override character here, the characters must be all LTR
2614 const gchar *utf8Chars = reinterpret_cast<const gchar*>(aString);
2615 InitTextRun(run, utf8Chars, aLength, 0, PR_TRUE);
2616 } else {
2617 // this is really gross...
2618 const char *chars = reinterpret_cast<const char*>(aString);
2619 NS_ConvertASCIItoUTF16 unicodeString(chars, aLength);
2620 nsCAutoString utf8;
2621 PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8);
2622 AppendUTF16toUTF8(unicodeString, utf8);
2623 InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE);
2625 run->FetchGlyphExtents(aParams->mContext);
2626 return run;
2629 #if defined(ENABLE_FAST_PATH_8BIT)
2630 PRBool
2631 gfxPangoFontGroup::CanTakeFastPath(PRUint32 aFlags)
2633 // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't.
2634 // We need to always use Pango for RTL text, in case glyph mirroring is
2635 // required.
2636 PRBool speed = aFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
2637 PRBool isRTL = aFlags & gfxTextRunFactory::TEXT_IS_RTL;
2638 return speed && !isRTL && PANGO_IS_FC_FONT(GetBasePangoFont());
2640 #endif
2642 gfxTextRun *
2643 gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
2644 const Parameters *aParams, PRUint32 aFlags)
2646 gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
2647 if (!run)
2648 return nsnull;
2650 run->RecordSurrogates(aString);
2652 nsCAutoString utf8;
2653 PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8);
2654 AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8);
2655 PRBool is8Bit = PR_FALSE;
2657 #if defined(ENABLE_FAST_PATH_8BIT)
2658 if (CanTakeFastPath(aFlags)) {
2659 PRUint32 allBits = 0;
2660 PRUint32 i;
2661 for (i = 0; i < aLength; ++i) {
2662 allBits |= aString[i];
2664 is8Bit = (allBits & 0xFF00) == 0;
2666 #endif
2667 InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit);
2668 run->FetchGlyphExtents(aParams->mContext);
2669 return run;
2672 void
2673 gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
2674 PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
2675 PRBool aTake8BitPath)
2677 #if defined(ENABLE_FAST_PATH_ALWAYS)
2678 CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
2679 #else
2680 #if defined(ENABLE_FAST_PATH_8BIT)
2681 if (aTake8BitPath && CanTakeFastPath(aTextRun->GetFlags())) {
2682 nsresult rv = CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
2683 if (NS_SUCCEEDED(rv))
2684 return;
2686 #endif
2688 CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength);
2689 #endif
2692 static void ReleaseDownloadedFontEntry(void *data)
2694 gfxDownloadedFcFontEntry *downloadedFontEntry =
2695 static_cast<gfxDownloadedFcFontEntry*>(data);
2696 NS_RELEASE(downloadedFontEntry);
2699 // This will fetch an existing scaled_font if one exists.
2700 static cairo_scaled_font_t *
2701 CreateScaledFont(FcPattern *aPattern)
2703 cairo_font_face_t *face = cairo_ft_font_face_create_for_pattern(aPattern);
2705 // If the face is created from a web font entry, hold a reference to the
2706 // font entry to keep the font face data.
2707 gfxDownloadedFcFontEntry *downloadedFontEntry =
2708 GetDownloadedFontEntry(aPattern);
2709 if (downloadedFontEntry &&
2710 cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) {
2711 static cairo_user_data_key_t sFontEntryKey;
2713 // Check whether this is a new cairo face
2714 void *currentEntry =
2715 cairo_font_face_get_user_data(face, &sFontEntryKey);
2716 if (!currentEntry) {
2717 NS_ADDREF(downloadedFontEntry);
2718 cairo_font_face_set_user_data(face, &sFontEntryKey,
2719 downloadedFontEntry,
2720 ReleaseDownloadedFontEntry);
2721 } else {
2722 NS_ASSERTION(currentEntry == downloadedFontEntry,
2723 "Unexpected cairo font face!");
2727 double size = GetPixelSize(aPattern);
2729 cairo_matrix_t fontMatrix;
2730 FcMatrix *fcMatrix;
2731 if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
2732 cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
2733 else
2734 cairo_matrix_init_identity(&fontMatrix);
2735 cairo_matrix_scale(&fontMatrix, size, size);
2737 // The cairo_scaled_font is created with a unit ctm so that metrics and
2738 // positions are in user space, but this means that hinting effects will
2739 // not be estimated accurately for non-unit transformations.
2740 cairo_matrix_t identityMatrix;
2741 cairo_matrix_init_identity(&identityMatrix);
2743 // Font options are set explicitly here to improve cairo's caching
2744 // behavior and to record the relevant parts of the pattern for
2745 // SetupCairoFont (so that the pattern can be released).
2747 // Most font_options have already been set as defaults on the FcPattern
2748 // with cairo_ft_font_options_substitute(), then user and system
2749 // fontconfig configurations were applied. The resulting font_options
2750 // have been recorded on the face during
2751 // cairo_ft_font_face_create_for_pattern().
2753 // None of the settings here cause this scaled_font to behave any
2754 // differently from how it would behave if it were created from the same
2755 // face with default font_options.
2757 // We set options explicitly so that the same scaled_font will be found in
2758 // the cairo_scaled_font_map when cairo loads glyphs from a context with
2759 // the same font_face, font_matrix, ctm, and surface font_options.
2761 // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
2762 // font_options on the cairo_ft_font_face, and doesn't consider default
2763 // option values to not match any explicit values.
2765 // Even after cairo_set_scaled_font is used to set font_options for the
2766 // cairo context, when cairo looks for a scaled_font for the context, it
2767 // will look for a font with some option values from the target surface if
2768 // any values are left default on the context font_options. If this
2769 // scaled_font is created with default font_options, cairo will not find
2770 // it.
2771 cairo_font_options_t *fontOptions = cairo_font_options_create();
2773 // The one option not recorded in the pattern is hint_metrics, which will
2774 // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
2775 // We should be considering the font_options of the surface on which this
2776 // font will be used, but currently we don't have different gfxFonts for
2777 // different surface font_options, so we'll create a font suitable for the
2778 // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
2779 cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_ON);
2781 // The remaining options have been recorded on the pattern and the face.
2782 // _cairo_ft_options_merge has some logic to decide which options from the
2783 // scaled_font or from the cairo_ft_font_face take priority in the way the
2784 // font behaves.
2786 // In the majority of cases, _cairo_ft_options_merge uses the options from
2787 // the cairo_ft_font_face, so sometimes it is not so important which
2788 // values are set here so long as they are not defaults, but we'll set
2789 // them to the exact values that we expect from the font, to be consistent
2790 // and to protect against changes in cairo.
2792 // In some cases, _cairo_ft_options_merge uses some options from the
2793 // scaled_font's font_options rather than options on the
2794 // cairo_ft_font_face (from fontconfig).
2795 // https://bugs.freedesktop.org/show_bug.cgi?id=11838
2797 // Surface font options were set on the pattern in
2798 // cairo_ft_font_options_substitute. If fontconfig has changed the
2799 // hint_style then that is what the user (or distribution) wants, so we
2800 // use the setting from the FcPattern.
2802 // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
2803 FcBool hinting;
2804 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
2805 hinting = FcTrue;
2807 cairo_hint_style_t hint_style;
2808 if (!hinting) {
2809 hint_style = CAIRO_HINT_STYLE_NONE;
2810 } else {
2811 #ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91.
2812 int fc_hintstyle;
2813 if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
2814 0, &fc_hintstyle ) != FcResultMatch) {
2815 fc_hintstyle = FC_HINT_FULL;
2817 switch (fc_hintstyle) {
2818 case FC_HINT_NONE:
2819 hint_style = CAIRO_HINT_STYLE_NONE;
2820 break;
2821 case FC_HINT_SLIGHT:
2822 hint_style = CAIRO_HINT_STYLE_SLIGHT;
2823 break;
2824 case FC_HINT_MEDIUM:
2825 default: // This fallback mirrors _get_pattern_ft_options in cairo.
2826 hint_style = CAIRO_HINT_STYLE_MEDIUM;
2827 break;
2828 case FC_HINT_FULL:
2829 hint_style = CAIRO_HINT_STYLE_FULL;
2830 break;
2832 #else // no FC_HINT_STYLE
2833 hint_style = CAIRO_HINT_STYLE_FULL;
2834 #endif
2836 cairo_font_options_set_hint_style(fontOptions, hint_style);
2838 int rgba;
2839 if (FcPatternGetInteger(aPattern,
2840 FC_RGBA, 0, &rgba) != FcResultMatch) {
2841 rgba = FC_RGBA_UNKNOWN;
2843 cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
2844 switch (rgba) {
2845 case FC_RGBA_UNKNOWN:
2846 case FC_RGBA_NONE:
2847 default:
2848 // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
2849 // is disabled through cairo_antialias_t.
2850 rgba = FC_RGBA_NONE;
2851 // subpixel_order won't be used by the font as we won't use
2852 // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
2853 // caching reasons described above. Fall through:
2854 case FC_RGBA_RGB:
2855 subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
2856 break;
2857 case FC_RGBA_BGR:
2858 subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
2859 break;
2860 case FC_RGBA_VRGB:
2861 subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
2862 break;
2863 case FC_RGBA_VBGR:
2864 subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
2865 break;
2867 cairo_font_options_set_subpixel_order(fontOptions, subpixel_order);
2869 FcBool fc_antialias;
2870 if (FcPatternGetBool(aPattern,
2871 FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
2872 fc_antialias = FcTrue;
2874 cairo_antialias_t antialias;
2875 if (!fc_antialias) {
2876 antialias = CAIRO_ANTIALIAS_NONE;
2877 } else if (rgba == FC_RGBA_NONE) {
2878 antialias = CAIRO_ANTIALIAS_GRAY;
2879 } else {
2880 antialias = CAIRO_ANTIALIAS_SUBPIXEL;
2882 cairo_font_options_set_antialias(fontOptions, antialias);
2884 cairo_scaled_font_t *scaledFont =
2885 cairo_scaled_font_create(face, &fontMatrix, &identityMatrix,
2886 fontOptions);
2888 cairo_font_options_destroy(fontOptions);
2889 cairo_font_face_destroy(face);
2891 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
2892 "Failed to create scaled font");
2893 return scaledFont;
2896 PRBool
2897 gfxFcFont::SetupCairoFont(gfxContext *aContext)
2899 cairo_t *cr = aContext->GetCairo();
2901 // The scaled font ctm is not relevant right here because
2902 // cairo_set_scaled_font does not record the scaled font itself, but
2903 // merely the font_face, font_matrix, font_options. The scaled_font used
2904 // for the target can be different from the scaled_font passed to
2905 // cairo_set_scaled_font. (Unfortunately we have measured only for an
2906 // identity ctm.)
2907 cairo_scaled_font_t *cairoFont = CairoScaledFont();
2909 if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
2910 // Don't cairo_set_scaled_font as that would propagate the error to
2911 // the cairo_t, precluding any further drawing.
2912 return PR_FALSE;
2914 // Thoughts on which font_options to set on the context:
2916 // cairoFont has been created for screen rendering.
2918 // When the context is being used for screen rendering, we should set
2919 // font_options such that the same scaled_font gets used (when the ctm is
2920 // the same). The use of explicit font_options recorded in
2921 // CreateScaledFont ensures that this will happen.
2923 // XXXkt: For pdf and ps surfaces, I don't know whether it's better to
2924 // remove surface-specific options, or try to draw with the same
2925 // scaled_font that was used to measure. As the same font_face is being
2926 // used, its font_options will often override some values anyway (unless
2927 // perhaps we remove those from the FcPattern at face creation).
2929 // I can't see any significant difference in printing, irrespective of
2930 // what is set here. It's too late to change things here as measuring has
2931 // already taken place. We should really be measuring with a different
2932 // font for pdf and ps surfaces (bug 403513).
2933 cairo_set_scaled_font(cr, cairoFont);
2934 return PR_TRUE;
2937 static void
2938 SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8Length,
2939 PRUint32 aUTF16Offset, PangoAnalysis *aAnalysis)
2941 if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) {
2942 // 8-bit text doesn't have clusters.
2943 // XXX is this true in all languages???
2944 // behdad: don't think so. Czech for example IIRC has a
2945 // 'ch' grapheme.
2946 return;
2949 // Pango says "the array of PangoLogAttr passed in must have at least N+1
2950 // elements, if there are N characters in the text being broken".
2951 // Could use g_utf8_strlen(aUTF8, aUTF8Length) + 1 but the memory savings
2952 // may not be worth the call.
2953 nsAutoTArray<PangoLogAttr,2000> buffer;
2954 if (!buffer.AppendElements(aUTF8Length + 1))
2955 return;
2957 pango_break(aUTF8, aUTF8Length, aAnalysis,
2958 buffer.Elements(), buffer.Length());
2960 const gchar *p = aUTF8;
2961 const gchar *end = aUTF8 + aUTF8Length;
2962 const PangoLogAttr *attr = buffer.Elements();
2963 gfxTextRun::CompressedGlyph g;
2964 while (p < end) {
2965 if (!attr->is_cursor_position) {
2966 aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
2968 ++aUTF16Offset;
2970 gunichar ch = g_utf8_get_char(p);
2971 NS_ASSERTION(ch != 0, "Shouldn't have NUL in pango_break");
2972 NS_ASSERTION(!IS_SURROGATE(ch), "Shouldn't have surrogates in UTF8");
2973 if (ch >= 0x10000) {
2974 // set glyph info for the UTF-16 low surrogate
2975 aTextRun->SetGlyphs(aUTF16Offset, g.SetComplex(PR_FALSE, PR_FALSE, 0), nsnull);
2976 ++aUTF16Offset;
2978 // We produced this utf8 so we don't need to worry about malformed stuff
2979 p = g_utf8_next_char(p);
2980 ++attr;
2984 static PRInt32
2985 ConvertPangoToAppUnits(PRInt32 aCoordinate, PRUint32 aAppUnitsPerDevUnit)
2987 PRInt64 v = (PRInt64(aCoordinate)*aAppUnitsPerDevUnit + PANGO_SCALE/2)/PANGO_SCALE;
2988 return PRInt32(v);
2992 * Given a run of Pango glyphs that should be treated as a single
2993 * cluster/ligature, store them in the textrun at the appropriate character
2994 * and set the other characters involved to be ligature/cluster continuations
2995 * as appropriate.
2997 static nsresult
2998 SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
2999 gfxTextRun *aTextRun,
3000 const gchar *aUTF8, PRUint32 aUTF8Length,
3001 PRUint32 *aUTF16Offset,
3002 PangoGlyphUnit aOverrideSpaceWidth)
3004 PRUint32 utf16Offset = *aUTF16Offset;
3005 PRUint32 textRunLength = aTextRun->GetLength();
3006 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
3007 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
3009 // Override the width of a space, but only for spaces that aren't
3010 // clustered with something else (like a freestanding diacritical mark)
3011 PangoGlyphUnit width = aGlyphs[0].geometry.width;
3012 if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
3013 (utf16Offset + 1 == textRunLength ||
3014 charGlyphs[utf16Offset].IsClusterStart())) {
3015 width = aOverrideSpaceWidth;
3017 PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
3019 gfxTextRun::CompressedGlyph g;
3020 PRBool atClusterStart = aTextRun->IsClusterStart(utf16Offset);
3021 // See if we fit in the compressed area.
3022 if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
3023 aGlyphs[0].geometry.x_offset == 0 &&
3024 aGlyphs[0].geometry.y_offset == 0 &&
3025 !IS_EMPTY_GLYPH(aGlyphs[0].glyph) &&
3026 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
3027 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
3028 aTextRun->SetSimpleGlyph(utf16Offset,
3029 g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
3030 } else {
3031 nsAutoTArray<gfxTextRun::DetailedGlyph,10> detailedGlyphs;
3032 if (!detailedGlyphs.AppendElements(aGlyphCount))
3033 return NS_ERROR_OUT_OF_MEMORY;
3035 PRInt32 direction = aTextRun->IsRightToLeft() ? -1 : 1;
3036 PRUint32 pangoIndex = direction > 0 ? 0 : aGlyphCount - 1;
3037 PRUint32 detailedIndex = 0;
3038 for (PRUint32 i = 0; i < aGlyphCount; ++i) {
3039 const PangoGlyphInfo &glyph = aGlyphs[pangoIndex];
3040 pangoIndex += direction;
3041 // The zero width characters return empty glyph ID at
3042 // shaping; we should skip these.
3043 if (IS_EMPTY_GLYPH(glyph.glyph))
3044 continue;
3046 gfxTextRun::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
3047 ++detailedIndex;
3049 details->mGlyphID = glyph.glyph;
3050 NS_ASSERTION(details->mGlyphID == glyph.glyph,
3051 "Seriously weird glyph ID detected!");
3052 details->mAdvance =
3053 ConvertPangoToAppUnits(glyph.geometry.width,
3054 appUnitsPerDevUnit);
3055 details->mXOffset =
3056 float(glyph.geometry.x_offset)*appUnitsPerDevUnit/PANGO_SCALE;
3057 details->mYOffset =
3058 float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
3060 g.SetComplex(atClusterStart, PR_TRUE, detailedIndex);
3061 aTextRun->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
3064 // Check for ligatures and set *aUTF16Offset.
3065 const gchar *p = aUTF8;
3066 const gchar *end = aUTF8 + aUTF8Length;
3067 while (1) {
3068 // Skip the CompressedGlyph that we have added, but check if the
3069 // character was supposed to be ignored. If it's supposed to be ignored,
3070 // overwrite the textrun entry with an invisible missing-glyph.
3071 gunichar ch = g_utf8_get_char(p);
3072 NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
3073 if (ch >= 0x10000) {
3074 // Skip surrogate
3075 ++utf16Offset;
3077 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(PRUnichar(ch)),
3078 "Invalid character detected");
3079 ++utf16Offset;
3081 // We produced this UTF8 so we don't need to worry about malformed stuff
3082 p = g_utf8_next_char(p);
3083 if (p >= end)
3084 break;
3086 if (utf16Offset >= textRunLength) {
3087 NS_ERROR("Someone has added too many glyphs!");
3088 return NS_ERROR_FAILURE;
3091 g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_FALSE, 0);
3092 aTextRun->SetGlyphs(utf16Offset, g, nsnull);
3094 *aUTF16Offset = utf16Offset;
3095 return NS_OK;
3098 nsresult
3099 gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
3100 const gchar *aUTF8, PRUint32 aUTF8Length,
3101 PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
3102 PangoGlyphUnit aOverrideSpaceWidth,
3103 PRBool aAbortOnMissingGlyph)
3105 gint numGlyphs = aGlyphs->num_glyphs;
3106 PangoGlyphInfo *glyphs = aGlyphs->glyphs;
3107 const gint *logClusters = aGlyphs->log_clusters;
3108 // We cannot make any assumptions about the order of glyph clusters
3109 // provided by pango_shape (see 375864), so we work through the UTF8 text
3110 // and process the glyph clusters in logical order.
3112 // logGlyphs is like an inverse of logClusters. For each UTF8 byte:
3113 // >= 0 indicates that the byte is first in a cluster and
3114 // gives the position of the starting glyph for the cluster.
3115 // -1 indicates that the byte does not start a cluster.
3116 nsAutoTArray<gint,2000> logGlyphs;
3117 if (!logGlyphs.AppendElements(aUTF8Length + 1))
3118 return NS_ERROR_OUT_OF_MEMORY;
3119 PRUint32 utf8Index = 0;
3120 for(; utf8Index < aUTF8Length; ++utf8Index)
3121 logGlyphs[utf8Index] = -1;
3122 logGlyphs[aUTF8Length] = numGlyphs;
3124 gint lastCluster = -1; // != utf8Index
3125 for (gint glyphIndex = 0; glyphIndex < numGlyphs; ++glyphIndex) {
3126 gint thisCluster = logClusters[glyphIndex];
3127 if (thisCluster != lastCluster) {
3128 lastCluster = thisCluster;
3129 NS_ASSERTION(0 <= thisCluster && thisCluster < gint(aUTF8Length),
3130 "garbage from pango_shape - this is bad");
3131 logGlyphs[thisCluster] = glyphIndex;
3135 PRUint32 utf16Offset = *aUTF16Offset;
3136 PRUint32 textRunLength = aTextRun->GetLength();
3137 utf8Index = 0;
3138 // The next glyph cluster in logical order.
3139 gint nextGlyphClusterStart = logGlyphs[utf8Index];
3140 NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
3141 while (utf8Index < aUTF8Length) {
3142 if (utf16Offset >= textRunLength) {
3143 NS_ERROR("Someone has added too many glyphs!");
3144 return NS_ERROR_FAILURE;
3146 gint glyphClusterStart = nextGlyphClusterStart;
3147 // Find the utf8 text associated with this glyph cluster.
3148 PRUint32 clusterUTF8Start = utf8Index;
3149 // Check we are consistent with pango_break data.
3150 NS_ASSERTION(aTextRun->GetCharacterGlyphs()->IsClusterStart(),
3151 "Glyph cluster not aligned on character cluster.");
3152 do {
3153 ++utf8Index;
3154 nextGlyphClusterStart = logGlyphs[utf8Index];
3155 } while (nextGlyphClusterStart < 0);
3156 const gchar *clusterUTF8 = &aUTF8[clusterUTF8Start];
3157 PRUint32 clusterUTF8Length = utf8Index - clusterUTF8Start;
3159 PRBool haveMissingGlyph = PR_FALSE;
3160 gint glyphIndex = glyphClusterStart;
3162 // It's now unncecessary to do NUL handling here.
3163 do {
3164 if (IS_MISSING_GLYPH(glyphs[glyphIndex].glyph)) {
3165 // Does pango ever provide more than one glyph in the
3166 // cluster if there is a missing glyph?
3167 // behdad: yes
3168 haveMissingGlyph = PR_TRUE;
3170 glyphIndex++;
3171 } while (glyphIndex < numGlyphs &&
3172 logClusters[glyphIndex] == gint(clusterUTF8Start));
3174 if (haveMissingGlyph && aAbortOnMissingGlyph)
3175 return NS_ERROR_FAILURE;
3177 nsresult rv;
3178 if (haveMissingGlyph) {
3179 rv = SetMissingGlyphs(aTextRun, clusterUTF8, clusterUTF8Length,
3180 &utf16Offset);
3181 } else {
3182 rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
3183 glyphIndex - glyphClusterStart,
3184 aTextRun,
3185 clusterUTF8, clusterUTF8Length,
3186 &utf16Offset, aOverrideSpaceWidth);
3188 NS_ENSURE_SUCCESS(rv,rv);
3190 *aUTF16Offset = utf16Offset;
3191 return NS_OK;
3194 nsresult
3195 gfxPangoFontGroup::SetMissingGlyphs(gfxTextRun *aTextRun,
3196 const gchar *aUTF8, PRUint32 aUTF8Length,
3197 PRUint32 *aUTF16Offset)
3199 PRUint32 utf16Offset = *aUTF16Offset;
3200 PRUint32 textRunLength = aTextRun->GetLength();
3201 for (PRUint32 index = 0; index < aUTF8Length;) {
3202 if (utf16Offset >= textRunLength) {
3203 NS_ERROR("Someone has added too many glyphs!");
3204 break;
3206 gunichar ch = g_utf8_get_char(aUTF8 + index);
3207 aTextRun->SetMissingGlyph(utf16Offset, ch);
3209 ++utf16Offset;
3210 NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
3211 if (ch >= 0x10000)
3212 ++utf16Offset;
3213 // We produced this UTF8 so we don't need to worry about malformed stuff
3214 index = g_utf8_next_char(aUTF8 + index) - aUTF8;
3217 *aUTF16Offset = utf16Offset;
3218 return NS_OK;
3221 #if defined(ENABLE_FAST_PATH_8BIT) || defined(ENABLE_FAST_PATH_ALWAYS)
3222 nsresult
3223 gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
3224 const gchar *aUTF8, PRUint32 aUTF8Length)
3226 const gchar *p = aUTF8;
3227 PangoFont *pangofont = GetBasePangoFont();
3228 PangoFcFont *fcfont = PANGO_FC_FONT (pangofont);
3229 gfxFcFont *gfxFont = gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(pangofont));
3230 PRUint32 utf16Offset = 0;
3231 gfxTextRun::CompressedGlyph g;
3232 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
3234 aTextRun->AddGlyphRun(gfxFont, 0);
3236 while (p < aUTF8 + aUTF8Length) {
3237 // glib-2.12.9: "If p does not point to a valid UTF-8 encoded
3238 // character, results are undefined." so it is not easy to assert that
3239 // aUTF8 in fact points to UTF8 data but asserting
3240 // g_unichar_validate(ch) may be mildly useful.
3241 gunichar ch = g_utf8_get_char(p);
3242 p = g_utf8_next_char(p);
3244 if (ch == 0) {
3245 // treat this null byte as a missing glyph. Pango
3246 // doesn't create glyphs for these, not even missing-glyphs.
3247 aTextRun->SetMissingGlyph(utf16Offset, 0);
3248 } else {
3249 NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected");
3250 FT_UInt glyph = pango_fc_font_get_glyph (fcfont, ch);
3251 if (!glyph) // character not in font,
3252 return NS_ERROR_FAILURE; // fallback to CreateGlyphRunsItemizing
3254 cairo_text_extents_t extents;
3255 gfxFont->GetGlyphExtents(glyph, &extents);
3257 PRInt32 advance = NS_lround(extents.x_advance * appUnitsPerDevUnit);
3258 if (advance >= 0 &&
3259 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
3260 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
3261 aTextRun->SetSimpleGlyph(utf16Offset,
3262 g.SetSimpleGlyph(advance, glyph));
3263 } else {
3264 gfxTextRun::DetailedGlyph details;
3265 details.mGlyphID = glyph;
3266 NS_ASSERTION(details.mGlyphID == glyph,
3267 "Seriously weird glyph ID detected!");
3268 details.mAdvance = advance;
3269 details.mXOffset = 0;
3270 details.mYOffset = 0;
3271 g.SetComplex(aTextRun->IsClusterStart(utf16Offset), PR_TRUE, 1);
3272 aTextRun->SetGlyphs(utf16Offset, g, &details);
3275 NS_ASSERTION(!IS_SURROGATE(ch), "Surrogates shouldn't appear in UTF8");
3276 if (ch >= 0x10000) {
3277 // This character is a surrogate pair in UTF16
3278 ++utf16Offset;
3282 ++utf16Offset;
3284 return NS_OK;
3286 #endif
3288 void
3289 gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
3290 const gchar *aUTF8, PRUint32 aUTF8Length,
3291 PRUint32 aUTF8HeaderLen)
3293 // This font group and gfxPangoFontMap are recorded on the PangoContext
3294 // passed to pango_itemize_with_base_dir().
3296 // pango_itemize_with_base_dir() divides the string into substrings for
3297 // each language, and queries gfxPangoFontMap::load_fontset() to provide
3298 // ordered lists of fonts for each language. gfxPangoFontMap passes the
3299 // request back to this font group, which returns a gfxFcPangoFontSet
3300 // handling the font sorting/selection.
3302 // For each character, pango_itemize_with_base_dir searches through these
3303 // lists of fonts for a font with support for the character. The
3304 // PangoItems returned represent substrings (or runs) of consectutive
3305 // characters to be shaped with the same PangoFont and having the same
3306 // script.
3308 // The PangoFonts in the PangoItems are from the gfxPangoFontMap and so
3309 // each have a gfxFont. This gfxFont represents the same face as the
3310 // PangoFont and so can render the same glyphs in the same way as
3311 // pango_shape measures.
3313 PangoContext *context = GetPangoContext();
3314 // we should set this to null if we don't have a text language from the page...
3315 // except that we almost always have something...
3316 pango_context_set_language(context, mPangoLanguage);
3317 SetFontGroup(context, this);
3319 PangoDirection dir = aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
3320 GList *items = pango_itemize_with_base_dir(context, dir, aUTF8, 0, aUTF8Length, nsnull, nsnull);
3322 PRUint32 utf16Offset = 0;
3323 #ifdef DEBUG
3324 PRBool isRTL = aTextRun->IsRightToLeft();
3325 #endif
3326 GList *pos = items;
3327 PangoGlyphString *glyphString = pango_glyph_string_new();
3328 if (!glyphString)
3329 goto out; // OOM
3331 for (; pos && pos->data; pos = pos->next) {
3332 PangoItem *item = (PangoItem *)pos->data;
3333 NS_ASSERTION(isRTL == item->analysis.level % 2, "RTL assumption mismatch");
3335 PRUint32 offset = item->offset;
3336 PRUint32 length = item->length;
3337 if (offset < aUTF8HeaderLen) {
3338 if (offset + length <= aUTF8HeaderLen)
3339 continue;
3341 length -= aUTF8HeaderLen - offset;
3342 offset = aUTF8HeaderLen;
3345 gfxFcFont *font =
3346 gfxPangoFcFont::GfxFont(GFX_PANGO_FC_FONT(item->analysis.font));
3348 nsresult rv = aTextRun->AddGlyphRun(font, utf16Offset);
3349 if (NS_FAILED(rv)) {
3350 NS_ERROR("AddGlyphRun Failed");
3351 goto out;
3354 PRUint32 spaceWidth =
3355 moz_pango_units_from_double(font->GetMetrics().spaceWidth);
3357 const gchar *p = aUTF8 + offset;
3358 const gchar *end = p + length;
3359 while (p < end) {
3360 if (*p == 0) {
3361 aTextRun->SetMissingGlyph(utf16Offset, 0);
3362 ++p;
3363 ++utf16Offset;
3364 continue;
3367 // It's necessary to loop over pango_shape as it treats
3368 // NULs as string terminators
3369 const gchar *text = p;
3370 do {
3371 ++p;
3372 } while(p < end && *p != 0);
3373 gint len = p - text;
3375 pango_shape(text, len, &item->analysis, glyphString);
3376 SetupClusterBoundaries(aTextRun, text, len, utf16Offset, &item->analysis);
3377 SetGlyphs(aTextRun, text, len, &utf16Offset, glyphString, spaceWidth, PR_FALSE);
3381 out:
3382 if (glyphString)
3383 pango_glyph_string_free(glyphString);
3385 for (pos = items; pos; pos = pos->next)
3386 pango_item_free((PangoItem *)pos->data);
3388 if (items)
3389 g_list_free(items);
3391 g_object_unref(context);
3394 /* static */
3395 PangoLanguage *
3396 GuessPangoLanguage(const nsACString& aLangGroup)
3398 // See if the lang group needs to be translated from Mozilla's
3399 // internal mapping into fontconfig's
3400 nsCAutoString lang;
3401 gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
3403 if (lang.IsEmpty())
3404 return NULL;
3406 return pango_language_from_string(lang.get());
3409 #ifdef MOZ_WIDGET_GTK2
3410 /***************************************************************************
3412 * This function must be last in the file because it uses the system cairo
3413 * library. Above this point the cairo library used is the tree cairo if
3414 * MOZ_TREE_CAIRO.
3417 #if MOZ_TREE_CAIRO
3418 // Tree cairo symbols have different names. Disable their activation through
3419 // preprocessor macros.
3420 #undef cairo_ft_font_options_substitute
3422 // The system cairo functions are not declared because the include paths cause
3423 // the gdk headers to pick up the tree cairo.h.
3424 extern "C" {
3425 NS_VISIBILITY_DEFAULT void
3426 cairo_ft_font_options_substitute (const cairo_font_options_t *options,
3427 FcPattern *pattern);
3429 #endif
3431 static void
3432 ApplyGdkScreenFontOptions(FcPattern *aPattern)
3434 const cairo_font_options_t *options =
3435 gdk_screen_get_font_options(gdk_screen_get_default());
3437 cairo_ft_font_options_substitute(options, aPattern);
3440 #endif // MOZ_WIDGET_GTK2