Bug 1772189 - Avoid potential infinite recursion when initializing vertical metrics, if the font has a broken 'vmtx' table. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D148928
This commit is contained in:
Jonathan Kew 2022-06-16 12:38:35 +00:00
Родитель a413b63464
Коммит d58446c275
5 изменённых файлов: 83 добавлений и 62 удалений

Просмотреть файл

@ -82,7 +82,6 @@ gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
AntialiasOption anAAOption)
: gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption),
mFontFace(aFontFace ? aFontFace : aUnscaledFont->GetFontFace()),
mMetrics(nullptr),
mUseSubpixelPositions(false),
mAllowManualShowGlyphs(true),
mAzureScaledFontUsedClearType(false) {
@ -112,7 +111,6 @@ gfxDWriteFont::~gfxDWriteFont() {
if (auto* scaledFont = mAzureScaledFontGDI.exchange(nullptr)) {
scaledFont->Release();
}
delete mMetrics;
}
/* static */
@ -305,6 +303,8 @@ bool gfxDWriteFont::GetFakeMetricsForArialBlack(
}
void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
::memset(&mMetrics, 0, sizeof(mMetrics));
DWRITE_FONT_METRICS fontMetrics;
if (!(mFontEntry->Weight().Min() == FontWeight::FromInt(900) &&
mFontEntry->Weight().Max() == FontWeight::FromInt(900) &&
@ -381,22 +381,19 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
mAllowManualShowGlyphs = false;
}
mMetrics = new gfxFont::Metrics;
::memset(mMetrics, 0, sizeof(*mMetrics));
mMetrics.xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
mMetrics.capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
mMetrics->capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
mMetrics.maxAscent = round(fontMetrics.ascent * mFUnitsConvFactor);
mMetrics.maxDescent = round(fontMetrics.descent * mFUnitsConvFactor);
mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent;
mMetrics->maxAscent = round(fontMetrics.ascent * mFUnitsConvFactor);
mMetrics->maxDescent = round(fontMetrics.descent * mFUnitsConvFactor);
mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
mMetrics.emHeight = mAdjustedSize;
mMetrics.emAscent =
mMetrics.emHeight * mMetrics.maxAscent / mMetrics.maxHeight;
mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
mMetrics->emHeight = mAdjustedSize;
mMetrics->emAscent =
mMetrics->emHeight * mMetrics->maxAscent / mMetrics->maxHeight;
mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
mMetrics->maxAdvance = mAdjustedSize;
mMetrics.maxAdvance = mAdjustedSize;
// try to get the true maxAdvance value from 'hhea'
gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
@ -406,28 +403,27 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
mMetrics->maxAdvance =
uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
mMetrics.maxAdvance = uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
}
}
mMetrics->internalLeading =
std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
mMetrics.internalLeading =
std::max(mMetrics.maxHeight - mMetrics.emHeight, 0.0);
mMetrics.externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
UINT32 ucs = L' ';
UINT16 glyph;
if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph)) && glyph != 0) {
mSpaceGlyph = glyph;
mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
mMetrics.spaceWidth = MeasureGlyphWidth(glyph);
} else {
mMetrics->spaceWidth = 0;
mMetrics.spaceWidth = 0;
}
// try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
// if the table is not available or if using hinted/pixel-snapped widths
if (mUseSubpixelPositions) {
mMetrics->aveCharWidth = 0;
mMetrics.aveCharWidth = 0;
gfxFontEntry::AutoTable os2Table(GetFontEntry(),
TRUETYPE_TAG('O', 'S', '/', '2'));
if (os2Table) {
@ -438,57 +434,56 @@ void gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) {
// Not checking against sizeof(mozilla::OS2Table) here because older
// versions of the table have different sizes; we only need the first
// two 16-bit fields here.
mMetrics->aveCharWidth =
int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
mMetrics.aveCharWidth = int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
}
}
}
if (mMetrics->aveCharWidth < 1) {
mMetrics->aveCharWidth = GetCharAdvance('x');
if (mMetrics->aveCharWidth < 1) {
if (mMetrics.aveCharWidth < 1) {
mMetrics.aveCharWidth = GetCharAdvance('x');
if (mMetrics.aveCharWidth < 1) {
// Let's just assume the X is square.
mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
mMetrics.aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
}
}
mMetrics->zeroWidth = GetCharAdvance('0');
mMetrics.zeroWidth = GetCharAdvance('0');
mMetrics->ideographicWidth = GetCharAdvance(kWaterIdeograph);
mMetrics.ideographicWidth = GetCharAdvance(kWaterIdeograph);
mMetrics->underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor;
mMetrics->underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor;
mMetrics->strikeoutOffset =
mMetrics.underlineOffset = fontMetrics.underlinePosition * mFUnitsConvFactor;
mMetrics.underlineSize = fontMetrics.underlineThickness * mFUnitsConvFactor;
mMetrics.strikeoutOffset =
fontMetrics.strikethroughPosition * mFUnitsConvFactor;
mMetrics->strikeoutSize =
mMetrics.strikeoutSize =
fontMetrics.strikethroughThickness * mFUnitsConvFactor;
SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
SanitizeMetrics(&mMetrics, GetFontEntry()->mIsBadUnderlineFont);
if (ApplySyntheticBold()) {
auto delta = GetSyntheticBoldOffset();
mMetrics->spaceWidth += delta;
mMetrics->aveCharWidth += delta;
mMetrics->maxAdvance += delta;
if (mMetrics->zeroWidth > 0) {
mMetrics->zeroWidth += delta;
mMetrics.spaceWidth += delta;
mMetrics.aveCharWidth += delta;
mMetrics.maxAdvance += delta;
if (mMetrics.zeroWidth > 0) {
mMetrics.zeroWidth += delta;
}
if (mMetrics->ideographicWidth > 0) {
mMetrics->ideographicWidth += delta;
if (mMetrics.ideographicWidth > 0) {
mMetrics.ideographicWidth += delta;
}
}
#if 0
printf("Font: %p (%s) size: %f\n", this,
NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
printf(" internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
printf(" spaceWidth: %f aveCharWidth: %f zeroWidth: %f\n",
mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->zeroWidth);
printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.zeroWidth);
printf(" xHeight: %f capHeight: %f\n", mMetrics.xHeight, mMetrics.capHeight);
printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
#endif
}
@ -766,7 +761,6 @@ bool gfxDWriteFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds,
void gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const {
gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
aSizes->mFontInstances += aMallocSizeOf(mMetrics);
if (mGlyphWidths) {
aSizes->mFontInstances +=
mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);

Просмотреть файл

@ -74,7 +74,7 @@ class gfxDWriteFont final : public gfxFont {
bool ShouldRoundXOffset(cairo_t* aCairo) const override;
protected:
const Metrics& GetHorizontalMetrics() const override { return *mMetrics; }
const Metrics& GetHorizontalMetrics() const override { return mMetrics; }
bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS* aFontMetrics);
@ -92,7 +92,7 @@ class gfxDWriteFont final : public gfxFont {
RefPtr<IDWriteFontFace> mFontFace;
RefPtr<IDWriteFontFace1> mFontFace1; // may be unavailable on older DWrite
Metrics* mMetrics;
Metrics mMetrics;
// cache of glyph widths in 16.16 fixed-point pixels
mozilla::UniquePtr<nsTHashMap<nsUint32HashKey, int32_t>> mGlyphWidths;

Просмотреть файл

@ -984,9 +984,17 @@ gfxFloat gfxFont::GetGlyphAdvance(uint16_t aGID, bool aVertical) {
NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
"missing font unit conversion factor");
if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
return (aVertical ? shaper->GetGlyphVAdvance(aGID)
: shaper->GetGlyphHAdvance(aGID)) /
65536.0;
if (aVertical) {
// Note that GetGlyphVAdvance may return -1 to indicate it was unable
// to retrieve vertical metrics; in that case we fall back to the
// aveCharWidth value as a default advance.
int32_t advance = shaper->GetGlyphVAdvance(aGID);
if (advance < 0) {
return GetMetrics(nsFontMetrics::eVertical).aveCharWidth;
}
return advance / 65536.0;
}
return shaper->GetGlyphHAdvance(aGID) / 65536.0;
}
return 0.0;
}
@ -3948,6 +3956,7 @@ void gfxFont::CreateVerticalMetrics() {
if (mFUnitsConvFactor < 0.0) {
uint16_t upem = GetFontEntry()->UnitsPerEm();
if (upem != gfxFontEntry::kInvalidUPEM) {
AutoWriteLock lock(mLock);
mFUnitsConvFactor = GetAdjustedSize() / upem;
}
}
@ -4016,7 +4025,17 @@ void gfxFont::CreateVerticalMetrics() {
metrics->maxDescent = halfExtent;
SET_SIGNED(externalLeading, vhea->lineGap);
}
metrics->ideographicWidth = GetCharAdvance(kWaterIdeograph, true);
// Call gfxHarfBuzzShaper::GetGlyphVAdvance directly, as GetCharAdvance
// would potentially recurse if no v-advance is available and it attempts
// to fall back to a value from mVerticalMetrics.
if (gfxHarfBuzzShaper* shaper = GetHarfBuzzShaper()) {
uint32_t gid = ProvidesGetGlyph()
? GetGlyph(kWaterIdeograph, 0)
: shaper->GetNominalGlyph(kWaterIdeograph);
int32_t advance = shaper->GetGlyphVAdvance(gid);
metrics->ideographicWidth =
advance < 0 ? metrics->aveCharWidth : mFUnitsConvFactor * advance;
}
}
}

Просмотреть файл

@ -320,10 +320,10 @@ hb_position_t gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) {
InitializeVertical();
if (!mVmtxTable) {
// Must be a "vertical" font that doesn't actually have vertical metrics;
// use a fixed advance.
return FloatToFixed(
mFont->GetMetrics(nsFontMetrics::eVertical).aveCharWidth);
// Must be a "vertical" font that doesn't actually have vertical metrics.
// Return an invalid (negative) value to tell the caller to fall back to
// something else.
return -1;
}
NS_ASSERTION(mNumLongVMetrics > 0,
@ -367,11 +367,17 @@ hb_position_t gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t* font,
// and provide hinted platform-specific vertical advances (analogous to the
// GetGlyphWidth method for horizontal advances). If that proves necessary,
// we'll add a new gfxFont method and call it from here.
//
hb_position_t advance = fcd->mShaper->GetGlyphVAdvance(glyph);
if (advance < 0) {
// Not available (e.g. broken metrics in the font); use a fallback value.
advance = FloatToFixed(fcd->mShaper->GetFont()
->GetMetrics(nsFontMetrics::eVertical)
.aveCharWidth);
}
// We negate the value from GetGlyphVAdvance here because harfbuzz shapes
// with a coordinate system where positive is upwards, whereas the inline
// direction in which glyphs advance is downwards.
return -fcd->mShaper->GetGlyphVAdvance(glyph);
return -advance;
}
struct VORG {

Просмотреть файл

@ -47,6 +47,8 @@ class gfxHarfBuzzShaper : public gfxFontShaper {
// get harfbuzz glyph advance, in font design units
hb_position_t GetGlyphHAdvance(hb_codepoint_t glyph) const;
// Get vertical glyph advance, or -1 if not available; caller should check
// for a negative result and provide a fallback or fail, as appropriate.
hb_position_t GetGlyphVAdvance(hb_codepoint_t glyph);
void GetGlyphVOrigin(hb_codepoint_t aGlyph, hb_position_t* aX,