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
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.
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
,
51 : gfxFont(aFontEntry
, aFontStyle
),
52 mATSFont(aFontEntry
->GetFontRef()),
58 mSyntheticBoldOffset
= 1; // devunit offset when double-striking text to fake boldness
61 mCGFont
= ::CGFontCreateWithPlatformFont(&mATSFont
);
67 // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
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
) {
80 sprintf(warnBuf
, "Failed to create Cairo font face: %s status: %d",
81 NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr
);
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
93 (mFontEntry
!= NULL
) &&
94 (!mFontEntry
->IsItalic() &&
95 (mStyle
.style
& (FONT_STYLE_ITALIC
| FONT_STYLE_OBLIQUE
)));
98 double skewfactor
= (needsOblique
? Fix2X(kATSItalicQDSkew
) : 0);
100 cairo_matrix_t style
;
101 cairo_matrix_init(&style
,
104 -1 * skewfactor
, //xy
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
115 (gfxFloat
)gfxPlatformMac::GetPlatform()->GetAntiAliasingThreshold()) {
116 cairo_font_options_set_antialias(fontOptions
, CAIRO_ANTIALIAS_NONE
);
119 mScaledFont
= cairo_scaled_font_create(mFontFace
, &sizeMatrix
, &ctm
,
121 cairo_font_options_destroy(fontOptions
);
123 cairoerr
= cairo_scaled_font_status(mScaledFont
);
124 if (cairoerr
!= CAIRO_STATUS_SUCCESS
) {
128 sprintf(warnBuf
, "Failed to create scaled font: %s status: %d",
129 NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr
);
134 if (FontCanSupportHarfBuzz()) {
135 mHarfBuzzShaper
= new gfxHarfBuzzShaper(this);
139 gfxMacFont::~gfxMacFont()
142 cairo_scaled_font_destroy(mScaledFont
);
145 cairo_font_face_destroy(mFontFace
);
148 // this is documented to be safe if mCGFont is null
149 ::CGFontRelease(mCGFont
);
153 gfxMacFont::CreatePlatformShaper()
155 mPlatformShaper
= new gfxCoreTextShaper(this);
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.
166 cairo_set_scaled_font(aContext
->GetCairo(), mScaledFont
);
171 gfxMacFont::InitMetrics()
174 ::memset(&mMetrics
, 0, sizeof(mMetrics
));
176 PRUint32 upem
= ::CGFontGetUnitsPerEm(mCGFont
);
180 sprintf(warnBuf
, "Bad font metrics for: %s (no unitsPerEm value)",
181 NS_ConvertUTF16toUTF8(mFontEntry
->Name()).get());
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();
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();
216 // this shouldn't happen, as we succeeded earlier before applying
217 // the size-adjust factor! But check anyway, for paranoia's sake.
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
235 ::CGFontCopyTableForTag(mCGFont
, TRUETYPE_TAG('c','m','a','p'));
238 if (mMetrics
.aveCharWidth
<= 0) {
239 mMetrics
.aveCharWidth
= GetCharWidth(cmap
, 'x', &glyphID
);
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
);
251 mMetrics
.spaceWidth
= mMetrics
.aveCharWidth
;
253 mSpaceGlyph
= glyphID
;
255 mMetrics
.zeroOrAveCharWidth
= GetCharWidth(cmap
, '0', &glyphID
);
257 mMetrics
.zeroOrAveCharWidth
= mMetrics
.aveCharWidth
;
264 CalculateDerivedMetrics(mMetrics
);
266 SanitizeMetrics(&mMetrics
, mFontEntry
->mIsBadUnderlineFont
);
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
);
281 gfxMacFont::GetCharWidth(CFDataRef aCmap
, PRUnichar aUniChar
,
287 glyph
= gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap
),
288 ::CFDataGetLength(aCmap
),
298 if (::CGFontGetGlyphAdvances(mCGFont
, &glyph
, 1, &advance
)) {
299 return advance
* mFUnitsConvFactor
;
307 gfxMacFont::DestroyBlobFunc(void* aUserData
)
309 ::CFRelease((CFDataRef
)aUserData
);
313 gfxMacFont::GetFontTable(PRUint32 aTag
)
315 CFDataRef dataRef
= ::CGFontCopyTableForTag(mCGFont
, aTag
);
317 return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef
),
318 ::CFDataGetLength(dataRef
),
319 HB_MEMORY_MODE_READONLY
,
320 DestroyBlobFunc
, (void*)dataRef
);
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.
333 gfxMacFont::InitMetricsFromATSMetrics()
335 ATSFontMetrics atsMetrics
;
338 err
= ::ATSFontGetHorizontalMetrics(mATSFont
, kATSOptionFlagsDefault
,
343 sprintf(warnBuf
, "Bad font metrics for: %s err: %8.8x",
344 NS_ConvertUTF16toUTF8(mFontEntry
->Name()).get(), PRUint32(err
));
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
;