Bug 1398802 - Support the AAT 'trak' table when shaping macOS fonts via Core Text. r=jrmuizel

This commit is contained in:
Jonathan Kew 2017-09-27 11:16:35 +01:00
Родитель cb611a9aff
Коммит e592a806a6
3 изменённых файлов: 192 добавлений и 3 удалений

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

@ -163,7 +163,8 @@ gfxMacFont::ShapeText(DrawTarget *aDrawTarget,
// Currently, we don't support vertical shaping via CoreText,
// so we ignore RequiresAATLayout if vertical is requested.
if (static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout() &&
auto macFontEntry = static_cast<MacOSFontEntry*>(GetFontEntry());
if (macFontEntry->RequiresAATLayout() &&
!aVertical) {
if (!mCoreTextShaper) {
mCoreTextShaper = MakeUnique<gfxCoreTextShaper>(this);
@ -173,6 +174,24 @@ gfxMacFont::ShapeText(DrawTarget *aDrawTarget,
aShapedText)) {
PostShapingFixup(aDrawTarget, aText, aOffset,
aLength, aVertical, aShapedText);
if (macFontEntry->HasTrackingTable()) {
// Convert font size from device pixels back to CSS px
// to use in selecting tracking value
float trackSize = GetAdjustedSize() *
aShapedText->GetAppUnitsPerDevUnit() /
AppUnitsPerCSSPixel();
float tracking =
macFontEntry->TrackingForCSSPx(trackSize) *
mFUnitsConvFactor;
// Applying tracking is a lot like the adjustment we do for
// synthetic bold: we want to apply between clusters, not to
// non-spacing glyphs within a cluster. So we can reuse that
// helper here.
aShapedText->AdjustAdvancesForSyntheticBold(tracking,
aOffset, aLength);
}
return true;
}
}

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

@ -40,6 +40,9 @@ public:
bool aIsDataUserFont, bool aIsLocal);
virtual ~MacOSFontEntry() {
if (mTrakTable) {
hb_blob_destroy(mTrakTable);
}
::CGFontRelease(mFontRef);
}
@ -61,12 +64,26 @@ public:
bool HasVariations();
bool IsCFF();
// Return true if the font has a 'trak' table (and we can successfully
// interpret it), otherwise false. This will load and cache the table
// the first time it is called.
bool HasTrackingTable();
// Return the tracking (in font units) to be applied for the given size.
// (This is a floating-point number because of possible interpolation.)
float TrackingForCSSPx(float aPointSize) const;
protected:
gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle,
bool aNeedsBold) override;
bool HasFontTable(uint32_t aTableTag) override;
// Helper for HasTrackingTable; check/parse the table and cache pointers
// to the subtables we need. Returns false on failure, in which case the
// table is unusable.
bool ParseTrakTable();
static void DestroyBlobFunc(void* aUserData);
CGFontRef mFontRef; // owning reference to the CGFont, released on destruction
@ -79,9 +96,18 @@ protected:
bool mIsCFFInitialized;
bool mHasVariations;
bool mHasVariationsInitialized;
bool mCheckedForTracking;
nsTHashtable<nsUint32HashKey> mAvailableTables;
mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
// For AAT font being shaped by Core Text, a strong reference to the 'trak'
// table (if present).
hb_blob_t* mTrakTable;
// Cached pointers to tables within 'trak', initialized by ParseTrakTable.
const mozilla::AutoSwap_PRInt16* mTrakValues;
const mozilla::AutoSwap_PRInt32* mTrakSizeTable;
uint16_t mNumTrakSizes;
};
class gfxMacPlatformFontList : public gfxPlatformFontList {

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

@ -312,7 +312,11 @@ MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
mIsCFF(false),
mIsCFFInitialized(false),
mHasVariations(false),
mHasVariationsInitialized(false)
mHasVariationsInitialized(false),
mCheckedForTracking(false),
mTrakTable(nullptr),
mTrakValues(nullptr),
mTrakSizeTable(nullptr)
{
mWeight = aWeight;
}
@ -331,7 +335,11 @@ MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
mIsCFF(false),
mIsCFFInitialized(false),
mHasVariations(false),
mHasVariationsInitialized(false)
mHasVariationsInitialized(false),
mCheckedForTracking(false),
mTrakTable(nullptr),
mTrakValues(nullptr),
mTrakSizeTable(nullptr)
{
mFontRef = aFontRef;
mFontRefInitialized = true;
@ -464,6 +472,142 @@ MacOSFontEntry::HasFontTable(uint32_t aTableTag)
return mAvailableTables.GetEntry(aTableTag);
}
typedef struct {
AutoSwap_PRUint32 version;
AutoSwap_PRUint16 format;
AutoSwap_PRUint16 horizOffset;
AutoSwap_PRUint16 vertOffset;
AutoSwap_PRUint16 reserved;
// TrackData horizData;
// TrackData vertData;
} TrakHeader;
typedef struct {
AutoSwap_PRUint16 nTracks;
AutoSwap_PRUint16 nSizes;
AutoSwap_PRUint32 sizeTableOffset;
// trackTableEntry trackTable[];
// fixed32 sizeTable[];
} TrackData;
typedef struct {
AutoSwap_PRUint32 track;
AutoSwap_PRUint16 nameIndex;
AutoSwap_PRUint16 offset;
} TrackTableEntry;
bool
MacOSFontEntry::HasTrackingTable()
{
if (!mCheckedForTracking) {
mCheckedForTracking = true;
mTrakTable = GetFontTable(TRUETYPE_TAG('t','r','a','k'));
if (mTrakTable) {
if (!ParseTrakTable()) {
hb_blob_destroy(mTrakTable);
mTrakTable = nullptr;
}
}
}
return mTrakTable != nullptr;
}
bool
MacOSFontEntry::ParseTrakTable()
{
// Check table validity and set up the subtable pointers we need;
// if 'trak' table is invalid, or doesn't contain a 'normal' track,
// return false to tell the caller not to try using it.
unsigned int len;
const char* data = hb_blob_get_data(mTrakTable, &len);
if (len < sizeof(TrakHeader)) {
return false;
}
auto trak = reinterpret_cast<const TrakHeader*>(data);
uint16_t horizOffset = trak->horizOffset;
if (trak->version != 0x00010000 ||
uint16_t(trak->format) != 0 ||
horizOffset == 0 ||
uint16_t(trak->reserved) != 0) {
return false;
}
// Find the horizontal trackData, and check it doesn't overrun the buffer.
if (horizOffset > len - sizeof(TrackData)) {
return false;
}
auto trackData = reinterpret_cast<const TrackData*>(data + horizOffset);
uint16_t nTracks = trackData->nTracks;
mNumTrakSizes = trackData->nSizes;
if (nTracks == 0 || mNumTrakSizes < 2) {
return false;
}
uint32_t sizeTableOffset = trackData->sizeTableOffset;
// Find the trackTable, and check it doesn't overrun the buffer.
if (horizOffset >
len - (sizeof(TrackData) + nTracks * sizeof(TrackTableEntry))) {
return false;
}
auto trackTable = reinterpret_cast<const TrackTableEntry*>
(data + horizOffset + sizeof(TrackData));
// Look for 'normal' tracking, bail out if no such track is present.
unsigned trackIndex;
for (trackIndex = 0; trackIndex < nTracks; ++trackIndex) {
if (trackTable[trackIndex].track == 0x00000000) {
break;
}
}
if (trackIndex == nTracks) {
return false;
}
// Find list of tracking values, and check they won't overrun.
uint16_t offset = trackTable[trackIndex].offset;
if (offset > len - mNumTrakSizes * sizeof(uint16_t)) {
return false;
}
mTrakValues = reinterpret_cast<const AutoSwap_PRInt16*>(data + offset);
// Find the size subtable, and check it doesn't overrun the buffer.
mTrakSizeTable =
reinterpret_cast<const AutoSwap_PRInt32*>(data + sizeTableOffset);
if (mTrakSizeTable + mNumTrakSizes >
reinterpret_cast<const AutoSwap_PRInt32*>(data + len)) {
return false;
}
return true;
}
float
MacOSFontEntry::TrackingForCSSPx(float aSize) const
{
MOZ_ASSERT(mTrakTable && mTrakValues && mTrakSizeTable);
// Find index of first sizeTable entry that is >= the requested size.
Fixed fixedSize = X2Fix(aSize);
unsigned sizeIndex;
for (sizeIndex = 0; sizeIndex < mNumTrakSizes; ++sizeIndex) {
if (mTrakSizeTable[sizeIndex] >= fixedSize) {
break;
}
}
// Return the tracking value for the requested size, or an interpolated
// value if the exact size isn't found.
if (sizeIndex == mNumTrakSizes) {
// Request is larger than last entry in the table, so just use that.
// (We don't attempt to extrapolate more extreme tracking values than
// the largest or smallest present in the table.)
return int16_t(mTrakValues[mNumTrakSizes - 1]);
}
if (sizeIndex == 0 || mTrakSizeTable[sizeIndex] == fixedSize) {
// Found an exact match, or size was smaller than the first entry.
return int16_t(mTrakValues[sizeIndex]);
}
// Requested size falls between two entries: interpolate value.
double s0 = Fix2X(mTrakSizeTable[sizeIndex - 1]);
double s1 = Fix2X(mTrakSizeTable[sizeIndex]);
double t = (aSize - s0) / (s1 - s0);
return (1.0 - t) * int16_t(mTrakValues[sizeIndex - 1]) +
t * int16_t(mTrakValues[sizeIndex]);
}
void
MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const