gfx/thebes/gfxMacFont.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "gfxMacFont.h"
michael@0 7
michael@0 8 #include "mozilla/MemoryReporting.h"
michael@0 9
michael@0 10 #include "gfxCoreTextShaper.h"
michael@0 11 #include "gfxHarfBuzzShaper.h"
michael@0 12 #include <algorithm>
michael@0 13 #include "gfxGraphiteShaper.h"
michael@0 14 #include "gfxPlatformMac.h"
michael@0 15 #include "gfxContext.h"
michael@0 16 #include "gfxFontUtils.h"
michael@0 17 #include "gfxMacPlatformFontList.h"
michael@0 18 #include "gfxFontConstants.h"
michael@0 19
michael@0 20 #include "cairo-quartz.h"
michael@0 21
michael@0 22 using namespace mozilla;
michael@0 23 using namespace mozilla::gfx;
michael@0 24
michael@0 25 gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
michael@0 26 bool aNeedsBold)
michael@0 27 : gfxFont(aFontEntry, aFontStyle),
michael@0 28 mCGFont(nullptr),
michael@0 29 mFontFace(nullptr)
michael@0 30 {
michael@0 31 mApplySyntheticBold = aNeedsBold;
michael@0 32
michael@0 33 mCGFont = aFontEntry->GetFontRef();
michael@0 34 if (!mCGFont) {
michael@0 35 mIsValid = false;
michael@0 36 return;
michael@0 37 }
michael@0 38
michael@0 39 // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
michael@0 40 InitMetrics();
michael@0 41 if (!mIsValid) {
michael@0 42 return;
michael@0 43 }
michael@0 44
michael@0 45 mFontFace = cairo_quartz_font_face_create_for_cgfont(mCGFont);
michael@0 46
michael@0 47 cairo_status_t cairoerr = cairo_font_face_status(mFontFace);
michael@0 48 if (cairoerr != CAIRO_STATUS_SUCCESS) {
michael@0 49 mIsValid = false;
michael@0 50 #ifdef DEBUG
michael@0 51 char warnBuf[1024];
michael@0 52 sprintf(warnBuf, "Failed to create Cairo font face: %s status: %d",
michael@0 53 NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
michael@0 54 NS_WARNING(warnBuf);
michael@0 55 #endif
michael@0 56 return;
michael@0 57 }
michael@0 58
michael@0 59 cairo_matrix_t sizeMatrix, ctm;
michael@0 60 cairo_matrix_init_identity(&ctm);
michael@0 61 cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
michael@0 62
michael@0 63 // synthetic oblique by skewing via the font matrix
michael@0 64 bool needsOblique =
michael@0 65 (mFontEntry != nullptr) &&
michael@0 66 (!mFontEntry->IsItalic() &&
michael@0 67 (mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)));
michael@0 68
michael@0 69 if (needsOblique) {
michael@0 70 double skewfactor = (needsOblique ? Fix2X(kATSItalicQDSkew) : 0);
michael@0 71
michael@0 72 cairo_matrix_t style;
michael@0 73 cairo_matrix_init(&style,
michael@0 74 1, //xx
michael@0 75 0, //yx
michael@0 76 -1 * skewfactor, //xy
michael@0 77 1, //yy
michael@0 78 0, //x0
michael@0 79 0); //y0
michael@0 80 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
michael@0 81 }
michael@0 82
michael@0 83 cairo_font_options_t *fontOptions = cairo_font_options_create();
michael@0 84
michael@0 85 // turn off font anti-aliasing based on user pref setting
michael@0 86 if (mAdjustedSize <=
michael@0 87 (gfxFloat)gfxPlatformMac::GetPlatform()->GetAntiAliasingThreshold()) {
michael@0 88 cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_NONE);
michael@0 89 mAntialiasOption = kAntialiasNone;
michael@0 90 } else if (mStyle.useGrayscaleAntialiasing) {
michael@0 91 cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_GRAY);
michael@0 92 mAntialiasOption = kAntialiasGrayscale;
michael@0 93 }
michael@0 94
michael@0 95 mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix, &ctm,
michael@0 96 fontOptions);
michael@0 97 cairo_font_options_destroy(fontOptions);
michael@0 98
michael@0 99 cairoerr = cairo_scaled_font_status(mScaledFont);
michael@0 100 if (cairoerr != CAIRO_STATUS_SUCCESS) {
michael@0 101 mIsValid = false;
michael@0 102 #ifdef DEBUG
michael@0 103 char warnBuf[1024];
michael@0 104 sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
michael@0 105 NS_ConvertUTF16toUTF8(GetName()).get(), cairoerr);
michael@0 106 NS_WARNING(warnBuf);
michael@0 107 #endif
michael@0 108 }
michael@0 109
michael@0 110 if (FontCanSupportGraphite()) {
michael@0 111 mGraphiteShaper = new gfxGraphiteShaper(this);
michael@0 112 }
michael@0 113 if (FontCanSupportHarfBuzz()) {
michael@0 114 mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 gfxMacFont::~gfxMacFont()
michael@0 119 {
michael@0 120 if (mScaledFont) {
michael@0 121 cairo_scaled_font_destroy(mScaledFont);
michael@0 122 }
michael@0 123 if (mFontFace) {
michael@0 124 cairo_font_face_destroy(mFontFace);
michael@0 125 }
michael@0 126 }
michael@0 127
michael@0 128 bool
michael@0 129 gfxMacFont::ShapeText(gfxContext *aContext,
michael@0 130 const char16_t *aText,
michael@0 131 uint32_t aOffset,
michael@0 132 uint32_t aLength,
michael@0 133 int32_t aScript,
michael@0 134 gfxShapedText *aShapedText,
michael@0 135 bool aPreferPlatformShaping)
michael@0 136 {
michael@0 137 if (!mIsValid) {
michael@0 138 NS_WARNING("invalid font! expect incorrect text rendering");
michael@0 139 return false;
michael@0 140 }
michael@0 141
michael@0 142 bool requiresAAT =
michael@0 143 static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout();
michael@0 144 return gfxFont::ShapeText(aContext, aText, aOffset, aLength,
michael@0 145 aScript, aShapedText, requiresAAT);
michael@0 146 }
michael@0 147
michael@0 148 void
michael@0 149 gfxMacFont::CreatePlatformShaper()
michael@0 150 {
michael@0 151 mPlatformShaper = new gfxCoreTextShaper(this);
michael@0 152 }
michael@0 153
michael@0 154 bool
michael@0 155 gfxMacFont::SetupCairoFont(gfxContext *aContext)
michael@0 156 {
michael@0 157 if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
michael@0 158 // Don't cairo_set_scaled_font as that would propagate the error to
michael@0 159 // the cairo_t, precluding any further drawing.
michael@0 160 return false;
michael@0 161 }
michael@0 162 cairo_set_scaled_font(aContext->GetCairo(), mScaledFont);
michael@0 163 return true;
michael@0 164 }
michael@0 165
michael@0 166 gfxFont::RunMetrics
michael@0 167 gfxMacFont::Measure(gfxTextRun *aTextRun,
michael@0 168 uint32_t aStart, uint32_t aEnd,
michael@0 169 BoundingBoxType aBoundingBoxType,
michael@0 170 gfxContext *aRefContext,
michael@0 171 Spacing *aSpacing)
michael@0 172 {
michael@0 173 gfxFont::RunMetrics metrics =
michael@0 174 gfxFont::Measure(aTextRun, aStart, aEnd,
michael@0 175 aBoundingBoxType, aRefContext, aSpacing);
michael@0 176
michael@0 177 // if aBoundingBoxType is not TIGHT_HINTED_OUTLINE_EXTENTS then we need to add
michael@0 178 // a pixel column each side of the bounding box in case of antialiasing "bleed"
michael@0 179 if (aBoundingBoxType != TIGHT_HINTED_OUTLINE_EXTENTS &&
michael@0 180 metrics.mBoundingBox.width > 0) {
michael@0 181 metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
michael@0 182 metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 2;
michael@0 183 }
michael@0 184
michael@0 185 return metrics;
michael@0 186 }
michael@0 187
michael@0 188 void
michael@0 189 gfxMacFont::InitMetrics()
michael@0 190 {
michael@0 191 mIsValid = false;
michael@0 192 ::memset(&mMetrics, 0, sizeof(mMetrics));
michael@0 193
michael@0 194 uint32_t upem = 0;
michael@0 195
michael@0 196 // try to get unitsPerEm from sfnt head table, to avoid calling CGFont
michael@0 197 // if possible (bug 574368) and because CGFontGetUnitsPerEm does not
michael@0 198 // return the true value for OpenType/CFF fonts (it normalizes to 1000,
michael@0 199 // which then leads to metrics errors when we read the 'hmtx' table to
michael@0 200 // get glyph advances for HarfBuzz, see bug 580863)
michael@0 201 CFDataRef headData =
michael@0 202 ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('h','e','a','d'));
michael@0 203 if (headData) {
michael@0 204 if (size_t(::CFDataGetLength(headData)) >= sizeof(HeadTable)) {
michael@0 205 const HeadTable *head =
michael@0 206 reinterpret_cast<const HeadTable*>(::CFDataGetBytePtr(headData));
michael@0 207 upem = head->unitsPerEm;
michael@0 208 }
michael@0 209 ::CFRelease(headData);
michael@0 210 }
michael@0 211 if (!upem) {
michael@0 212 upem = ::CGFontGetUnitsPerEm(mCGFont);
michael@0 213 }
michael@0 214
michael@0 215 if (upem < 16 || upem > 16384) {
michael@0 216 // See http://www.microsoft.com/typography/otspec/head.htm
michael@0 217 #ifdef DEBUG
michael@0 218 char warnBuf[1024];
michael@0 219 sprintf(warnBuf, "Bad font metrics for: %s (invalid unitsPerEm value)",
michael@0 220 NS_ConvertUTF16toUTF8(mFontEntry->Name()).get());
michael@0 221 NS_WARNING(warnBuf);
michael@0 222 #endif
michael@0 223 return;
michael@0 224 }
michael@0 225
michael@0 226 mAdjustedSize = std::max(mStyle.size, 1.0);
michael@0 227 mFUnitsConvFactor = mAdjustedSize / upem;
michael@0 228
michael@0 229 // For CFF fonts, when scaling values read from CGFont* APIs, we need to
michael@0 230 // use CG's idea of unitsPerEm, which may differ from the "true" value in
michael@0 231 // the head table of the font (see bug 580863)
michael@0 232 gfxFloat cgConvFactor;
michael@0 233 if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
michael@0 234 cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
michael@0 235 } else {
michael@0 236 cgConvFactor = mFUnitsConvFactor;
michael@0 237 }
michael@0 238
michael@0 239 // Try to read 'sfnt' metrics; for local, non-sfnt fonts ONLY, fall back to
michael@0 240 // platform APIs. The InitMetrics...() functions will set mIsValid on success.
michael@0 241 if (!InitMetricsFromSfntTables(mMetrics) &&
michael@0 242 (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
michael@0 243 InitMetricsFromPlatform();
michael@0 244 }
michael@0 245 if (!mIsValid) {
michael@0 246 return;
michael@0 247 }
michael@0 248
michael@0 249 if (mMetrics.xHeight == 0.0) {
michael@0 250 mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
michael@0 251 }
michael@0 252
michael@0 253 if (mStyle.sizeAdjust != 0.0 && mStyle.size > 0.0 &&
michael@0 254 mMetrics.xHeight > 0.0) {
michael@0 255 // apply font-size-adjust, and recalculate metrics
michael@0 256 gfxFloat aspect = mMetrics.xHeight / mStyle.size;
michael@0 257 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
michael@0 258 mFUnitsConvFactor = mAdjustedSize / upem;
michael@0 259 if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
michael@0 260 cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
michael@0 261 } else {
michael@0 262 cgConvFactor = mFUnitsConvFactor;
michael@0 263 }
michael@0 264 mMetrics.xHeight = 0.0;
michael@0 265 if (!InitMetricsFromSfntTables(mMetrics) &&
michael@0 266 (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
michael@0 267 InitMetricsFromPlatform();
michael@0 268 }
michael@0 269 if (!mIsValid) {
michael@0 270 // this shouldn't happen, as we succeeded earlier before applying
michael@0 271 // the size-adjust factor! But check anyway, for paranoia's sake.
michael@0 272 return;
michael@0 273 }
michael@0 274 if (mMetrics.xHeight == 0.0) {
michael@0 275 mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
michael@0 276 }
michael@0 277 }
michael@0 278
michael@0 279 // Once we reach here, we've got basic metrics and set mIsValid = TRUE;
michael@0 280 // there should be no further points of actual failure in InitMetrics().
michael@0 281 // (If one is introduced, be sure to reset mIsValid to FALSE!)
michael@0 282
michael@0 283 mMetrics.emHeight = mAdjustedSize;
michael@0 284
michael@0 285 // Measure/calculate additional metrics, independent of whether we used
michael@0 286 // the tables directly or ATS metrics APIs
michael@0 287
michael@0 288 CFDataRef cmap =
michael@0 289 ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c','m','a','p'));
michael@0 290
michael@0 291 uint32_t glyphID;
michael@0 292 if (mMetrics.aveCharWidth <= 0) {
michael@0 293 mMetrics.aveCharWidth = GetCharWidth(cmap, 'x', &glyphID,
michael@0 294 cgConvFactor);
michael@0 295 if (glyphID == 0) {
michael@0 296 // we didn't find 'x', so use maxAdvance rather than zero
michael@0 297 mMetrics.aveCharWidth = mMetrics.maxAdvance;
michael@0 298 }
michael@0 299 }
michael@0 300 if (IsSyntheticBold()) {
michael@0 301 mMetrics.aveCharWidth += GetSyntheticBoldOffset();
michael@0 302 mMetrics.maxAdvance += GetSyntheticBoldOffset();
michael@0 303 }
michael@0 304
michael@0 305 mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID, cgConvFactor);
michael@0 306 if (glyphID == 0) {
michael@0 307 // no space glyph?!
michael@0 308 mMetrics.spaceWidth = mMetrics.aveCharWidth;
michael@0 309 }
michael@0 310 mSpaceGlyph = glyphID;
michael@0 311
michael@0 312 mMetrics.zeroOrAveCharWidth = GetCharWidth(cmap, '0', &glyphID,
michael@0 313 cgConvFactor);
michael@0 314 if (glyphID == 0) {
michael@0 315 mMetrics.zeroOrAveCharWidth = mMetrics.aveCharWidth;
michael@0 316 }
michael@0 317
michael@0 318 if (cmap) {
michael@0 319 ::CFRelease(cmap);
michael@0 320 }
michael@0 321
michael@0 322 CalculateDerivedMetrics(mMetrics);
michael@0 323
michael@0 324 SanitizeMetrics(&mMetrics, mFontEntry->mIsBadUnderlineFont);
michael@0 325
michael@0 326 #if 0
michael@0 327 fprintf (stderr, "Font: %p (%s) size: %f\n", this,
michael@0 328 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
michael@0 329 // 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);
michael@0 330 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
michael@0 331 fprintf (stderr, " maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
michael@0 332 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
michael@0 333 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
michael@0 334 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);
michael@0 335 #endif
michael@0 336 }
michael@0 337
michael@0 338 gfxFloat
michael@0 339 gfxMacFont::GetCharWidth(CFDataRef aCmap, char16_t aUniChar,
michael@0 340 uint32_t *aGlyphID, gfxFloat aConvFactor)
michael@0 341 {
michael@0 342 CGGlyph glyph = 0;
michael@0 343
michael@0 344 if (aCmap) {
michael@0 345 glyph = gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap),
michael@0 346 ::CFDataGetLength(aCmap),
michael@0 347 aUniChar);
michael@0 348 }
michael@0 349
michael@0 350 if (aGlyphID) {
michael@0 351 *aGlyphID = glyph;
michael@0 352 }
michael@0 353
michael@0 354 if (glyph) {
michael@0 355 int advance;
michael@0 356 if (::CGFontGetGlyphAdvances(mCGFont, &glyph, 1, &advance)) {
michael@0 357 return advance * aConvFactor;
michael@0 358 }
michael@0 359 }
michael@0 360
michael@0 361 return 0;
michael@0 362 }
michael@0 363
michael@0 364 // Try to initialize font metrics via platform APIs (CG/CT),
michael@0 365 // and set mIsValid = TRUE on success.
michael@0 366 // We ONLY call this for local (platform) fonts that are not sfnt format;
michael@0 367 // for sfnts, including ALL downloadable fonts, we prefer to use
michael@0 368 // InitMetricsFromSfntTables and avoid platform APIs.
michael@0 369 void
michael@0 370 gfxMacFont::InitMetricsFromPlatform()
michael@0 371 {
michael@0 372 CTFontRef ctFont = ::CTFontCreateWithGraphicsFont(mCGFont,
michael@0 373 mAdjustedSize,
michael@0 374 nullptr, nullptr);
michael@0 375 if (!ctFont) {
michael@0 376 return;
michael@0 377 }
michael@0 378
michael@0 379 mMetrics.underlineOffset = ::CTFontGetUnderlinePosition(ctFont);
michael@0 380 mMetrics.underlineSize = ::CTFontGetUnderlineThickness(ctFont);
michael@0 381
michael@0 382 mMetrics.externalLeading = ::CTFontGetLeading(ctFont);
michael@0 383
michael@0 384 mMetrics.maxAscent = ::CTFontGetAscent(ctFont);
michael@0 385 mMetrics.maxDescent = ::CTFontGetDescent(ctFont);
michael@0 386
michael@0 387 // this is not strictly correct, but neither CTFont nor CGFont seems to
michael@0 388 // provide maxAdvance, unless we were to iterate over all the glyphs
michael@0 389 // (which isn't worth the cost here)
michael@0 390 CGRect r = ::CTFontGetBoundingBox(ctFont);
michael@0 391 mMetrics.maxAdvance = r.size.width;
michael@0 392
michael@0 393 // aveCharWidth is also not provided, so leave it at zero
michael@0 394 // (fallback code in gfxMacFont::InitMetrics will then try measuring 'x');
michael@0 395 // this could lead to less-than-"perfect" text field sizing when width is
michael@0 396 // specified as a number of characters, and the font in use is a non-sfnt
michael@0 397 // legacy font, but that's a sufficiently obscure edge case that we can
michael@0 398 // ignore the potential discrepancy.
michael@0 399 mMetrics.aveCharWidth = 0;
michael@0 400
michael@0 401 mMetrics.xHeight = ::CTFontGetXHeight(ctFont);
michael@0 402
michael@0 403 ::CFRelease(ctFont);
michael@0 404
michael@0 405 mIsValid = true;
michael@0 406 }
michael@0 407
michael@0 408 TemporaryRef<ScaledFont>
michael@0 409 gfxMacFont::GetScaledFont(DrawTarget *aTarget)
michael@0 410 {
michael@0 411 if (!mAzureScaledFont) {
michael@0 412 NativeFont nativeFont;
michael@0 413 nativeFont.mType = NativeFontType::MAC_FONT_FACE;
michael@0 414 nativeFont.mFont = GetCGFontRef();
michael@0 415 mAzureScaledFont = mozilla::gfx::Factory::CreateScaledFontWithCairo(nativeFont, GetAdjustedSize(), mScaledFont);
michael@0 416 }
michael@0 417
michael@0 418 return mAzureScaledFont;
michael@0 419 }
michael@0 420
michael@0 421 void
michael@0 422 gfxMacFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
michael@0 423 FontCacheSizes* aSizes) const
michael@0 424 {
michael@0 425 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 426 // mCGFont is shared with the font entry, so not counted here;
michael@0 427 // and we don't have APIs to measure the cairo mFontFace object
michael@0 428 }
michael@0 429
michael@0 430 void
michael@0 431 gfxMacFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
michael@0 432 FontCacheSizes* aSizes) const
michael@0 433 {
michael@0 434 aSizes->mFontInstances += aMallocSizeOf(this);
michael@0 435 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
michael@0 436 }

mercurial