1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Foundation code.
16 : *
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.
20 : *
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
27 : *
28 : * based on nsFontMetricsPango.cpp by
29 : * Christopher Blizzard <blizzard@mozilla.org>
30 : *
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.
42 : *
43 : * ***** END LICENSE BLOCK ***** */
44 :
45 : #include "mozilla/Util.h"
46 :
47 : #define PANGO_ENABLE_BACKEND
48 : #define PANGO_ENABLE_ENGINE
49 :
50 : #include "prtypes.h"
51 : #include "prlink.h"
52 : #include "gfxTypes.h"
53 :
54 : #include "nsTArray.h"
55 :
56 : #include "gfxContext.h"
57 : #ifdef MOZ_WIDGET_GTK2
58 : #include "gfxPlatformGtk.h"
59 : #endif
60 : #ifdef MOZ_WIDGET_QT
61 : #include "gfxQtPlatform.h"
62 : #endif
63 : #include "gfxPangoFonts.h"
64 : #include "gfxFT2FontBase.h"
65 : #include "gfxFT2Utils.h"
66 : #include "harfbuzz/hb-unicode.h"
67 : #include "harfbuzz/hb-ot-tag.h"
68 : #include "gfxHarfBuzzShaper.h"
69 : #ifdef MOZ_GRAPHITE
70 : #include "gfxGraphiteShaper.h"
71 : #endif
72 : #include "nsUnicodeProperties.h"
73 : #include "nsUnicodeScriptCodes.h"
74 : #include "gfxFontconfigUtils.h"
75 : #include "gfxUserFontSet.h"
76 : #include "gfxAtoms.h"
77 :
78 : #include <cairo.h>
79 : #include <cairo-ft.h>
80 :
81 : #include <fontconfig/fcfreetype.h>
82 : #include <pango/pango.h>
83 : #include <pango/pangocairo.h>
84 : #include <pango/pango-modules.h>
85 : #include <pango/pangofc-fontmap.h>
86 :
87 : #ifdef MOZ_WIDGET_GTK2
88 : #include <gdk/gdk.h>
89 : #endif
90 :
91 : #include <math.h>
92 :
93 : using namespace mozilla;
94 : using namespace mozilla::unicode;
95 :
96 : #define FLOAT_PANGO_SCALE ((gfxFloat)PANGO_SCALE)
97 :
98 : #ifndef PANGO_VERSION_CHECK
99 : #define PANGO_VERSION_CHECK(x,y,z) 0
100 : #endif
101 : #ifndef PANGO_GLYPH_UNKNOWN_FLAG
102 : #define PANGO_GLYPH_UNKNOWN_FLAG ((PangoGlyph)0x10000000)
103 : #endif
104 : #ifndef PANGO_GLYPH_EMPTY
105 : #define PANGO_GLYPH_EMPTY ((PangoGlyph)0)
106 : #endif
107 : // For g a PangoGlyph,
108 : #define IS_MISSING_GLYPH(g) ((g) & PANGO_GLYPH_UNKNOWN_FLAG)
109 : #define IS_EMPTY_GLYPH(g) ((g) == PANGO_GLYPH_EMPTY)
110 :
111 : #define PRINTING_FC_PROPERTY "gfx.printing"
112 :
113 : struct gfxPangoFcFont;
114 :
115 : // Same as pango_units_from_double from Pango 1.16 (but not in older versions)
116 0 : int moz_pango_units_from_double(double d) {
117 0 : return NS_lround(d * FLOAT_PANGO_SCALE);
118 : }
119 :
120 : static PangoLanguage *GuessPangoLanguage(nsIAtom *aLanguage);
121 :
122 : static cairo_scaled_font_t *
123 : CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace);
124 : static void SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
125 : PRUint32 aUTF8Length, PRUint32 *aUTF16Offset,
126 : gfxFont *aFont);
127 :
128 : static PangoFontMap *gPangoFontMap;
129 : static PangoFontMap *GetPangoFontMap();
130 : static bool gUseFontMapProperty;
131 :
132 : static FT_Library gFTLibrary;
133 :
134 : template <class T>
135 0 : class gfxGObjectRefTraits : public nsPointerRefTraits<T> {
136 : public:
137 0 : static void Release(T *aPtr) { g_object_unref(aPtr); }
138 : static void AddRef(T *aPtr) { g_object_ref(aPtr); }
139 : };
140 :
141 : template <>
142 0 : class nsAutoRefTraits<PangoFont> : public gfxGObjectRefTraits<PangoFont> { };
143 :
144 : template <>
145 : class nsAutoRefTraits<PangoCoverage>
146 0 : : public nsPointerRefTraits<PangoCoverage> {
147 : public:
148 0 : static void Release(PangoCoverage *aPtr) { pango_coverage_unref(aPtr); }
149 : static void AddRef(PangoCoverage *aPtr) { pango_coverage_ref(aPtr); }
150 : };
151 :
152 :
153 : // FC_FAMILYLANG and FC_FULLNAME were introduced in fontconfig-2.2.97
154 : // and so fontconfig-2.3.0 (2005).
155 : #ifndef FC_FAMILYLANG
156 : #define FC_FAMILYLANG "familylang"
157 : #endif
158 : #ifndef FC_FULLNAME
159 : #define FC_FULLNAME "fullname"
160 : #endif
161 :
162 : static PRFuncPtr
163 0 : FindFunctionSymbol(const char *name)
164 : {
165 0 : PRLibrary *lib = nsnull;
166 0 : PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(name, &lib);
167 0 : if (lib) {
168 0 : PR_UnloadLibrary(lib);
169 : }
170 :
171 0 : return result;
172 : }
173 :
174 0 : static bool HasChar(FcPattern *aFont, FcChar32 wc)
175 : {
176 0 : FcCharSet *charset = NULL;
177 0 : FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
178 :
179 0 : return charset && FcCharSetHasChar(charset, wc);
180 : }
181 :
182 : /**
183 : * gfxFcFontEntry:
184 : *
185 : * An abstract base class of for gfxFontEntry implementations used by
186 : * gfxFcFont and gfxUserFontSet.
187 : */
188 :
189 0 : class gfxFcFontEntry : public gfxFontEntry {
190 : public:
191 : // For all FontEntrys attached to gfxFcFonts, there will be only one
192 : // pattern in this array. This is always a font pattern, not a fully
193 : // resolved pattern. gfxFcFont only uses this to construct a PangoFont.
194 : //
195 : // FontEntrys for src:local() fonts in gfxUserFontSet may return more than
196 : // one pattern. (See comment in gfxUserFcFontEntry.)
197 0 : const nsTArray< nsCountedRef<FcPattern> >& GetPatterns()
198 : {
199 0 : return mPatterns;
200 : }
201 :
202 : bool ShouldUseHarfBuzz(PRInt32 aRunScript);
203 0 : void SkipHarfBuzz() { mSkipHarfBuzz = true; }
204 :
205 0 : static gfxFcFontEntry *LookupFontEntry(cairo_font_face_t *aFace)
206 : {
207 : return static_cast<gfxFcFontEntry*>
208 0 : (cairo_font_face_get_user_data(aFace, &sFontEntryKey));
209 : }
210 :
211 : // override the default impl in gfxFontEntry because we don't organize
212 : // gfxFcFontEntries in families; just read the name from fontconfig
213 : virtual nsString FamilyName() const;
214 :
215 : // override the gfxFontEntry impl to read the name from fontconfig
216 : // instead of trying to get the 'name' table, as we don't implement
217 : // GetFontTable() here
218 : virtual nsString RealFaceName();
219 :
220 : // This is needed to make gfxFontEntry::HasCharacter(aCh) work.
221 0 : virtual bool TestCharacterMap(PRUint32 aCh)
222 : {
223 0 : for (PRUint32 i = 0; i < mPatterns.Length(); ++i) {
224 0 : if (HasChar(mPatterns[i], aCh)) {
225 0 : return true;
226 : }
227 : }
228 0 : return false;
229 : }
230 :
231 : protected:
232 0 : gfxFcFontEntry(const nsAString& aName)
233 : : gfxFontEntry(aName),
234 0 : mSkipHarfBuzz(false), mSkipGraphiteCheck(false)
235 : {
236 0 : }
237 :
238 : #ifdef MOZ_GRAPHITE
239 : virtual void CheckForGraphiteTables();
240 : #endif
241 :
242 : // One pattern is the common case and some subclasses rely on successful
243 : // addition of the first element to the array.
244 : nsAutoTArray<nsCountedRef<FcPattern>,1> mPatterns;
245 : bool mSkipHarfBuzz;
246 : bool mSkipGraphiteCheck;
247 :
248 : static cairo_user_data_key_t sFontEntryKey;
249 : };
250 :
251 : cairo_user_data_key_t gfxFcFontEntry::sFontEntryKey;
252 :
253 : nsString
254 0 : gfxFcFontEntry::FamilyName() const
255 : {
256 0 : if (mIsUserFont) {
257 : // for user fonts, we want the name of the family
258 : // as specified in the user font set
259 0 : return gfxFontEntry::FamilyName();
260 : }
261 : FcChar8 *familyname;
262 0 : if (!mPatterns.IsEmpty() &&
263 0 : FcPatternGetString(mPatterns[0],
264 0 : FC_FAMILY, 0, &familyname) == FcResultMatch) {
265 0 : return NS_ConvertUTF8toUTF16((const char*)familyname);
266 : }
267 0 : return gfxFontEntry::FamilyName();
268 : }
269 :
270 : nsString
271 0 : gfxFcFontEntry::RealFaceName()
272 : {
273 : FcChar8 *name;
274 0 : if (!mPatterns.IsEmpty()) {
275 0 : if (FcPatternGetString(mPatterns[0],
276 0 : FC_FULLNAME, 0, &name) == FcResultMatch) {
277 0 : return NS_ConvertUTF8toUTF16((const char*)name);
278 : }
279 0 : if (FcPatternGetString(mPatterns[0],
280 0 : FC_FAMILY, 0, &name) == FcResultMatch) {
281 0 : NS_ConvertUTF8toUTF16 result((const char*)name);
282 0 : if (FcPatternGetString(mPatterns[0],
283 0 : FC_STYLE, 0, &name) == FcResultMatch) {
284 0 : result.AppendLiteral(" ");
285 0 : AppendUTF8toUTF16((const char*)name, result);
286 : }
287 0 : return result;
288 : }
289 : }
290 0 : return gfxFontEntry::RealFaceName();
291 : }
292 :
293 : #ifdef MOZ_GRAPHITE
294 : void
295 0 : gfxFcFontEntry::CheckForGraphiteTables()
296 : {
297 : FcChar8 *capability;
298 : mHasGraphiteTables =
299 0 : !mPatterns.IsEmpty() &&
300 0 : FcPatternGetString(mPatterns[0],
301 0 : FC_CAPABILITY, 0, &capability) == FcResultMatch &&
302 0 : FcStrStr(capability, gfxFontconfigUtils::ToFcChar8("ttable:Silf"));
303 0 : }
304 : #endif
305 :
306 : bool
307 0 : gfxFcFontEntry::ShouldUseHarfBuzz(PRInt32 aRunScript) {
308 0 : if (mSkipHarfBuzz ||
309 0 : !gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript))
310 : {
311 0 : return false;
312 : }
313 :
314 0 : if (mSkipGraphiteCheck) {
315 0 : return true;
316 : }
317 :
318 : // Check whether to fall back to Pango for Graphite shaping.
319 : // pango-graphite checks for ttable:Silf.
320 : FcChar8 *capability;
321 : // FontEntries used at shaping have only one pattern.
322 0 : if (mPatterns.IsEmpty() ||
323 0 : FcPatternGetString(mPatterns[0],
324 0 : FC_CAPABILITY, 0, &capability) == FcResultNoMatch ||
325 0 : !FcStrStr(capability, gfxFontconfigUtils::ToFcChar8("ttable:Silf")))
326 : {
327 0 : mSkipGraphiteCheck = true;
328 0 : return true;
329 : }
330 :
331 : // Mimicing gfxHarfBuzzShaper::ShapeWord
332 : hb_script_t script = (aRunScript <= MOZ_SCRIPT_INHERITED) ?
333 : HB_SCRIPT_LATIN :
334 0 : hb_script_t(GetScriptTagForCode(aRunScript));
335 :
336 : // Prefer HarfBuzz if the font also has support for OpenType shaping of
337 : // this script.
338 0 : const FcChar8 otCapTemplate[] = "otlayout:XXXX";
339 : FcChar8 otCap[NS_ARRAY_LENGTH(otCapTemplate)];
340 0 : memcpy(otCap, otCapTemplate, ArrayLength(otCapTemplate));
341 : // Subtract 5, for 4 characters and NUL.
342 0 : const PRUint32 scriptOffset = ArrayLength(otCapTemplate) - 5;
343 :
344 : hb_tag_t tags[2];
345 0 : hb_ot_tags_from_script(script, &tags[0], &tags[1]);
346 0 : for (int i = 0; i < 2; ++i) {
347 0 : hb_tag_t scriptTag = tags[i];
348 0 : if (scriptTag == HB_TAG('D','F','L','T')) { // e.g. HB_SCRIPT_UNKNOWN
349 0 : continue;
350 : }
351 :
352 : // FcChar8 is unsigned so truncates appropriately.
353 0 : otCap[scriptOffset + 0] = scriptTag >> 24;
354 0 : otCap[scriptOffset + 1] = scriptTag >> 16;
355 0 : otCap[scriptOffset + 2] = scriptTag >> 8;
356 0 : otCap[scriptOffset + 3] = scriptTag;
357 0 : if (FcStrStr(capability, otCap)) {
358 0 : return true;
359 : }
360 : }
361 :
362 0 : return false; // use Pango for Graphite
363 : }
364 :
365 : /**
366 : * gfxSystemFcFontEntry:
367 : *
368 : * An implementation of gfxFcFontEntry used by gfxFcFonts for system fonts,
369 : * including those from regular family-name based font selection as well as
370 : * those from src:local().
371 : *
372 : * All gfxFcFonts using the same cairo_font_face_t share the same FontEntry.
373 : */
374 :
375 : class gfxSystemFcFontEntry : public gfxFcFontEntry {
376 : public:
377 : // For memory efficiency, aFontPattern should be a font pattern,
378 : // not a fully resolved pattern.
379 0 : gfxSystemFcFontEntry(cairo_font_face_t *aFontFace,
380 : FcPattern *aFontPattern,
381 : const nsAString& aName)
382 0 : : gfxFcFontEntry(aName), mFontFace(aFontFace)
383 : {
384 0 : cairo_font_face_reference(mFontFace);
385 0 : cairo_font_face_set_user_data(mFontFace, &sFontEntryKey, this, NULL);
386 0 : mPatterns.AppendElement();
387 : // mPatterns is an nsAutoTArray with 1 space always available, so the
388 : // AppendElement always succeeds.
389 0 : mPatterns[0] = aFontPattern;
390 0 : }
391 :
392 0 : ~gfxSystemFcFontEntry()
393 0 : {
394 0 : cairo_font_face_set_user_data(mFontFace, &sFontEntryKey, NULL, NULL);
395 0 : cairo_font_face_destroy(mFontFace);
396 0 : }
397 : private:
398 : cairo_font_face_t *mFontFace;
399 : };
400 :
401 : // A namespace for @font-face family names in FcPatterns so that fontconfig
402 : // aliases do not pick up families from @font-face rules and so that
403 : // fontconfig rules can distinguish between web fonts and platform fonts.
404 : // http://lists.freedesktop.org/archives/fontconfig/2008-November/003037.html
405 : #define FONT_FACE_FAMILY_PREFIX "@font-face:"
406 :
407 : /**
408 : * gfxUserFcFontEntry:
409 : *
410 : * An abstract class for objects in a gfxUserFontSet that can provide
411 : * FcPattern* handles to fonts.
412 : *
413 : * Separate implementations of this class support local fonts from src:local()
414 : * and web fonts from src:url().
415 : */
416 :
417 : // There is a one-to-one correspondence between gfxUserFcFontEntry objects and
418 : // @font-face rules, but sometimes a one-to-many correspondence between font
419 : // entries and font patterns.
420 : //
421 : // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802#font-descriptions
422 : // provided a font-size descriptor to specify the sizes supported by the face,
423 : // but the "Editor's Draft 27 June 2008"
424 : // http://dev.w3.org/csswg/css3-fonts/#font-resources does not provide such a
425 : // descriptor, and Mozilla does not recognize such a descriptor.
426 : //
427 : // Font face names used in src:local() also do not usually specify a size.
428 : //
429 : // PCF format fonts have each size in a different file, and each of these
430 : // files is referenced by its own pattern, but really these are each
431 : // different sizes of one face with one name.
432 : //
433 : // Multiple patterns in an entry also effectively deals with a set of
434 : // PostScript Type 1 font files that all have the same face name but are in
435 : // several files because of the limit on the number of glyphs in a Type 1 font
436 : // file. (e.g. Computer Modern.)
437 :
438 0 : class gfxUserFcFontEntry : public gfxFcFontEntry {
439 : protected:
440 0 : gfxUserFcFontEntry(const gfxProxyFontEntry &aProxyEntry)
441 : // store the family name
442 0 : : gfxFcFontEntry(aProxyEntry.mFamily->Name())
443 : {
444 0 : mItalic = aProxyEntry.mItalic;
445 0 : mWeight = aProxyEntry.mWeight;
446 0 : mStretch = aProxyEntry.mStretch;
447 0 : mIsUserFont = true;
448 0 : }
449 :
450 : // Helper function to change a pattern so that it matches the CSS style
451 : // descriptors and so gets properly sorted in font selection. This also
452 : // avoids synthetic style effects being added by the renderer when the
453 : // style of the font itself does not match the descriptor provided by the
454 : // author.
455 : void AdjustPatternToCSS(FcPattern *aPattern);
456 : };
457 :
458 : void
459 0 : gfxUserFcFontEntry::AdjustPatternToCSS(FcPattern *aPattern)
460 : {
461 0 : int fontWeight = -1;
462 0 : FcPatternGetInteger(aPattern, FC_WEIGHT, 0, &fontWeight);
463 0 : int cssWeight = gfxFontconfigUtils::FcWeightForBaseWeight(mWeight / 100);
464 0 : if (cssWeight != fontWeight) {
465 0 : FcPatternDel(aPattern, FC_WEIGHT);
466 0 : FcPatternAddInteger(aPattern, FC_WEIGHT, cssWeight);
467 : }
468 :
469 : int fontSlant;
470 0 : FcResult res = FcPatternGetInteger(aPattern, FC_SLANT, 0, &fontSlant);
471 : // gfxFontEntry doesn't understand the difference between oblique
472 : // and italic.
473 0 : if (res != FcResultMatch ||
474 0 : IsItalic() != (fontSlant != FC_SLANT_ROMAN)) {
475 0 : FcPatternDel(aPattern, FC_SLANT);
476 : FcPatternAddInteger(aPattern, FC_SLANT,
477 0 : IsItalic() ? FC_SLANT_OBLIQUE : FC_SLANT_ROMAN);
478 : }
479 :
480 0 : int fontWidth = -1;
481 0 : FcPatternGetInteger(aPattern, FC_WIDTH, 0, &fontWidth);
482 0 : int cssWidth = gfxFontconfigUtils::FcWidthForThebesStretch(mStretch);
483 0 : if (cssWidth != fontWidth) {
484 0 : FcPatternDel(aPattern, FC_WIDTH);
485 0 : FcPatternAddInteger(aPattern, FC_WIDTH, cssWidth);
486 : }
487 :
488 : // Ensure that there is a fullname property (if there is a family
489 : // property) so that fontconfig rules can identify the real name of the
490 : // font, because the family property will be replaced.
491 : FcChar8 *unused;
492 0 : if (FcPatternGetString(aPattern,
493 0 : FC_FULLNAME, 0, &unused) == FcResultNoMatch) {
494 0 : nsCAutoString fullname;
495 0 : if (gfxFontconfigUtils::GetFullnameFromFamilyAndStyle(aPattern,
496 : &fullname)) {
497 : FcPatternAddString(aPattern, FC_FULLNAME,
498 0 : gfxFontconfigUtils::ToFcChar8(fullname));
499 : }
500 : }
501 :
502 0 : nsCAutoString family;
503 0 : family.Append(FONT_FACE_FAMILY_PREFIX);
504 0 : AppendUTF16toUTF8(Name(), family);
505 :
506 0 : FcPatternDel(aPattern, FC_FAMILY);
507 0 : FcPatternDel(aPattern, FC_FAMILYLANG);
508 : FcPatternAddString(aPattern, FC_FAMILY,
509 0 : gfxFontconfigUtils::ToFcChar8(family));
510 0 : }
511 :
512 : /**
513 : * gfxLocalFcFontEntry:
514 : *
515 : * An implementation of gfxUserFcFontEntry for local fonts from src:local().
516 : *
517 : * This class is used only in gfxUserFontSet and for providing FcPattern*
518 : * handles to system fonts for font selection. gfxFcFonts created from these
519 : * patterns will use gfxSystemFcFontEntrys, which may be shared with
520 : * gfxFcFonts from regular family-name based font selection.
521 : */
522 :
523 0 : class gfxLocalFcFontEntry : public gfxUserFcFontEntry {
524 : public:
525 0 : gfxLocalFcFontEntry(const gfxProxyFontEntry &aProxyEntry,
526 : const nsTArray< nsCountedRef<FcPattern> >& aPatterns)
527 0 : : gfxUserFcFontEntry(aProxyEntry)
528 : {
529 0 : if (!mPatterns.SetCapacity(aPatterns.Length()))
530 0 : return; // OOM
531 :
532 0 : for (PRUint32 i = 0; i < aPatterns.Length(); ++i) {
533 0 : FcPattern *pattern = FcPatternDuplicate(aPatterns.ElementAt(i));
534 0 : if (!pattern)
535 0 : return; // OOM
536 :
537 0 : AdjustPatternToCSS(pattern);
538 :
539 0 : mPatterns.AppendElement();
540 0 : mPatterns[i].own(pattern);
541 : }
542 0 : mIsLocalUserFont = true;
543 : }
544 : };
545 :
546 : /**
547 : * gfxDownloadedFcFontEntry:
548 : *
549 : * An implementation of gfxFcFontEntry for web fonts from src:url().
550 : *
551 : * When a cairo_font_face_t is created for these fonts, the cairo_font_face_t
552 : * keeps a reference to the FontEntry to keep the font data alive.
553 : */
554 :
555 : class gfxDownloadedFcFontEntry : public gfxUserFcFontEntry {
556 : public:
557 : // This takes ownership of the face and its underlying data
558 0 : gfxDownloadedFcFontEntry(const gfxProxyFontEntry &aProxyEntry,
559 : const PRUint8 *aData, FT_Face aFace)
560 0 : : gfxUserFcFontEntry(aProxyEntry), mFontData(aData), mFace(aFace)
561 : {
562 0 : NS_PRECONDITION(aFace != NULL, "aFace is NULL!");
563 0 : InitPattern();
564 0 : }
565 :
566 : virtual ~gfxDownloadedFcFontEntry();
567 :
568 : // Returns true on success
569 : bool SetCairoFace(cairo_font_face_t *aFace);
570 :
571 : // Returns a PangoCoverage owned by the FontEntry. The caller must add a
572 : // reference if it wishes to keep the PangoCoverage longer than the
573 : // lifetime of the FontEntry.
574 : PangoCoverage *GetPangoCoverage();
575 :
576 : protected:
577 : void InitPattern();
578 :
579 : // mFontData holds the data used to instantiate the FT_Face;
580 : // this has to persist until we are finished with the face,
581 : // then be released with NS_Free().
582 : const PRUint8* mFontData;
583 :
584 : FT_Face mFace;
585 :
586 : // mPangoCoverage is the charset property of the pattern translated to a
587 : // format that Pango understands. A reference is kept here so that it can
588 : // be shared by multiple PangoFonts (of different sizes).
589 : nsAutoRef<PangoCoverage> mPangoCoverage;
590 : };
591 :
592 : // A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
593 : static const char *kFontEntryFcProp = "-moz-font-entry";
594 :
595 0 : static FcBool AddDownloadedFontEntry(FcPattern *aPattern,
596 : gfxDownloadedFcFontEntry *aFontEntry)
597 : {
598 : FcValue value;
599 0 : value.type = FcTypeFTFace; // void* field of union
600 0 : value.u.f = aFontEntry;
601 :
602 0 : return FcPatternAdd(aPattern, kFontEntryFcProp, value, FcFalse);
603 : }
604 :
605 0 : static FcBool DelDownloadedFontEntry(FcPattern *aPattern)
606 : {
607 0 : return FcPatternDel(aPattern, kFontEntryFcProp);
608 : }
609 :
610 0 : static gfxDownloadedFcFontEntry *GetDownloadedFontEntry(FcPattern *aPattern)
611 : {
612 : FcValue value;
613 0 : if (FcPatternGet(aPattern, kFontEntryFcProp, 0, &value) != FcResultMatch)
614 0 : return nsnull;
615 :
616 0 : if (value.type != FcTypeFTFace) {
617 0 : NS_NOTREACHED("Wrong type for -moz-font-entry font property");
618 0 : return nsnull;
619 : }
620 :
621 0 : return static_cast<gfxDownloadedFcFontEntry*>(value.u.f);
622 : }
623 :
624 0 : gfxDownloadedFcFontEntry::~gfxDownloadedFcFontEntry()
625 : {
626 0 : if (mPatterns.Length() != 0) {
627 : // Remove back reference to this font entry and the face in case
628 : // anyone holds a reference to the pattern.
629 0 : NS_ASSERTION(mPatterns.Length() == 1,
630 : "More than one pattern in gfxDownloadedFcFontEntry!");
631 0 : DelDownloadedFontEntry(mPatterns[0]);
632 0 : FcPatternDel(mPatterns[0], FC_FT_FACE);
633 : }
634 0 : FT_Done_Face(mFace);
635 0 : NS_Free((void*)mFontData);
636 0 : }
637 :
638 : typedef FcPattern* (*QueryFaceFunction)(const FT_Face face,
639 : const FcChar8 *file, int id,
640 : FcBlanks *blanks);
641 :
642 : void
643 0 : gfxDownloadedFcFontEntry::InitPattern()
644 : {
645 : static QueryFaceFunction sQueryFacePtr =
646 : reinterpret_cast<QueryFaceFunction>
647 0 : (FindFunctionSymbol("FcFreeTypeQueryFace"));
648 : FcPattern *pattern;
649 :
650 : // FcFreeTypeQueryFace is the same function used to construct patterns for
651 : // system fonts and so is the preferred function to use for this purpose.
652 : // This will set up the langset property, which helps with sorting, and
653 : // the foundry, fullname, and fontversion properties, which properly
654 : // identify the font to fontconfig rules. However, FcFreeTypeQueryFace is
655 : // available only from fontconfig-2.4.2 (December 2006). (CentOS 5.0 has
656 : // fontconfig-2.4.1.)
657 0 : if (sQueryFacePtr) {
658 : // The "file" argument cannot be NULL (in fontconfig-2.6.0 at least).
659 : // The dummy file passed here is removed below.
660 : //
661 : // When fontconfig scans the system fonts, FcConfigGetBlanks(NULL) is
662 : // passed as the "blanks" argument, which provides that unexpectedly
663 : // blank glyphs are elided. Here, however, we pass NULL for "blanks",
664 : // effectively assuming that, if the font has a blank glyph, then the
665 : // author intends any associated character to be rendered blank.
666 : pattern =
667 0 : (*sQueryFacePtr)(mFace, gfxFontconfigUtils::ToFcChar8(""), 0, NULL);
668 0 : if (!pattern)
669 : // Either OOM, or fontconfig chose to skip this font because it
670 : // has "no encoded characters", which I think means "BDF and PCF
671 : // fonts which are not in Unicode (or the effectively equivalent
672 : // ISO Latin-1) encoding".
673 0 : return;
674 :
675 : // These properties don't make sense for this face without a file.
676 0 : FcPatternDel(pattern, FC_FILE);
677 0 : FcPatternDel(pattern, FC_INDEX);
678 :
679 : } else {
680 : // Do the minimum necessary to construct a pattern for sorting.
681 :
682 : // FC_CHARSET is vital to determine which characters are supported.
683 0 : nsAutoRef<FcCharSet> charset(FcFreeTypeCharSet(mFace, NULL));
684 : // If there are no characters then assume we don't know how to read
685 : // this font.
686 0 : if (!charset || FcCharSetCount(charset) == 0)
687 : return;
688 :
689 0 : pattern = FcPatternCreate();
690 0 : FcPatternAddCharSet(pattern, FC_CHARSET, charset);
691 :
692 : // FC_PIXEL_SIZE can be important for font selection of fixed-size
693 : // fonts.
694 0 : if (!(mFace->face_flags & FT_FACE_FLAG_SCALABLE)) {
695 0 : for (FT_Int i = 0; i < mFace->num_fixed_sizes; ++i) {
696 : #if HAVE_FT_BITMAP_SIZE_Y_PPEM
697 0 : double size = FLOAT_FROM_26_6(mFace->available_sizes[i].y_ppem);
698 : #else
699 : double size = mFace->available_sizes[i].height;
700 : #endif
701 0 : FcPatternAddDouble (pattern, FC_PIXEL_SIZE, size);
702 : }
703 :
704 : // Not sure whether this is important;
705 : // imitating FcFreeTypeQueryFace:
706 0 : FcPatternAddBool (pattern, FC_ANTIALIAS, FcFalse);
707 : }
708 :
709 : // Setting up the FC_LANGSET property is very difficult with the APIs
710 : // available prior to FcFreeTypeQueryFace. Having no FC_LANGSET
711 : // property seems better than having a property with an empty LangSet.
712 : // With no FC_LANGSET property, fontconfig sort functions will
713 : // consider this face to have the same priority as (otherwise equal)
714 : // faces that have support for the primary requested language, but
715 : // will not consider any language to have been satisfied (and so will
716 : // continue to look for a face with language support in fallback
717 : // fonts).
718 : }
719 :
720 0 : AdjustPatternToCSS(pattern);
721 :
722 0 : FcPatternAddFTFace(pattern, FC_FT_FACE, mFace);
723 0 : AddDownloadedFontEntry(pattern, this);
724 :
725 : // There is never more than one pattern
726 0 : mPatterns.AppendElement();
727 0 : mPatterns[0].own(pattern);
728 : }
729 :
730 0 : static void ReleaseDownloadedFontEntry(void *data)
731 : {
732 : gfxDownloadedFcFontEntry *downloadedFontEntry =
733 0 : static_cast<gfxDownloadedFcFontEntry*>(data);
734 0 : NS_RELEASE(downloadedFontEntry);
735 0 : }
736 :
737 0 : bool gfxDownloadedFcFontEntry::SetCairoFace(cairo_font_face_t *aFace)
738 : {
739 0 : if (CAIRO_STATUS_SUCCESS !=
740 : cairo_font_face_set_user_data(aFace, &sFontEntryKey, this,
741 0 : ReleaseDownloadedFontEntry))
742 0 : return false;
743 :
744 : // Hold a reference to this font entry to keep the font face data.
745 0 : NS_ADDREF(this);
746 0 : return true;
747 : }
748 :
749 0 : static PangoCoverage *NewPangoCoverage(FcPattern *aFont)
750 : {
751 : // This uses g_slice_alloc which will abort on OOM rather than return NULL.
752 0 : PangoCoverage *coverage = pango_coverage_new();
753 :
754 : FcCharSet *charset;
755 0 : if (FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset) != FcResultMatch)
756 0 : return coverage; // empty
757 :
758 : FcChar32 base;
759 : FcChar32 map[FC_CHARSET_MAP_SIZE];
760 : FcChar32 next;
761 0 : for (base = FcCharSetFirstPage(charset, map, &next);
762 : base != FC_CHARSET_DONE;
763 0 : base = FcCharSetNextPage(charset, map, &next)) {
764 0 : for (PRUint32 i = 0; i < FC_CHARSET_MAP_SIZE; ++i) {
765 0 : PRUint32 offset = 0;
766 0 : FcChar32 bitmap = map[i];
767 0 : for (; bitmap; bitmap >>= 1) {
768 0 : if (bitmap & 1) {
769 : pango_coverage_set(coverage, base + offset,
770 0 : PANGO_COVERAGE_EXACT);
771 : }
772 0 : ++offset;
773 : }
774 0 : base += 32;
775 : }
776 : }
777 0 : return coverage;
778 : }
779 :
780 : PangoCoverage *
781 0 : gfxDownloadedFcFontEntry::GetPangoCoverage()
782 : {
783 0 : NS_ASSERTION(mPatterns.Length() != 0,
784 : "Can't get coverage without a pattern!");
785 0 : if (!mPangoCoverage) {
786 0 : mPangoCoverage.own(NewPangoCoverage(mPatterns[0]));
787 : }
788 0 : return mPangoCoverage;
789 : }
790 :
791 : /*
792 : * gfxFcFont
793 : *
794 : * This is a gfxFont implementation using a CAIRO_FONT_TYPE_FT
795 : * cairo_scaled_font created from an FcPattern.
796 : */
797 :
798 : class gfxFcFont : public gfxFT2FontBase {
799 : public:
800 : virtual ~gfxFcFont();
801 : static already_AddRefed<gfxFcFont>
802 : GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
803 : const gfxFontStyle *aFontStyle);
804 :
805 : // The PangoFont returned is owned by the gfxFcFont
806 0 : PangoFont *GetPangoFont() {
807 0 : if (!mPangoFont) {
808 0 : MakePangoFont();
809 : }
810 0 : return mPangoFont;
811 : }
812 :
813 : protected:
814 : virtual bool ShapeWord(gfxContext *aContext,
815 : gfxShapedWord *aShapedWord,
816 : const PRUnichar *aString,
817 : bool aPreferPlatformShaping);
818 :
819 : bool InitGlyphRunWithPango(gfxShapedWord *aTextRun,
820 : const PRUnichar *aString);
821 :
822 : private:
823 : gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
824 : const gfxFontStyle *aFontStyle);
825 :
826 : void MakePangoFont();
827 :
828 : PangoFont *mPangoFont;
829 :
830 : // key for locating a gfxFcFont corresponding to a cairo_scaled_font
831 : static cairo_user_data_key_t sGfxFontKey;
832 : };
833 :
834 : /**
835 : * gfxPangoFcFont:
836 : *
837 : * An implementation of PangoFcFont that wraps a gfxFont so that it can be
838 : * passed to PangoRenderFc shapers.
839 : *
840 : * Many of these will be created for pango_itemize, but most will only be
841 : * tested for coverage of individual characters (and sometimes not even that).
842 : * Therefore the gfxFont is only constructed if and when needed.
843 : */
844 :
845 : #define GFX_TYPE_PANGO_FC_FONT (gfx_pango_fc_font_get_type())
846 : #define GFX_PANGO_FC_FONT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFont))
847 : #define GFX_IS_PANGO_FC_FONT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FC_FONT))
848 :
849 : /* static */
850 : GType gfx_pango_fc_font_get_type (void);
851 :
852 : #define GFX_PANGO_FC_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFontClass))
853 : #define GFX_IS_PANGO_FC_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FC_FONT))
854 : #define GFX_PANGO_FC_FONT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FC_FONT, gfxPangoFcFontClass))
855 :
856 : // This struct is POD so that it can be used as a GObject.
857 : struct gfxPangoFcFont {
858 : PangoFcFont parent_instance;
859 :
860 : PangoCoverage *mCoverage;
861 : gfxFcFont *mGfxFont;
862 :
863 : // The caller promises to ensure that |aGfxFont| remains valid until the
864 : // new gfxPangoFcFont is destroyed. See PangoFontToggleNotify.
865 : //
866 : // The gfxPangoFcFont holds a reference to |aFontPattern|.
867 : // Providing one of fontconfig's font patterns uses much less memory than
868 : // using a fully resolved pattern, because fontconfig's font patterns are
869 : // shared and will exist anyway.
870 : static nsReturnRef<PangoFont>
871 : NewFont(gfxFcFont *aGfxFont, FcPattern *aFontPattern);
872 :
873 0 : gfxFcFont *GfxFont() { return mGfxFont; }
874 :
875 0 : cairo_scaled_font_t *CairoFont()
876 : {
877 0 : return GfxFont()->CairoScaledFont();
878 : }
879 :
880 : private:
881 : void SetFontMap();
882 : };
883 :
884 : struct gfxPangoFcFontClass {
885 : PangoFcFontClass parent_class;
886 : };
887 :
888 0 : G_DEFINE_TYPE (gfxPangoFcFont, gfx_pango_fc_font, PANGO_TYPE_FC_FONT)
889 :
890 : /* static */ nsReturnRef<PangoFont>
891 0 : gfxPangoFcFont::NewFont(gfxFcFont *aGfxFont, FcPattern *aFontPattern)
892 : {
893 : // The font pattern is needed for pango_fc_font_finalize.
894 : gfxPangoFcFont *font = static_cast<gfxPangoFcFont*>
895 0 : (g_object_new(GFX_TYPE_PANGO_FC_FONT, "pattern", aFontPattern, NULL));
896 :
897 0 : font->mGfxFont = aGfxFont;
898 0 : font->SetFontMap();
899 :
900 0 : PangoFcFont *fc_font = &font->parent_instance;
901 0 : cairo_scaled_font_t *scaled_font = aGfxFont->CairoScaledFont();
902 : // Normally the is_hinted field of PangoFcFont is set based on the
903 : // FC_HINTING property on the pattern at construction, but this property
904 : // is not on an unresolved aFontPattern. is_hinted is used by
905 : // pango_fc_font_kern_glyphs, which is sometimes used by
906 : // pango_ot_buffer_output.
907 0 : cairo_font_options_t *options = cairo_font_options_create();
908 0 : cairo_scaled_font_get_font_options(scaled_font, options);
909 0 : cairo_hint_style_t hint_style = cairo_font_options_get_hint_style(options);
910 0 : cairo_font_options_destroy(options);
911 0 : fc_font->is_hinted = hint_style != CAIRO_HINT_STYLE_NONE;
912 :
913 : // is_transformed does not appear to be used anywhere but looks
914 : // like it should be set.
915 : cairo_matrix_t matrix;
916 0 : cairo_scaled_font_get_font_matrix(scaled_font, &matrix);
917 : fc_font->is_transformed = (matrix.xy != 0.0 || matrix.yx != 0.0 ||
918 0 : matrix.xx != matrix.yy);
919 :
920 0 : return nsReturnRef<PangoFont>(PANGO_FONT(font));
921 : }
922 :
923 : void
924 0 : gfxPangoFcFont::SetFontMap()
925 : {
926 : // PangoFcFont::get_coverage wants a PangoFcFontMap. (PangoFcFontMap
927 : // would usually set this after calling PangoFcFontMap::create_font()
928 : // or new_font().)
929 0 : PangoFontMap *fontmap = GetPangoFontMap();
930 : // In Pango-1.24.4, we can use the "fontmap" property; by setting the
931 : // property, the PangoFcFont base class manages the pointer (as a weak
932 : // reference).
933 0 : PangoFcFont *fc_font = &parent_instance;
934 0 : if (gUseFontMapProperty) {
935 0 : g_object_set(this, "fontmap", fontmap, NULL);
936 : } else {
937 : // In Pango versions up to 1.20.5, the parent class will decrement
938 : // the reference count of the fontmap during shutdown() or
939 : // finalize() of the font. In Pango versions from 1.22.0 this no
940 : // longer happens, so we'll end up leaking the (singleton)
941 : // fontmap.
942 0 : fc_font->fontmap = fontmap;
943 0 : g_object_ref(fc_font->fontmap);
944 : }
945 0 : }
946 :
947 : static void
948 0 : gfx_pango_fc_font_init(gfxPangoFcFont *font)
949 : {
950 0 : }
951 :
952 : static void
953 0 : gfx_pango_fc_font_finalize(GObject *object)
954 : {
955 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(object);
956 :
957 0 : if (self->mCoverage)
958 0 : pango_coverage_unref(self->mCoverage);
959 :
960 0 : G_OBJECT_CLASS(gfx_pango_fc_font_parent_class)->finalize(object);
961 0 : }
962 :
963 : static PangoCoverage *
964 0 : gfx_pango_fc_font_get_coverage(PangoFont *font, PangoLanguage *lang)
965 : {
966 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
967 :
968 : // The coverage is requested often enough that it is worth holding a
969 : // reference on the font.
970 0 : if (!self->mCoverage) {
971 0 : FcPattern *pattern = self->parent_instance.font_pattern;
972 : gfxDownloadedFcFontEntry *downloadedFontEntry =
973 0 : GetDownloadedFontEntry(pattern);
974 : // The parent class implementation requires the font pattern to have
975 : // a file and caches results against that filename. This is not
976 : // suitable for web fonts.
977 0 : if (!downloadedFontEntry) {
978 : self->mCoverage =
979 0 : PANGO_FONT_CLASS(gfx_pango_fc_font_parent_class)->
980 0 : get_coverage(font, lang);
981 : } else {
982 : self->mCoverage =
983 0 : pango_coverage_ref(downloadedFontEntry->GetPangoCoverage());
984 : }
985 : }
986 :
987 0 : return pango_coverage_ref(self->mCoverage);
988 : }
989 :
990 : static PRInt32
991 0 : GetDPI()
992 : {
993 : #if defined(MOZ_WIDGET_GTK2)
994 0 : return gfxPlatformGtk::GetDPI();
995 : #elif defined(MOZ_WIDGET_QT)
996 : return gfxQtPlatform::GetDPI();
997 : #else
998 : return 96;
999 : #endif
1000 : }
1001 :
1002 : static PangoFontDescription *
1003 0 : gfx_pango_fc_font_describe(PangoFont *font)
1004 : {
1005 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1006 0 : PangoFcFont *fcFont = &self->parent_instance;
1007 : PangoFontDescription *result =
1008 0 : pango_font_description_copy(fcFont->description);
1009 :
1010 0 : gfxFcFont *gfxFont = self->GfxFont();
1011 0 : if (gfxFont) {
1012 0 : double pixelsize = gfxFont->GetStyle()->size;
1013 0 : double dpi = GetDPI();
1014 0 : gint size = moz_pango_units_from_double(pixelsize * dpi / 72.0);
1015 0 : pango_font_description_set_size(result, size);
1016 : }
1017 0 : return result;
1018 : }
1019 :
1020 : static PangoFontDescription *
1021 0 : gfx_pango_fc_font_describe_absolute(PangoFont *font)
1022 : {
1023 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1024 0 : PangoFcFont *fcFont = &self->parent_instance;
1025 : PangoFontDescription *result =
1026 0 : pango_font_description_copy(fcFont->description);
1027 :
1028 0 : gfxFcFont *gfxFont = self->GfxFont();
1029 0 : if (gfxFont) {
1030 0 : double size = gfxFont->GetStyle()->size * PANGO_SCALE;
1031 0 : pango_font_description_set_absolute_size(result, size);
1032 : }
1033 0 : return result;
1034 : }
1035 :
1036 : static void
1037 0 : gfx_pango_fc_font_get_glyph_extents(PangoFont *font, PangoGlyph glyph,
1038 : PangoRectangle *ink_rect,
1039 : PangoRectangle *logical_rect)
1040 : {
1041 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1042 0 : gfxFcFont *gfxFont = self->GfxFont();
1043 :
1044 0 : if (IS_MISSING_GLYPH(glyph)) {
1045 0 : const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
1046 :
1047 : PangoRectangle rect;
1048 0 : rect.x = 0;
1049 0 : rect.y = moz_pango_units_from_double(-metrics.maxAscent);
1050 0 : rect.width = moz_pango_units_from_double(metrics.aveCharWidth);
1051 0 : rect.height = moz_pango_units_from_double(metrics.maxHeight);
1052 0 : if (ink_rect) {
1053 0 : *ink_rect = rect;
1054 : }
1055 0 : if (logical_rect) {
1056 0 : *logical_rect = rect;
1057 : }
1058 0 : return;
1059 : }
1060 :
1061 0 : if (logical_rect) {
1062 : // logical_rect.width is possibly used by pango_ot_buffer_output (used
1063 : // by many shapers) and used by fallback_engine_shape (possibly used
1064 : // by pango_shape and pango_itemize when no glyphs are found). I
1065 : // doubt the other fields will be used but we won't have any way to
1066 : // detecting if they are so we'd better set them.
1067 0 : const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
1068 0 : logical_rect->y = moz_pango_units_from_double(-metrics.maxAscent);
1069 0 : logical_rect->height = moz_pango_units_from_double(metrics.maxHeight);
1070 : }
1071 :
1072 : cairo_text_extents_t extents;
1073 0 : if (IS_EMPTY_GLYPH(glyph)) {
1074 0 : new (&extents) cairo_text_extents_t(); // zero
1075 : } else {
1076 0 : gfxFont->GetGlyphExtents(glyph, &extents);
1077 : }
1078 :
1079 0 : if (ink_rect) {
1080 0 : ink_rect->x = moz_pango_units_from_double(extents.x_bearing);
1081 0 : ink_rect->y = moz_pango_units_from_double(extents.y_bearing);
1082 0 : ink_rect->width = moz_pango_units_from_double(extents.width);
1083 0 : ink_rect->height = moz_pango_units_from_double(extents.height);
1084 : }
1085 0 : if (logical_rect) {
1086 0 : logical_rect->x = 0;
1087 0 : logical_rect->width = moz_pango_units_from_double(extents.x_advance);
1088 : }
1089 : }
1090 :
1091 : static PangoFontMetrics *
1092 0 : gfx_pango_fc_font_get_metrics(PangoFont *font, PangoLanguage *language)
1093 : {
1094 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1095 :
1096 : // This uses g_slice_alloc which will abort on OOM rather than return NULL.
1097 0 : PangoFontMetrics *result = pango_font_metrics_new();
1098 :
1099 0 : gfxFcFont *gfxFont = self->GfxFont();
1100 0 : if (gfxFont) {
1101 0 : const gfxFont::Metrics& metrics = gfxFont->GetMetrics();
1102 :
1103 0 : result->ascent = moz_pango_units_from_double(metrics.maxAscent);
1104 0 : result->descent = moz_pango_units_from_double(metrics.maxDescent);
1105 : result->approximate_char_width =
1106 0 : moz_pango_units_from_double(metrics.aveCharWidth);
1107 : result->approximate_digit_width =
1108 0 : moz_pango_units_from_double(metrics.zeroOrAveCharWidth);
1109 : result->underline_position =
1110 0 : moz_pango_units_from_double(metrics.underlineOffset);
1111 : result->underline_thickness =
1112 0 : moz_pango_units_from_double(metrics.underlineSize);
1113 : result->strikethrough_position =
1114 0 : moz_pango_units_from_double(metrics.strikeoutOffset);
1115 : result->strikethrough_thickness =
1116 0 : moz_pango_units_from_double(metrics.strikeoutSize);
1117 : }
1118 0 : return result;
1119 : }
1120 :
1121 : static FT_Face
1122 0 : gfx_pango_fc_font_lock_face(PangoFcFont *font)
1123 : {
1124 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1125 0 : return cairo_ft_scaled_font_lock_face(self->CairoFont());
1126 : }
1127 :
1128 : static void
1129 0 : gfx_pango_fc_font_unlock_face(PangoFcFont *font)
1130 : {
1131 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1132 0 : cairo_ft_scaled_font_unlock_face(self->CairoFont());
1133 0 : }
1134 :
1135 : static guint
1136 0 : gfx_pango_fc_font_get_glyph(PangoFcFont *font, gunichar wc)
1137 : {
1138 0 : gfxPangoFcFont *self = GFX_PANGO_FC_FONT(font);
1139 0 : gfxFcFont *gfxFont = self->GfxFont();
1140 0 : return gfxFont->GetGlyph(wc);
1141 : }
1142 :
1143 : typedef int (*PangoVersionFunction)();
1144 :
1145 : static void
1146 0 : gfx_pango_fc_font_class_init (gfxPangoFcFontClass *klass)
1147 : {
1148 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
1149 0 : PangoFontClass *font_class = PANGO_FONT_CLASS (klass);
1150 0 : PangoFcFontClass *fc_font_class = PANGO_FC_FONT_CLASS (klass);
1151 :
1152 0 : object_class->finalize = gfx_pango_fc_font_finalize;
1153 :
1154 0 : font_class->get_coverage = gfx_pango_fc_font_get_coverage;
1155 : // describe is called on errors in pango_shape.
1156 0 : font_class->describe = gfx_pango_fc_font_describe;
1157 0 : font_class->get_glyph_extents = gfx_pango_fc_font_get_glyph_extents;
1158 : // get_metrics and describe_absolute are not likely to be used but
1159 : // implemented because the class makes them available.
1160 0 : font_class->get_metrics = gfx_pango_fc_font_get_metrics;
1161 0 : font_class->describe_absolute = gfx_pango_fc_font_describe_absolute;
1162 : // font_class->find_shaper,get_font_map are inherited from PangoFcFontClass
1163 :
1164 : // fc_font_class->has_char is inherited
1165 0 : fc_font_class->lock_face = gfx_pango_fc_font_lock_face;
1166 0 : fc_font_class->unlock_face = gfx_pango_fc_font_unlock_face;
1167 0 : fc_font_class->get_glyph = gfx_pango_fc_font_get_glyph;
1168 :
1169 : // The "fontmap" property on PangoFcFont was introduced for Pango-1.24.0
1170 : // but versions prior to Pango-1.24.4 leaked weak pointers for every font,
1171 : // which would causes crashes when shutting down the FontMap. For the
1172 : // early Pango-1.24.x versions we're better off setting the fontmap member
1173 : // ourselves, which will not create weak pointers to leak, and instead
1174 : // we'll leak the FontMap on shutdown. pango_version() and
1175 : // PANGO_VERSION_ENCODE require Pango-1.16.
1176 : PangoVersionFunction pango_version =
1177 : reinterpret_cast<PangoVersionFunction>
1178 0 : (FindFunctionSymbol("pango_version"));
1179 0 : gUseFontMapProperty = pango_version && (*pango_version)() >= 12404;
1180 0 : }
1181 :
1182 : /**
1183 : * gfxFcFontSet:
1184 : *
1185 : * Translation from a desired FcPattern to a sorted set of font references
1186 : * (fontconfig cache data) and (when needed) fonts.
1187 : */
1188 :
1189 0 : class gfxFcFontSet {
1190 : public:
1191 0 : NS_INLINE_DECL_REFCOUNTING(gfxFcFontSet)
1192 :
1193 0 : explicit gfxFcFontSet(FcPattern *aPattern,
1194 : gfxUserFontSet *aUserFontSet)
1195 : : mSortPattern(aPattern), mUserFontSet(aUserFontSet),
1196 : mFcFontsTrimmed(0),
1197 0 : mHaveFallbackFonts(false)
1198 : {
1199 : bool waitForUserFont;
1200 0 : mFcFontSet = SortPreferredFonts(waitForUserFont);
1201 0 : mWaitingForUserFont = waitForUserFont;
1202 0 : }
1203 :
1204 : // A reference is held by the FontSet.
1205 : // The caller may add a ref to keep the font alive longer than the FontSet.
1206 0 : gfxFcFont *GetFontAt(PRUint32 i, const gfxFontStyle *aFontStyle)
1207 : {
1208 0 : if (i >= mFonts.Length() || !mFonts[i].mFont) {
1209 : // GetFontPatternAt sets up mFonts
1210 0 : FcPattern *fontPattern = GetFontPatternAt(i);
1211 0 : if (!fontPattern)
1212 0 : return NULL;
1213 :
1214 0 : mFonts[i].mFont =
1215 : gfxFcFont::GetOrMakeFont(mSortPattern, fontPattern,
1216 0 : aFontStyle);
1217 : }
1218 0 : return mFonts[i].mFont;
1219 : }
1220 :
1221 : FcPattern *GetFontPatternAt(PRUint32 i);
1222 :
1223 0 : bool WaitingForUserFont() const {
1224 0 : return mWaitingForUserFont;
1225 : }
1226 :
1227 : private:
1228 : nsReturnRef<FcFontSet> SortPreferredFonts(bool& aWaitForUserFont);
1229 : nsReturnRef<FcFontSet> SortFallbackFonts();
1230 :
1231 0 : struct FontEntry {
1232 0 : explicit FontEntry(FcPattern *aPattern) : mPattern(aPattern) {}
1233 : nsCountedRef<FcPattern> mPattern;
1234 : nsRefPtr<gfxFcFont> mFont;
1235 : nsCountedRef<PangoFont> mPangoFont;
1236 : };
1237 :
1238 0 : struct LangSupportEntry {
1239 0 : LangSupportEntry(FcChar8 *aLang, FcLangResult aSupport) :
1240 0 : mLang(aLang), mBestSupport(aSupport) {}
1241 : FcChar8 *mLang;
1242 : FcLangResult mBestSupport;
1243 : };
1244 :
1245 : public:
1246 : // public for nsTArray
1247 : class LangComparator {
1248 : public:
1249 0 : bool Equals(const LangSupportEntry& a, const FcChar8 *b) const
1250 : {
1251 0 : return FcStrCmpIgnoreCase(a.mLang, b) == 0;
1252 : }
1253 : };
1254 :
1255 : private:
1256 : // The requested pattern
1257 : nsCountedRef<FcPattern> mSortPattern;
1258 : // Fonts from @font-face rules
1259 : nsRefPtr<gfxUserFontSet> mUserFontSet;
1260 : // A (trimmed) list of font patterns and fonts that is built up as
1261 : // required.
1262 : nsTArray<FontEntry> mFonts;
1263 : // Holds a list of font patterns that will be trimmed. This is first set
1264 : // to a list of preferred fonts. Then, if/when all the preferred fonts
1265 : // have been trimmed and added to mFonts, this is set to a list of
1266 : // fallback fonts.
1267 : nsAutoRef<FcFontSet> mFcFontSet;
1268 : // The set of characters supported by the fonts in mFonts.
1269 : nsAutoRef<FcCharSet> mCharSet;
1270 : // The index of the next font in mFcFontSet that has not yet been
1271 : // considered for mFonts.
1272 : int mFcFontsTrimmed;
1273 : // True iff fallback fonts are either stored in mFcFontSet or have been
1274 : // trimmed and added to mFonts (so that mFcFontSet is NULL).
1275 : bool mHaveFallbackFonts;
1276 : // True iff there was a user font set with pending downloads,
1277 : // so the set may be updated when downloads complete
1278 : bool mWaitingForUserFont;
1279 : };
1280 :
1281 : // Find the FcPattern for an @font-face font suitable for CSS family |aFamily|
1282 : // and style |aStyle| properties.
1283 : static const nsTArray< nsCountedRef<FcPattern> >*
1284 0 : FindFontPatterns(gfxUserFontSet *mUserFontSet,
1285 : const nsACString &aFamily, PRUint8 aStyle,
1286 : PRUint16 aWeight, PRInt16 aStretch,
1287 : bool& aFoundFamily, bool& aWaitForUserFont)
1288 : {
1289 : // Convert to UTF16
1290 0 : NS_ConvertUTF8toUTF16 utf16Family(aFamily);
1291 :
1292 : // needsBold is not used here. Instead synthetic bold is enabled through
1293 : // FcFontRenderPrepare when the weight in the requested pattern is
1294 : // compared against the weight in the font pattern.
1295 : bool needsBold;
1296 :
1297 0 : gfxFontStyle style;
1298 0 : style.style = aStyle;
1299 0 : style.weight = aWeight;
1300 0 : style.stretch = aStretch;
1301 :
1302 : gfxUserFcFontEntry *fontEntry = static_cast<gfxUserFcFontEntry*>
1303 : (mUserFontSet->FindFontEntry(utf16Family, style, aFoundFamily,
1304 0 : needsBold, aWaitForUserFont));
1305 :
1306 : // Accept synthetic oblique for italic and oblique.
1307 0 : if (!fontEntry && aStyle != FONT_STYLE_NORMAL) {
1308 0 : style.style = FONT_STYLE_NORMAL;
1309 : fontEntry = static_cast<gfxUserFcFontEntry*>
1310 : (mUserFontSet->FindFontEntry(utf16Family, style, aFoundFamily,
1311 0 : needsBold, aWaitForUserFont));
1312 : }
1313 :
1314 0 : if (!fontEntry)
1315 0 : return NULL;
1316 :
1317 0 : return &fontEntry->GetPatterns();
1318 : }
1319 :
1320 : typedef FcBool (*FcPatternRemoveFunction)(FcPattern *p, const char *object,
1321 : int id);
1322 :
1323 : // FcPatternRemove is available in fontconfig-2.3.0 (2005)
1324 : static FcBool
1325 0 : moz_FcPatternRemove(FcPattern *p, const char *object, int id)
1326 : {
1327 : static FcPatternRemoveFunction sFcPatternRemovePtr =
1328 : reinterpret_cast<FcPatternRemoveFunction>
1329 0 : (FindFunctionSymbol("FcPatternRemove"));
1330 :
1331 0 : if (!sFcPatternRemovePtr)
1332 0 : return FcFalse;
1333 :
1334 0 : return (*sFcPatternRemovePtr)(p, object, id);
1335 : }
1336 :
1337 : // fontconfig always prefers a matching family to a matching slant, but CSS
1338 : // mostly prioritizes slant. The logic here is from CSS 2.1.
1339 : static bool
1340 0 : SlantIsAcceptable(FcPattern *aFont, int aRequestedSlant)
1341 : {
1342 : // CSS accepts (possibly synthetic) oblique for italic.
1343 0 : if (aRequestedSlant == FC_SLANT_ITALIC)
1344 0 : return true;
1345 :
1346 : int slant;
1347 0 : FcResult result = FcPatternGetInteger(aFont, FC_SLANT, 0, &slant);
1348 : // Not having a value would be strange.
1349 : // fontconfig sort and match functions would consider no value a match.
1350 0 : if (result != FcResultMatch)
1351 0 : return true;
1352 :
1353 0 : switch (aRequestedSlant) {
1354 : case FC_SLANT_ROMAN:
1355 : // CSS requires an exact match
1356 0 : return slant == aRequestedSlant;
1357 : case FC_SLANT_OBLIQUE:
1358 : // Accept synthetic oblique from Roman,
1359 : // but CSS doesn't accept italic.
1360 0 : return slant != FC_SLANT_ITALIC;
1361 : }
1362 :
1363 0 : return true;
1364 : }
1365 :
1366 : // fontconfig prefers a matching family or lang to pixelsize of bitmap
1367 : // fonts. CSS suggests a tolerance of 20% on pixelsize.
1368 : static bool
1369 0 : SizeIsAcceptable(FcPattern *aFont, double aRequestedSize)
1370 : {
1371 : double size;
1372 0 : int v = 0;
1373 0 : while (FcPatternGetDouble(aFont,
1374 0 : FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
1375 0 : ++v;
1376 0 : if (5.0 * fabs(size - aRequestedSize) < aRequestedSize)
1377 0 : return true;
1378 : }
1379 :
1380 : // No size means scalable
1381 0 : return v == 0;
1382 : }
1383 :
1384 : // Sorting only the preferred fonts first usually saves having to sort through
1385 : // every font on the system.
1386 : nsReturnRef<FcFontSet>
1387 0 : gfxFcFontSet::SortPreferredFonts(bool &aWaitForUserFont)
1388 : {
1389 0 : aWaitForUserFont = false;
1390 :
1391 0 : gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
1392 0 : if (!utils)
1393 0 : return nsReturnRef<FcFontSet>();
1394 :
1395 : // The list of families in mSortPattern has values with both weak and
1396 : // strong bindings. Values with strong bindings should be preferred.
1397 : // Values with weak bindings are default fonts that should be considered
1398 : // only when the font provides the best support for a requested language
1399 : // or after other fonts have satisfied all the requested languages.
1400 : //
1401 : // There are no direct fontconfig APIs to get the binding type. The
1402 : // binding only takes effect in the sort and match functions.
1403 :
1404 : // |requiredLangs| is a list of requested languages that have not yet been
1405 : // satisfied. gfxFontconfigUtils only sets one FC_LANG property value,
1406 : // but FcConfigSubstitute may add more values (e.g. prepending "en" to
1407 : // "ja" will use western fonts to render Latin/Arabic numerals in Japanese
1408 : // text.)
1409 0 : nsAutoTArray<LangSupportEntry,10> requiredLangs;
1410 0 : for (int v = 0; ; ++v) {
1411 : FcChar8 *lang;
1412 0 : FcResult result = FcPatternGetString(mSortPattern, FC_LANG, v, &lang);
1413 0 : if (result != FcResultMatch) {
1414 : // No need to check FcPatternGetLangSet() because
1415 : // gfxFontconfigUtils sets only a string value for FC_LANG and
1416 : // FcConfigSubstitute cannot add LangSets.
1417 0 : NS_ASSERTION(result != FcResultTypeMismatch,
1418 : "Expected a string for FC_LANG");
1419 : break;
1420 : }
1421 :
1422 0 : if (!requiredLangs.Contains(lang, LangComparator())) {
1423 0 : FcLangResult bestLangSupport = utils->GetBestLangSupport(lang);
1424 0 : if (bestLangSupport != FcLangDifferentLang) {
1425 : requiredLangs.
1426 0 : AppendElement(LangSupportEntry(lang, bestLangSupport));
1427 : }
1428 : }
1429 : }
1430 :
1431 0 : nsAutoRef<FcFontSet> fontSet(FcFontSetCreate());
1432 0 : if (!fontSet)
1433 0 : return fontSet.out();
1434 :
1435 : // FcDefaultSubstitute() ensures a slant on mSortPattern, but, if that ever
1436 : // doesn't happen, Roman will be used.
1437 0 : int requestedSlant = FC_SLANT_ROMAN;
1438 0 : FcPatternGetInteger(mSortPattern, FC_SLANT, 0, &requestedSlant);
1439 0 : double requestedSize = -1.0;
1440 0 : FcPatternGetDouble(mSortPattern, FC_PIXEL_SIZE, 0, &requestedSize);
1441 :
1442 0 : nsTHashtable<gfxFontconfigUtils::DepFcStrEntry> existingFamilies;
1443 0 : existingFamilies.Init(50);
1444 : FcChar8 *family;
1445 0 : for (int v = 0;
1446 : FcPatternGetString(mSortPattern,
1447 0 : FC_FAMILY, v, &family) == FcResultMatch; ++v) {
1448 0 : const nsTArray< nsCountedRef<FcPattern> > *familyFonts = nsnull;
1449 :
1450 : // Is this an @font-face family?
1451 0 : bool isUserFont = false;
1452 0 : if (mUserFontSet) {
1453 : // Have some @font-face definitions
1454 :
1455 0 : nsDependentCString cFamily(gfxFontconfigUtils::ToCString(family));
1456 0 : NS_NAMED_LITERAL_CSTRING(userPrefix, FONT_FACE_FAMILY_PREFIX);
1457 :
1458 0 : if (StringBeginsWith(cFamily, userPrefix)) {
1459 0 : isUserFont = true;
1460 :
1461 : // Trim off the prefix
1462 0 : nsDependentCSubstring cssFamily(cFamily, userPrefix.Length());
1463 :
1464 : PRUint8 thebesStyle =
1465 0 : gfxFontconfigUtils::FcSlantToThebesStyle(requestedSlant);
1466 : PRUint16 thebesWeight =
1467 0 : gfxFontconfigUtils::GetThebesWeight(mSortPattern);
1468 : PRInt16 thebesStretch =
1469 0 : gfxFontconfigUtils::GetThebesStretch(mSortPattern);
1470 :
1471 : bool foundFamily, waitForUserFont;
1472 : familyFonts = FindFontPatterns(mUserFontSet, cssFamily,
1473 : thebesStyle,
1474 : thebesWeight, thebesStretch,
1475 0 : foundFamily, waitForUserFont);
1476 0 : if (waitForUserFont) {
1477 0 : aWaitForUserFont = true;
1478 : }
1479 0 : NS_ASSERTION(foundFamily,
1480 : "expected to find a user font, but it's missing!");
1481 : }
1482 : }
1483 :
1484 0 : if (!isUserFont) {
1485 0 : familyFonts = &utils->GetFontsForFamily(family);
1486 : }
1487 :
1488 0 : if (!familyFonts || familyFonts->Length() == 0) {
1489 : // There are no fonts matching this family, so there is no point
1490 : // in searching for this family in the FontSort.
1491 : //
1492 : // Perhaps the original pattern should be retained for
1493 : // FcFontRenderPrepare. However, the only a useful config
1494 : // substitution test against missing families that i can imagine
1495 : // would only be interested in the preferred family
1496 : // (qual="first"), so always keep the first family and use the
1497 : // same pattern for Sort and RenderPrepare.
1498 0 : if (v != 0 && moz_FcPatternRemove(mSortPattern, FC_FAMILY, v)) {
1499 0 : --v;
1500 : }
1501 0 : continue;
1502 : }
1503 :
1504 : // Aliases seem to often end up occurring more than once, but
1505 : // duplicate families can't be removed from the sort pattern without
1506 : // knowing whether duplicates have the same binding.
1507 : gfxFontconfigUtils::DepFcStrEntry *entry =
1508 0 : existingFamilies.PutEntry(family);
1509 0 : if (entry) {
1510 0 : if (entry->mKey) // old entry
1511 0 : continue;
1512 :
1513 0 : entry->mKey = family; // initialize new entry
1514 : }
1515 :
1516 0 : for (PRUint32 f = 0; f < familyFonts->Length(); ++f) {
1517 0 : FcPattern *font = familyFonts->ElementAt(f);
1518 :
1519 : // User fonts are already filtered by slant (but not size) in
1520 : // mUserFontSet->FindFontEntry().
1521 0 : if (!isUserFont && !SlantIsAcceptable(font, requestedSlant))
1522 0 : continue;
1523 0 : if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
1524 0 : continue;
1525 :
1526 0 : for (PRUint32 r = 0; r < requiredLangs.Length(); ++r) {
1527 0 : const LangSupportEntry& entry = requiredLangs[r];
1528 : FcLangResult support =
1529 0 : gfxFontconfigUtils::GetLangSupport(font, entry.mLang);
1530 0 : if (support <= entry.mBestSupport) { // lower is better
1531 0 : requiredLangs.RemoveElementAt(r);
1532 0 : --r;
1533 : }
1534 : }
1535 :
1536 : // FcFontSetDestroy will remove a reference but FcFontSetAdd
1537 : // does _not_ take a reference!
1538 0 : if (FcFontSetAdd(fontSet, font)) {
1539 0 : FcPatternReference(font);
1540 : }
1541 : }
1542 : }
1543 :
1544 0 : FcPattern *truncateMarker = NULL;
1545 0 : for (PRUint32 r = 0; r < requiredLangs.Length(); ++r) {
1546 : const nsTArray< nsCountedRef<FcPattern> >& langFonts =
1547 0 : utils->GetFontsForLang(requiredLangs[r].mLang);
1548 :
1549 0 : bool haveLangFont = false;
1550 0 : for (PRUint32 f = 0; f < langFonts.Length(); ++f) {
1551 0 : FcPattern *font = langFonts[f];
1552 0 : if (!SlantIsAcceptable(font, requestedSlant))
1553 0 : continue;
1554 0 : if (requestedSize != -1.0 && !SizeIsAcceptable(font, requestedSize))
1555 0 : continue;
1556 :
1557 0 : haveLangFont = true;
1558 0 : if (FcFontSetAdd(fontSet, font)) {
1559 0 : FcPatternReference(font);
1560 : }
1561 : }
1562 :
1563 0 : if (!haveLangFont && langFonts.Length() > 0) {
1564 : // There is a font that supports this language but it didn't pass
1565 : // the slant and size criteria. Weak default font families should
1566 : // not be considered until the language has been satisfied.
1567 : //
1568 : // Insert a font that supports the language so that it will mark
1569 : // the position of fonts from weak families in the sorted set and
1570 : // they can be removed. The language and weak families will be
1571 : // considered in the fallback fonts, which use fontconfig's
1572 : // algorithm.
1573 : //
1574 : // Of the fonts that don't meet slant and size criteria, strong
1575 : // default font families should be considered before (other) fonts
1576 : // for this language, so this marker font will be removed (as well
1577 : // as the fonts from weak families), and strong families will be
1578 : // reconsidered in the fallback fonts.
1579 0 : FcPattern *font = langFonts[0];
1580 0 : if (FcFontSetAdd(fontSet, font)) {
1581 0 : FcPatternReference(font);
1582 0 : truncateMarker = font;
1583 : }
1584 0 : break;
1585 : }
1586 : }
1587 :
1588 0 : FcFontSet *sets[1] = { fontSet };
1589 : FcResult result;
1590 : #ifdef SOLARIS
1591 : // Get around a crash of FcFontSetSort when FcConfig is NULL
1592 : // Solaris's FcFontSetSort needs an FcConfig (bug 474758)
1593 : fontSet.own(FcFontSetSort(FcConfigGetCurrent(), sets, 1, mSortPattern,
1594 : FcFalse, NULL, &result));
1595 : #else
1596 : fontSet.own(FcFontSetSort(NULL, sets, 1, mSortPattern,
1597 0 : FcFalse, NULL, &result));
1598 : #endif
1599 :
1600 0 : if (truncateMarker != NULL && fontSet) {
1601 0 : nsAutoRef<FcFontSet> truncatedSet(FcFontSetCreate());
1602 :
1603 0 : for (int f = 0; f < fontSet->nfont; ++f) {
1604 0 : FcPattern *font = fontSet->fonts[f];
1605 0 : if (font == truncateMarker)
1606 0 : break;
1607 :
1608 0 : if (FcFontSetAdd(truncatedSet, font)) {
1609 0 : FcPatternReference(font);
1610 : }
1611 : }
1612 :
1613 0 : fontSet.steal(truncatedSet);
1614 : }
1615 :
1616 0 : return fontSet.out();
1617 : }
1618 :
1619 : nsReturnRef<FcFontSet>
1620 0 : gfxFcFontSet::SortFallbackFonts()
1621 : {
1622 : // Setting trim to FcTrue would provide a much smaller (~ 1/10) FcFontSet,
1623 : // but would take much longer due to comparing all the character sets.
1624 : //
1625 : // The references to fonts in this FcFontSet are almost free
1626 : // as they are pointers into mmaped cache files.
1627 : //
1628 : // GetFontPatternAt() will trim lazily if and as needed, which will also
1629 : // remove duplicates of preferred fonts.
1630 : FcResult result;
1631 : return nsReturnRef<FcFontSet>(FcFontSort(NULL, mSortPattern,
1632 0 : FcFalse, NULL, &result));
1633 : }
1634 :
1635 : // GetFontAt relies on this setting up all patterns up to |i|.
1636 : FcPattern *
1637 0 : gfxFcFontSet::GetFontPatternAt(PRUint32 i)
1638 : {
1639 0 : while (i >= mFonts.Length()) {
1640 0 : while (!mFcFontSet) {
1641 0 : if (mHaveFallbackFonts)
1642 0 : return nsnull;
1643 :
1644 0 : mFcFontSet = SortFallbackFonts();
1645 0 : mHaveFallbackFonts = true;
1646 0 : mFcFontsTrimmed = 0;
1647 : // Loop to test that mFcFontSet is non-NULL.
1648 : }
1649 :
1650 0 : while (mFcFontsTrimmed < mFcFontSet->nfont) {
1651 0 : FcPattern *font = mFcFontSet->fonts[mFcFontsTrimmed];
1652 0 : ++mFcFontsTrimmed;
1653 :
1654 0 : if (mFonts.Length() != 0) {
1655 : // See if the next font provides support for any extra
1656 : // characters. Most often the next font is not going to
1657 : // support more characters so check for a SubSet first before
1658 : // allocating a new CharSet with Union.
1659 0 : FcCharSet *supportedChars = mCharSet;
1660 0 : if (!supportedChars) {
1661 0 : FcPatternGetCharSet(mFonts[mFonts.Length() - 1].mPattern,
1662 0 : FC_CHARSET, 0, &supportedChars);
1663 : }
1664 :
1665 0 : if (supportedChars) {
1666 0 : FcCharSet *newChars = NULL;
1667 0 : FcPatternGetCharSet(font, FC_CHARSET, 0, &newChars);
1668 0 : if (newChars) {
1669 0 : if (FcCharSetIsSubset(newChars, supportedChars))
1670 0 : continue;
1671 :
1672 0 : mCharSet.own(FcCharSetUnion(supportedChars, newChars));
1673 0 : } else if (!mCharSet) {
1674 0 : mCharSet.own(FcCharSetCopy(supportedChars));
1675 : }
1676 : }
1677 : }
1678 :
1679 0 : mFonts.AppendElement(font);
1680 0 : if (mFonts.Length() >= i)
1681 0 : break;
1682 : }
1683 :
1684 0 : if (mFcFontsTrimmed == mFcFontSet->nfont) {
1685 : // finished with this font set
1686 0 : mFcFontSet.reset();
1687 : }
1688 : }
1689 :
1690 0 : return mFonts[i].mPattern;
1691 : }
1692 :
1693 : /**
1694 : * gfxPangoFontMap: An implementation of a PangoFontMap.
1695 : *
1696 : * This is a PangoFcFontMap for gfxPangoFcFont. It will only ever be used if
1697 : * some day pango_cairo_font_map_get_default() does not return a
1698 : * PangoFcFontMap.
1699 : */
1700 :
1701 : #define GFX_TYPE_PANGO_FONT_MAP (gfx_pango_font_map_get_type())
1702 : #define GFX_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMap))
1703 : #define GFX_IS_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONT_MAP))
1704 :
1705 : GType gfx_pango_font_map_get_type (void);
1706 :
1707 : #define GFX_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
1708 : #define GFX_IS_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONT_MAP))
1709 : #define GFX_PANGO_FONT_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
1710 :
1711 : // Do not instantiate this class directly, but use NewFontMap.
1712 : // This struct is POD so that it can be used as a GObject.
1713 : struct gfxPangoFontMap {
1714 : PangoFcFontMap parent_instance;
1715 :
1716 : static PangoFontMap *
1717 0 : NewFontMap()
1718 : {
1719 : gfxPangoFontMap *fontmap = static_cast<gfxPangoFontMap *>
1720 0 : (g_object_new(GFX_TYPE_PANGO_FONT_MAP, NULL));
1721 :
1722 0 : return PANGO_FONT_MAP(fontmap);
1723 : }
1724 : };
1725 :
1726 : struct gfxPangoFontMapClass {
1727 : PangoFcFontMapClass parent_class;
1728 : };
1729 :
1730 0 : G_DEFINE_TYPE (gfxPangoFontMap, gfx_pango_font_map, PANGO_TYPE_FC_FONT_MAP)
1731 :
1732 : static void
1733 0 : gfx_pango_font_map_init(gfxPangoFontMap *fontset)
1734 : {
1735 0 : }
1736 :
1737 : static PangoFcFont *
1738 0 : gfx_pango_font_map_new_font(PangoFcFontMap *fontmap,
1739 : FcPattern *pattern)
1740 : {
1741 : // new_font is not likely to be used, but the class makes the method
1742 : // available and shapers have access to the class through the font. Not
1743 : // bothering to make an effort here because this will only ever be used if
1744 : // pango_cairo_font_map_get_default() does not return a PangoFcFontMap and
1745 : // a shaper tried to create a different font from the one it was provided.
1746 : // Only a basic implementation is provided that simply refuses to
1747 : // create a new font. PangoFcFontMap allows NULL return values here.
1748 0 : return NULL;
1749 : }
1750 :
1751 : static void
1752 0 : gfx_pango_font_map_class_init(gfxPangoFontMapClass *klass)
1753 : {
1754 : // inherit GObjectClass::finalize from parent as this class adds no data.
1755 :
1756 : // inherit PangoFontMap::load_font (which is not likely to be used)
1757 : // from PangoFcFontMap
1758 : // inherit PangoFontMap::list_families (which is not likely to be used)
1759 : // from PangoFcFontMap
1760 : // inherit PangoFontMap::load_fontset (which is not likely to be used)
1761 : // from PangoFcFontMap
1762 : // inherit PangoFontMap::shape_engine_type from PangoFcFontMap
1763 :
1764 0 : PangoFcFontMapClass *fcfontmap_class = PANGO_FC_FONT_MAP_CLASS (klass);
1765 : // default_substitute is not required.
1766 : // The API for create_font changed between Pango 1.22 and 1.24 so new_font
1767 : // is provided instead.
1768 0 : fcfontmap_class->new_font = gfx_pango_font_map_new_font;
1769 : // get_resolution is not required.
1770 : // context_key_* virtual functions are only necessary if we want to
1771 : // dynamically respond to changes in the screen cairo_font_options_t.
1772 0 : }
1773 :
1774 : #ifdef MOZ_WIDGET_GTK2
1775 : static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
1776 : #endif
1777 :
1778 : // Apply user settings and defaults to pattern in preparation for matching.
1779 : static void
1780 0 : PrepareSortPattern(FcPattern *aPattern, double aFallbackSize,
1781 : double aSizeAdjustFactor, bool aIsPrinterFont)
1782 : {
1783 0 : FcConfigSubstitute(NULL, aPattern, FcMatchPattern);
1784 :
1785 : // This gets cairo_font_options_t for the Screen. We should have
1786 : // different font options for printing (no hinting) but we are not told
1787 : // what we are measuring for.
1788 : //
1789 : // If cairo adds support for lcd_filter, gdk will not provide the default
1790 : // setting for that option. We could get the default setting by creating
1791 : // an xlib surface once, recording its font_options, and then merging the
1792 : // gdk options.
1793 : //
1794 : // Using an xlib surface would also be an option to get Screen font
1795 : // options for non-GTK X11 toolkits, but less efficient than using GDK to
1796 : // pick up dynamic changes.
1797 0 : if(aIsPrinterFont) {
1798 0 : cairo_font_options_t *options = cairo_font_options_create();
1799 0 : cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
1800 0 : cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
1801 0 : cairo_ft_font_options_substitute(options, aPattern);
1802 0 : cairo_font_options_destroy(options);
1803 0 : FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
1804 : } else {
1805 : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
1806 : cairo_font_options_t *options = cairo_font_options_create();
1807 : cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
1808 : cairo_ft_font_options_substitute(options, aPattern);
1809 : cairo_font_options_destroy(options);
1810 : #endif
1811 : #ifdef MOZ_WIDGET_GTK2
1812 0 : ApplyGdkScreenFontOptions(aPattern);
1813 : #endif
1814 : }
1815 :
1816 : // Protect against any fontconfig settings that may have incorrectly
1817 : // modified the pixelsize, and consider aSizeAdjustFactor.
1818 0 : double size = aFallbackSize;
1819 0 : if (FcPatternGetDouble(aPattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch
1820 : || aSizeAdjustFactor != 1.0) {
1821 0 : FcPatternDel(aPattern, FC_PIXEL_SIZE);
1822 0 : FcPatternAddDouble(aPattern, FC_PIXEL_SIZE, size * aSizeAdjustFactor);
1823 : }
1824 :
1825 0 : FcDefaultSubstitute(aPattern);
1826 0 : }
1827 :
1828 : /**
1829 : ** gfxPangoFontGroup
1830 : **/
1831 :
1832 : struct FamilyCallbackData {
1833 0 : FamilyCallbackData(nsTArray<nsString> *aFcFamilyList,
1834 : gfxUserFontSet *aUserFontSet)
1835 0 : : mFcFamilyList(aFcFamilyList), mUserFontSet(aUserFontSet)
1836 : {
1837 0 : }
1838 : nsTArray<nsString> *mFcFamilyList;
1839 : const gfxUserFontSet *mUserFontSet;
1840 : };
1841 :
1842 : static int
1843 0 : FFRECountHyphens (const nsAString &aFFREName)
1844 : {
1845 0 : int h = 0;
1846 0 : PRInt32 hyphen = 0;
1847 0 : while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) {
1848 0 : ++h;
1849 0 : ++hyphen;
1850 : }
1851 0 : return h;
1852 : }
1853 :
1854 : static bool
1855 0 : FamilyCallback (const nsAString& fontName, const nsACString& genericName,
1856 : bool aUseFontSet, void *closure)
1857 : {
1858 0 : FamilyCallbackData *data = static_cast<FamilyCallbackData*>(closure);
1859 0 : nsTArray<nsString> *list = data->mFcFamilyList;
1860 :
1861 : // We ignore prefs that have three hypens since they are X style prefs.
1862 0 : if (genericName.Length() && FFRECountHyphens(fontName) >= 3)
1863 0 : return true;
1864 :
1865 0 : if (!list->Contains(fontName)) {
1866 : // The family properties of FcPatterns for @font-face fonts have a
1867 : // namespace to identify them among system fonts. (see
1868 : // FONT_FACE_FAMILY_PREFIX.)
1869 : //
1870 : // Earlier versions of this code allowed the CSS family name to match
1871 : // either the @font-face family or the system font family, so both
1872 : // were added here. This was in accordance with earlier versions of
1873 : // the W3C specifications regarding @font-face.
1874 : //
1875 : // The current (2011-02-27) draft of CSS3 Fonts says
1876 : //
1877 : // (Section 4.2: Font family: the font-family descriptor):
1878 : // "If the font family name is the same as a font family available in
1879 : // a given user's environment, it effectively hides the underlying
1880 : // font for documents that use the stylesheet."
1881 : //
1882 : // (Section 5: Font matching algorithm)
1883 : // "... the user agent attempts to find the family name among fonts
1884 : // defined via @font-face rules and then among available system fonts,
1885 : // .... If a font family defined via @font-face rules contains only
1886 : // invalid font data, it should be considered as if a font was present
1887 : // but contained an empty character map; matching a platform font with
1888 : // the same name must not occur in this case."
1889 : //
1890 : // Therefore, for names present in the user font set, this code no
1891 : // longer includes the family name for matching against system fonts.
1892 : //
1893 0 : const gfxUserFontSet *userFontSet = data->mUserFontSet;
1894 0 : if (aUseFontSet && genericName.Length() == 0 &&
1895 0 : userFontSet && userFontSet->HasFamily(fontName)) {
1896 : nsAutoString userFontName =
1897 0 : NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
1898 0 : list->AppendElement(userFontName);
1899 : } else {
1900 0 : list->AppendElement(fontName);
1901 : }
1902 : }
1903 :
1904 0 : return true;
1905 : }
1906 :
1907 0 : gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families,
1908 : const gfxFontStyle *aStyle,
1909 : gfxUserFontSet *aUserFontSet)
1910 : : gfxFontGroup(families, aStyle, aUserFontSet),
1911 0 : mPangoLanguage(GuessPangoLanguage(aStyle->language))
1912 : {
1913 : // This language is passed to the font for shaping.
1914 : // Shaping doesn't know about lang groups so make it a real language.
1915 0 : if (mPangoLanguage) {
1916 0 : mStyle.language = do_GetAtom(pango_language_to_string(mPangoLanguage));
1917 : }
1918 :
1919 0 : mFonts.AppendElements(1);
1920 0 : }
1921 :
1922 0 : gfxPangoFontGroup::~gfxPangoFontGroup()
1923 : {
1924 0 : }
1925 :
1926 : gfxFontGroup *
1927 0 : gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
1928 : {
1929 0 : return new gfxPangoFontGroup(mFamilies, aStyle, mUserFontSet);
1930 : }
1931 :
1932 : // An array of family names suitable for fontconfig
1933 : void
1934 0 : gfxPangoFontGroup::GetFcFamilies(nsTArray<nsString> *aFcFamilyList,
1935 : nsIAtom *aLanguage)
1936 : {
1937 0 : FamilyCallbackData data(aFcFamilyList, mUserFontSet);
1938 : // Leave non-existing fonts in the list so that fontconfig can get the
1939 : // best match.
1940 : ForEachFontInternal(mFamilies, aLanguage, true, false, true,
1941 0 : FamilyCallback, &data);
1942 0 : }
1943 :
1944 : gfxFcFont *
1945 0 : gfxPangoFontGroup::GetBaseFont()
1946 : {
1947 0 : if (!mFonts[0]) {
1948 0 : mFonts[0] = GetBaseFontSet()->GetFontAt(0, GetStyle());
1949 : }
1950 :
1951 0 : return static_cast<gfxFcFont*>(mFonts[0].get());
1952 : }
1953 :
1954 : gfxFont *
1955 0 : gfxPangoFontGroup::GetFontAt(PRInt32 i) {
1956 : // If it turns out to be hard for all clients that cache font
1957 : // groups to call UpdateFontList at appropriate times, we could
1958 : // instead consider just calling UpdateFontList from someplace
1959 : // more central (such as here).
1960 0 : NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
1961 : "Whoever was caching this font group should have "
1962 : "called UpdateFontList on it");
1963 :
1964 0 : NS_PRECONDITION(i == 0, "Only have one font");
1965 :
1966 0 : return GetBaseFont();
1967 : }
1968 :
1969 : void
1970 0 : gfxPangoFontGroup::UpdateFontList()
1971 : {
1972 0 : if (!mUserFontSet)
1973 0 : return;
1974 :
1975 0 : PRUint64 newGeneration = mUserFontSet->GetGeneration();
1976 0 : if (newGeneration == mCurrGeneration)
1977 0 : return;
1978 :
1979 0 : mFonts[0] = NULL;
1980 0 : mFontSets.Clear();
1981 0 : mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
1982 0 : mCurrGeneration = newGeneration;
1983 0 : mSkipDrawing = false;
1984 : }
1985 :
1986 : already_AddRefed<gfxFcFontSet>
1987 0 : gfxPangoFontGroup::MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
1988 : nsAutoRef<FcPattern> *aMatchPattern)
1989 : {
1990 0 : const char *lang = pango_language_to_string(aLang);
1991 :
1992 0 : nsRefPtr <nsIAtom> langGroup;
1993 0 : if (aLang != mPangoLanguage) {
1994 : // Set up langGroup for Mozilla's font prefs.
1995 0 : langGroup = do_GetAtom(lang);
1996 : }
1997 :
1998 0 : nsAutoTArray<nsString, 20> fcFamilyList;
1999 : GetFcFamilies(&fcFamilyList,
2000 0 : langGroup ? langGroup.get() : mStyle.language.get());
2001 :
2002 : // To consider: A fontset cache here could be helpful.
2003 :
2004 : // Get a pattern suitable for matching.
2005 : nsAutoRef<FcPattern> pattern
2006 0 : (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
2007 :
2008 0 : PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont);
2009 :
2010 : nsRefPtr<gfxFcFontSet> fontset =
2011 0 : new gfxFcFontSet(pattern, mUserFontSet);
2012 :
2013 0 : mSkipDrawing = fontset->WaitingForUserFont();
2014 :
2015 0 : if (aMatchPattern)
2016 0 : aMatchPattern->steal(pattern);
2017 :
2018 0 : return fontset.forget();
2019 : }
2020 :
2021 0 : gfxPangoFontGroup::
2022 : FontSetByLangEntry::FontSetByLangEntry(PangoLanguage *aLang,
2023 : gfxFcFontSet *aFontSet)
2024 0 : : mLang(aLang), mFontSet(aFontSet)
2025 : {
2026 0 : }
2027 :
2028 : gfxFcFontSet *
2029 0 : gfxPangoFontGroup::GetFontSet(PangoLanguage *aLang)
2030 : {
2031 0 : GetBaseFontSet(); // sets mSizeAdjustFactor and mFontSets[0]
2032 :
2033 0 : if (!aLang)
2034 0 : return mFontSets[0].mFontSet;
2035 :
2036 0 : for (PRUint32 i = 0; i < mFontSets.Length(); ++i) {
2037 0 : if (mFontSets[i].mLang == aLang)
2038 0 : return mFontSets[i].mFontSet;
2039 : }
2040 :
2041 : nsRefPtr<gfxFcFontSet> fontSet =
2042 0 : MakeFontSet(aLang, mSizeAdjustFactor);
2043 0 : mFontSets.AppendElement(FontSetByLangEntry(aLang, fontSet));
2044 :
2045 0 : return fontSet;
2046 : }
2047 :
2048 : already_AddRefed<gfxFont>
2049 0 : gfxPangoFontGroup::FindFontForChar(PRUint32 aCh, PRUint32 aPrevCh,
2050 : PRInt32 aRunScript,
2051 : gfxFont *aPrevMatchedFont,
2052 : PRUint8 *aMatchType)
2053 : {
2054 0 : if (aPrevMatchedFont) {
2055 : // Don't switch fonts for control characters, regardless of
2056 : // whether they are present in the current font, as they won't
2057 : // actually be rendered (see bug 716229)
2058 0 : PRUint8 category = GetGeneralCategory(aCh);
2059 0 : if (category == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
2060 0 : return nsRefPtr<gfxFont>(aPrevMatchedFont).forget();
2061 : }
2062 :
2063 : // if this character is a join-control or the previous is a join-causer,
2064 : // use the same font as the previous range if we can
2065 0 : if (gfxFontUtils::IsJoinControl(aCh) ||
2066 0 : gfxFontUtils::IsJoinCauser(aPrevCh)) {
2067 0 : if (aPrevMatchedFont->HasCharacter(aCh)) {
2068 0 : return nsRefPtr<gfxFont>(aPrevMatchedFont).forget();
2069 : }
2070 : }
2071 : }
2072 :
2073 : // if this character is a variation selector,
2074 : // use the previous font regardless of whether it supports VS or not.
2075 : // otherwise the text run will be divided.
2076 0 : if (gfxFontUtils::IsVarSelector(aCh)) {
2077 0 : if (aPrevMatchedFont) {
2078 0 : return nsRefPtr<gfxFont>(aPrevMatchedFont).forget();
2079 : }
2080 : // VS alone. it's meaningless to search different fonts
2081 0 : return nsnull;
2082 : }
2083 :
2084 : // The real fonts that fontconfig provides for generic/fallback families
2085 : // depend on the language used, so a different FontSet is used for each
2086 : // language (except for the variation below).
2087 : //
2088 : // With most fontconfig configurations any real family names prior to a
2089 : // fontconfig generic with corresponding fonts installed will still lead
2090 : // to the same leading fonts in each FontSet.
2091 : //
2092 : // There is an inefficiency here therefore because the same base FontSet
2093 : // could often be used if these real families support the character.
2094 : // However, with fontconfig aliases, it is difficult to distinguish
2095 : // where exactly alias fonts end and generic/fallback fonts begin.
2096 : //
2097 : // The variation from pure language-based matching used here is that the
2098 : // same primary/base font is always used irrespective of the language.
2099 : // This provides that SCRIPT_COMMON characters are consistently rendered
2100 : // with the same font (bug 339513 and bug 416725). This is particularly
2101 : // important with the word cache as script can't be reliably determined
2102 : // from surrounding words. It also often avoids the unnecessary extra
2103 : // FontSet efficiency mentioned above.
2104 : //
2105 : // However, in two situations, the base font is not checked before the
2106 : // language-specific FontSet.
2107 : //
2108 : // 1. When we don't have a language to make a good choice for
2109 : // the base font.
2110 : //
2111 : // 2. For system fonts, use the default Pango behavior to give
2112 : // consistency with other apps. This is relevant when un-localized
2113 : // builds are run in non-Latin locales. This special-case probably
2114 : // wouldn't be necessary but for bug 91190.
2115 :
2116 0 : gfxFcFontSet *fontSet = GetBaseFontSet();
2117 0 : PRUint32 nextFont = 0;
2118 0 : FcPattern *basePattern = NULL;
2119 0 : if (!mStyle.systemFont && mPangoLanguage) {
2120 0 : basePattern = fontSet->GetFontPatternAt(0);
2121 0 : if (HasChar(basePattern, aCh)) {
2122 0 : *aMatchType = gfxTextRange::kFontGroup;
2123 0 : return nsRefPtr<gfxFont>(GetBaseFont()).forget();
2124 : }
2125 :
2126 0 : nextFont = 1;
2127 : }
2128 :
2129 : // Pango, GLib, and Thebes (but not harfbuzz!) all happen to use the same
2130 : // script codes, so we can just cast the value here.
2131 0 : const PangoScript script = static_cast<PangoScript>(aRunScript);
2132 : // Might be nice to call pango_language_includes_script only once for the
2133 : // run rather than for each character.
2134 : PangoLanguage *scriptLang;
2135 0 : if ((!basePattern ||
2136 0 : !pango_language_includes_script(mPangoLanguage, script)) &&
2137 : (scriptLang = pango_script_get_sample_language(script))) {
2138 0 : fontSet = GetFontSet(scriptLang);
2139 0 : nextFont = 0;
2140 : }
2141 :
2142 0 : for (PRUint32 i = nextFont;
2143 0 : FcPattern *pattern = fontSet->GetFontPatternAt(i);
2144 : ++i) {
2145 0 : if (pattern == basePattern) {
2146 0 : continue; // already checked basePattern
2147 : }
2148 :
2149 0 : if (HasChar(pattern, aCh)) {
2150 0 : *aMatchType = gfxTextRange::kFontGroup;
2151 0 : return nsRefPtr<gfxFont>(fontSet->GetFontAt(i, GetStyle())).forget();
2152 : }
2153 : }
2154 :
2155 0 : return nsnull;
2156 : }
2157 :
2158 : // Sanity-check: spot-check a few constants to confirm that Thebes and
2159 : // Pango script codes really do match
2160 : PR_STATIC_ASSERT(MOZ_SCRIPT_COMMON == PANGO_SCRIPT_COMMON);
2161 : PR_STATIC_ASSERT(MOZ_SCRIPT_INHERITED == PANGO_SCRIPT_INHERITED);
2162 : PR_STATIC_ASSERT(MOZ_SCRIPT_ARABIC == PANGO_SCRIPT_ARABIC);
2163 : PR_STATIC_ASSERT(MOZ_SCRIPT_LATIN == PANGO_SCRIPT_LATIN);
2164 : PR_STATIC_ASSERT(MOZ_SCRIPT_UNKNOWN == PANGO_SCRIPT_UNKNOWN);
2165 : PR_STATIC_ASSERT(MOZ_SCRIPT_NKO == PANGO_SCRIPT_NKO);
2166 :
2167 : /**
2168 : ** gfxFcFont
2169 : **/
2170 :
2171 : cairo_user_data_key_t gfxFcFont::sGfxFontKey;
2172 :
2173 0 : gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
2174 : gfxFcFontEntry *aFontEntry,
2175 : const gfxFontStyle *aFontStyle)
2176 : : gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle),
2177 0 : mPangoFont()
2178 : {
2179 0 : cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, this, NULL);
2180 0 : }
2181 :
2182 : // The gfxFcFont keeps (only) a toggle_ref on mPangoFont.
2183 : // While mPangoFont has other references, a reference to the
2184 : // gfxFcFont is held. While mPangoFont has no other references, the reference
2185 : // to the gfxFcFont is removed.
2186 : static void
2187 0 : PangoFontToggleNotify(gpointer data, GObject* object, gboolean is_last_ref)
2188 : {
2189 0 : gfxFcFont *font = static_cast<gfxFcFont*>(data);
2190 0 : if (is_last_ref) { // gfxFcFont has last ref to PangoFont
2191 0 : NS_RELEASE(font);
2192 : } else {
2193 0 : NS_ADDREF(font);
2194 : }
2195 0 : }
2196 :
2197 : void
2198 0 : gfxFcFont::MakePangoFont()
2199 : {
2200 : // Switch from a normal reference to a toggle_ref.
2201 0 : gfxFcFontEntry *fe = static_cast<gfxFcFontEntry*>(mFontEntry.get());
2202 : nsAutoRef<PangoFont> pangoFont
2203 0 : (gfxPangoFcFont::NewFont(this, fe->GetPatterns()[0]));
2204 0 : mPangoFont = pangoFont;
2205 0 : g_object_add_toggle_ref(G_OBJECT(mPangoFont), PangoFontToggleNotify, this);
2206 : // This self-reference gets removed when the normal reference to the
2207 : // PangoFont is removed as the nsAutoRef goes out of scope.
2208 0 : NS_ADDREF(this);
2209 0 : }
2210 :
2211 0 : gfxFcFont::~gfxFcFont()
2212 : {
2213 0 : cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, NULL, NULL);
2214 0 : if (mPangoFont) {
2215 0 : g_object_remove_toggle_ref(G_OBJECT(mPangoFont),
2216 0 : PangoFontToggleNotify, this);
2217 : }
2218 0 : }
2219 :
2220 : bool
2221 0 : gfxFcFont::ShapeWord(gfxContext *aContext,
2222 : gfxShapedWord *aShapedWord,
2223 : const PRUnichar *aString,
2224 : bool aPreferPlatformShaping)
2225 : {
2226 0 : gfxFcFontEntry *fontEntry = static_cast<gfxFcFontEntry*>(GetFontEntry());
2227 :
2228 : #ifdef MOZ_GRAPHITE
2229 0 : if (FontCanSupportGraphite()) {
2230 0 : if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
2231 0 : if (!mGraphiteShaper) {
2232 0 : mGraphiteShaper = new gfxGraphiteShaper(this);
2233 : }
2234 0 : if (mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString)) {
2235 0 : return true;
2236 : }
2237 : }
2238 : }
2239 : #endif
2240 :
2241 0 : if (fontEntry->ShouldUseHarfBuzz(aShapedWord->Script())) {
2242 0 : if (!mHarfBuzzShaper) {
2243 0 : gfxFT2LockedFace face(this);
2244 0 : mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
2245 : // Used by gfxHarfBuzzShaper, currently only for kerning
2246 0 : mFUnitsConvFactor = face.XScale();
2247 : }
2248 0 : if (mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString)) {
2249 0 : return true;
2250 : }
2251 :
2252 : // Wrong font type for HarfBuzz
2253 0 : fontEntry->SkipHarfBuzz();
2254 0 : mHarfBuzzShaper = nsnull;
2255 : }
2256 :
2257 0 : bool ok = InitGlyphRunWithPango(aShapedWord, aString);
2258 :
2259 0 : NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
2260 0 : return ok;
2261 : }
2262 :
2263 : /* static */ void
2264 3 : gfxPangoFontGroup::Shutdown()
2265 : {
2266 3 : if (gPangoFontMap) {
2267 0 : g_object_unref(gPangoFontMap);
2268 0 : gPangoFontMap = NULL;
2269 : }
2270 :
2271 : // Resetting gFTLibrary in case this is wanted again after a
2272 : // cairo_debug_reset_static_data.
2273 3 : gFTLibrary = NULL;
2274 3 : }
2275 :
2276 : /* static */ gfxFontEntry *
2277 0 : gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
2278 : const nsAString& aFullname)
2279 : {
2280 0 : gfxFontconfigUtils *utils = gfxFontconfigUtils::GetFontconfigUtils();
2281 0 : if (!utils)
2282 0 : return nsnull;
2283 :
2284 : // The font face name from @font-face { src: local() } is not well
2285 : // defined.
2286 : //
2287 : // On MS Windows, this name gets compared with
2288 : // ENUMLOGFONTEXW::elfFullName, which for OpenType fonts seems to be the
2289 : // full font name from the name table. For CFF OpenType fonts this is the
2290 : // same as the PostScript name, but for TrueType fonts it is usually
2291 : // different.
2292 : //
2293 : // On Mac, the font face name is compared with the PostScript name, even
2294 : // for TrueType fonts.
2295 : //
2296 : // Fontconfig only records the full font names, so the behavior here
2297 : // follows that on MS Windows. However, to provide the possibility
2298 : // of aliases to compensate for variations, the font face name is passed
2299 : // through FcConfigSubstitute.
2300 :
2301 0 : nsAutoRef<FcPattern> pattern(FcPatternCreate());
2302 0 : if (!pattern)
2303 0 : return nsnull;
2304 :
2305 0 : NS_ConvertUTF16toUTF8 fullname(aFullname);
2306 : FcPatternAddString(pattern, FC_FULLNAME,
2307 0 : gfxFontconfigUtils::ToFcChar8(fullname));
2308 0 : FcConfigSubstitute(NULL, pattern, FcMatchPattern);
2309 :
2310 : FcChar8 *name;
2311 0 : for (int v = 0;
2312 0 : FcPatternGetString(pattern, FC_FULLNAME, v, &name) == FcResultMatch;
2313 : ++v) {
2314 : const nsTArray< nsCountedRef<FcPattern> >& fonts =
2315 0 : utils->GetFontsForFullname(name);
2316 :
2317 0 : if (fonts.Length() != 0)
2318 0 : return new gfxLocalFcFontEntry(aProxyEntry, fonts);
2319 : }
2320 :
2321 0 : return nsnull;
2322 : }
2323 :
2324 : /* static */ FT_Library
2325 0 : gfxPangoFontGroup::GetFTLibrary()
2326 : {
2327 0 : if (!gFTLibrary) {
2328 : // Use cairo's FT_Library so that cairo takes care of shutdown of the
2329 : // FT_Library after it has destroyed its font_faces, and FT_Done_Face
2330 : // has been called on each FT_Face, at least until this bug is fixed:
2331 : // https://bugs.freedesktop.org/show_bug.cgi?id=18857
2332 : //
2333 : // Cairo's FT_Library can be obtained from any cairo_scaled_font. The
2334 : // font properties requested here are chosen to get an FT_Face that is
2335 : // likely to be also used elsewhere.
2336 0 : gfxFontStyle style;
2337 : nsRefPtr<gfxPangoFontGroup> fontGroup =
2338 0 : new gfxPangoFontGroup(NS_LITERAL_STRING("sans-serif"),
2339 0 : &style, nsnull);
2340 :
2341 0 : gfxFcFont *font = fontGroup->GetBaseFont();
2342 0 : if (!font)
2343 0 : return NULL;
2344 :
2345 0 : gfxFT2LockedFace face(font);
2346 0 : if (!face.get())
2347 0 : return NULL;
2348 :
2349 0 : gFTLibrary = face.get()->glyph->library;
2350 : }
2351 :
2352 0 : return gFTLibrary;
2353 : }
2354 :
2355 : /* static */ gfxFontEntry *
2356 0 : gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
2357 : const PRUint8 *aFontData, PRUint32 aLength)
2358 : {
2359 : // Ownership of aFontData is passed in here, and transferred to the
2360 : // new fontEntry, which will release it when no longer needed.
2361 :
2362 : // Using face_index = 0 for the first face in the font, as we have no
2363 : // other information. FT_New_Memory_Face checks for a NULL FT_Library.
2364 : FT_Face face;
2365 : FT_Error error =
2366 0 : FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
2367 0 : if (error != 0) {
2368 0 : NS_Free((void*)aFontData);
2369 0 : return nsnull;
2370 : }
2371 :
2372 0 : return new gfxDownloadedFcFontEntry(aProxyEntry, aFontData, face);
2373 : }
2374 :
2375 :
2376 : static double
2377 0 : GetPixelSize(FcPattern *aPattern)
2378 : {
2379 : double size;
2380 0 : if (FcPatternGetDouble(aPattern,
2381 0 : FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
2382 0 : return size;
2383 :
2384 0 : NS_NOTREACHED("No size on pattern");
2385 0 : return 0.0;
2386 : }
2387 :
2388 : /**
2389 : * The following gfxFcFonts are accessed from the cairo_scaled_font or created
2390 : * from the FcPattern, not from the gfxFontCache hash table. The gfxFontCache
2391 : * hash table is keyed by desired family and style, whereas here we only know
2392 : * actual family and style. There may be more than one of these fonts with
2393 : * the same family and style, but different PangoFont and actual font face.
2394 : *
2395 : * The point of this is to record the exact font face for gfxTextRun glyph
2396 : * indices. The style of this font does not necessarily represent the exact
2397 : * gfxFontStyle used to build the text run. Notably, the language is not
2398 : * recorded.
2399 : */
2400 :
2401 : /* static */
2402 : already_AddRefed<gfxFcFont>
2403 0 : gfxFcFont::GetOrMakeFont(FcPattern *aRequestedPattern, FcPattern *aFontPattern,
2404 : const gfxFontStyle *aFontStyle)
2405 : {
2406 : nsAutoRef<FcPattern> renderPattern
2407 0 : (FcFontRenderPrepare(NULL, aRequestedPattern, aFontPattern));
2408 : cairo_font_face_t *face =
2409 0 : cairo_ft_font_face_create_for_pattern(renderPattern);
2410 :
2411 : // Reuse an existing font entry if available.
2412 0 : nsRefPtr<gfxFcFontEntry> fe = gfxFcFontEntry::LookupFontEntry(face);
2413 0 : if (!fe) {
2414 : gfxDownloadedFcFontEntry *downloadedFontEntry =
2415 0 : GetDownloadedFontEntry(aFontPattern);
2416 0 : if (downloadedFontEntry) {
2417 : // Web font
2418 0 : fe = downloadedFontEntry;
2419 0 : if (cairo_font_face_status(face) == CAIRO_STATUS_SUCCESS) {
2420 : // cairo_font_face_t is using the web font data.
2421 : // Hold a reference to the font entry to keep the font face
2422 : // data.
2423 0 : if (!downloadedFontEntry->SetCairoFace(face)) {
2424 : // OOM. Let cairo pick a fallback font
2425 0 : cairo_font_face_destroy(face);
2426 0 : face = cairo_ft_font_face_create_for_pattern(aRequestedPattern);
2427 0 : fe = gfxFcFontEntry::LookupFontEntry(face);
2428 : }
2429 : }
2430 : }
2431 0 : if (!fe) {
2432 : // Get a unique name for the font face from the file and id.
2433 0 : nsAutoString name;
2434 : FcChar8 *fc_file;
2435 0 : if (FcPatternGetString(renderPattern,
2436 0 : FC_FILE, 0, &fc_file) == FcResultMatch) {
2437 : int index;
2438 0 : if (FcPatternGetInteger(renderPattern,
2439 0 : FC_INDEX, 0, &index) != FcResultMatch) {
2440 : // cairo defaults to 0.
2441 0 : index = 0;
2442 : }
2443 :
2444 0 : AppendUTF8toUTF16(gfxFontconfigUtils::ToCString(fc_file), name);
2445 0 : if (index != 0) {
2446 0 : name.AppendLiteral("/");
2447 0 : name.AppendInt(index);
2448 : }
2449 : }
2450 :
2451 0 : fe = new gfxSystemFcFontEntry(face, aFontPattern, name);
2452 : }
2453 : }
2454 :
2455 0 : gfxFontStyle style(*aFontStyle);
2456 0 : style.size = GetPixelSize(renderPattern);
2457 0 : style.style = gfxFontconfigUtils::GetThebesStyle(renderPattern);
2458 0 : style.weight = gfxFontconfigUtils::GetThebesWeight(renderPattern);
2459 :
2460 0 : nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(fe, &style);
2461 0 : if (!font) {
2462 : // Note that a file/index pair (or FT_Face) and the gfxFontStyle are
2463 : // not necessarily enough to provide a key that will describe a unique
2464 : // font. cairoFont contains information from renderPattern, which is a
2465 : // fully resolved pattern from FcFontRenderPrepare.
2466 : // FcFontRenderPrepare takes the requested pattern and the face
2467 : // pattern as input and can modify elements of the resulting pattern
2468 : // that affect rendering but are not included in the gfxFontStyle.
2469 0 : cairo_scaled_font_t *cairoFont = CreateScaledFont(renderPattern, face);
2470 0 : font = new gfxFcFont(cairoFont, fe, &style);
2471 0 : gfxFontCache::GetCache()->AddNew(font);
2472 0 : cairo_scaled_font_destroy(cairoFont);
2473 : }
2474 :
2475 0 : cairo_font_face_destroy(face);
2476 :
2477 0 : nsRefPtr<gfxFcFont> retval(static_cast<gfxFcFont*>(font.get()));
2478 0 : return retval.forget();
2479 : }
2480 :
2481 : static PangoFontMap *
2482 0 : GetPangoFontMap()
2483 : {
2484 0 : if (!gPangoFontMap) {
2485 : // This is the same FontMap used by GDK, so that the same
2486 : // PangoCoverage cache is shared.
2487 0 : gPangoFontMap = pango_cairo_font_map_get_default();
2488 :
2489 0 : if (PANGO_IS_FC_FONT_MAP(gPangoFontMap)) {
2490 0 : g_object_ref(gPangoFontMap);
2491 : } else {
2492 : // Future proofing: We need a PangoFcFontMap for gfxPangoFcFont.
2493 : // pango_cairo_font_map_get_default() is expected to return a
2494 : // PangoFcFontMap on Linux systems, but, just in case this ever
2495 : // changes, we provide our own basic implementation.
2496 0 : gPangoFontMap = gfxPangoFontMap::NewFontMap();
2497 : }
2498 : }
2499 0 : return gPangoFontMap;
2500 : }
2501 :
2502 : gfxFcFontSet *
2503 0 : gfxPangoFontGroup::GetBaseFontSet()
2504 : {
2505 0 : if (mFontSets.Length() > 0)
2506 0 : return mFontSets[0].mFontSet;
2507 :
2508 0 : mSizeAdjustFactor = 1.0; // will be adjusted below if necessary
2509 0 : nsAutoRef<FcPattern> pattern;
2510 : nsRefPtr<gfxFcFontSet> fontSet =
2511 0 : MakeFontSet(mPangoLanguage, mSizeAdjustFactor, &pattern);
2512 :
2513 0 : double size = GetPixelSize(pattern);
2514 0 : if (size != 0.0 && mStyle.sizeAdjust != 0.0) {
2515 0 : gfxFcFont *font = fontSet->GetFontAt(0, GetStyle());
2516 0 : if (font) {
2517 0 : const gfxFont::Metrics& metrics = font->GetMetrics();
2518 :
2519 : // The factor of 0.1 ensures that xHeight is sane so fonts don't
2520 : // become huge. Strictly ">" ensures that xHeight and emHeight are
2521 : // not both zero.
2522 0 : if (metrics.xHeight > 0.1 * metrics.emHeight) {
2523 : mSizeAdjustFactor =
2524 0 : mStyle.sizeAdjust * metrics.emHeight / metrics.xHeight;
2525 :
2526 0 : size *= mSizeAdjustFactor;
2527 0 : FcPatternDel(pattern, FC_PIXEL_SIZE);
2528 0 : FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
2529 :
2530 0 : fontSet = new gfxFcFontSet(pattern, mUserFontSet);
2531 : }
2532 : }
2533 : }
2534 :
2535 0 : PangoLanguage *pangoLang = mPangoLanguage;
2536 : FcChar8 *fcLang;
2537 0 : if (!pangoLang &&
2538 0 : FcPatternGetString(pattern, FC_LANG, 0, &fcLang) == FcResultMatch) {
2539 : pangoLang =
2540 0 : pango_language_from_string(gfxFontconfigUtils::ToCString(fcLang));
2541 : }
2542 :
2543 0 : mFontSets.AppendElement(FontSetByLangEntry(pangoLang, fontSet));
2544 :
2545 0 : return fontSet;
2546 : }
2547 :
2548 : /**
2549 : ** gfxTextRun
2550 : *
2551 : * A serious problem:
2552 : *
2553 : * -- We draw with a font that's hinted for the CTM, but we measure with a font
2554 : * hinted to the identity matrix, so our "bounding metrics" may not be accurate.
2555 : *
2556 : **/
2557 :
2558 : // This will fetch an existing scaled_font if one exists.
2559 : static cairo_scaled_font_t *
2560 0 : CreateScaledFont(FcPattern *aPattern, cairo_font_face_t *aFace)
2561 : {
2562 0 : double size = GetPixelSize(aPattern);
2563 :
2564 : cairo_matrix_t fontMatrix;
2565 : FcMatrix *fcMatrix;
2566 0 : if (FcPatternGetMatrix(aPattern, FC_MATRIX, 0, &fcMatrix) == FcResultMatch)
2567 0 : cairo_matrix_init(&fontMatrix, fcMatrix->xx, -fcMatrix->yx, -fcMatrix->xy, fcMatrix->yy, 0, 0);
2568 : else
2569 0 : cairo_matrix_init_identity(&fontMatrix);
2570 0 : cairo_matrix_scale(&fontMatrix, size, size);
2571 :
2572 : FcBool printing;
2573 0 : if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) != FcResultMatch) {
2574 0 : printing = FcFalse;
2575 : }
2576 :
2577 : // The cairo_scaled_font is created with a unit ctm so that metrics and
2578 : // positions are in user space, but this means that hinting effects will
2579 : // not be estimated accurately for non-unit transformations.
2580 : cairo_matrix_t identityMatrix;
2581 0 : cairo_matrix_init_identity(&identityMatrix);
2582 :
2583 : // Font options are set explicitly here to improve cairo's caching
2584 : // behavior and to record the relevant parts of the pattern for
2585 : // SetupCairoFont (so that the pattern can be released).
2586 : //
2587 : // Most font_options have already been set as defaults on the FcPattern
2588 : // with cairo_ft_font_options_substitute(), then user and system
2589 : // fontconfig configurations were applied. The resulting font_options
2590 : // have been recorded on the face during
2591 : // cairo_ft_font_face_create_for_pattern().
2592 : //
2593 : // None of the settings here cause this scaled_font to behave any
2594 : // differently from how it would behave if it were created from the same
2595 : // face with default font_options.
2596 : //
2597 : // We set options explicitly so that the same scaled_font will be found in
2598 : // the cairo_scaled_font_map when cairo loads glyphs from a context with
2599 : // the same font_face, font_matrix, ctm, and surface font_options.
2600 : //
2601 : // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
2602 : // font_options on the cairo_ft_font_face, and doesn't consider default
2603 : // option values to not match any explicit values.
2604 : //
2605 : // Even after cairo_set_scaled_font is used to set font_options for the
2606 : // cairo context, when cairo looks for a scaled_font for the context, it
2607 : // will look for a font with some option values from the target surface if
2608 : // any values are left default on the context font_options. If this
2609 : // scaled_font is created with default font_options, cairo will not find
2610 : // it.
2611 0 : cairo_font_options_t *fontOptions = cairo_font_options_create();
2612 :
2613 : // The one option not recorded in the pattern is hint_metrics, which will
2614 : // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
2615 : // We should be considering the font_options of the surface on which this
2616 : // font will be used, but currently we don't have different gfxFonts for
2617 : // different surface font_options, so we'll create a font suitable for the
2618 : // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
2619 : #ifdef MOZ_GFX_OPTIMIZE_MOBILE
2620 : cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
2621 : #else
2622 0 : if (printing) {
2623 0 : cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
2624 : } else {
2625 0 : cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_ON);
2626 : }
2627 : #endif
2628 :
2629 : // The remaining options have been recorded on the pattern and the face.
2630 : // _cairo_ft_options_merge has some logic to decide which options from the
2631 : // scaled_font or from the cairo_ft_font_face take priority in the way the
2632 : // font behaves.
2633 : //
2634 : // In the majority of cases, _cairo_ft_options_merge uses the options from
2635 : // the cairo_ft_font_face, so sometimes it is not so important which
2636 : // values are set here so long as they are not defaults, but we'll set
2637 : // them to the exact values that we expect from the font, to be consistent
2638 : // and to protect against changes in cairo.
2639 : //
2640 : // In some cases, _cairo_ft_options_merge uses some options from the
2641 : // scaled_font's font_options rather than options on the
2642 : // cairo_ft_font_face (from fontconfig).
2643 : // https://bugs.freedesktop.org/show_bug.cgi?id=11838
2644 : //
2645 : // Surface font options were set on the pattern in
2646 : // cairo_ft_font_options_substitute. If fontconfig has changed the
2647 : // hint_style then that is what the user (or distribution) wants, so we
2648 : // use the setting from the FcPattern.
2649 : //
2650 : // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
2651 0 : FcBool hinting = FcFalse;
2652 : #ifndef MOZ_GFX_OPTIMIZE_MOBILE
2653 0 : if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
2654 0 : hinting = FcTrue;
2655 : }
2656 : #endif
2657 : cairo_hint_style_t hint_style;
2658 0 : if (printing || !hinting) {
2659 0 : hint_style = CAIRO_HINT_STYLE_NONE;
2660 : } else {
2661 : #ifdef FC_HINT_STYLE // FC_HINT_STYLE is available from fontconfig 2.2.91.
2662 : int fc_hintstyle;
2663 0 : if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
2664 0 : 0, &fc_hintstyle ) != FcResultMatch) {
2665 0 : fc_hintstyle = FC_HINT_FULL;
2666 : }
2667 0 : switch (fc_hintstyle) {
2668 : case FC_HINT_NONE:
2669 0 : hint_style = CAIRO_HINT_STYLE_NONE;
2670 0 : break;
2671 : case FC_HINT_SLIGHT:
2672 0 : hint_style = CAIRO_HINT_STYLE_SLIGHT;
2673 0 : break;
2674 : case FC_HINT_MEDIUM:
2675 : default: // This fallback mirrors _get_pattern_ft_options in cairo.
2676 0 : hint_style = CAIRO_HINT_STYLE_MEDIUM;
2677 0 : break;
2678 : case FC_HINT_FULL:
2679 0 : hint_style = CAIRO_HINT_STYLE_FULL;
2680 0 : break;
2681 : }
2682 : #else // no FC_HINT_STYLE
2683 : hint_style = CAIRO_HINT_STYLE_FULL;
2684 : #endif
2685 : }
2686 0 : cairo_font_options_set_hint_style(fontOptions, hint_style);
2687 :
2688 : int rgba;
2689 0 : if (FcPatternGetInteger(aPattern,
2690 0 : FC_RGBA, 0, &rgba) != FcResultMatch) {
2691 0 : rgba = FC_RGBA_UNKNOWN;
2692 : }
2693 0 : cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
2694 0 : switch (rgba) {
2695 : case FC_RGBA_UNKNOWN:
2696 : case FC_RGBA_NONE:
2697 : default:
2698 : // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
2699 : // is disabled through cairo_antialias_t.
2700 0 : rgba = FC_RGBA_NONE;
2701 : // subpixel_order won't be used by the font as we won't use
2702 : // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
2703 : // caching reasons described above. Fall through:
2704 : case FC_RGBA_RGB:
2705 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
2706 0 : break;
2707 : case FC_RGBA_BGR:
2708 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
2709 0 : break;
2710 : case FC_RGBA_VRGB:
2711 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
2712 0 : break;
2713 : case FC_RGBA_VBGR:
2714 0 : subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
2715 0 : break;
2716 : }
2717 0 : cairo_font_options_set_subpixel_order(fontOptions, subpixel_order);
2718 :
2719 : FcBool fc_antialias;
2720 0 : if (FcPatternGetBool(aPattern,
2721 0 : FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
2722 0 : fc_antialias = FcTrue;
2723 : }
2724 : cairo_antialias_t antialias;
2725 0 : if (!fc_antialias) {
2726 0 : antialias = CAIRO_ANTIALIAS_NONE;
2727 0 : } else if (rgba == FC_RGBA_NONE) {
2728 0 : antialias = CAIRO_ANTIALIAS_GRAY;
2729 : } else {
2730 0 : antialias = CAIRO_ANTIALIAS_SUBPIXEL;
2731 : }
2732 0 : cairo_font_options_set_antialias(fontOptions, antialias);
2733 :
2734 : cairo_scaled_font_t *scaledFont =
2735 : cairo_scaled_font_create(aFace, &fontMatrix, &identityMatrix,
2736 0 : fontOptions);
2737 :
2738 0 : cairo_font_options_destroy(fontOptions);
2739 :
2740 0 : NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
2741 : "Failed to create scaled font");
2742 0 : return scaledFont;
2743 : }
2744 :
2745 : static PRInt32
2746 0 : ConvertPangoToAppUnits(PRInt32 aCoordinate, PRUint32 aAppUnitsPerDevUnit)
2747 : {
2748 0 : PRInt64 v = (PRInt64(aCoordinate)*aAppUnitsPerDevUnit + PANGO_SCALE/2)/PANGO_SCALE;
2749 0 : return PRInt32(v);
2750 : }
2751 :
2752 : /**
2753 : * Given a run of Pango glyphs that should be treated as a single
2754 : * cluster/ligature, store them in the textrun at the appropriate character
2755 : * and set the other characters involved to be ligature/cluster continuations
2756 : * as appropriate.
2757 : */
2758 : static nsresult
2759 0 : SetGlyphsForCharacterGroup(const PangoGlyphInfo *aGlyphs, PRUint32 aGlyphCount,
2760 : gfxShapedWord *aShapedWord,
2761 : const gchar *aUTF8, PRUint32 aUTF8Length,
2762 : PRUint32 *aUTF16Offset,
2763 : PangoGlyphUnit aOverrideSpaceWidth)
2764 : {
2765 0 : PRUint32 utf16Offset = *aUTF16Offset;
2766 0 : PRUint32 wordLength = aShapedWord->Length();
2767 0 : const PRUint32 appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
2768 :
2769 : // Override the width of a space, but only for spaces that aren't
2770 : // clustered with something else (like a freestanding diacritical mark)
2771 0 : PangoGlyphUnit width = aGlyphs[0].geometry.width;
2772 0 : if (aOverrideSpaceWidth && aUTF8[0] == ' ' &&
2773 : (utf16Offset + 1 == wordLength ||
2774 0 : aShapedWord->IsClusterStart(utf16Offset))) {
2775 0 : width = aOverrideSpaceWidth;
2776 : }
2777 0 : PRInt32 advance = ConvertPangoToAppUnits(width, appUnitsPerDevUnit);
2778 :
2779 0 : gfxShapedWord::CompressedGlyph g;
2780 0 : bool atClusterStart = aShapedWord->IsClusterStart(utf16Offset);
2781 : // See if we fit in the compressed area.
2782 0 : if (aGlyphCount == 1 && advance >= 0 && atClusterStart &&
2783 : aGlyphs[0].geometry.x_offset == 0 &&
2784 : aGlyphs[0].geometry.y_offset == 0 &&
2785 0 : !IS_EMPTY_GLYPH(aGlyphs[0].glyph) &&
2786 0 : gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
2787 0 : gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(aGlyphs[0].glyph)) {
2788 : aShapedWord->SetSimpleGlyph(utf16Offset,
2789 0 : g.SetSimpleGlyph(advance, aGlyphs[0].glyph));
2790 : } else {
2791 0 : nsAutoTArray<gfxShapedWord::DetailedGlyph,10> detailedGlyphs;
2792 0 : if (!detailedGlyphs.AppendElements(aGlyphCount))
2793 0 : return NS_ERROR_OUT_OF_MEMORY;
2794 :
2795 0 : PRInt32 direction = aShapedWord->IsRightToLeft() ? -1 : 1;
2796 0 : PRUint32 pangoIndex = direction > 0 ? 0 : aGlyphCount - 1;
2797 0 : PRUint32 detailedIndex = 0;
2798 0 : for (PRUint32 i = 0; i < aGlyphCount; ++i) {
2799 0 : const PangoGlyphInfo &glyph = aGlyphs[pangoIndex];
2800 0 : pangoIndex += direction;
2801 : // The zero width characters return empty glyph ID at
2802 : // shaping; we should skip these.
2803 0 : if (IS_EMPTY_GLYPH(glyph.glyph))
2804 0 : continue;
2805 :
2806 0 : gfxShapedWord::DetailedGlyph *details = &detailedGlyphs[detailedIndex];
2807 0 : ++detailedIndex;
2808 :
2809 0 : details->mGlyphID = glyph.glyph;
2810 0 : NS_ASSERTION(details->mGlyphID == glyph.glyph,
2811 : "Seriously weird glyph ID detected!");
2812 : details->mAdvance =
2813 : ConvertPangoToAppUnits(glyph.geometry.width,
2814 0 : appUnitsPerDevUnit);
2815 : details->mXOffset =
2816 0 : float(glyph.geometry.x_offset)*appUnitsPerDevUnit/PANGO_SCALE;
2817 : details->mYOffset =
2818 0 : float(glyph.geometry.y_offset)*appUnitsPerDevUnit/PANGO_SCALE;
2819 : }
2820 0 : g.SetComplex(atClusterStart, true, detailedIndex);
2821 0 : aShapedWord->SetGlyphs(utf16Offset, g, detailedGlyphs.Elements());
2822 : }
2823 :
2824 : // Check for ligatures and set *aUTF16Offset.
2825 0 : const gchar *p = aUTF8;
2826 0 : const gchar *end = aUTF8 + aUTF8Length;
2827 0 : while (1) {
2828 : // Skip the CompressedGlyph that we have added, but check if the
2829 : // character was supposed to be ignored. If it's supposed to be ignored,
2830 : // overwrite the textrun entry with an invisible missing-glyph.
2831 0 : gunichar ch = g_utf8_get_char(p);
2832 0 : NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
2833 0 : if (ch >= 0x10000) {
2834 : // Skip surrogate
2835 0 : ++utf16Offset;
2836 : }
2837 0 : NS_ASSERTION(!gfxFontGroup::IsInvalidChar(PRUnichar(ch)),
2838 : "Invalid character detected");
2839 0 : ++utf16Offset;
2840 :
2841 : // We produced this UTF8 so we don't need to worry about malformed stuff
2842 0 : p = g_utf8_next_char(p);
2843 0 : if (p >= end)
2844 : break;
2845 :
2846 0 : if (utf16Offset >= wordLength) {
2847 0 : NS_ERROR("Someone has added too many glyphs!");
2848 0 : return NS_ERROR_FAILURE;
2849 : }
2850 :
2851 0 : g.SetComplex(aShapedWord->IsClusterStart(utf16Offset), false, 0);
2852 0 : aShapedWord->SetGlyphs(utf16Offset, g, nsnull);
2853 : }
2854 0 : *aUTF16Offset = utf16Offset;
2855 0 : return NS_OK;
2856 : }
2857 :
2858 : static nsresult
2859 0 : SetGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8, PRUint32 aUTF8Length,
2860 : PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
2861 : PangoGlyphUnit aOverrideSpaceWidth,
2862 : gfxFont *aFont)
2863 : {
2864 0 : gint numGlyphs = aGlyphs->num_glyphs;
2865 0 : PangoGlyphInfo *glyphs = aGlyphs->glyphs;
2866 0 : const gint *logClusters = aGlyphs->log_clusters;
2867 : // We cannot make any assumptions about the order of glyph clusters
2868 : // provided by pango_shape (see 375864), so we work through the UTF8 text
2869 : // and process the glyph clusters in logical order.
2870 :
2871 : // logGlyphs is like an inverse of logClusters. For each UTF8 byte:
2872 : // >= 0 indicates that the byte is first in a cluster and
2873 : // gives the position of the starting glyph for the cluster.
2874 : // -1 indicates that the byte does not start a cluster.
2875 0 : nsAutoTArray<gint,2000> logGlyphs;
2876 0 : if (!logGlyphs.AppendElements(aUTF8Length + 1))
2877 0 : return NS_ERROR_OUT_OF_MEMORY;
2878 0 : PRUint32 utf8Index = 0;
2879 0 : for(; utf8Index < aUTF8Length; ++utf8Index)
2880 0 : logGlyphs[utf8Index] = -1;
2881 0 : logGlyphs[aUTF8Length] = numGlyphs;
2882 :
2883 0 : gint lastCluster = -1; // != utf8Index
2884 0 : for (gint glyphIndex = 0; glyphIndex < numGlyphs; ++glyphIndex) {
2885 0 : gint thisCluster = logClusters[glyphIndex];
2886 0 : if (thisCluster != lastCluster) {
2887 0 : lastCluster = thisCluster;
2888 0 : NS_ASSERTION(0 <= thisCluster && thisCluster < gint(aUTF8Length),
2889 : "garbage from pango_shape - this is bad");
2890 0 : logGlyphs[thisCluster] = glyphIndex;
2891 : }
2892 : }
2893 :
2894 0 : PRUint32 utf16Offset = *aUTF16Offset;
2895 0 : PRUint32 wordLength = aShapedWord->Length();
2896 0 : utf8Index = 0;
2897 : // The next glyph cluster in logical order.
2898 0 : gint nextGlyphClusterStart = logGlyphs[utf8Index];
2899 0 : NS_ASSERTION(nextGlyphClusterStart >= 0, "No glyphs! - NUL in string?");
2900 0 : while (utf8Index < aUTF8Length) {
2901 0 : if (utf16Offset >= wordLength) {
2902 0 : NS_ERROR("Someone has added too many glyphs!");
2903 0 : return NS_ERROR_FAILURE;
2904 : }
2905 0 : gint glyphClusterStart = nextGlyphClusterStart;
2906 : // Find the utf8 text associated with this glyph cluster.
2907 0 : PRUint32 clusterUTF8Start = utf8Index;
2908 : // Check whether we are consistent with pango_break data.
2909 0 : NS_WARN_IF_FALSE(aShapedWord->IsClusterStart(utf16Offset),
2910 : "Glyph cluster not aligned on character cluster.");
2911 0 : do {
2912 0 : ++utf8Index;
2913 0 : nextGlyphClusterStart = logGlyphs[utf8Index];
2914 : } while (nextGlyphClusterStart < 0);
2915 0 : const gchar *clusterUTF8 = &aUTF8[clusterUTF8Start];
2916 0 : PRUint32 clusterUTF8Length = utf8Index - clusterUTF8Start;
2917 :
2918 0 : bool haveMissingGlyph = false;
2919 0 : gint glyphIndex = glyphClusterStart;
2920 :
2921 : // It's now unncecessary to do NUL handling here.
2922 0 : do {
2923 0 : if (IS_MISSING_GLYPH(glyphs[glyphIndex].glyph)) {
2924 : // Does pango ever provide more than one glyph in the
2925 : // cluster if there is a missing glyph?
2926 : // behdad: yes
2927 0 : haveMissingGlyph = true;
2928 : }
2929 0 : glyphIndex++;
2930 : } while (glyphIndex < numGlyphs &&
2931 0 : logClusters[glyphIndex] == gint(clusterUTF8Start));
2932 :
2933 : nsresult rv;
2934 0 : if (haveMissingGlyph) {
2935 : SetMissingGlyphs(aShapedWord, clusterUTF8, clusterUTF8Length,
2936 0 : &utf16Offset, aFont);
2937 : } else {
2938 : rv = SetGlyphsForCharacterGroup(&glyphs[glyphClusterStart],
2939 : glyphIndex - glyphClusterStart,
2940 : aShapedWord,
2941 : clusterUTF8, clusterUTF8Length,
2942 0 : &utf16Offset, aOverrideSpaceWidth);
2943 0 : NS_ENSURE_SUCCESS(rv,rv);
2944 : }
2945 : }
2946 0 : *aUTF16Offset = utf16Offset;
2947 0 : return NS_OK;
2948 : }
2949 :
2950 : static void
2951 0 : SetMissingGlyphs(gfxShapedWord *aShapedWord, const gchar *aUTF8,
2952 : PRUint32 aUTF8Length, PRUint32 *aUTF16Offset,
2953 : gfxFont *aFont)
2954 : {
2955 0 : PRUint32 utf16Offset = *aUTF16Offset;
2956 0 : PRUint32 wordLength = aShapedWord->Length();
2957 0 : for (PRUint32 index = 0; index < aUTF8Length;) {
2958 0 : if (utf16Offset >= wordLength) {
2959 0 : NS_ERROR("Someone has added too many glyphs!");
2960 0 : break;
2961 : }
2962 0 : gunichar ch = g_utf8_get_char(aUTF8 + index);
2963 0 : aShapedWord->SetMissingGlyph(utf16Offset, ch, aFont);
2964 :
2965 0 : ++utf16Offset;
2966 0 : NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
2967 0 : if (ch >= 0x10000)
2968 0 : ++utf16Offset;
2969 : // We produced this UTF8 so we don't need to worry about malformed stuff
2970 0 : index = g_utf8_next_char(aUTF8 + index) - aUTF8;
2971 : }
2972 :
2973 0 : *aUTF16Offset = utf16Offset;
2974 0 : }
2975 :
2976 : static void
2977 0 : InitGlyphRunWithPangoAnalysis(gfxShapedWord *aShapedWord,
2978 : const gchar *aUTF8, PRUint32 aUTF8Length,
2979 : PangoAnalysis *aAnalysis,
2980 : PangoGlyphUnit aOverrideSpaceWidth,
2981 : gfxFont *aFont)
2982 : {
2983 0 : PRUint32 utf16Offset = 0;
2984 0 : PangoGlyphString *glyphString = pango_glyph_string_new();
2985 :
2986 0 : const gchar *p = aUTF8;
2987 0 : const gchar *end = p + aUTF8Length;
2988 0 : while (p < end) {
2989 0 : if (*p == 0) {
2990 0 : aShapedWord->SetMissingGlyph(utf16Offset, 0, aFont);
2991 0 : ++p;
2992 0 : ++utf16Offset;
2993 0 : continue;
2994 : }
2995 :
2996 : // It's necessary to loop over pango_shape as it treats
2997 : // NULs as string terminators
2998 0 : const gchar *text = p;
2999 0 : do {
3000 0 : ++p;
3001 : } while(p < end && *p != 0);
3002 0 : gint len = p - text;
3003 :
3004 0 : pango_shape(text, len, aAnalysis, glyphString);
3005 : SetGlyphs(aShapedWord, text, len, &utf16Offset, glyphString,
3006 0 : aOverrideSpaceWidth, aFont);
3007 : }
3008 :
3009 0 : pango_glyph_string_free(glyphString);
3010 0 : }
3011 :
3012 : // PangoAnalysis is part of Pango's ABI but over time extra fields have been
3013 : // inserted into padding. This union is used so that the code here can be
3014 : // compiled against older Pango versions but run against newer versions.
3015 : typedef union {
3016 : PangoAnalysis pango;
3017 : // This struct matches PangoAnalysis from Pango version
3018 : // 1.16.5 to 1.28.1 (at least).
3019 : struct {
3020 : PangoEngineShape *shape_engine;
3021 : PangoEngineLang *lang_engine;
3022 : PangoFont *font;
3023 : guint8 level;
3024 : guint8 gravity; /* PangoGravity */
3025 : guint8 flags;
3026 : guint8 script; /* PangoScript */
3027 : PangoLanguage *language;
3028 : GSList *extra_attrs;
3029 : } local;
3030 : } PangoAnalysisUnion;
3031 :
3032 : bool
3033 0 : gfxFcFont::InitGlyphRunWithPango(gfxShapedWord *aShapedWord,
3034 : const PRUnichar *aString)
3035 : {
3036 0 : const PangoScript script = static_cast<PangoScript>(aShapedWord->Script());
3037 0 : NS_ConvertUTF16toUTF8 utf8(aString, aShapedWord->Length());
3038 :
3039 0 : PangoFont *font = GetPangoFont();
3040 :
3041 0 : hb_language_t languageOverride = NULL;
3042 0 : if (GetStyle()->languageOverride) {
3043 : languageOverride =
3044 0 : hb_ot_tag_to_language(GetStyle()->languageOverride);
3045 0 : } else if (GetFontEntry()->mLanguageOverride) {
3046 : languageOverride =
3047 0 : hb_ot_tag_to_language(GetFontEntry()->mLanguageOverride);
3048 : }
3049 :
3050 : PangoLanguage *language;
3051 0 : if (languageOverride) {
3052 : language =
3053 0 : pango_language_from_string(hb_language_to_string(languageOverride));
3054 : } else {
3055 : #if 0 // FIXME ??
3056 : language = fontGroup->GetPangoLanguage();
3057 : #endif
3058 : // FIXME: should probably cache this in the gfxFcFont
3059 0 : language = GuessPangoLanguage(GetStyle()->language);
3060 :
3061 : // The language that we have here is often not as good an indicator for
3062 : // the run as the script. This is not so important for the PangoMaps
3063 : // here as all the default Pango shape and lang engines are selected
3064 : // by script only (not language) anyway, but may be important in the
3065 : // PangoAnalysis as the shaper sometimes accesses language-specific
3066 : // tables.
3067 : PangoLanguage *scriptLang;
3068 0 : if ((!language ||
3069 0 : !pango_language_includes_script(language, script)) &&
3070 : (scriptLang = pango_script_get_sample_language(script))) {
3071 0 : language = scriptLang;
3072 : }
3073 : }
3074 :
3075 : static GQuark engineLangId =
3076 0 : g_quark_from_static_string(PANGO_ENGINE_TYPE_LANG);
3077 : static GQuark renderNoneId =
3078 0 : g_quark_from_static_string(PANGO_RENDER_TYPE_NONE);
3079 0 : PangoMap *langMap = pango_find_map(language, engineLangId, renderNoneId);
3080 :
3081 : static GQuark engineShapeId =
3082 0 : g_quark_from_static_string(PANGO_ENGINE_TYPE_SHAPE);
3083 : static GQuark renderFcId =
3084 0 : g_quark_from_static_string(PANGO_RENDER_TYPE_FC);
3085 0 : PangoMap *shapeMap = pango_find_map(language, engineShapeId, renderFcId);
3086 0 : if (!shapeMap) {
3087 0 : return false;
3088 : }
3089 :
3090 : // The preferred shape engine for language and script
3091 : PangoEngineShape *shapeEngine =
3092 0 : PANGO_ENGINE_SHAPE(pango_map_get_engine(shapeMap, script));
3093 0 : if (!shapeEngine) {
3094 0 : return false;
3095 : }
3096 :
3097 : PangoEngineShapeClass *shapeClass = static_cast<PangoEngineShapeClass*>
3098 0 : (g_type_class_peek(PANGO_TYPE_ENGINE_SHAPE));
3099 :
3100 : // The |covers| method in the PangoEngineShape base class, which is the
3101 : // method used by Pango shapers, merely copies the fontconfig coverage map
3102 : // to a PangoCoverage and checks that the character is supported. We've
3103 : // already checked for character support, so we can avoid this copy for
3104 : // these shapers.
3105 : //
3106 : // With SIL Graphite shapers, however, |covers| also checks that the font
3107 : // is a Graphite font. (bug 397860)
3108 0 : if (!shapeClass ||
3109 : PANGO_ENGINE_SHAPE_GET_CLASS(shapeEngine)->covers != shapeClass->covers)
3110 : {
3111 : GSList *exact_engines;
3112 : GSList *fallback_engines;
3113 : pango_map_get_engines(shapeMap, script,
3114 0 : &exact_engines, &fallback_engines);
3115 :
3116 0 : GSList *engines = g_slist_concat(exact_engines, fallback_engines);
3117 0 : for (GSList *link = engines; link; link = link->next) {
3118 0 : PangoEngineShape *engine = PANGO_ENGINE_SHAPE(link->data);
3119 : PangoCoverageLevel (*covers)(PangoEngineShape*, PangoFont*,
3120 : PangoLanguage*, gunichar) =
3121 0 : PANGO_ENGINE_SHAPE_GET_CLASS(shapeEngine)->covers;
3122 :
3123 0 : if ((shapeClass && covers == shapeClass->covers) ||
3124 0 : covers(engine, font, language, ' ') != PANGO_COVERAGE_NONE)
3125 : {
3126 0 : shapeEngine = engine;
3127 0 : break;
3128 : }
3129 : }
3130 0 : g_slist_free(engines); // Frees exact and fallback links
3131 : }
3132 :
3133 : PangoAnalysisUnion analysis;
3134 0 : memset(&analysis, 0, sizeof(analysis));
3135 :
3136 : // For pango_shape
3137 0 : analysis.local.shape_engine = shapeEngine;
3138 : // For pango_break
3139 : analysis.local.lang_engine =
3140 0 : PANGO_ENGINE_LANG(pango_map_get_engine(langMap, script));
3141 :
3142 0 : analysis.local.font = font;
3143 0 : analysis.local.level = aShapedWord->IsRightToLeft() ? 1 : 0;
3144 : // gravity and flags are used in Pango 1.14.10 and newer.
3145 : //
3146 : // PANGO_GRAVITY_SOUTH is what we want for upright horizontal text. The
3147 : // constant is not available when compiling with older Pango versions, but
3148 : // is zero so the zero memset initialization is sufficient.
3149 : //
3150 : // Pango uses non-zero flags for vertical gravities only
3151 : // (up to version 1.28 at least), so using zero is fine for flags too.
3152 : #if 0
3153 : analysis.local.gravity = PANGO_GRAVITY_SOUTH;
3154 : analysis.local.flags = 0;
3155 : #endif
3156 : // Only used in Pango 1.16.5 and newer.
3157 0 : analysis.local.script = script;
3158 :
3159 0 : analysis.local.language = language;
3160 : // Non-font attributes. Not used here.
3161 0 : analysis.local.extra_attrs = NULL;
3162 :
3163 : PangoGlyphUnit spaceWidth =
3164 0 : moz_pango_units_from_double(GetMetrics().spaceWidth);
3165 :
3166 : InitGlyphRunWithPangoAnalysis(aShapedWord, utf8.get(), utf8.Length(),
3167 0 : &analysis.pango, spaceWidth, this);
3168 0 : return true;
3169 : }
3170 :
3171 : /* static */
3172 : PangoLanguage *
3173 0 : GuessPangoLanguage(nsIAtom *aLanguage)
3174 : {
3175 0 : if (!aLanguage)
3176 0 : return NULL;
3177 :
3178 : // Pango and fontconfig won't understand mozilla's internal langGroups, so
3179 : // find a real language.
3180 0 : nsCAutoString lang;
3181 0 : gfxFontconfigUtils::GetSampleLangForGroup(aLanguage, &lang);
3182 0 : if (lang.IsEmpty())
3183 0 : return NULL;
3184 :
3185 0 : return pango_language_from_string(lang.get());
3186 : }
3187 :
3188 : #ifdef MOZ_WIDGET_GTK2
3189 : /***************************************************************************
3190 : *
3191 : * This function must be last in the file because it uses the system cairo
3192 : * library. Above this point the cairo library used is the tree cairo if
3193 : * MOZ_TREE_CAIRO.
3194 : */
3195 :
3196 : #if MOZ_TREE_CAIRO
3197 : // Tree cairo symbols have different names. Disable their activation through
3198 : // preprocessor macros.
3199 : #undef cairo_ft_font_options_substitute
3200 :
3201 : // The system cairo functions are not declared because the include paths cause
3202 : // the gdk headers to pick up the tree cairo.h.
3203 : extern "C" {
3204 : NS_VISIBILITY_DEFAULT void
3205 : cairo_ft_font_options_substitute (const cairo_font_options_t *options,
3206 : FcPattern *pattern);
3207 : }
3208 : #endif
3209 :
3210 : static void
3211 0 : ApplyGdkScreenFontOptions(FcPattern *aPattern)
3212 : {
3213 : const cairo_font_options_t *options =
3214 0 : gdk_screen_get_font_options(gdk_screen_get_default());
3215 :
3216 0 : cairo_ft_font_options_substitute(options, aPattern);
3217 0 : }
3218 :
3219 : #endif // MOZ_WIDGET_GTK2
|