Bug 463806 - [PATCH][@font-face] Downloaded font activation on Mac may fail due to...
[wine-gecko.git] / gfx / thebes / src / gfxFT2Fonts.cpp
blobd792f8afc017695ea0edfaa02d9987d98ce6d858
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Foundation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #if defined(MOZ_WIDGET_GTK2)
38 #include "gfxPlatformGtk.h"
39 #define gfxToolkitPlatform gfxPlatformGtk
40 #elif defined(MOZ_WIDGET_QT)
41 #include "gfxQtPlatform.h"
42 #include <qfontinfo.h>
43 #define gfxToolkitPlatform gfxQtPlatform
44 #endif
45 #include "gfxTypes.h"
46 #include "gfxFT2Fonts.h"
47 #include <locale.h>
48 #include "cairo-ft.h"
49 #include <freetype/tttables.h>
50 #include "gfxFontUtils.h"
52 /**
53 * FontEntry
56 FontEntry::FontEntry(const FontEntry& aFontEntry) :
57 gfxFontEntry(aFontEntry)
59 if (aFontEntry.mFontFace)
60 mFontFace = cairo_font_face_reference(aFontEntry.mFontFace);
61 else
62 mFontFace = nsnull;
65 FontEntry::~FontEntry()
67 if (mFontFace) {
68 cairo_font_face_destroy(mFontFace);
69 mFontFace = nsnull;
73 static void
74 FTFontDestroyFunc(void *data)
76 FT_Face face = (FT_Face)data;
77 FT_Done_Face(face);
80 FontEntry*
81 gfxFT2Font::GetFontEntry()
83 return static_cast<FontEntry*> (mFontEntry.get());
86 cairo_font_face_t *
87 FontEntry::CairoFontFace()
89 static cairo_user_data_key_t key;
91 if (!mFontFace) {
92 FT_Face face;
93 FT_New_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), mFilename.get(), mFTFontIndex, &face);
94 mFontFace = cairo_ft_font_face_create_for_ft_face(face, 0);
95 cairo_font_face_set_user_data(mFontFace, &key, face, FTFontDestroyFunc);
97 return mFontFace;
100 FontEntry *
101 FontFamily::FindFontEntry(const gfxFontStyle& aFontStyle)
103 PRBool italic = (aFontStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
105 FontEntry *weightList[10] = { 0 };
106 for (PRUint32 j = 0; j < 2; j++) {
107 PRBool matchesSomething = PR_FALSE;
108 // build up an array of weights that match the italicness we're looking for
109 for (PRUint32 i = 0; i < mFaces.Length(); i++) {
110 FontEntry *fe = mFaces[i];
111 const PRUint8 weight = (fe->mWeight / 100);
112 if (fe->mItalic == italic) {
113 weightList[weight] = fe;
114 matchesSomething = PR_TRUE;
117 if (matchesSomething)
118 break;
119 italic = !italic;
122 PRInt8 baseWeight, weightDistance;
123 aFontStyle.ComputeWeightAndOffset(&baseWeight, &weightDistance);
125 // 500 isn't quite bold so we want to treat it as 400 if we don't
126 // have a 500 weight
127 if (baseWeight == 5 && weightDistance == 0) {
128 // If we have a 500 weight then use it
129 if (weightList[5])
130 return weightList[5];
132 // Otherwise treat as 400
133 baseWeight = 4;
136 PRInt8 matchBaseWeight = 0;
137 PRInt8 direction = (baseWeight > 5) ? 1 : -1;
138 for (PRInt8 i = baseWeight; ; i += direction) {
139 if (weightList[i]) {
140 matchBaseWeight = i;
141 break;
144 // if we've reached one side without finding a font,
145 // go the other direction until we find a match
146 if (i == 1 || i == 9)
147 direction = -direction;
150 FontEntry *matchFE;
151 const PRInt8 absDistance = abs(weightDistance);
152 direction = (weightDistance >= 0) ? 1 : -1;
153 for (PRInt8 i = matchBaseWeight, k = 0; i < 10 && i > 0; i += direction) {
154 if (weightList[i]) {
155 matchFE = weightList[i];
156 k++;
158 if (k > absDistance)
159 break;
162 if (!matchFE)
163 matchFE = weightList[matchBaseWeight];
165 NS_ASSERTION(matchFE, "we should always be able to return something here");
166 return matchFE;
172 * gfxFT2FontGroup
175 PRBool
176 gfxFT2FontGroup::FontCallback(const nsAString& fontName,
177 const nsACString& genericName,
178 void *closure)
180 nsStringArray *sa = static_cast<nsStringArray*>(closure);
182 if (!fontName.IsEmpty() && sa->IndexOf(fontName) < 0) {
183 sa->AppendString(fontName);
184 #ifdef DEBUG_pavlov
185 printf(" - %s\n", NS_ConvertUTF16toUTF8(fontName).get());
186 #endif
189 return PR_TRUE;
193 * Look up the font in the gfxFont cache. If we don't find it, create one.
194 * In either case, add a ref, append it to the aFonts array, and return it ---
195 * except for OOM in which case we do nothing and return null.
197 static already_AddRefed<gfxFT2Font>
198 GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle)
200 nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(aName, aStyle);
201 if (!font) {
202 FontEntry *fe = gfxToolkitPlatform::GetPlatform()->FindFontEntry(aName, *aStyle);
203 if (!fe) {
204 printf("Failed to find font entry for %s\n", NS_ConvertUTF16toUTF8(aName).get());
205 return nsnull;
208 font = new gfxFT2Font(fe, aStyle);
209 if (!font)
210 return nsnull;
211 gfxFontCache::GetCache()->AddNew(font);
213 gfxFont *f = nsnull;
214 font.swap(f);
215 return static_cast<gfxFT2Font *>(f);
219 gfxFT2FontGroup::gfxFT2FontGroup(const nsAString& families,
220 const gfxFontStyle *aStyle)
221 : gfxFontGroup(families, aStyle)
223 #ifdef DEBUG_pavlov
224 printf("Looking for %s\n", NS_ConvertUTF16toUTF8(families).get());
225 #endif
226 nsStringArray familyArray;
227 ForEachFont(FontCallback, &familyArray);
229 if (familyArray.Count() == 0) {
230 nsAutoString prefFamilies;
231 gfxToolkitPlatform::GetPlatform()->GetPrefFonts(aStyle->langGroup.get(), prefFamilies, nsnull);
232 if (!prefFamilies.IsEmpty()) {
233 ForEachFont(prefFamilies, aStyle->langGroup, FontCallback, &familyArray);
236 #if defined(MOZ_WIDGET_QT) /* FIXME DFB */
237 if (familyArray.Count() == 0) {
238 printf("failde to find a font. sadface\n");
239 // We want to get rid of this entirely at some point, but first we need real lists of fonts.
240 QFont defaultFont;
241 QFontInfo fi (defaultFont);
242 familyArray.AppendString(nsDependentString(static_cast<const PRUnichar *>(fi.family().utf16())));
244 #endif
246 for (int i = 0; i < familyArray.Count(); i++) {
247 nsRefPtr<gfxFT2Font> font = GetOrMakeFont(*familyArray[i], &mStyle);
248 if (font) {
249 mFonts.AppendElement(font);
254 gfxFT2FontGroup::~gfxFT2FontGroup()
258 gfxFontGroup *
259 gfxFT2FontGroup::Copy(const gfxFontStyle *aStyle)
261 return new gfxFT2FontGroup(mFamilies, aStyle);
265 * We use this to append an LTR or RTL Override character to the start of the
266 * string. This forces Pango to honour our direction even if there are neutral
267 * characters in the string.
269 static PRInt32 AppendDirectionalIndicatorUTF8(PRBool aIsRTL, nsACString& aString)
271 static const PRUnichar overrides[2][2] = { { 0x202d, 0 }, { 0x202e, 0 }}; // LRO, RLO
272 AppendUTF16toUTF8(overrides[aIsRTL], aString);
273 return 3; // both overrides map to 3 bytes in UTF8
276 gfxTextRun *gfxFT2FontGroup::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
277 const Parameters* aParams, PRUint32 aFlags)
279 gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
280 if (!textRun)
281 return nsnull;
283 textRun->RecordSurrogates(aString);
285 mString.Assign(nsDependentSubstring(aString, aString + aLength));
287 InitTextRun(textRun);
289 textRun->FetchGlyphExtents(aParams->mContext);
291 return textRun;
294 gfxTextRun *gfxFT2FontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
295 const Parameters *aParams, PRUint32 aFlags)
297 NS_ASSERTION(aFlags & TEXT_IS_8BIT, "8bit should have been set");
298 gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
299 if (!textRun)
300 return nsnull;
302 const char *chars = reinterpret_cast<const char *>(aString);
304 mString.Assign(NS_ConvertASCIItoUTF16(nsDependentCSubstring(chars, chars + aLength)));
306 InitTextRun(textRun);
308 textRun->FetchGlyphExtents(aParams->mContext);
310 return textRun;
313 void gfxFT2FontGroup::InitTextRun(gfxTextRun *aTextRun)
315 CreateGlyphRunsFT(aTextRun);
319 // Helper function to return the leading UTF-8 character in a char pointer
320 // as 32bit number. Also sets the length of the current character (i.e. the
321 // offset to the next one) in the second argument
322 PRUint32 getUTF8CharAndNext(const PRUint8 *aString, PRUint8 *aLength)
324 *aLength = 1;
325 if (aString[0] < 0x80) { // normal 7bit ASCII char
326 return aString[0];
328 if ((aString[0] >> 5) == 6) { // two leading ones -> two bytes
329 *aLength = 2;
330 return ((aString[0] & 0x1F) << 6) + (aString[1] & 0x3F);
332 if ((aString[0] >> 4) == 14) { // three leading ones -> three bytes
333 *aLength = 3;
334 return ((aString[0] & 0x0F) << 12) + ((aString[1] & 0x3F) << 6) +
335 (aString[2] & 0x3F);
337 if ((aString[0] >> 4) == 15) { // four leading ones -> four bytes
338 *aLength = 4;
339 return ((aString[0] & 0x07) << 18) + ((aString[1] & 0x3F) << 12) +
340 ((aString[2] & 0x3F) << 6) + (aString[3] & 0x3F);
342 return aString[0];
352 PRBool
353 HasCharacter(gfxFT2Font *aFont, PRUint32 ch)
355 if (aFont->GetFontEntry()->mCharacterMap.test(ch))
356 return PR_TRUE;
358 // XXX move this lock way way out
359 FT_Face face = cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont());
360 FT_UInt gid = FT_Get_Char_Index(face, ch);
361 cairo_ft_scaled_font_unlock_face(aFont->CairoScaledFont());
363 if (gid != 0) {
364 aFont->GetFontEntry()->mCharacterMap.set(ch);
365 return PR_TRUE;
367 return PR_FALSE;
370 #if 0
371 inline FontEntry *
372 gfxFT2FontGroup::WhichFontSupportsChar(const nsTArray<>& foo, PRUint32 ch)
374 for (int i = 0; i < aGroup->FontListLength(); i++) {
375 nsRefPtr<gfxFT2Font> font = aGroup->GetFontAt(i);
376 if (HasCharacter(font, ch))
377 return font;
379 return nsnull;
381 #endif
383 inline gfxFT2Font *
384 gfxFT2FontGroup::FindFontForChar(PRUint32 ch, PRUint32 prevCh, PRUint32 nextCh, gfxFT2Font *aFont)
386 gfxFT2Font *selectedFont;
388 // if this character or the next one is a joiner use the
389 // same font as the previous range if we can
390 if (gfxFontUtils::IsJoiner(ch) || gfxFontUtils::IsJoiner(prevCh) || gfxFontUtils::IsJoiner(nextCh)) {
391 if (aFont && HasCharacter(aFont, ch))
392 return aFont;
395 for (PRUint32 i = 0; i < FontListLength(); i++) {
396 nsRefPtr<gfxFT2Font> font = GetFontAt(i);
397 if (HasCharacter(font, ch))
398 return font;
400 return nsnull;
402 #if 0
403 // check the list of fonts
404 selectedFont = WhichFontSupportsChar(mGroup->GetFontList(), ch);
407 // don't look in other fonts if the character is in a Private Use Area
408 if ((ch >= 0xE000 && ch <= 0xF8FF) ||
409 (ch >= 0xF0000 && ch <= 0x10FFFD))
410 return selectedFont;
412 // check out the style's language group
413 if (!selectedFont) {
414 nsAutoTArray<nsRefPtr<FontEntry>, 5> fonts;
415 this->GetPrefFonts(mGroup->GetStyle()->langGroup.get(), fonts);
416 selectedFont = WhichFontSupportsChar(fonts, ch);
419 // otherwise search prefs
420 if (!selectedFont) {
421 /* first check with the script properties to see what they think */
422 if (ch <= 0xFFFF) {
423 PRUint32 unicodeRange = FindCharUnicodeRange(ch);
425 /* special case CJK */
426 if (unicodeRange == kRangeSetCJK) {
427 if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
428 PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: CJK"));
430 nsAutoTArray<nsRefPtr<FontEntry>, 15> fonts;
431 this->GetCJKPrefFonts(fonts);
432 selectedFont = WhichFontSupportsChar(fonts, ch);
433 } else {
434 const char *langGroup = LangGroupFromUnicodeRange(unicodeRange);
435 if (langGroup) {
436 PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: %s", langGroup));
438 nsAutoTArray<nsRefPtr<FontEntry>, 5> fonts;
439 this->GetPrefFonts(langGroup, fonts);
440 selectedFont = WhichFontSupportsChar(fonts, ch);
446 // before searching for something else check the font used for the previous character
447 if (!selectedFont && aFont && HasCharacter(aFont, ch))
448 selectedFont = aFont;
450 // otherwise look for other stuff
451 if (!selectedFont) {
452 PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Looking for best match"));
454 nsRefPtr<gfxWindowsFont> refFont = mGroup->GetFontAt(0);
455 gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
456 selectedFont = platform->FindFontForChar(ch, refFont);
459 return selectedFont;
460 #endif
463 PRUint32
464 gfxFT2FontGroup::ComputeRanges()
466 const PRUnichar *str = mString.get();
467 PRUint32 len = mString.Length();
469 mRanges.Clear();
471 PRUint32 prevCh = 0;
472 for (PRUint32 i = 0; i < len; i++) {
473 const PRUint32 origI = i; // save off incase we increase for surrogate
474 PRUint32 ch = str[i];
475 if ((i+1 < len) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(str[i+1])) {
476 i++;
477 ch = SURROGATE_TO_UCS4(ch, str[i]);
480 PRUint32 nextCh = 0;
481 if (i+1 < len) {
482 nextCh = str[i+1];
483 if ((i+2 < len) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(str[i+2]))
484 nextCh = SURROGATE_TO_UCS4(nextCh, str[i+2]);
486 gfxFT2Font *fe = FindFontForChar(ch,
487 prevCh,
488 nextCh,
489 (mRanges.Length() == 0) ? nsnull : mRanges[mRanges.Length() - 1].font);
491 prevCh = ch;
493 if (mRanges.Length() == 0) {
494 TextRange r(0,1);
495 r.font = fe;
496 mRanges.AppendElement(r);
497 } else {
498 TextRange& prevRange = mRanges[mRanges.Length() - 1];
499 if (prevRange.font != fe) {
500 // close out the previous range
501 prevRange.end = origI;
503 TextRange r(origI, i+1);
504 r.font = fe;
505 mRanges.AppendElement(r);
509 mRanges[mRanges.Length()-1].end = len;
511 PRUint32 nranges = mRanges.Length();
512 return nranges;
515 void gfxFT2FontGroup::CreateGlyphRunsFT(gfxTextRun *aTextRun)
517 ComputeRanges();
519 const PRUnichar *strStart = mString.get();
520 for (PRUint32 i = 0; i < mRanges.Length(); ++i) {
521 const TextRange& range = mRanges[i];
522 const PRUnichar *rangeString = strStart + range.start;
523 PRUint32 rangeLength = range.Length();
525 gfxFT2Font *font = range.font ? range.font.get() : GetFontAt(0);
526 AddRange(aTextRun, font, rangeString, rangeLength);
531 void
532 gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnichar *str, PRUint32 len)
534 const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
535 // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
536 FT_Face face = cairo_ft_scaled_font_lock_face(font->CairoScaledFont());
538 gfxTextRun::CompressedGlyph g;
540 aTextRun->AddGlyphRun(font, 0);
541 for (PRUint32 i = 0; i < len; i++) {
542 PRUint32 ch = str[i];
544 if (ch == 0) {
545 // treat this null byte as a missing glyph, don't create a glyph for it
546 aTextRun->SetMissingGlyph(i, 0);
547 continue;
550 NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected");
551 FT_UInt gid = FT_Get_Char_Index(face, ch); // find the glyph id
552 PRInt32 advance = 0;
554 if (gid == font->GetSpaceGlyph()) {
555 advance = (int)(font->GetMetrics().spaceWidth * appUnitsPerDevUnit);
556 } else if (gid == 0) {
557 advance = -1; // trigger the missing glyphs case below
558 } else {
559 // find next character and its glyph -- in case they exist
560 // and exist in the current font face -- to compute kerning
561 PRUint32 chNext = 0;
562 FT_UInt gidNext = 0;
563 FT_Pos lsbDeltaNext = 0;
565 if (FT_HAS_KERNING(face) && i + 1 < len) {
566 chNext = str[i+1];
567 if (chNext != 0) {
568 gidNext = FT_Get_Char_Index(face, chNext);
569 if (gidNext && gidNext != font->GetSpaceGlyph()) {
570 FT_Load_Glyph(face, gidNext, FT_LOAD_DEFAULT);
571 lsbDeltaNext = face->glyph->lsb_delta;
576 // now load the current glyph
577 FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT); // load glyph into the slot
578 advance = face->glyph->advance.x;
580 // now add kerning to the current glyph's advance
581 if (chNext && gidNext) {
582 FT_Vector kerning; kerning.x = 0;
583 FT_Get_Kerning(face, gid, gidNext, FT_KERNING_DEFAULT, &kerning);
584 advance += kerning.x;
585 if (face->glyph->rsb_delta - lsbDeltaNext >= 32) {
586 advance -= 64;
587 } else if (face->glyph->rsb_delta - lsbDeltaNext < -32) {
588 advance += 64;
592 // now apply unit conversion and scaling
593 advance = (advance >> 6) * appUnitsPerDevUnit;
595 #ifdef DEBUG_thebes_2
596 printf(" gid=%d, advance=%d (%s)\n", gid, advance,
597 NS_LossyConvertUTF16toASCII(font->GetName()).get());
598 #endif
600 if (advance >= 0 &&
601 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
602 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(gid)) {
603 aTextRun->SetSimpleGlyph(i, g.SetSimpleGlyph(advance, gid));
604 } else if (gid == 0) {
605 // gid = 0 only happens when the glyph is missing from the font
606 aTextRun->SetMissingGlyph(i, ch);
607 } else {
608 gfxTextRun::DetailedGlyph details;
609 details.mGlyphID = gid;
610 NS_ASSERTION(details.mGlyphID == gid, "Seriously weird glyph ID detected!");
611 details.mAdvance = advance;
612 details.mXOffset = 0;
613 details.mYOffset = 0;
614 g.SetComplex(aTextRun->IsClusterStart(i), PR_TRUE, 1);
615 aTextRun->SetGlyphs(i, g, &details);
619 cairo_ft_scaled_font_unlock_face(font->CairoScaledFont());
623 * gfxFT2Font
625 gfxFT2Font::gfxFT2Font(FontEntry *aFontEntry,
626 const gfxFontStyle *aFontStyle)
627 : gfxFont(aFontEntry, aFontStyle),
628 mScaledFont(nsnull),
629 mHasSpaceGlyph(PR_FALSE),
630 mSpaceGlyph(0),
631 mHasMetrics(PR_FALSE),
632 mAdjustedSize(0)
634 mFontEntry = aFontEntry;
635 NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
638 gfxFT2Font::~gfxFT2Font()
640 if (mScaledFont) {
641 cairo_scaled_font_destroy(mScaledFont);
642 mScaledFont = nsnull;
646 // rounding and truncation functions for a Freetype floating point number
647 // (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
648 // part and low 6 bits for the fractional part.
649 #define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1
650 #define MOZ_FT_TRUNC(x) ((x) >> 6)
651 #define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \
652 MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s))))
654 const gfxFont::Metrics&
655 gfxFT2Font::GetMetrics()
657 if (mHasMetrics)
658 return mMetrics;
660 mMetrics.emHeight = GetStyle()->size;
662 FT_Face face = cairo_ft_scaled_font_lock_face(CairoScaledFont());
664 if (!face) {
665 // Abort here already, otherwise we crash in the following
666 // this can happen if the font-size requested is zero.
667 // The metrics will be incomplete, but then we don't care.
668 return mMetrics;
671 mMetrics.emHeight = GetStyle()->size;
673 FT_UInt gid; // glyph ID
675 const double emUnit = 1.0 * face->units_per_EM;
676 const double xScale = face->size->metrics.x_ppem / emUnit;
677 const double yScale = face->size->metrics.y_ppem / emUnit;
679 // properties of space
680 gid = FT_Get_Char_Index(face, ' ');
681 if (gid) {
682 FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT);
683 // face->glyph->metrics.width doesn't work for spaces, use advance.x instead
684 mMetrics.spaceWidth = face->glyph->advance.x >> 6;
685 // save the space glyph
686 mSpaceGlyph = gid;
687 } else {
688 NS_ASSERTION(0, "blah");
691 // properties of 'x', also use its width as average width
692 gid = FT_Get_Char_Index(face, 'x'); // select the glyph
693 if (gid) {
694 // Load glyph into glyph slot. Here, use no_scale to get font units.
695 FT_Load_Glyph(face, gid, FT_LOAD_NO_SCALE);
696 mMetrics.xHeight = face->glyph->metrics.height * yScale;
697 mMetrics.aveCharWidth = face->glyph->metrics.width * xScale;
698 } else {
699 // this font doesn't have an 'x'...
700 // fake these metrics using a fraction of the font size
701 mMetrics.xHeight = mMetrics.emHeight * 0.5;
702 mMetrics.aveCharWidth = mMetrics.emHeight * 0.5;
705 // compute an adjusted size if we need to
706 if (mAdjustedSize == 0 && GetStyle()->sizeAdjust != 0) {
707 gfxFloat aspect = mMetrics.xHeight / GetStyle()->size;
708 mAdjustedSize = GetStyle()->GetAdjustedSize(aspect);
709 mMetrics.emHeight = mAdjustedSize;
712 // now load the OS/2 TrueType table to load access some more properties
713 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
714 if (os2 && os2->version != 0xFFFF) { // should be there if not old Mac font
715 // if we are here we can improve the avgCharWidth
716 mMetrics.aveCharWidth = os2->xAvgCharWidth * xScale;
718 mMetrics.superscriptOffset = os2->ySuperscriptYOffset * yScale;
719 mMetrics.superscriptOffset = PR_MAX(1, mMetrics.superscriptOffset);
720 // some fonts have the incorrect sign (from gfxPangoFonts)
721 mMetrics.subscriptOffset = fabs(os2->ySubscriptYOffset * yScale);
722 mMetrics.subscriptOffset = PR_MAX(1, fabs(mMetrics.subscriptOffset));
723 mMetrics.strikeoutOffset = os2->yStrikeoutPosition * yScale;
724 mMetrics.strikeoutSize = os2->yStrikeoutSize * yScale;
725 } else {
726 // use fractions of emHeight instead of xHeight for these to be more robust
727 mMetrics.superscriptOffset = mMetrics.emHeight * 0.5;
728 mMetrics.subscriptOffset = mMetrics.emHeight * 0.2;
729 mMetrics.strikeoutOffset = mMetrics.emHeight * 0.3;
730 mMetrics.strikeoutSize = face->underline_thickness * yScale;
732 // seems that underlineOffset really has to be negative
733 mMetrics.underlineOffset = face->underline_position * yScale;
734 mMetrics.underlineSize = face->underline_thickness * yScale;
736 // descents are negative in FT but Thebes wants them positive
737 mMetrics.emAscent = face->ascender * yScale;
738 mMetrics.emDescent = -face->descender * yScale;
739 mMetrics.maxHeight = face->height * yScale;
740 mMetrics.maxAscent = face->bbox.yMax * yScale;
741 mMetrics.maxDescent = -face->bbox.yMin * yScale;
742 mMetrics.maxAdvance = face->max_advance_width * xScale;
743 // leading are not available directly (only for WinFNTs)
744 double lineHeight = mMetrics.maxAscent + mMetrics.maxDescent;
745 if (lineHeight > mMetrics.emHeight) {
746 mMetrics.internalLeading = lineHeight - mMetrics.emHeight;
747 } else {
748 mMetrics.internalLeading = 0;
750 mMetrics.externalLeading = 0; // normal value for OS/2 fonts, too
752 SanitizeMetrics(&mMetrics, PR_FALSE);
755 printf("gfxOS2Font[%#x]::GetMetrics():\n"
756 " emHeight=%f == %f=gfxFont::style.size == %f=adjSz\n"
757 " maxHeight=%f xHeight=%f\n"
758 " aveCharWidth=%f==xWidth spaceWidth=%f\n"
759 " supOff=%f SubOff=%f strOff=%f strSz=%f\n"
760 " undOff=%f undSz=%f intLead=%f extLead=%f\n"
761 " emAsc=%f emDesc=%f maxH=%f\n"
762 " maxAsc=%f maxDes=%f maxAdv=%f\n",
763 (unsigned)this,
764 mMetrics.emHeight, GetStyle()->size, mAdjustedSize,
765 mMetrics.maxHeight, mMetrics.xHeight,
766 mMetrics.aveCharWidth, mMetrics.spaceWidth,
767 mMetrics.superscriptOffset, mMetrics.subscriptOffset,
768 mMetrics.strikeoutOffset, mMetrics.strikeoutSize,
769 mMetrics.underlineOffset, mMetrics.underlineSize,
770 mMetrics.internalLeading, mMetrics.externalLeading,
771 mMetrics.emAscent, mMetrics.emDescent, mMetrics.maxHeight,
772 mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance
776 // XXX mMetrics.height needs to be set.
777 cairo_ft_scaled_font_unlock_face(CairoScaledFont());
779 mHasMetrics = PR_TRUE;
780 return mMetrics;
784 nsString
785 gfxFT2Font::GetUniqueName()
787 return GetName();
790 PRUint32
791 gfxFT2Font::GetSpaceGlyph()
793 NS_ASSERTION (GetStyle ()->size != 0,
794 "forgot to short-circuit a text run with zero-sized font?");
796 if(!mHasSpaceGlyph)
798 FT_UInt gid = 0; // glyph ID
799 FT_Face face = cairo_ft_scaled_font_lock_face(CairoScaledFont());
800 gid = FT_Get_Char_Index(face, ' ');
801 FT_Load_Glyph(face, gid, FT_LOAD_DEFAULT);
802 mSpaceGlyph = gid;
803 mHasSpaceGlyph = PR_TRUE;
804 cairo_ft_scaled_font_unlock_face(CairoScaledFont());
806 return mSpaceGlyph;
809 cairo_font_face_t *
810 gfxFT2Font::CairoFontFace()
812 // XXX we need to handle fake bold here (or by having a sepaerate font entry)
813 if (mStyle.weight >= 600 && mFontEntry->mWeight < 600)
814 printf("** We want fake weight\n");
815 return GetFontEntry()->CairoFontFace();
818 cairo_scaled_font_t *
819 gfxFT2Font::CairoScaledFont()
821 if (!mScaledFont) {
822 cairo_matrix_t sizeMatrix;
823 cairo_matrix_t identityMatrix;
825 // XXX deal with adjusted size
826 cairo_matrix_init_scale(&sizeMatrix, mStyle.size, mStyle.size);
827 cairo_matrix_init_identity(&identityMatrix);
829 // synthetic oblique by skewing via the font matrix
830 PRBool needsOblique = (!mFontEntry->mItalic && (mStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)));
832 if (needsOblique) {
833 const double kSkewFactor = 0.25;
835 cairo_matrix_t style;
836 cairo_matrix_init(&style,
837 1, //xx
838 0, //yx
839 -1 * kSkewFactor, //xy
840 1, //yy
841 0, //x0
842 0); //y0
843 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
846 cairo_font_options_t *fontOptions = cairo_font_options_create();
847 mScaledFont = cairo_scaled_font_create(CairoFontFace(), &sizeMatrix,
848 &identityMatrix, fontOptions);
849 cairo_font_options_destroy(fontOptions);
852 NS_ASSERTION(mAdjustedSize == 0.0 ||
853 cairo_scaled_font_status(mScaledFont) == CAIRO_STATUS_SUCCESS,
854 "Failed to make scaled font");
856 return mScaledFont;
859 PRBool
860 gfxFT2Font::SetupCairoFont(gfxContext *aContext)
862 cairo_scaled_font_t *scaledFont = CairoScaledFont();
864 if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
865 // Don't cairo_set_scaled_font as that would propagate the error to
866 // the cairo_t, precluding any further drawing.
867 return PR_FALSE;
869 cairo_set_scaled_font(aContext->GetCairo(), scaledFont);
870 return PR_TRUE;