Bug 1711479 - Support various metrics (not only ex-height) as the basis for font-size-adjust calculations in the gfx font implementations. r=layout-reviewers,emilio

Differential Revision: https://phabricator.services.mozilla.com/D115211
This commit is contained in:
Jonathan Kew 2021-06-07 08:58:12 +00:00
parent 57c18282b4
commit ddbc4e9bed
12 changed files with 418 additions and 187 deletions

View File

@ -119,8 +119,7 @@ nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
mVertical(false),
mTextOrientation(mozilla::StyleTextOrientation::Mixed) {
gfxFontStyle style(aFont.style, aFont.weight, aFont.stretch,
gfxFloat(aFont.size.ToAppUnits()) / mP2A,
aFont.sizeAdjust.IsEx() ? aFont.sizeAdjust.AsEx() : -1.0f,
gfxFloat(aFont.size.ToAppUnits()) / mP2A, aFont.sizeAdjust,
aFont.systemFont, mDeviceContext->IsPrinterContext(),
aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,

View File

@ -153,14 +153,53 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
mFontFace->GetMetrics(&fontMetrics);
}
if (mStyle.sizeAdjust >= 0.0) {
gfxFloat aspect =
(gfxFloat)fontMetrics.xHeight / fontMetrics.designUnitsPerEm;
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
} else {
mAdjustedSize = GetAdjustedSize();
if (GetAdjustedSize() > 0.0 && mStyle.sizeAdjust >= 0.0 &&
FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
FontSizeAdjust::Tag::None) {
// For accurate measurement during the font-size-adjust computations;
// these may be reset later according to the adjusted size.
mUseSubpixelPositions = true;
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
gfxFloat aspect;
switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
default:
MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
aspect = 0.0;
break;
case FontSizeAdjust::Tag::Ex:
aspect = (gfxFloat)fontMetrics.xHeight / fontMetrics.designUnitsPerEm;
break;
case FontSizeAdjust::Tag::Cap:
aspect = (gfxFloat)fontMetrics.capHeight / fontMetrics.designUnitsPerEm;
break;
case FontSizeAdjust::Tag::Ch: {
aspect = GetCharAdvance('0');
if (aspect < 0.0) {
// '0' not found, default to 0.5em.
aspect = 0.5;
} else {
aspect /= mAdjustedSize;
}
break;
}
case FontSizeAdjust::Tag::Ic:
aspect = GetCharAdvance(0x6C34);
if (aspect < 0.0) {
// U+6C34 not found, default to 1em.
aspect = 1.0;
} else {
aspect /= mAdjustedSize;
}
break;
}
if (aspect > 0.0) {
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
}
}
// Update now that we've adjusted the size if necessary.
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
// Note that GetMeasuringMode depends on mAdjustedSize
if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType() &&
GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
@ -168,6 +207,8 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
mUseSubpixelPositions = true;
// note that this may be reset to FALSE if we determine that a bitmap
// strike is going to be used
} else {
mUseSubpixelPositions = false;
}
gfxDWriteFontEntry* fe = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
@ -185,8 +226,6 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
mMetrics = new gfxFont::Metrics;
::memset(mMetrics, 0, sizeof(*mMetrics));
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
mMetrics->capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
@ -248,22 +287,14 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
}
if (mMetrics->aveCharWidth < 1) {
ucs = L'x';
if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) {
mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
}
mMetrics->aveCharWidth = GetCharAdvance('x');
if (mMetrics->aveCharWidth < 1) {
// Let's just assume the X is square.
mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
}
}
ucs = L'0';
if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) {
mMetrics->zeroWidth = MeasureGlyphWidth(glyph);
} else {
mMetrics->zeroWidth = -1.0; // indicates not found
}
mMetrics->zeroWidth = GetCharAdvance('0');
mMetrics->underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor;
mMetrics->underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor;

View File

