Bug 574778 - Fix win widget's ConstrainPosition so that it supports full screen windo...
[mozilla-central.git] / gfx / thebes / gfxMacFont.cpp
blob593d3af6d2f6f26c99ba2368bc17b389eae4e752
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 Corporation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2006-2010
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Vladimir Vukicevic <vladimir@pobox.com>
23 * Masayuki Nakano <masayuki@d-toybox.com>
24 * John Daggett <jdaggett@mozilla.com>
25 * Jonathan Kew <jfkthame@gmail.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "gfxMacFont.h"
42 #include "gfxCoreTextShaper.h"
43 #include "gfxHarfBuzzShaper.h"
44 #include "gfxPlatformMac.h"
45 #include "gfxContext.h"
47 #include "cairo-quartz.h"
49 gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
50 PRBool aNeedsBold)
51 : gfxFont(aFontEntry, aFontStyle),
52 mATSFont(aFontEntry->GetFontRef()),
53 mCGFont(nsnull),
54 mFontFace(nsnull),
55 mScaledFont(nsnull)
57 if (aNeedsBold) {
58 mSyntheticBoldOffset = 1; // devunit offset when double-striking text to fake boldness
61 mCGFont = ::CGFontCreateWithPlatformFont(&mATSFont);
62 if (!mCGFont) {
63 mIsValid = PR_FALSE;
64 return;
67 // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
68 InitMetrics();
69 if (!mIsValid) {
70 return;
73 mFontFace = cairo_quartz_font_face_create_for_cgfont(mCGFont);
75 cairo_status_t cairoerr = cairo_font_face_status(mFontFace);
76 if (cairoerr != CAIRO_STATUS_SUCCESS) {
77 mIsValid = PR_FALSE;
78 #ifdef DEBUG
79 char warnBuf[1024];
80 sprintf(warnBuf, "Failed to create Cairo font face: %s status: %d",
81 NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
82 NS_WARNING(warnBuf);
83 #endif
84 return;
87 cairo_matrix_t sizeMatrix, ctm;
88 cairo_matrix_init_identity(&ctm);
89 cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
91 // synthetic oblique by skewing via the font matrix
92 PRBool needsOblique =
93 (mFontEntry != NULL) &&
94 (!mFontEntry->IsItalic() &&
95 (mStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)));
97 if (needsOblique) {
98 double skewfactor = (needsOblique ? Fix2X(kATSItalicQDSkew) : 0);
100 cairo_matrix_t style;
101 cairo_matrix_init(&style,
102 1, //xx
103 0, //yx
104 -1 * skewfactor, //xy
105 1, //yy
106 0, //x0
107 0); //y0
108 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
111 cairo_font_options_t *fontOptions = cairo_font_options_create();
113 // turn off font anti-aliasing based on user pref setting
114 if (mAdjustedSize <=
115 (gfxFloat)gfxPlatformMac::GetPlatform()->GetAntiAliasingThreshold()) {
116 cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_NONE);
119 mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix, &ctm,
120 fontOptions);
121 cairo_font_options_destroy(fontOptions);
123 cairoerr = cairo_scaled_font_status(mScaledFont);
124 if (cairoerr != CAIRO_STATUS_SUCCESS) {
125 mIsValid = PR_FALSE;
126 #ifdef DEBUG
127 char warnBuf[1024];
128 sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
129 NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
130 NS_WARNING(warnBuf);
131 #endif
134 if (FontCanSupportHarfBuzz()) {
135 mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
139 gfxMacFont::~gfxMacFont()
141 if (mScaledFont) {
142 cairo_scaled_font_destroy(mScaledFont);
144 if (mFontFace) {
145 cairo_font_face_destroy(mFontFace);
148 // this is documented to be safe if mCGFont is null
149 ::CGFontRelease(mCGFont);
152 void
153 gfxMacFont::CreatePlatformShaper()
155 mPlatformShaper = new gfxCoreTextShaper(this);
158 PRBool
159 gfxMacFont::SetupCairoFont(gfxContext *aContext)
161 if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
162 // Don't cairo_set_scaled_font as that would propagate the error to
163 // the cairo_t, precluding any further drawing.
164 return PR_FALSE;
166 cairo_set_scaled_font(aContext->GetCairo(), mScaledFont);
167 return PR_TRUE;
170 void
171 gfxMacFont::InitMetrics()
173 mIsValid = PR_FALSE;
174 ::memset(&mMetrics, 0, sizeof(mMetrics));
176 PRUint32 upem = ::CGFontGetUnitsPerEm(mCGFont);
177 if (!upem) {
178 #ifdef DEBUG
179 char warnBuf[1024];
180 sprintf(warnBuf, "Bad font metrics for: %s (no unitsPerEm value)",
181 NS_ConvertUTF16toUTF8(mFontEntry->Name()).get());
182 NS_WARNING(warnBuf);
183 #endif
184 return;
187 mAdjustedSize = PR_MAX(mStyle.size, 1.0f);
188 mFUnitsConvFactor = mAdjustedSize / upem;
190 // Try to read 'sfnt' metrics; for local, non-sfnt fonts ONLY, fall back to
191 // platform APIs. The InitMetrics...() functions will set mIsValid on success.
192 if (!InitMetricsFromSfntTables(mMetrics) &&
193 (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
194 InitMetricsFromATSMetrics();
196 if (!mIsValid) {
197 return;
200 if (mMetrics.xHeight == 0.0) {
201 mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * mFUnitsConvFactor;
204 if (mStyle.sizeAdjust != 0.0 && mStyle.size > 0.0 &&
205 mMetrics.xHeight > 0.0) {
206 // apply font-size-adjust, and recalculate metrics
207 gfxFloat aspect = mMetrics.xHeight / mStyle.size;
208 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
209 mFUnitsConvFactor = mAdjustedSize / upem;
210 mMetrics.xHeight = 0.0;
211 if (!InitMetricsFromSfntTables(mMetrics) &&
212 (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
213 InitMetricsFromATSMetrics();
215 if (!mIsValid) {
216 // this shouldn't happen, as we succeeded earlier before applying
217 // the size-adjust factor! But check anyway, for paranoia's sake.
218 return;
220 if (mMetrics.xHeight == 0.0) {
221 mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * mFUnitsConvFactor;
225 // Once we reach here, we've got basic metrics and set mIsValid = TRUE;
226 // there should be no further points of actual failure in InitMetrics().
227 // (If one is introduced, be sure to reset mIsValid to FALSE!)
229 mMetrics.emHeight = mAdjustedSize;
231 // Measure/calculate additional metrics, independent of whether we used
232 // the tables directly or ATS metrics APIs
234 CFDataRef cmap =
235 ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c','m','a','p'));
237 PRUint32 glyphID;
238 if (mMetrics.aveCharWidth <= 0) {
239 mMetrics.aveCharWidth = GetCharWidth(cmap, 'x', &glyphID);
240 if (glyphID == 0) {
241 // we didn't find 'x', so use maxAdvance rather than zero
242 mMetrics.aveCharWidth = mMetrics.maxAdvance;
245 mMetrics.aveCharWidth += mSyntheticBoldOffset;
246 mMetrics.maxAdvance += mSyntheticBoldOffset;
248 mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID);
249 if (glyphID == 0) {
250 // no space glyph?!
251 mMetrics.spaceWidth = mMetrics.aveCharWidth;
253 mSpaceGlyph = glyphID;
255 mMetrics.zeroOrAveCharWidth = GetCharWidth(cmap, '0', &glyphID);
256 if (glyphID == 0) {
257 mMetrics.zeroOrAveCharWidth = mMetrics.aveCharWidth;
260 if (cmap) {
261 ::CFRelease(cmap);
264 CalculateDerivedMetrics(mMetrics);
266 SanitizeMetrics(&mMetrics, mFontEntry->mIsBadUnderlineFont);
268 #if 0
269 fprintf (stderr, "Font: %p (%s) size: %f\n", this,
270 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
271 // fprintf (stderr, " fbounds.origin.x %f y %f size.width %f height %f\n", fbounds.origin.x, fbounds.origin.y, fbounds.size.width, fbounds.size.height);
272 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
273 fprintf (stderr, " maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
274 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
275 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
276 fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f supOff: %f subOff: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset);
277 #endif
280 gfxFloat
281 gfxMacFont::GetCharWidth(CFDataRef aCmap, PRUnichar aUniChar,
282 PRUint32 *aGlyphID)
284 CGGlyph glyph = 0;
286 if (aCmap) {
287 glyph = gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap),
288 ::CFDataGetLength(aCmap),
289 aUniChar);
292 if (aGlyphID) {
293 *aGlyphID = glyph;
296 if (glyph) {
297 int advance;
298 if (::CGFontGetGlyphAdvances(mCGFont, &glyph, 1, &advance)) {
299 return advance * mFUnitsConvFactor;
303 return 0;
306 /*static*/ void
307 gfxMacFont::DestroyBlobFunc(void* aUserData)
309 ::CFRelease((CFDataRef)aUserData);
312 hb_blob_t *
313 gfxMacFont::GetFontTable(PRUint32 aTag)
315 CFDataRef dataRef = ::CGFontCopyTableForTag(mCGFont, aTag);
316 if (dataRef) {
317 return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
318 ::CFDataGetLength(dataRef),
319 HB_MEMORY_MODE_READONLY,
320 DestroyBlobFunc, (void*)dataRef);
323 return nsnull;
326 // Try to initialize font metrics via ATS font metrics APIs,
327 // and set mIsValid = TRUE on success.
328 // We ONLY call this for local (platform) fonts that are not sfnt format;
329 // for sfnts, including ALL downloadable fonts, use InitMetricsFromSfntTables
330 // because ATSFontGetHorizontalMetrics() has been known to crash when
331 // presented with bad fonts.
332 void
333 gfxMacFont::InitMetricsFromATSMetrics()
335 ATSFontMetrics atsMetrics;
336 OSStatus err;
338 err = ::ATSFontGetHorizontalMetrics(mATSFont, kATSOptionFlagsDefault,
339 &atsMetrics);
340 if (err != noErr) {
341 #ifdef DEBUG
342 char warnBuf[1024];
343 sprintf(warnBuf, "Bad font metrics for: %s err: %8.8x",
344 NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(), PRUint32(err));
345 NS_WARNING(warnBuf);
346 #endif
347 return;
350 mMetrics.underlineOffset = atsMetrics.underlinePosition * mAdjustedSize;
351 mMetrics.underlineSize = atsMetrics.underlineThickness * mAdjustedSize;
353 mMetrics.externalLeading = atsMetrics.leading * mAdjustedSize;
355 mMetrics.maxAscent = atsMetrics.ascent * mAdjustedSize;
356 mMetrics.maxDescent = -atsMetrics.descent * mAdjustedSize;
358 mMetrics.maxAdvance = atsMetrics.maxAdvanceWidth * mAdjustedSize;
359 mMetrics.aveCharWidth = atsMetrics.avgAdvanceWidth * mAdjustedSize;
360 mMetrics.xHeight = atsMetrics.xHeight * mAdjustedSize;
362 mIsValid = PR_TRUE;