bug 449292 - part 6 - font table cache and accessors to support harfbuzz. r=jdaggett sr=roc

This commit is contained in:
Jonathan Kew 2010-06-11 20:14:38 +01:00
Родитель 37f3b9fe59
Коммит 673bdd945e
11 изменённых файлов: 433 добавлений и 125 удалений

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

@ -44,6 +44,9 @@
#include "gfxUserFontSet.h"
#include "cairo-win32.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
/**
* \brief Class representing a font face for a font entry.
*/
@ -72,6 +75,10 @@ public:
IDWriteFontFace *GetFontFace() { return mFontFace.get(); }
// override gfxFont table access function to bypass gfxFontEntry cache,
// use DWrite API to get direct access to system font data
virtual hb_blob_t *GetFontTable(PRUint32 aTag);
protected:
void ComputeMetrics();
@ -79,6 +86,8 @@ protected:
cairo_scaled_font_t *CairoScaledFont();
static void DestroyBlobFunc(void* userArg);
nsRefPtr<IDWriteFontFace> mFontFace;
cairo_font_face_t *mCairoFontFace;
cairo_scaled_font_t *mCairoScaledFont;

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

@ -48,6 +48,7 @@
#include "gfxFontUtils.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "gfxSkipChars.h"
#include "gfxRect.h"
@ -71,6 +72,8 @@ class gfxUserFontData;
class nsILanguageAtomService;
typedef struct _hb_blob_t hb_blob_t;
// We should eliminate these synonyms when it won't cause many merge conflicts.
#define FONT_STYLE_NORMAL NS_FONT_STYLE_NORMAL
#define FONT_STYLE_ITALIC NS_FONT_STYLE_ITALIC
@ -217,7 +220,15 @@ public:
const nsString& FamilyName();
already_AddRefed<gfxFont> FindOrMakeFont(const gfxFontStyle *aStyle, PRBool aNeedsBold);
already_AddRefed<gfxFont> FindOrMakeFont(const gfxFontStyle *aStyle,
PRBool aNeedsBold);
// Subclasses should override this if they can do something more efficient
// than getting tables with GetFontTable() and caching them in the entry.
//
// Note that some gfxFont implementations may not call this at all,
// if it is more efficient to get the table from the OS at that level.
virtual hb_blob_t *GetFontTable(PRUint32 aTag);
nsString mName;
@ -273,6 +284,61 @@ protected:
gfxFontFamily *mFamily;
/*
* Font table cache, to support GetFontTable for harfbuzz.
*
* The harfbuzz shaper (and potentially other clients) needs access to raw
* font table data. This needs to be cached so that it can be used
* repeatedly (each time we construct a text run; in some cases, for
* each character/glyph within the run) without re-fetching large tables
* every time.
*
* Because we may instantiate many gfxFonts for the same physical font
* file (at different sizes), we should ensure that they can share a
* single cached copy of the font tables. To do this, we implement table
* access and caching on the fontEntry rather than the font itself.
*
* The default implementation uses GetFontTable() to read font table
* data into byte arrays, and caches these in a hashtable along with
* hb_blob_t wrappers. The entry can then return blobs to harfbuzz.
*
* Harfbuzz will "destroy" the blobs when it is finished with them;
* they are created with a destroy callback that removes them from
* the hashtable when all references are released.
*/
class FontTableCacheEntry {
public:
// create a cache entry by adopting the content of an existing buffer
FontTableCacheEntry(nsTArray<PRUint8>& aBuffer,
PRUint32 aTag,
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry>& aCache);
~FontTableCacheEntry() {
MOZ_COUNT_DTOR(FontTableCacheEntry);
}
hb_blob_t *GetBlob() const { return mBlob; }
protected:
// the data block, owned (via adoption) by the entry
nsTArray<PRUint8> mData;
// a harfbuzz blob wrapper that we can return to clients
hb_blob_t *mBlob;
// the blob destroy function needs to know the table tag
// and the owning hashtable, so that it can remove the entry
PRUint32 mTag;
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry>&
mCache;
private:
// not implemented
FontTableCacheEntry(const FontTableCacheEntry&);
static void Destroy(void *aUserData);
};
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry> mFontTableCache;
private:
gfxFontEntry(const gfxFontEntry&);
gfxFontEntry& operator=(const gfxFontEntry&);
@ -779,6 +845,20 @@ public:
return nsnull;
}
// Access to raw font table data (needed for Harfbuzz):
// returns a pointer to data owned by the fontEntry or the OS,
// which will remain valid until released.
//
// Default implementations forward to the font entry, which
// maintains a shared table cache; however, subclasses may
// override if they can provide more efficient table access.
// Get pointer to a specific font table, or an empty blob if
// the table doesn't exist in the font
virtual hb_blob_t *GetFontTable(PRUint32 aTag) {
return mFontEntry->GetFontTable(aTag);
}
// Font metrics
struct Metrics {
gfxFloat xHeight;

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

@ -505,35 +505,39 @@ public:
}
static nsresult
ReadCMAPTableFormat12(PRUint8 *aBuf, PRUint32 aLength,
ReadCMAPTableFormat12(const PRUint8 *aBuf, PRUint32 aLength,
gfxSparseBitSet& aCharacterMap);
static nsresult
ReadCMAPTableFormat4(PRUint8 *aBuf, PRUint32 aLength,
ReadCMAPTableFormat4(const PRUint8 *aBuf, PRUint32 aLength,
gfxSparseBitSet& aCharacterMap);
static nsresult
ReadCMAPTableFormat14(PRUint8 *aBuf, PRUint32 aLength,
ReadCMAPTableFormat14(const PRUint8 *aBuf, PRUint32 aLength,
PRUint8*& aTable);
static PRUint32
FindPreferredSubtable(PRUint8 *aBuf, PRUint32 aBufLength,
FindPreferredSubtable(const PRUint8 *aBuf, PRUint32 aBufLength,
PRUint32 *aTableOffset, PRUint32 *aUVSTableOffset,
PRBool *aSymbolEncoding);
static nsresult
ReadCMAP(PRUint8 *aBuf, PRUint32 aBufLength, gfxSparseBitSet& aCharacterMap,
ReadCMAP(const PRUint8 *aBuf, PRUint32 aBufLength,
gfxSparseBitSet& aCharacterMap,
PRUint32& aUVSOffset,
PRPackedBool& aUnicodeFont, PRPackedBool& aSymbolFont);
static PRUint32
MapCharToGlyphFormat4(const PRUint8 *aBuf, PRUnichar aCh);
static PRUint32
MapCharToGlyphFormat12(const PRUint8 *aBuf, PRUint32 aCh);
static PRUint16
MapUVSToGlyphFormat14(const PRUint8 *aBuf, PRUint32 aCh, PRUint32 aVS);
static PRUint32
MapCharToGlyph(PRUint8 *aBuf, PRUint32 aBufLength, PRUnichar aCh);
MapCharToGlyph(const PRUint8 *aBuf, PRUint32 aBufLength, PRUnichar aCh);
#ifdef XP_WIN

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

@ -66,6 +66,7 @@ EXTRA_DSO_LDOPTS += \
$(NSPR_LIBS) \
$(ZLIB_LIBS) \
$(QCMS_LIBS) \
$(MOZ_HARFBUZZ_LIBS) \
$(NULL)
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)

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

@ -43,6 +43,8 @@
#include "gfxDWriteTextAnalysis.h"
#include "harfbuzz/hb-blob.h"
// Chosen this as to resemble DWrite's own oblique face style.
#define OBLIQUE_SKEW_FACTOR 0.3
@ -318,3 +320,48 @@ gfxDWriteFont::CairoScaledFont()
return mCairoScaledFont;
}
// Access to font tables packaged in hb_blob_t form
// object attached to the Harfbuzz blob, used to release
// the table when the blob is destroyed
class FontTableRec {
public:
FontTableRec(IDWriteFontFace *aFontFace, void *aContext)
: mFontFace(aFontFace), mContext(aContext)
{ }
~FontTableRec() {
mFontFace->ReleaseFontTable(mContext);
}
private:
IDWriteFontFace *mFontFace;
void *mContext;
};
/*static*/ void
gfxDWriteFont::DestroyBlobFunc(void* aUserData)
{
FontTableRec *ftr = static_cast<FontTableRec*>(aUserData);
delete ftr;
}
hb_blob_t *
gfxDWriteFont::GetFontTable(PRUint32 aTag)
{
const void *data;
UINT32 size;
void *context;
BOOL exists;
HRESULT hr = mFontFace->TryGetFontTable(NS_SWAP32(aTag),
&data, &size, &context, &exists);
if (SUCCEEDED(hr) && exists) {
FontTableRec *ftr = new FontTableRec(mFontFace, context);
return hb_blob_create(static_cast<const char*>(data), size,
HB_MEMORY_MODE_READONLY,
DestroyBlobFunc, ftr);
}
return hb_blob_create_empty();
}

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

@ -63,6 +63,8 @@
#include "cairo.h"
#include "gfxFontTest.h"
#include "harfbuzz/hb-blob.h"
#include "nsCRT.h"
gfxFontCache *gfxFontCache::gGlobalCache = nsnull;
@ -87,11 +89,11 @@ static PRUint32 gGlyphExtentsSetupFallBackToTight = 0;
gfxFontEntry::~gfxFontEntry()
{
if (mUserFontData)
if (mUserFontData) {
delete mUserFontData;
}
}
PRBool gfxFontEntry::TestCharacterMap(PRUint32 aCh)
{
if (!mCmapInitialized) {
@ -100,7 +102,6 @@ PRBool gfxFontEntry::TestCharacterMap(PRUint32 aCh)
return mCharacterMap.test(aCh);
}
nsresult gfxFontEntry::InitializeUVSMap()
{
// mUVSOffset will not be initialized
@ -137,7 +138,6 @@ nsresult gfxFontEntry::InitializeUVSMap()
return NS_OK;
}
PRUint16 gfxFontEntry::GetUVSGlyph(PRUint32 aCh, PRUint32 aVS)
{
InitializeUVSMap();
@ -149,14 +149,12 @@ PRUint16 gfxFontEntry::GetUVSGlyph(PRUint32 aCh, PRUint32 aVS)
return 0;
}
nsresult gfxFontEntry::ReadCMAP()
{
mCmapInitialized = PR_TRUE;
return NS_OK;
}
const nsString& gfxFontEntry::FamilyName()
{
NS_ASSERTION(mFamily, "gfxFontEntry is not a member of a family");
@ -185,6 +183,66 @@ gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, PRBool aNeedsBold)
return f;
}
gfxFontEntry::FontTableCacheEntry::FontTableCacheEntry
(nsTArray<PRUint8>& aBuffer,
PRUint32 aTag,
nsClassHashtable<nsUint32HashKey,FontTableCacheEntry>& aCache)
: mTag(aTag), mCache(aCache)
{
MOZ_COUNT_CTOR(FontTableCacheEntry);
mData.SwapElements(aBuffer);
mBlob = hb_blob_create((const char*)mData.Elements(), mData.Length(),
HB_MEMORY_MODE_READONLY,
gfxFontEntry::FontTableCacheEntry::Destroy,
this);
}
/* static */ void
gfxFontEntry::FontTableCacheEntry::Destroy(void *aUserData)
{
gfxFontEntry::FontTableCacheEntry *ftce =
static_cast<gfxFontEntry::FontTableCacheEntry*>(aUserData);
ftce->mCache.Remove(ftce->mTag);
}
hb_blob_t *
gfxFontEntry::GetFontTable(PRUint32 aTag)
{
if (!mFontTableCache.IsInitialized()) {
// we do this here rather than on fontEntry construction
// because not all shapers will access the table cache at all
mFontTableCache.Init(10);
}
FontTableCacheEntry *entry = nsnull;
if (!mFontTableCache.Get(aTag, &entry)) {
nsTArray<PRUint8> buffer;
if (NS_SUCCEEDED(GetFontTable(aTag, buffer))) {
entry = new FontTableCacheEntry(buffer, // adopts buffer elements
aTag, mFontTableCache);
if (mFontTableCache.Put(aTag, entry)) {
return entry->GetBlob();
}
hb_blob_destroy(entry->GetBlob());
delete entry; // we failed to cache it!
return hb_blob_create_empty();
}
}
if (entry) {
return hb_blob_reference(entry->GetBlob());
}
return hb_blob_create_empty();
}
//////////////////////////////////////////////////////////////////////////////
//
// class gfxFontFamily
//
//////////////////////////////////////////////////////////////////////////////
// we consider faces with mStandardFace == PR_TRUE to be "greater than" those with PR_FALSE,
// because during style matching, later entries will replace earlier ones
class FontEntryStandardFaceComparator {

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

@ -60,6 +60,10 @@
#define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
#define UNICODE_BMP_LIMIT 0x10000
using namespace mozilla; // for the AutoSwap_* types
/* Unicode subrange table
* from: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/unicode_63ub.asp
*
@ -230,45 +234,62 @@ static const struct UnicodeRangeTableEntry gUnicodeRanges[] = {
{ 111, 0x1D360, 0x1D37F, "Counting Rod Numerals" }
};
#pragma pack(1)
typedef struct {
AutoSwap_PRUint16 format;
AutoSwap_PRUint16 reserved;
AutoSwap_PRUint32 length;
AutoSwap_PRUint32 language;
AutoSwap_PRUint32 numGroups;
} Format12CmapHeader;
typedef struct {
AutoSwap_PRUint32 startCharCode;
AutoSwap_PRUint32 endCharCode;
AutoSwap_PRUint32 startGlyphId;
} Format12Group;
#pragma pack()
nsresult
gfxFontUtils::ReadCMAPTableFormat12(PRUint8 *aBuf, PRUint32 aLength, gfxSparseBitSet& aCharacterMap)
gfxFontUtils::ReadCMAPTableFormat12(const PRUint8 *aBuf, PRUint32 aLength,
gfxSparseBitSet& aCharacterMap)
{
enum {
OffsetFormat = 0,
OffsetReserved = 2,
OffsetTableLength = 4,
OffsetLanguage = 8,
OffsetNumberGroups = 12,
OffsetGroups = 16,
// Ensure table is large enough that we can safely read the header
NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader),
NS_ERROR_GFX_CMAP_MALFORMED);
SizeOfGroup = 12,
GroupOffsetStartCode = 0,
GroupOffsetEndCode = 4
};
NS_ENSURE_TRUE(aLength >= 16, NS_ERROR_GFX_CMAP_MALFORMED);
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 12,
// Sanity-check header fields
const Format12CmapHeader *cmap12 =
reinterpret_cast<const Format12CmapHeader*>(aBuf);
NS_ENSURE_TRUE(PRUint16(cmap12->format) == 12,
NS_ERROR_GFX_CMAP_MALFORMED);
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetReserved) == 0,
NS_ENSURE_TRUE(PRUint16(cmap12->reserved) == 0,
NS_ERROR_GFX_CMAP_MALFORMED);
PRUint32 tablelen = ReadLongAt(aBuf, OffsetTableLength);
NS_ENSURE_TRUE(tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
NS_ENSURE_TRUE(tablelen >= 16, NS_ERROR_GFX_CMAP_MALFORMED);
PRUint32 tablelen = cmap12->length;
NS_ENSURE_TRUE(tablelen >= sizeof(Format12CmapHeader) &&
tablelen <= aLength, NS_ERROR_GFX_CMAP_MALFORMED);
NS_ENSURE_TRUE(ReadLongAt(aBuf, OffsetLanguage) == 0,
NS_ENSURE_TRUE(cmap12->language == 0, NS_ERROR_GFX_CMAP_MALFORMED);
// Check that the table is large enough for the group array
const PRUint32 numGroups = cmap12->numGroups;
NS_ENSURE_TRUE((tablelen - sizeof(Format12CmapHeader)) /
sizeof(Format12Group) >= numGroups,
NS_ERROR_GFX_CMAP_MALFORMED);
const PRUint32 numGroups = ReadLongAt(aBuf, OffsetNumberGroups);
NS_ENSURE_TRUE(tablelen >= 16 + (12 * numGroups),
NS_ERROR_GFX_CMAP_MALFORMED);
// The array of groups immediately follows the subtable header.
const Format12Group *group =
reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
const PRUint8 *groups = aBuf + OffsetGroups;
// Check that groups are in correct order and do not overlap,
// and record character coverage in aCharacterMap.
PRUint32 prevEndCharCode = 0;
for (PRUint32 i = 0; i < numGroups; i++, groups += SizeOfGroup) {
const PRUint32 startCharCode = ReadLongAt(groups, GroupOffsetStartCode);
const PRUint32 endCharCode = ReadLongAt(groups, GroupOffsetEndCode);
for (PRUint32 i = 0; i < numGroups; i++, group++) {
const PRUint32 startCharCode = group->startCharCode;
const PRUint32 endCharCode = group->endCharCode;
NS_ENSURE_TRUE((prevEndCharCode < startCharCode || i == 0) &&
startCharCode <= endCharCode &&
endCharCode <= CMAP_MAX_CODEPOINT,
@ -283,7 +304,8 @@ gfxFontUtils::ReadCMAPTableFormat12(PRUint8 *aBuf, PRUint32 aLength, gfxSparseBi
}
nsresult
gfxFontUtils::ReadCMAPTableFormat4(PRUint8 *aBuf, PRUint32 aLength, gfxSparseBitSet& aCharacterMap)
gfxFontUtils::ReadCMAPTableFormat4(const PRUint8 *aBuf, PRUint32 aLength,
gfxSparseBitSet& aCharacterMap)
{
enum {
OffsetFormat = 0,
@ -360,7 +382,7 @@ gfxFontUtils::ReadCMAPTableFormat4(PRUint8 *aBuf, PRUint32 aLength, gfxSparseBit
}
nsresult
gfxFontUtils::ReadCMAPTableFormat14(PRUint8 *aBuf, PRUint32 aLength,
gfxFontUtils::ReadCMAPTableFormat14(const PRUint8 *aBuf, PRUint32 aLength,
PRUint8*& aTable)
{
enum {
@ -473,8 +495,9 @@ gfxFontUtils::ReadCMAPTableFormat14(PRUint8 *aBuf, PRUint32 aLength,
#define isUVSEncoding(p, e) ((p) == PLATFORM_ID_UNICODE && (e) == EncodingIDUVSForUnicodePlatform)
PRUint32
gfxFontUtils::FindPreferredSubtable(PRUint8 *aBuf, PRUint32 aBufLength,
PRUint32 *aTableOffset, PRUint32 *aUVSTableOffset,
gfxFontUtils::FindPreferredSubtable(const PRUint8 *aBuf, PRUint32 aBufLength,
PRUint32 *aTableOffset,
PRUint32 *aUVSTableOffset,
PRBool *aSymbolEncoding)
{
enum {
@ -508,7 +531,7 @@ gfxFontUtils::FindPreferredSubtable(PRUint8 *aBuf, PRUint32 aBufLength,
// save the format we want here
PRUint32 keepFormat = 0;
PRUint8 *table = aBuf + SizeOfHeader;
const PRUint8 *table = aBuf + SizeOfHeader;
for (PRUint16 i = 0; i < numTables; ++i, table += SizeOfTable) {
const PRUint16 platformID = ReadShortAt(table, TableOffsetPlatformID);
if (!acceptablePlatform(platformID))
@ -551,13 +574,15 @@ gfxFontUtils::FindPreferredSubtable(PRUint8 *aBuf, PRUint32 aBufLength,
}
nsresult
gfxFontUtils::ReadCMAP(PRUint8 *aBuf, PRUint32 aBufLength, gfxSparseBitSet& aCharacterMap,
gfxFontUtils::ReadCMAP(const PRUint8 *aBuf, PRUint32 aBufLength,
gfxSparseBitSet& aCharacterMap,
PRUint32& aUVSOffset,
PRPackedBool& aUnicodeFont, PRPackedBool& aSymbolFont)
{
PRUint32 offset;
PRBool symbol;
PRUint32 format = FindPreferredSubtable(aBuf, aBufLength, &offset, &aUVSOffset, &symbol);
PRUint32 format = FindPreferredSubtable(aBuf, aBufLength,
&offset, &aUVSOffset, &symbol);
if (format == 4) {
if (symbol) {
@ -567,20 +592,20 @@ gfxFontUtils::ReadCMAP(PRUint8 *aBuf, PRUint32 aBufLength, gfxSparseBitSet& aCha
aUnicodeFont = PR_TRUE;
aSymbolFont = PR_FALSE;
}
return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset, aCharacterMap);
return ReadCMAPTableFormat4(aBuf + offset, aBufLength - offset,
aCharacterMap);
}
if (format == 12) {
aUnicodeFont = PR_TRUE;
aSymbolFont = PR_FALSE;
return ReadCMAPTableFormat12(aBuf + offset, aBufLength - offset, aCharacterMap);
return ReadCMAPTableFormat12(aBuf + offset, aBufLength - offset,
aCharacterMap);
}
return NS_ERROR_FAILURE;
}
using namespace mozilla; // for the AutoSwap_* types
#pragma pack(1)
typedef struct {
@ -635,11 +660,6 @@ gfxFontUtils::MapCharToGlyphFormat4(const PRUint8 *aBuf, PRUnichar aCh)
PRUint16 rangeShiftOver2;
PRUint16 index;
// not needed because PRUnichar cannot exceed 0xFFFF
// if (aCh >= 0x10000) {
// return 0;
// }
segCount = (PRUint16)(cmap4->segCountX2) / 2;
endCodes = &cmap4->arrays[0];
@ -683,6 +703,54 @@ gfxFontUtils::MapCharToGlyphFormat4(const PRUint8 *aBuf, PRUnichar aCh)
return 0;
}
PRUint32
gfxFontUtils::MapCharToGlyphFormat12(const PRUint8 *aBuf, PRUint32 aCh)
{
const Format12CmapHeader *cmap12 =
reinterpret_cast<const Format12CmapHeader*>(aBuf);
// We know that numGroups is within range for the subtable size
// because it was checked by ReadCMAPTableFormat12.
PRUint32 numGroups = cmap12->numGroups;
// The array of groups immediately follows the subtable header.
const Format12Group *groups =
reinterpret_cast<const Format12Group*>(aBuf + sizeof(Format12CmapHeader));
// For most efficient binary search, we want to work on a range that
// is a power of 2 so that we can always halve it by shifting.
// So we find the largest power of 2 that is <= numGroups.
// We will offset this range by rangeOffset so as to reach the end
// of the table, provided that doesn't put us beyond the target
// value from the outset.
PRUint32 powerOf2 = mozilla::FindHighestBit(numGroups);
PRUint32 rangeOffset = numGroups - powerOf2;
PRUint32 range = 0;
PRUint32 startCharCode;
if (groups[rangeOffset].startCharCode <= aCh) {
range = rangeOffset;
}
// Repeatedly halve the size of the range until we find the target group
while (powerOf2 > 1) {
powerOf2 >>= 1;
if (groups[range + powerOf2].startCharCode <= aCh) {
range += powerOf2;
}
}
// Check if the character is actually present in the range and return
// the corresponding glyph ID
startCharCode = groups[range].startCharCode;
if (startCharCode <= aCh && groups[range].endCharCode >= aCh) {
return groups[range].startGlyphId + aCh - startCharCode;
}
// Else it's not present, so return the .notdef glyph
return 0;
}
PRUint16
gfxFontUtils::MapUVSToGlyphFormat14(const PRUint8 *aBuf, PRUint32 aCh, PRUint32 aVS)
{
@ -732,19 +800,23 @@ gfxFontUtils::MapUVSToGlyphFormat14(const PRUint8 *aBuf, PRUint32 aCh, PRUint32
}
PRUint32
gfxFontUtils::MapCharToGlyph(PRUint8 *aBuf, PRUint32 aBufLength, PRUnichar aCh)
gfxFontUtils::MapCharToGlyph(const PRUint8 *aBuf, PRUint32 aBufLength,
PRUnichar aCh)
{
PRUint32 offset;
PRBool symbol;
PRUint32 format = FindPreferredSubtable(aBuf, aBufLength, &offset, nsnull, &symbol);
PRUint32 format = FindPreferredSubtable(aBuf, aBufLength, &offset,
nsnull, &symbol);
if (format == 4)
return MapCharToGlyphFormat4(aBuf + offset, aCh);
// other formats not currently supported; this is used only for the
// Mac OS X 10.6 LiGothic font hack (bug 532346)
return 0;
switch (format) {
case 4:
return aCh < UNICODE_BMP_LIMIT ?
MapCharToGlyphFormat4(aBuf + offset, aCh) : 0;
case 12:
return MapCharToGlyphFormat12(aBuf + offset, aCh);
default:
return 0;
}
}
PRUint8 gfxFontUtils::CharRangeBit(PRUint32 ch) {

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

@ -246,13 +246,19 @@ GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, PRBool aNeedsBo
nsresult
GDIFontEntry::GetFontTable(PRUint32 aTableTag, nsTArray<PRUint8>& aBuffer)
{
if (!IsTrueType()) {
return NS_ERROR_FAILURE;
}
AutoDC dc;
AutoSelectFont font(dc.GetDC(), &mLogFont);
if (font.IsValid()) {
PRInt32 tableSize = ::GetFontData(dc.GetDC(), NS_SWAP32(aTableTag), 0, NULL, NULL);
PRInt32 tableSize =
::GetFontData(dc.GetDC(), NS_SWAP32(aTableTag), 0, NULL, NULL);
if (tableSize != GDI_ERROR) {
if (aBuffer.SetLength(tableSize)) {
::GetFontData(dc.GetDC(), NS_SWAP32(aTableTag), 0, aBuffer.Elements(), tableSize);
::GetFontData(dc.GetDC(), NS_SWAP32(aTableTag), 0,
aBuffer.Elements(), tableSize);
return NS_OK;
}
return NS_ERROR_OUT_OF_MEMORY;

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

@ -49,6 +49,7 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl
PRBool aNeedsBold)
: gfxFont(aFontEntry, aFontStyle),
mATSFont(aFontEntry->GetFontRef()),
mCGFont(nsnull),
mFontFace(nsnull),
mScaledFont(nsnull),
mAdjustedSize(0.0)
@ -57,14 +58,19 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl
mSyntheticBoldOffset = 1; // devunit offset when double-striking text to fake boldness
}
mCGFont = ::CGFontCreateWithPlatformFont(&mATSFont);
if (!mCGFont) {
mIsValid = PR_FALSE;
return;
}
// InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
InitMetrics();
if (!mIsValid)
if (!mIsValid) {
return;
}
CGFontRef cgFont = ::CGFontCreateWithPlatformFont(&mATSFont);
mFontFace = cairo_quartz_font_face_create_for_cgfont(cgFont);
::CGFontRelease(cgFont);
mFontFace = cairo_quartz_font_face_create_for_cgfont(mCGFont);
cairo_status_t cairoerr = cairo_font_face_status(mFontFace);
if (cairoerr != CAIRO_STATUS_SUCCESS) {
@ -133,6 +139,9 @@ gfxMacFont::~gfxMacFont()
if (mFontFace) {
cairo_font_face_destroy(mFontFace);
}
// this is documented to be safe if mCGFont is null
::CGFontRelease(mCGFont);
}
PRBool
@ -158,6 +167,17 @@ gfxMacFont::InitMetrics()
{
gfxFloat size =
PR_MAX(((mAdjustedSize != 0.0f) ? mAdjustedSize : mStyle.size), 1.0f);
PRUint32 upem = ::CGFontGetUnitsPerEm(mCGFont);
if (!upem) {
mIsValid = PR_FALSE;
#ifdef DEBUG
char warnBuf[1024];
sprintf(warnBuf, "Bad font metrics for: %s (no unitsPerEm value)",
NS_ConvertUTF16toUTF8(mFontEntry->Name()).get());
NS_WARNING(warnBuf);
#endif
return;
}
ATSFontMetrics atsMetrics;
OSStatus err;
@ -176,16 +196,10 @@ gfxMacFont::InitMetrics()
return;
}
// create a temporary local CTFont for glyph measurement
CTFontRef aCTFont =
::CTFontCreateWithPlatformFont(mATSFont, size, NULL, NULL);
// prefer to get xHeight from ATS metrics (unhinted) rather than Core Text (hinted),
// see bug 429605.
if (atsMetrics.xHeight > 0)
mMetrics.xHeight = atsMetrics.xHeight * size;
else
mMetrics.xHeight = GetCharHeight(aCTFont, 'x');
mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * size / upem;
if (mAdjustedSize == 0.0f) {
if (mMetrics.xHeight != 0.0f && mStyle.sizeAdjust != 0.0f) {
@ -195,9 +209,6 @@ gfxMacFont::InitMetrics()
// the recursive call to InitMetrics will see the adjusted size,
// and set up the rest of the metrics fields accordingly
InitMetrics();
// release our temporary CTFont
::CFRelease(aCTFont);
return;
}
mAdjustedSize = size;
@ -228,8 +239,11 @@ gfxMacFont::InitMetrics()
mMetrics.emAscent = mMetrics.maxAscent * mMetrics.emHeight / mMetrics.maxHeight;
mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
CFDataRef cmap =
::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c','m','a','p'));
PRUint32 glyphID;
float xWidth = GetCharWidth(aCTFont, 'x', &glyphID);
gfxFloat xWidth = GetCharWidth(cmap, upem, size, 'x', &glyphID);
if (atsMetrics.avgAdvanceWidth != 0.0)
mMetrics.aveCharWidth = PR_MIN(atsMetrics.avgAdvanceWidth * size, xWidth);
else if (glyphID != 0)
@ -245,14 +259,16 @@ gfxMacFont::InitMetrics()
mMetrics.maxAdvance = mMetrics.aveCharWidth;
}
mMetrics.spaceWidth = GetCharWidth(aCTFont, ' ', &glyphID);
mMetrics.spaceWidth = GetCharWidth(cmap, upem, size, ' ', &glyphID);
mSpaceGlyph = glyphID;
mMetrics.zeroOrAveCharWidth = GetCharWidth(aCTFont, '0', &glyphID);
mMetrics.zeroOrAveCharWidth = GetCharWidth(cmap, upem, size, '0', &glyphID);
if (glyphID == 0)
mMetrics.zeroOrAveCharWidth = mMetrics.aveCharWidth;
::CFRelease(aCTFont);
if (cmap) {
::CFRelease(cmap);
}
SanitizeMetrics(&mMetrics, mFontEntry->mIsBadUnderlineFont);
@ -270,44 +286,48 @@ gfxMacFont::InitMetrics()
#endif
}
float
gfxMacFont::GetCharWidth(CTFontRef aCTFont, PRUnichar aUniChar,
PRUint32 *aGlyphID)
gfxFloat
gfxMacFont::GetCharWidth(CFDataRef aCmap, PRUint32 aUpem, gfxFloat aSize,
PRUnichar aUniChar, PRUint32 *aGlyphID)
{
UniChar c = aUniChar;
CGGlyph glyph;
if (::CTFontGetGlyphsForCharacters(aCTFont, &c, &glyph, 1)) {
CGSize advance;
::CTFontGetAdvancesForGlyphs(aCTFont,
kCTFontHorizontalOrientation,
&glyph,
&advance,
1);
if (aGlyphID != nsnull)
*aGlyphID = glyph;
return advance.width;
CGGlyph glyph = 0;
if (aCmap) {
glyph = gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap),
::CFDataGetLength(aCmap),
aUniChar);
}
// couldn't get glyph for the char
if (aGlyphID != nsnull)
*aGlyphID = 0;
return 0;
}
if (aGlyphID) {
*aGlyphID = glyph;
}
float
gfxMacFont::GetCharHeight(CTFontRef aCTFont, PRUnichar aUniChar)
{
UniChar c = aUniChar;
CGGlyph glyph;
if (::CTFontGetGlyphsForCharacters(aCTFont, &c, &glyph, 1)) {
CGRect boundingRect;
::CTFontGetBoundingRectsForGlyphs(aCTFont,
kCTFontHorizontalOrientation,
&glyph,
&boundingRect,
1);
return boundingRect.size.height;
if (glyph) {
int advance;
if (::CGFontGetGlyphAdvances(mCGFont, &glyph, 1, &advance)) {
return advance * aSize / aUpem;
}
}
return 0;
}
/*static*/ void
gfxMacFont::DestroyBlobFunc(void* aUserData)
{
::CFRelease((CFDataRef)aUserData);
}
hb_blob_t *
gfxMacFont::GetFontTable(PRUint32 aTag)
{
CFDataRef dataRef = ::CGFontCopyTableForTag(mCGFont, aTag);
if (dataRef) {
return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
::CFDataGetLength(dataRef),
HB_MEMORY_MODE_READONLY,
DestroyBlobFunc, (void*)dataRef);
}
return nsnull;
}

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

@ -55,6 +55,7 @@ public:
virtual ~gfxMacFont();
ATSFontRef GetATSFontRef() const { return mATSFont; }
CGFontRef GetCGFontRef() const { return mCGFont; }
// TODO: probably should move this up to gfxFont
// and ensure it is handled uniformly across all platforms
@ -71,14 +72,20 @@ public:
virtual PRBool SetupCairoFont(gfxContext *aContext);
// override gfxFont table access function to bypass gfxFontEntry cache,
// use CGFontRef API to get direct access to system font data
virtual hb_blob_t *GetFontTable(PRUint32 aTag);
protected:
void InitMetrics();
float GetCharWidth(CTFontRef aCTFont, PRUnichar aUniChar,
PRUint32 *aGlyphID);
float GetCharHeight(CTFontRef aCTFont, PRUnichar aUniChar);
gfxFloat GetCharWidth(CFDataRef aCmap, PRUint32 aUpem, gfxFloat aSize,
PRUnichar aUniChar, PRUint32 *aGlyphID);
static void DestroyBlobFunc(void* aUserData);
ATSFontRef mATSFont;
CGFontRef mCGFont;
cairo_font_face_t *mFontFace;
cairo_scaled_font_t *mScaledFont;

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

@ -292,15 +292,19 @@ MacOSFontEntry::GetFontTable(PRUint32 aTableTag, nsTArray<PRUint8>& aBuffer)
nsAutoreleasePool localPool;
ATSFontRef fontRef = GetFontRef();
if (fontRef == (ATSFontRef)kATSUInvalidFontID)
if (fontRef == (ATSFontRef)kATSUInvalidFontID) {
return NS_ERROR_FAILURE;
}
ByteCount dataLength;
OSStatus status = ::ATSFontGetTable(fontRef, aTableTag, 0, 0, 0, &dataLength);
NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
if (status != noErr) {
return NS_ERROR_FAILURE;
}
if (!aBuffer.AppendElements(dataLength))
if (!aBuffer.AppendElements(dataLength)) {
return NS_ERROR_OUT_OF_MEMORY;
}
PRUint8 *dataPtr = aBuffer.Elements();
status = ::ATSFontGetTable(fontRef, aTableTag, 0, dataLength, dataPtr, &dataLength);