@ -212,14 +212,15 @@ static double FindClosestSize(FT_Face aFace, double aSize) {
void gfxFT2FontBase::InitMetrics() {
mFUnitsConvFactor = 0.0;
if (MOZ_UNLIKELY(GetStyle()->size <= 0.0) ||
MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) {
if (MOZ_UNLIKELY(mStyle.AdjustedSizeMustBeZero())) {
memset(&mMetrics, 0, sizeof(mMetrics)); // zero initialize
mSpaceGlyph = GetGlyph(' ');
return;
}
if (GetStyle()->sizeAdjust > 0.0 && mFTSize == 0.0) {
if (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
FontSizeAdjust::Tag::None &&
mStyle.sizeAdjust >= 0.0 && mFTSize == 0.0) {
// If font-size-adjust is in effect, we need to get metrics in order to
// determine the aspect ratio, then compute the final adjusted size and
// re-initialize metrics.
@ -228,11 +229,37 @@ void gfxFT2FontBase::InitMetrics() {
mFTSize = 1.0;
InitMetrics();
// Now do the font-size-adjust calculation and set the final size.
gfxFloat aspect = mMetrics.xHeight / mMetrics.emHeight;
mAdjustedSize = GetStyle()->GetAdjustedSize(aspect);
// Ensure the FT_Face will be reconfigured for the new size next time we
// need to use it.
mFTFace->ForgetLockOwner(this);
gfxFloat aspect;
switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
default:
MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
aspect = 0.0;
break;
case FontSizeAdjust::Tag::Ex:
aspect = mMetrics.xHeight / mAdjustedSize;
break;
case FontSizeAdjust::Tag::Cap:
aspect = mMetrics.capHeight / mAdjustedSize;
break;
case FontSizeAdjust::Tag::Ch:
aspect =
mMetrics.zeroWidth > 0.0 ? mMetrics.zeroWidth / mAdjustedSize : 0.5;
break;
case FontSizeAdjust::Tag::Ic: {
// We might want to add an icWidth field to the Metrics struct,
// especially when we implement 'ic' as a CSS unit, but for now
// we can look it up here if required.
gfxFloat advance = GetCharAdvance(0x6C34);
aspect = advance > 0 ? advance / mAdjustedSize : 1.0;
break;
}
}
if (aspect > 0.0) {
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
// Ensure the FT_Face will be reconfigured for the new size next time we
// need to use it.
mFTFace->ForgetLockOwner(this);
}
}
// Set mAdjustedSize if it hasn't already been set by a font-size-adjust
@ -438,19 +465,27 @@ void gfxFT2FontBase::InitMetrics() {
mMetrics.zeroWidth = -1.0; // indicates not found
}
// Prefering a measured x over sxHeight because sxHeight doesn't consider
// hinting, but maybe the x extents are not quite right in some fancy
// script fonts. CSS 2.1 suggests possibly using the height of an "o",
// which would have a more consistent glyph across fonts.
// If we didn't get a usable x-height or cap-height above, try measuring
// specific glyphs. This can be affected by hinting, leading to erratic
// behavior across font sizes and system configuration, so we prefer to
// use the metrics directly from the font if possible.
// Using glyph bounds for x-height or cap-height may not really be right,
// if fonts have fancy swashes etc. For x-height, CSS 2.1 suggests possibly
// using the height of an "o", which may be more consistent across fonts,
// but then curve-overshoot should also be accounted for.
gfxFloat xWidth;
gfxRect xBounds;
if (GetCharExtents('x', &xWidth, &xBounds) && xBounds.y < 0.0) {
mMetrics.xHeight = -xBounds.y;
mMetrics.aveCharWidth = std::max(mMetrics.aveCharWidth, xWidth);
if (mMetrics.xHeight == 0.0) {
if (GetCharExtents('x', &xWidth, &xBounds) && xBounds.y < 0.0) {
mMetrics.xHeight = -xBounds.y;
mMetrics.aveCharWidth = std::max(mMetrics.aveCharWidth, xWidth);
}
}
if (GetCharExtents('H', nullptr, &xBounds) && xBounds.y < 0.0) {
mMetrics.capHeight = -xBounds.y;
if (mMetrics.capHeight == 0.0) {
if (GetCharExtents('H', nullptr, &xBounds) && xBounds.y < 0.0) {
mMetrics.capHeight = -xBounds.y;
}
}
mMetrics.aveCharWidth = std::max(mMetrics.aveCharWidth, mMetrics.zeroWidth);

View File

@ -259,8 +259,7 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
mFontPattern(aFontPattern),
mFTFaceInitialized(false),
mIgnoreFcCharmap(aIgnoreFcCharmap),
mHasVariationsInitialized(false),
mAspect(0.0) {
mHasVariationsInitialized(false) {
GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
}
@ -318,8 +317,7 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
mFTFace(std::move(aFace)),
mFTFaceInitialized(true),
mIgnoreFcCharmap(true),
mHasVariationsInitialized(false),
mAspect(0.0) {
mHasVariationsInitialized(false) {
mWeightRange = aWeight;
mStyleRange = aStyle;
mStretchRange = aStretch;
@ -336,8 +334,7 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
: gfxFT2FontEntryBase(aFaceName),
mFontPattern(aFontPattern),
mFTFaceInitialized(false),
mHasVariationsInitialized(false),
mAspect(0.0) {
mHasVariationsInitialized(false) {
mWeightRange = aWeight;
mStyleRange = aStyle;
mStretchRange = aStretch;
@ -492,49 +489,70 @@ hb_blob_t* gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) {
return gfxFontEntry::GetFontTable(aTableTag);
}
double gfxFontconfigFontEntry::GetAspect() {
if (mAspect != 0.0) {
return mAspect;
}
// try to compute aspect from OS/2 metrics if available
AutoTable os2Table(this, TRUETYPE_TAG('O', 'S', '/', '2'));
if (os2Table) {
uint16_t upem = UnitsPerEm();
if (upem != kInvalidUPEM) {
uint32_t len;
auto os2 =
reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
if (uint16_t(os2->version) >= 2) {
if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
int16_t(os2->sxHeight) > 0.1 * upem) {
mAspect = double(int16_t(os2->sxHeight)) / upem;
return mAspect;
double gfxFontconfigFontEntry::GetAspect(uint8_t aSizeAdjustBasis) {
using FontSizeAdjust = gfxFont::FontSizeAdjust;
if (FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::Ex ||
FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::Cap) {
// try to compute aspect from OS/2 metrics if available
AutoTable os2Table(this, TRUETYPE_TAG('O', 'S', '/', '2'));
if (os2Table) {
uint16_t upem = UnitsPerEm();
if (upem != kInvalidUPEM) {
uint32_t len;
const auto* os2 =
reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
if (uint16_t(os2->version) >= 2) {
// XXX(jfkthame) Other implementations don't have the check for
// values <= 0.1em; should we drop that here? Just require it to be
// a positive number?
if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
FontSizeAdjust::Tag::Ex) {
if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
int16_t(os2->sxHeight) > 0.1 * upem) {
return double(int16_t(os2->sxHeight)) / upem;
}
}
if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
FontSizeAdjust::Tag::Cap) {
if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
int16_t(os2->sCapHeight) > 0.1 * upem) {
return double(int16_t(os2->sCapHeight)) / upem;
}
}
}
}
}
}
// default to aspect = 0.5 if the code below fails
mAspect = 0.5;
// create a font to calculate x-height / em-height
// create a font to calculate the requested aspect
gfxFontStyle s;
s.size = 100.0; // pick large size to avoid possible hinting artifacts
s.size = 256.0; // pick large size to reduce hinting artifacts
RefPtr<gfxFont> font = FindOrMakeFont(&s);
if (font) {
const gfxFont::Metrics& metrics =
font->GetMetrics(nsFontMetrics::eHorizontal);
// The factor of 0.1 ensures that xHeight is sane so fonts don't
// become huge. Strictly ">" ensures that xHeight and emHeight are
// not both zero.
if (metrics.xHeight > 0.1 * metrics.emHeight) {
mAspect = metrics.xHeight / metrics.emHeight;
if (metrics.emHeight == 0) {
return 0;
}
switch (FontSizeAdjust::Tag(aSizeAdjustBasis)) {
case FontSizeAdjust::Tag::Ex:
return metrics.xHeight / metrics.emHeight;
case FontSizeAdjust::Tag::Cap:
return metrics.capHeight / metrics.emHeight;
case FontSizeAdjust::Tag::Ch:
return metrics.zeroWidth > 0 ? metrics.zeroWidth / metrics.emHeight
: 0.5;
case FontSizeAdjust::Tag::Ic: {
gfxFloat advance = font->GetCharAdvance(0x6C34);
return advance > 0 ? advance / metrics.emHeight : 1.0;
}
default:
break;
}
}
return mAspect;
MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
return 0.5;
}
static void PrepareFontOptions(FcPattern* aPattern, int* aOutLoadFlags,
@ -759,8 +777,10 @@ gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const std::string& aFile,
static inline gfxFloat SizeForStyle(gfxFontconfigFontEntry* aEntry,
const gfxFontStyle& aStyle) {
return aStyle.sizeAdjust >= 0.0 ? aStyle.GetAdjustedSize(aEntry->GetAspect())
: aStyle.size * aEntry->mSizeAdjust;
return StyleFontSizeAdjust::Tag(aStyle.sizeAdjustBasis) !=
StyleFontSizeAdjust::Tag::None
? aStyle.GetAdjustedSize(aEntry->GetAspect(aStyle.sizeAdjustBasis))
: aStyle.size * aEntry->mSizeAdjust;
}
static double ChooseFontSize(gfxFontconfigFontEntry* aEntry,

View File

@ -107,7 +107,7 @@ class gfxFontconfigFontEntry final : public gfxFT2FontEntryBase {
nsresult CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>&) override;
hb_blob_t* GetFontTable(uint32_t aTableTag) override;
double GetAspect();
double GetAspect(uint8_t aSizeAdjustBasis);
protected:
virtual ~gfxFontconfigFontEntry();
@ -135,8 +135,6 @@ class gfxFontconfigFontEntry final : public gfxFT2FontEntryBase {
bool mHasVariations;
bool mHasVariationsInitialized;
double mAspect;
class UnscaledFontCache {
public:
already_AddRefed<mozilla::gfx::UnscaledFontFontconfig> Lookup(

View File

@ -878,7 +878,7 @@ gfxFont::RoundingFlags gfxFont::GetRoundOffsetsToPixels(
}
}
gfxFloat gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID) {
gfxFloat gfxFont::GetGlyphHAdvance(uint16_t aGID) {
if (ProvidesGlyphWidths()) {
return GetGlyphWidth(aGID) / 65536.0;
}
@ -898,6 +898,27 @@ gfxFloat gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID) {
return shaper->GetGlyphHAdvance(aGID) / 65536.0;
}
gfxFloat gfxFont::GetCharAdvance(uint32_t aUnicode) {
uint32_t gid = 0;
if (ProvidesGetGlyph()) {
gid = GetGlyph(aUnicode, 0);
} else {
if (!mHarfBuzzShaper) {
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
}
gfxHarfBuzzShaper* shaper =
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
if (!shaper->Initialize()) {
return -1.0;
}
gid = shaper->GetNominalGlyph(aUnicode);
}
if (!gid) {
return -1.0;
}
return GetGlyphHAdvance(gid);
}
static void CollectLookupsByFeature(hb_face_t* aFace, hb_tag_t aTableTag,
uint32_t aFeatureIndex,
hb_set_t* aLookups) {
@ -2543,8 +2564,7 @@ gfxFont::RunMetrics gfxFont::Measure(const gfxTextRun* aTextRun,
gfxGlyphExtents* extents =
((aBoundingBoxType == LOOSE_INK_EXTENTS && !needsGlyphExtents &&
!aTextRun->HasDetailedGlyphs()) ||
(MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) ||
(MOZ_UNLIKELY(GetStyle()->size == 0)))
MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero()))
? nullptr
: GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
double x = 0;
@ -3665,8 +3685,7 @@ void gfxFont::SanitizeMetrics(gfxFont::Metrics* aMetrics,
// Even if this font size is zero, this font is created with non-zero size.
// However, for layout and others, we should return the metrics of zero size
// font.
if (mStyle.size == 0.0 || mStyle.sizeAdjust == 0.0 ||
mFontEntry->mSizeAdjust == 0.0) {
if (mStyle.AdjustedSizeMustBeZero()) {
memset(aMetrics, 0, sizeof(gfxFont::Metrics));
return;
}
@ -3996,7 +4015,7 @@ void gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver* aObserver) {
gfxFontStyle::gfxFontStyle()
: size(DEFAULT_PIXEL_FONT_SIZE),
sizeAdjust(-1.0f),
sizeAdjust(0.0f),
baselineOffset(0.0f),
languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
fontSmoothingBackgroundColor(NS_RGBA(0, 0, 0, 0)),
@ -4005,6 +4024,7 @@ gfxFontStyle::gfxFontStyle()
style(FontSlantStyle::Normal()),
variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
sizeAdjustBasis(uint8_t(FontSizeAdjust::Tag::None)),
systemFont(true),
printerFont(false),
useGrayscaleAntialiasing(false),
@ -4014,12 +4034,11 @@ gfxFontStyle::gfxFontStyle()
gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
FontStretch aStretch, gfxFloat aSize,
float aSizeAdjust, bool aSystemFont,
const FontSizeAdjust& aSizeAdjust, bool aSystemFont,
bool aPrinterFont, bool aAllowWeightSynthesis,
bool aAllowStyleSynthesis,
uint32_t aLanguageOverride)
: size(aSize),
sizeAdjust(aSizeAdjust),
baselineOffset(0.0f),
languageOverride(aLanguageOverride),
fontSmoothingBackgroundColor(NS_RGBA(0, 0, 0, 0)),
@ -4035,8 +4054,32 @@ gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
allowSyntheticStyle(aAllowStyleSynthesis),
noFallbackVariantFeatures(true) {
MOZ_ASSERT(!mozilla::IsNaN(size));
switch (aSizeAdjust.tag) {
case FontSizeAdjust::Tag::None:
sizeAdjust = 0.0f;
break;
case FontSizeAdjust::Tag::Ex:
sizeAdjust = aSizeAdjust.AsEx();
break;
case FontSizeAdjust::Tag::Cap:
sizeAdjust = aSizeAdjust.AsCap();
break;
case FontSizeAdjust::Tag::Ch:
sizeAdjust = aSizeAdjust.AsCh();
break;
case FontSizeAdjust::Tag::Ic:
sizeAdjust = aSizeAdjust.AsIc();
break;
}
MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
sizeAdjustBasis = uint8_t(aSizeAdjust.tag);
// sizeAdjustBasis is currently a small bitfield, so let's assert that the
// tag value was not truncated.
MOZ_ASSERT(FontSizeAdjust::Tag(sizeAdjustBasis) == aSizeAdjust.tag,
"gfxFontStyle.sizeAdjustBasis too small?");
if (weight > FontWeight(1000)) {
weight = FontWeight(1000);
}
@ -4046,7 +4089,8 @@ gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
if (size >= FONT_MAX_SIZE) {
size = FONT_MAX_SIZE;
sizeAdjust = -1.0f;
sizeAdjust = 0.0f;
sizeAdjustBasis = uint8_t(FontSizeAdjust::Tag::None);
} else if (size < 0.0) {
NS_WARNING("negative font size");
size = 0.0;

View File

@ -94,15 +94,16 @@ typedef struct _cairo_scaled_font cairo_scaled_font_t;
#endif
struct gfxFontStyle {
typedef mozilla::FontStretch FontStretch;
typedef mozilla::FontSlantStyle FontSlantStyle;
typedef mozilla::FontWeight FontWeight;
using FontStretch = mozilla::FontStretch;
using FontSlantStyle = mozilla::FontSlantStyle;
using FontWeight = mozilla::FontWeight;
using FontSizeAdjust = mozilla::StyleFontSizeAdjust;
gfxFontStyle();
gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
gfxFloat aSize, float aSizeAdjust, bool aSystemFont,
bool aPrinterFont, bool aWeightSynthesis, bool aStyleSynthesis,
uint32_t aLanguageOverride);
gfxFloat aSize, const FontSizeAdjust& aSizeAdjust,
bool aSystemFont, bool aPrinterFont, bool aWeightSynthesis,
bool aStyleSynthesis, uint32_t aLanguageOverride);
// Features are composed of (1) features from style rules (2) features
// from feature settings rules and (3) family-specific features. (1) and
// (3) are guaranteed to be mutually exclusive
@ -170,15 +171,18 @@ struct gfxFontStyle {
return weight.IsNormal() && style.IsNormal() && stretch.IsNormal();
}
// We pack these two small-integer fields into a single byte to avoid
// We pack these three small-integer fields into a single byte to avoid
// overflowing an 8-byte boundary [in a 64-bit build] and ending up with
// 7 bytes of padding at the end of the struct.
// caps variant (small-caps, petite-caps, etc.)
uint8_t variantCaps : 4; // uses range 0..6
uint8_t variantCaps : 3; // uses range 0..6
// sub/superscript variant
uint8_t variantSubSuper : 4; // uses range 0..2
uint8_t variantSubSuper : 2; // uses range 0..2
// font metric used as basis of font-size-adjust
uint8_t sizeAdjustBasis : 3; // uses range 0..4
// Say that this font is a system font and therefore does not
// require certain fixup that we do for fonts from untrusted
@ -200,15 +204,24 @@ struct gfxFontStyle {
bool noFallbackVariantFeatures : 1;
// Return the final adjusted font size for the given aspect ratio.
// Not meant to be called when sizeAdjust = -1.0.
// Not meant to be called when sizeAdjustBasis is NONE.
gfxFloat GetAdjustedSize(gfxFloat aspect) const {
NS_ASSERTION(sizeAdjust >= 0.0,
"Not meant to be called when sizeAdjust = -1.0");
MOZ_ASSERT(
FontSizeAdjust::Tag(sizeAdjustBasis) != FontSizeAdjust::Tag::None,
"Not meant to be called when sizeAdjustBasis is none");
gfxFloat adjustedSize =
std::max(NS_round(size * (sizeAdjust / aspect)), 1.0);
return std::min(adjustedSize, FONT_MAX_SIZE);
}
// Some callers want to take a short-circuit path if they can be sure the
// adjusted size will be zero.
bool AdjustedSizeMustBeZero() const {
return size == 0.0 ||
(FontSizeAdjust::Tag(sizeAdjustBasis) != FontSizeAdjust::Tag::None &&
sizeAdjust == 0.0);
}
PLDHashNumber Hash() const;
// Adjust this style to simulate sub/superscript (as requested in the
@ -233,6 +246,7 @@ struct gfxFontStyle {
(useGrayscaleAntialiasing == other.useGrayscaleAntialiasing) &&
(baselineOffset == other.baselineOffset) &&
mozilla::NumbersAreBitwiseIdentical(sizeAdjust, other.sizeAdjust) &&
(sizeAdjustBasis == other.sizeAdjustBasis) &&
(featureSettings == other.featureSettings) &&
(variantAlternates == other.variantAlternates) &&
(featureValueLookup == other.featureValueLookup) &&
@ -1392,14 +1406,15 @@ class gfxFont {
friend class gfxGraphiteShaper;
protected:
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::unicode::Script Script;
typedef mozilla::SVGContextPaint SVGContextPaint;
using DrawTarget = mozilla::gfx::DrawTarget;
using Script = mozilla::unicode::Script;
using SVGContextPaint = mozilla::SVGContextPaint;
typedef gfxFontShaper::RoundingFlags RoundingFlags;
using RoundingFlags = gfxFontShaper::RoundingFlags;
public:
typedef mozilla::FontSlantStyle FontSlantStyle;
using FontSlantStyle = mozilla::FontSlantStyle;
using FontSizeAdjust = mozilla::StyleFontSizeAdjust;
nsrefcnt AddRef(void) {
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
@ -1497,7 +1512,7 @@ class gfxFont {
// but it may be overridden by a value computed in metrics initialization
// from font-size-adjust.
if (mAdjustedSize < 0.0) {
mAdjustedSize = mStyle.sizeAdjust == 0.0
mAdjustedSize = mStyle.AdjustedSizeMustBeZero()
? 0.0
: mStyle.size * mFontEntry->mSizeAdjust;
}
@ -1558,8 +1573,13 @@ class gfxFont {
virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector) {
return 0;
}
// Return the horizontal advance of a glyph.
gfxFloat GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID);
// Return the advance of a glyph.
gfxFloat GetGlyphHAdvance(uint16_t aGID);
// Return the advance of a given Unicode char in isolation.
// Returns -1.0 if the char is not supported.
gfxFloat GetCharAdvance(uint32_t aUnicode);
gfxFloat SynthesizeSpaceWidth(uint32_t aCh);

View File

@ -119,29 +119,67 @@ void gfxGDIFont::Initialize() {
if (mAdjustedSize == 0.0) {
mAdjustedSize = GetAdjustedSize();
if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) {
// to implement font-size-adjust, we first create the "unadjusted" font
FillLogFont(logFont, mAdjustedSize);
mFont = ::CreateFontIndirectW(&logFont);
if (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
FontSizeAdjust::Tag::None) {
if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) {
// to implement font-size-adjust, we first create the "unadjusted" font
FillLogFont(logFont, mAdjustedSize);
mFont = ::CreateFontIndirectW(&logFont);
// initialize its metrics so we can calculate size adjustment
Initialize();
// initialize its metrics so we can calculate size adjustment
Initialize();
// Unless the font was so small that GDI metrics rounded to zero,
// calculate the properly adjusted size, and then proceed
// to recreate mFont and recalculate metrics
if (mMetrics->xHeight > 0.0 && mMetrics->emHeight > 0.0) {
gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
// Unless the font was so small that GDI metrics rounded to zero,
// calculate the properly adjusted size, and then proceed
// to recreate mFont and recalculate metrics
if (mMetrics->emHeight > 0.0) {
gfxFloat aspect;
switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
default:
MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
aspect = 0.0;
break;
case FontSizeAdjust::Tag::Ex:
aspect = mMetrics->xHeight / mMetrics->emHeight;
break;
case FontSizeAdjust::Tag::Cap:
aspect = mMetrics->capHeight / mMetrics->emHeight;
break;
case FontSizeAdjust::Tag::Ch:
aspect = GetCharAdvance('0');
if (aspect < 0.0) {
// '0' not found, default to 0.5em.
aspect = 0.5;
} else {
aspect /= mMetrics->emHeight;
}
break;
case FontSizeAdjust::Tag::Ic:
// We might want to add an icWidth field to the Metrics struct,
// especially when we implement 'ic' as a CSS unit, but for now
// we can look it up here if required.
aspect = GetCharAdvance(0x6C34);
if (aspect < 0.0) {
// U+6C34 not found, default to 1em.
aspect = 1.0;
} else {
aspect /= mMetrics->emHeight;
}
break;
}
if (aspect > 0.0) {
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
}
}
// delete the temporary font and metrics
::DeleteObject(mFont);
mFont = nullptr;
delete mMetrics;
mMetrics = nullptr;
} else {
mAdjustedSize = 0.0;
}
// delete the temporary font and metrics
::DeleteObject(mFont);
mFont = nullptr;
delete mMetrics;
mMetrics = nullptr;
} else if (mStyle.sizeAdjust == 0.0) {
mAdjustedSize = 0.0;
}
}

View File

@ -19,6 +19,7 @@
#include "gfxTextRun.h"
#include "gfxUtils.h"
#include "nsCocoaFeatures.h"
#include "AppleUtils.h"
#include "cairo-quartz.h"
using namespace mozilla;
@ -219,7 +220,7 @@ void gfxMacFont::InitMetrics() {
// return the true value for OpenType/CFF fonts (it normalizes to 1000,
// which then leads to metrics errors when we read the 'hmtx' table to
// get glyph advances for HarfBuzz, see bug 580863)
CFDataRef headData =
AutoCFRelease<CFDataRef> headData =
::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('h', 'e', 'a', 'd'));
if (headData) {
if (size_t(::CFDataGetLength(headData)) >= sizeof(HeadTable)) {
@ -227,7 +228,6 @@ void gfxMacFont::InitMetrics() {
reinterpret_cast<const HeadTable*>(::CFDataGetBytePtr(headData));
upem = head->unitsPerEm;
}
::CFRelease(headData);
}
if (!upem) {
upem = ::CGFontGetUnitsPerEm(mCGFont);
@ -273,33 +273,82 @@ void gfxMacFont::InitMetrics() {
if (mMetrics.xHeight == 0.0) {
mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
}
if (mMetrics.capHeight == 0.0) {
mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
}
if (mStyle.sizeAdjust > 0.0 && mMetrics.xHeight > 0.0) {
AutoCFRelease<CFDataRef> cmap =
::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c', 'm', 'a', 'p'));
uint32_t glyphID;
mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
if (glyphID == 0) {
mMetrics.zeroWidth = -1.0; // indicates not found
}
if (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
FontSizeAdjust::Tag::None &&
mStyle.sizeAdjust >= 0.0) {
// apply font-size-adjust, and recalculate metrics
gfxFloat aspect = mMetrics.xHeight / mAdjustedSize;
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
mFUnitsConvFactor = mAdjustedSize / upem;
if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
} else {
cgConvFactor = mFUnitsConvFactor;
gfxFloat aspect;
switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
default:
MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
aspect = 0.0;
break;
case FontSizeAdjust::Tag::Ex:
aspect = mMetrics.xHeight / mAdjustedSize;
break;
case FontSizeAdjust::Tag::Cap:
aspect = mMetrics.capHeight / mAdjustedSize;
break;
case FontSizeAdjust::Tag::Ch:
aspect =
mMetrics.zeroWidth < 0.0 ? 0.5 : mMetrics.zeroWidth / mAdjustedSize;
break;
case FontSizeAdjust::Tag::Ic: {
// We might want to add an icWidth field to the Metrics struct,
// especially when we implement 'ic' as a CSS unit, but for now
// we can look it up here if required.
aspect = GetCharWidth(cmap, 0x6C34, nullptr, cgConvFactor);
if (aspect <= 0.0) {
// U+6C34 not found, default to 1em.
aspect = 1.0;
} else {
aspect /= mAdjustedSize;
}
break;
}
}
mMetrics.xHeight = 0.0;
if (!InitMetricsFromSfntTables(mMetrics) &&
(!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
InitMetricsFromPlatform();
}
if (!mIsValid) {
// this shouldn't happen, as we succeeded earlier before applying
// the size-adjust factor! But check anyway, for paranoia's sake.
return;
}
if (mMetrics.xHeight == 0.0) {
mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
if (aspect > 0.0) {
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
mFUnitsConvFactor = mAdjustedSize / upem;
if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
} else {
cgConvFactor = mFUnitsConvFactor;
}
mMetrics.xHeight = 0.0;
if (!InitMetricsFromSfntTables(mMetrics) &&
(!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
InitMetricsFromPlatform();
}
if (!mIsValid) {
// this shouldn't happen, as we succeeded earlier before applying
// the size-adjust factor! But check anyway, for paranoia's sake.
return;
}
// Update metrics from the re-scaled font.
if (mMetrics.xHeight == 0.0) {
mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
}
if (mMetrics.capHeight == 0.0) {
mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
}
mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
if (glyphID == 0) {
mMetrics.zeroWidth = -1.0; // indicates not found
}
}
}
@ -312,10 +361,6 @@ void gfxMacFont::InitMetrics() {
// Measure/calculate additional metrics, independent of whether we used
// the tables directly or ATS metrics APIs
CFDataRef cmap =
::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c', 'm', 'a', 'p'));
uint32_t glyphID;
if (mMetrics.aveCharWidth <= 0) {
mMetrics.aveCharWidth = GetCharWidth(cmap, 'x', &glyphID, cgConvFactor);
if (glyphID == 0) {
@ -323,10 +368,6 @@ void gfxMacFont::InitMetrics() {
mMetrics.aveCharWidth = mMetrics.maxAdvance;
}
}
if (IsSyntheticBold()) {
mMetrics.aveCharWidth += GetSyntheticBoldOffset();
mMetrics.maxAdvance += GetSyntheticBoldOffset();
}
mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID, cgConvFactor);
if (glyphID == 0) {
@ -335,13 +376,13 @@ void gfxMacFont::InitMetrics() {
}
mSpaceGlyph = glyphID;
mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
if (glyphID == 0) {
mMetrics.zeroWidth = -1.0; // indicates not found
}
if (cmap) {
::CFRelease(cmap);
if (IsSyntheticBold()) {
mMetrics.spaceWidth += GetSyntheticBoldOffset();
mMetrics.aveCharWidth += GetSyntheticBoldOffset();
mMetrics.maxAdvance += GetSyntheticBoldOffset();
if (mMetrics.zeroWidth > 0) {
mMetrics.zeroWidth += GetSyntheticBoldOffset();
}
}
CalculateDerivedMetrics(mMetrics);
@ -415,22 +456,19 @@ CTFontRef gfxMacFont::CreateCTFontFromCGFontWithVariations(
CTFontRef ctFont;
if (nsCocoaFeatures::OnSierraExactly() ||
(aInstalledFont && nsCocoaFeatures::OnHighSierraOrLater())) {
CFDictionaryRef variations = ::CGFontCopyVariations(aCGFont);
AutoCFRelease<CFDictionaryRef> variations = ::CGFontCopyVariations(aCGFont);
if (variations) {
CFDictionaryRef varAttr = ::CFDictionaryCreate(
AutoCFRelease<CFDictionaryRef> varAttr = ::CFDictionaryCreate(
nullptr, (const void**)&kCTFontVariationAttribute,
(const void**)&variations, 1, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
::CFRelease(variations);
CTFontDescriptorRef varDesc =
AutoCFRelease<CTFontDescriptorRef> varDesc =
aFontDesc
? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc, varAttr)
: ::CTFontDescriptorCreateWithAttributes(varAttr);
::CFRelease(varAttr);
ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc);
::CFRelease(varDesc);
} else {
ctFont =
::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
@ -497,7 +535,7 @@ bool gfxMacFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
// for sfnts, including ALL downloadable fonts, we prefer to use
// InitMetricsFromSfntTables and avoid platform APIs.
void gfxMacFont::InitMetricsFromPlatform() {
CTFontRef ctFont =
AutoCFRelease<CTFontRef> ctFont =
::CTFontCreateWithGraphicsFont(mCGFont, mAdjustedSize, nullptr, nullptr);
if (!ctFont) {
return;
@ -528,8 +566,6 @@ void gfxMacFont::InitMetricsFromPlatform() {
mMetrics.xHeight = ::CTFontGetXHeight(ctFont);
mMetrics.capHeight = ::CTFontGetCapHeight(ctFont);
::CFRelease(ctFont);
mIsValid = true;
}

View File

@ -1610,8 +1610,7 @@ void gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget) {
for (uint32_t i = 0; i < runCount; ++i) {
const GlyphRun& run = glyphRuns[i];
gfxFont* font = run.mFont;
if (MOZ_UNLIKELY(font->GetStyle()->size == 0) ||
MOZ_UNLIKELY(font->GetStyle()->sizeAdjust == 0.0f)) {
if (MOZ_UNLIKELY(font->GetStyle()->AdjustedSizeMustBeZero())) {
continue;
}
@ -2352,8 +2351,7 @@ already_AddRefed<gfxTextRun> gfxFontGroup::MakeSpaceTextRun(
}
gfxFont* font = GetFirstValidFont();
if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
// Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
// them, and always create at least size 1 fonts, i.e. they still
// render something for size 0 fonts.
@ -2453,8 +2451,7 @@ already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun(
aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
// Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
// them, and always create at least size 1 fonts, i.e. they still
// render something for size 0 fonts.
@ -2484,8 +2481,7 @@ already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun(
if (aLength == 1 && aString[0] == ' ') {
return MakeSpaceTextRun(aParams, aFlags, aFlags2);
}
if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
return MakeBlankTextRun(aString, aLength, aParams, aFlags, aFlags2);
}

View File

@ -9581,9 +9581,23 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
aSystemFont->size = Length::FromPixels(fontStyle.size);
// aSystemFont->langGroup = fontStyle.langGroup;
aSystemFont->sizeAdjust = fontStyle.sizeAdjust < 0.0
? StyleFontSizeAdjust::None()
: StyleFontSizeAdjust::Ex(fontStyle.sizeAdjust);
switch (StyleFontSizeAdjust::Tag(fontStyle.sizeAdjustBasis)) {
case StyleFontSizeAdjust::Tag::None:
aSystemFont->sizeAdjust = StyleFontSizeAdjust::None();
break;
case StyleFontSizeAdjust::Tag::Ex:
aSystemFont->sizeAdjust = StyleFontSizeAdjust::Ex(fontStyle.sizeAdjust);
break;
case StyleFontSizeAdjust::Tag::Cap:
aSystemFont->sizeAdjust = StyleFontSizeAdjust::Cap(fontStyle.sizeAdjust);
break;
case StyleFontSizeAdjust::Tag::Ch:
aSystemFont->sizeAdjust = StyleFontSizeAdjust::Ch(fontStyle.sizeAdjust);
break;
case StyleFontSizeAdjust::Tag::Ic:
aSystemFont->sizeAdjust = StyleFontSizeAdjust::Ic(fontStyle.sizeAdjust);
break;
}
if (aFontID == LookAndFeel::FontID::MozField ||
aFontID == LookAndFeel::FontID::MozButton ||

View File

@ -527,8 +527,8 @@ already_AddRefed<gfxTextRun> nsOpenTypeTable::MakeTextRun(
gfxTextRun::DetailedGlyph detailedGlyph;
detailedGlyph.mGlyphID = aGlyph.glyphID;
detailedGlyph.mAdvance = NSToCoordRound(
aAppUnitsPerDevPixel * aFontGroup->GetFirstValidFont()->GetGlyphHAdvance(
aDrawTarget, aGlyph.glyphID));
aAppUnitsPerDevPixel *
aFontGroup->GetFirstValidFont()->GetGlyphHAdvance(aGlyph.glyphID));
textRun->SetDetailedGlyphs(0, 1, &detailedGlyph);
return textRun.forget();