michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_FONT_UTILS_H michael@0: #define GFX_FONT_UTILS_H michael@0: michael@0: #include "gfxPlatform.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/Endian.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "zlib.h" michael@0: #include michael@0: michael@0: /* Bug 341128 - w32api defines min/max which causes problems with */ michael@0: #ifdef __MINGW32__ michael@0: #undef min michael@0: #undef max michael@0: #endif michael@0: michael@0: typedef struct hb_blob_t hb_blob_t; michael@0: michael@0: class gfxSparseBitSet { michael@0: private: michael@0: enum { BLOCK_SIZE = 32 }; // ==> 256 codepoints per block michael@0: enum { BLOCK_SIZE_BITS = BLOCK_SIZE * 8 }; michael@0: enum { BLOCK_INDEX_SHIFT = 8 }; michael@0: michael@0: struct Block { michael@0: Block(const Block& aBlock) { memcpy(mBits, aBlock.mBits, sizeof(mBits)); } michael@0: Block(unsigned char memsetValue = 0) { memset(mBits, memsetValue, BLOCK_SIZE); } michael@0: uint8_t mBits[BLOCK_SIZE]; michael@0: }; michael@0: michael@0: public: michael@0: gfxSparseBitSet() { } michael@0: gfxSparseBitSet(const gfxSparseBitSet& aBitset) { michael@0: uint32_t len = aBitset.mBlocks.Length(); michael@0: mBlocks.AppendElements(len); michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: Block *block = aBitset.mBlocks[i]; michael@0: if (block) michael@0: mBlocks[i] = new Block(*block); michael@0: } michael@0: } michael@0: michael@0: bool Equals(const gfxSparseBitSet *aOther) const { michael@0: if (mBlocks.Length() != aOther->mBlocks.Length()) { michael@0: return false; michael@0: } michael@0: size_t n = mBlocks.Length(); michael@0: for (size_t i = 0; i < n; ++i) { michael@0: const Block *b1 = mBlocks[i]; michael@0: const Block *b2 = aOther->mBlocks[i]; michael@0: if (!b1 != !b2) { michael@0: return false; michael@0: } michael@0: if (!b1) { michael@0: continue; michael@0: } michael@0: if (memcmp(&b1->mBits, &b2->mBits, BLOCK_SIZE) != 0) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool test(uint32_t aIndex) const { michael@0: NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad"); michael@0: uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS; michael@0: if (blockIndex >= mBlocks.Length()) michael@0: return false; michael@0: Block *block = mBlocks[blockIndex]; michael@0: if (!block) michael@0: return false; michael@0: return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0; michael@0: } michael@0: michael@0: #if PR_LOGGING michael@0: // dump out contents of bitmap michael@0: void Dump(const char* aPrefix, eGfxLog aWhichLog) const; michael@0: #endif michael@0: michael@0: bool TestRange(uint32_t aStart, uint32_t aEnd) { michael@0: uint32_t startBlock, endBlock, blockLen; michael@0: michael@0: // start point is beyond the end of the block array? return false immediately michael@0: startBlock = aStart >> BLOCK_INDEX_SHIFT; michael@0: blockLen = mBlocks.Length(); michael@0: if (startBlock >= blockLen) return false; michael@0: michael@0: // check for blocks in range, if none, return false michael@0: uint32_t blockIndex; michael@0: bool hasBlocksInRange = false; michael@0: michael@0: endBlock = aEnd >> BLOCK_INDEX_SHIFT; michael@0: blockIndex = startBlock; michael@0: for (blockIndex = startBlock; blockIndex <= endBlock; blockIndex++) { michael@0: if (blockIndex < blockLen && mBlocks[blockIndex]) michael@0: hasBlocksInRange = true; michael@0: } michael@0: if (!hasBlocksInRange) return false; michael@0: michael@0: Block *block; michael@0: uint32_t i, start, end; michael@0: michael@0: // first block, check bits michael@0: if ((block = mBlocks[startBlock])) { michael@0: start = aStart; michael@0: end = std::min(aEnd, ((startBlock+1) << BLOCK_INDEX_SHIFT) - 1); michael@0: for (i = start; i <= end; i++) { michael@0: if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) michael@0: return true; michael@0: } michael@0: } michael@0: if (endBlock == startBlock) return false; michael@0: michael@0: // [2..n-1] blocks check bytes michael@0: for (blockIndex = startBlock + 1; blockIndex < endBlock; blockIndex++) { michael@0: uint32_t index; michael@0: michael@0: if (blockIndex >= blockLen || !(block = mBlocks[blockIndex])) continue; michael@0: for (index = 0; index < BLOCK_SIZE; index++) { michael@0: if (block->mBits[index]) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // last block, check bits michael@0: if (endBlock < blockLen && (block = mBlocks[endBlock])) { michael@0: start = endBlock << BLOCK_INDEX_SHIFT; michael@0: end = aEnd; michael@0: for (i = start; i <= end; i++) { michael@0: if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7))) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void set(uint32_t aIndex) { michael@0: uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS; michael@0: if (blockIndex >= mBlocks.Length()) { michael@0: nsAutoPtr *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length()); michael@0: if (MOZ_UNLIKELY(!blocks)) // OOM michael@0: return; michael@0: } michael@0: Block *block = mBlocks[blockIndex]; michael@0: if (!block) { michael@0: block = new Block; michael@0: mBlocks[blockIndex] = block; michael@0: } michael@0: block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7); michael@0: } michael@0: michael@0: void set(uint32_t aIndex, bool aValue) { michael@0: if (aValue) michael@0: set(aIndex); michael@0: else michael@0: clear(aIndex); michael@0: } michael@0: michael@0: void SetRange(uint32_t aStart, uint32_t aEnd) { michael@0: const uint32_t startIndex = aStart/BLOCK_SIZE_BITS; michael@0: const uint32_t endIndex = aEnd/BLOCK_SIZE_BITS; michael@0: michael@0: if (endIndex >= mBlocks.Length()) { michael@0: uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length(); michael@0: nsAutoPtr *blocks = mBlocks.AppendElements(numNewBlocks); michael@0: if (MOZ_UNLIKELY(!blocks)) // OOM michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = startIndex; i <= endIndex; ++i) { michael@0: const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS; michael@0: const uint32_t blockLastBit = blockFirstBit + BLOCK_SIZE_BITS - 1; michael@0: michael@0: Block *block = mBlocks[i]; michael@0: if (!block) { michael@0: bool fullBlock = false; michael@0: if (aStart <= blockFirstBit && aEnd >= blockLastBit) michael@0: fullBlock = true; michael@0: michael@0: block = new Block(fullBlock ? 0xFF : 0); michael@0: mBlocks[i] = block; michael@0: michael@0: if (fullBlock) michael@0: continue; michael@0: } michael@0: michael@0: const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0; michael@0: const uint32_t end = std::min(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1); michael@0: michael@0: for (uint32_t bit = start; bit <= end; ++bit) { michael@0: block->mBits[bit>>3] |= 1 << (bit & 0x7); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void clear(uint32_t aIndex) { michael@0: uint32_t blockIndex = aIndex/BLOCK_SIZE_BITS; michael@0: if (blockIndex >= mBlocks.Length()) { michael@0: nsAutoPtr *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length()); michael@0: if (MOZ_UNLIKELY(!blocks)) // OOM michael@0: return; michael@0: } michael@0: Block *block = mBlocks[blockIndex]; michael@0: if (!block) { michael@0: return; michael@0: } michael@0: block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] &= ~(1 << (aIndex & 0x7)); michael@0: } michael@0: michael@0: void ClearRange(uint32_t aStart, uint32_t aEnd) { michael@0: const uint32_t startIndex = aStart/BLOCK_SIZE_BITS; michael@0: const uint32_t endIndex = aEnd/BLOCK_SIZE_BITS; michael@0: michael@0: if (endIndex >= mBlocks.Length()) { michael@0: uint32_t numNewBlocks = endIndex + 1 - mBlocks.Length(); michael@0: nsAutoPtr *blocks = mBlocks.AppendElements(numNewBlocks); michael@0: if (MOZ_UNLIKELY(!blocks)) // OOM michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = startIndex; i <= endIndex; ++i) { michael@0: const uint32_t blockFirstBit = i * BLOCK_SIZE_BITS; michael@0: michael@0: Block *block = mBlocks[i]; michael@0: if (!block) { michael@0: // any nonexistent block is implicitly all clear, michael@0: // so there's no need to even create it michael@0: continue; michael@0: } michael@0: michael@0: const uint32_t start = aStart > blockFirstBit ? aStart - blockFirstBit : 0; michael@0: const uint32_t end = std::min(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1); michael@0: michael@0: for (uint32_t bit = start; bit <= end; ++bit) { michael@0: block->mBits[bit>>3] &= ~(1 << (bit & 0x7)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: size_t total = mBlocks.SizeOfExcludingThis(aMallocSizeOf); michael@0: for (uint32_t i = 0; i < mBlocks.Length(); i++) { michael@0: if (mBlocks[i]) { michael@0: total += aMallocSizeOf(mBlocks[i]); michael@0: } michael@0: } michael@0: return total; michael@0: } michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: // clear out all blocks in the array michael@0: void reset() { michael@0: uint32_t i; michael@0: for (i = 0; i < mBlocks.Length(); i++) michael@0: mBlocks[i] = nullptr; michael@0: } michael@0: michael@0: // set this bitset to the union of its current contents and another michael@0: void Union(const gfxSparseBitSet& aBitset) { michael@0: // ensure mBlocks is large enough michael@0: uint32_t blockCount = aBitset.mBlocks.Length(); michael@0: if (blockCount > mBlocks.Length()) { michael@0: uint32_t needed = blockCount - mBlocks.Length(); michael@0: nsAutoPtr *blocks = mBlocks.AppendElements(needed); michael@0: if (MOZ_UNLIKELY(!blocks)) { // OOM michael@0: return; michael@0: } michael@0: } michael@0: // for each block that may be present in aBitset... michael@0: for (uint32_t i = 0; i < blockCount; ++i) { michael@0: // if it is missing (implicitly empty), just skip michael@0: if (!aBitset.mBlocks[i]) { michael@0: continue; michael@0: } michael@0: // if the block is missing in this set, just copy the other michael@0: if (!mBlocks[i]) { michael@0: mBlocks[i] = new Block(*aBitset.mBlocks[i]); michael@0: continue; michael@0: } michael@0: // else set existing block to the union of both michael@0: uint32_t *dst = reinterpret_cast(mBlocks[i]->mBits); michael@0: const uint32_t *src = michael@0: reinterpret_cast(aBitset.mBlocks[i]->mBits); michael@0: for (uint32_t j = 0; j < BLOCK_SIZE / 4; ++j) { michael@0: dst[j] |= src[j]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void Compact() { michael@0: mBlocks.Compact(); michael@0: } michael@0: michael@0: uint32_t GetChecksum() const { michael@0: uint32_t check = adler32(0, Z_NULL, 0); michael@0: for (uint32_t i = 0; i < mBlocks.Length(); i++) { michael@0: if (mBlocks[i]) { michael@0: const Block *block = mBlocks[i]; michael@0: check = adler32(check, (uint8_t*) (&i), 4); michael@0: check = adler32(check, (uint8_t*) block, sizeof(Block)); michael@0: } michael@0: } michael@0: return check; michael@0: } michael@0: michael@0: private: michael@0: nsTArray< nsAutoPtr > mBlocks; michael@0: }; michael@0: michael@0: #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Byte-swapping types and name table structure definitions moved from michael@0: // gfxFontUtils.cpp to .h file so that gfxFont.cpp can also refer to them michael@0: #pragma pack(1) michael@0: michael@0: struct AutoSwap_PRUint16 { michael@0: #ifdef __SUNPRO_CC michael@0: AutoSwap_PRUint16& operator = (const uint16_t aValue) michael@0: { michael@0: this->value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: return *this; michael@0: } michael@0: #else michael@0: AutoSwap_PRUint16(uint16_t aValue) michael@0: { michael@0: value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: } michael@0: #endif michael@0: operator uint16_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: operator uint32_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: operator uint64_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: private: michael@0: uint16_t value; michael@0: }; michael@0: michael@0: struct AutoSwap_PRInt16 { michael@0: #ifdef __SUNPRO_CC michael@0: AutoSwap_PRInt16& operator = (const int16_t aValue) michael@0: { michael@0: this->value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: return *this; michael@0: } michael@0: #else michael@0: AutoSwap_PRInt16(int16_t aValue) michael@0: { michael@0: value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: } michael@0: #endif michael@0: operator int16_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: operator uint32_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: private: michael@0: int16_t value; michael@0: }; michael@0: michael@0: struct AutoSwap_PRUint32 { michael@0: #ifdef __SUNPRO_CC michael@0: AutoSwap_PRUint32& operator = (const uint32_t aValue) michael@0: { michael@0: this->value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: return *this; michael@0: } michael@0: #else michael@0: AutoSwap_PRUint32(uint32_t aValue) michael@0: { michael@0: value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: } michael@0: #endif michael@0: operator uint32_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: private: michael@0: uint32_t value; michael@0: }; michael@0: michael@0: struct AutoSwap_PRInt32 { michael@0: #ifdef __SUNPRO_CC michael@0: AutoSwap_PRInt32& operator = (const int32_t aValue) michael@0: { michael@0: this->value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: return *this; michael@0: } michael@0: #else michael@0: AutoSwap_PRInt32(int32_t aValue) michael@0: { michael@0: value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: } michael@0: #endif michael@0: operator int32_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: private: michael@0: int32_t value; michael@0: }; michael@0: michael@0: struct AutoSwap_PRUint64 { michael@0: #ifdef __SUNPRO_CC michael@0: AutoSwap_PRUint64& operator = (const uint64_t aValue) michael@0: { michael@0: this->value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: return *this; michael@0: } michael@0: #else michael@0: AutoSwap_PRUint64(uint64_t aValue) michael@0: { michael@0: value = mozilla::NativeEndian::swapToBigEndian(aValue); michael@0: } michael@0: #endif michael@0: operator uint64_t() const michael@0: { michael@0: return mozilla::NativeEndian::swapFromBigEndian(value); michael@0: } michael@0: michael@0: private: michael@0: uint64_t value; michael@0: }; michael@0: michael@0: struct AutoSwap_PRUint24 { michael@0: operator uint32_t() const { return value[0] << 16 | value[1] << 8 | value[2]; } michael@0: private: michael@0: AutoSwap_PRUint24() { } michael@0: uint8_t value[3]; michael@0: }; michael@0: michael@0: struct SFNTHeader { michael@0: AutoSwap_PRUint32 sfntVersion; // Fixed, 0x00010000 for version 1.0. michael@0: AutoSwap_PRUint16 numTables; // Number of tables. michael@0: AutoSwap_PRUint16 searchRange; // (Maximum power of 2 <= numTables) x 16. michael@0: AutoSwap_PRUint16 entrySelector; // Log2(maximum power of 2 <= numTables). michael@0: AutoSwap_PRUint16 rangeShift; // NumTables x 16-searchRange. michael@0: }; michael@0: michael@0: struct TableDirEntry { michael@0: AutoSwap_PRUint32 tag; // 4 -byte identifier. michael@0: AutoSwap_PRUint32 checkSum; // CheckSum for this table. michael@0: AutoSwap_PRUint32 offset; // Offset from beginning of TrueType font file. michael@0: AutoSwap_PRUint32 length; // Length of this table. michael@0: }; michael@0: michael@0: struct HeadTable { michael@0: enum { michael@0: HEAD_VERSION = 0x00010000, michael@0: HEAD_MAGIC_NUMBER = 0x5F0F3CF5, michael@0: HEAD_CHECKSUM_CALC_CONST = 0xB1B0AFBA michael@0: }; michael@0: michael@0: AutoSwap_PRUint32 tableVersionNumber; // Fixed, 0x00010000 for version 1.0. michael@0: AutoSwap_PRUint32 fontRevision; // Set by font manufacturer. michael@0: AutoSwap_PRUint32 checkSumAdjustment; // To compute: set it to 0, sum the entire font as ULONG, then store 0xB1B0AFBA - sum. michael@0: AutoSwap_PRUint32 magicNumber; // Set to 0x5F0F3CF5. michael@0: AutoSwap_PRUint16 flags; michael@0: AutoSwap_PRUint16 unitsPerEm; // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines. michael@0: AutoSwap_PRUint64 created; // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer michael@0: AutoSwap_PRUint64 modified; // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer michael@0: AutoSwap_PRInt16 xMin; // For all glyph bounding boxes. michael@0: AutoSwap_PRInt16 yMin; // For all glyph bounding boxes. michael@0: AutoSwap_PRInt16 xMax; // For all glyph bounding boxes. michael@0: AutoSwap_PRInt16 yMax; // For all glyph bounding boxes. michael@0: AutoSwap_PRUint16 macStyle; // Bit 0: Bold (if set to 1); michael@0: AutoSwap_PRUint16 lowestRecPPEM; // Smallest readable size in pixels. michael@0: AutoSwap_PRInt16 fontDirectionHint; michael@0: AutoSwap_PRInt16 indexToLocFormat; michael@0: AutoSwap_PRInt16 glyphDataFormat; michael@0: }; michael@0: michael@0: struct OS2Table { michael@0: AutoSwap_PRUint16 version; // 0004 = OpenType 1.5 michael@0: AutoSwap_PRInt16 xAvgCharWidth; michael@0: AutoSwap_PRUint16 usWeightClass; michael@0: AutoSwap_PRUint16 usWidthClass; michael@0: AutoSwap_PRUint16 fsType; michael@0: AutoSwap_PRInt16 ySubscriptXSize; michael@0: AutoSwap_PRInt16 ySubscriptYSize; michael@0: AutoSwap_PRInt16 ySubscriptXOffset; michael@0: AutoSwap_PRInt16 ySubscriptYOffset; michael@0: AutoSwap_PRInt16 ySuperscriptXSize; michael@0: AutoSwap_PRInt16 ySuperscriptYSize; michael@0: AutoSwap_PRInt16 ySuperscriptXOffset; michael@0: AutoSwap_PRInt16 ySuperscriptYOffset; michael@0: AutoSwap_PRInt16 yStrikeoutSize; michael@0: AutoSwap_PRInt16 yStrikeoutPosition; michael@0: AutoSwap_PRInt16 sFamilyClass; michael@0: uint8_t panose[10]; michael@0: AutoSwap_PRUint32 unicodeRange1; michael@0: AutoSwap_PRUint32 unicodeRange2; michael@0: AutoSwap_PRUint32 unicodeRange3; michael@0: AutoSwap_PRUint32 unicodeRange4; michael@0: uint8_t achVendID[4]; michael@0: AutoSwap_PRUint16 fsSelection; michael@0: AutoSwap_PRUint16 usFirstCharIndex; michael@0: AutoSwap_PRUint16 usLastCharIndex; michael@0: AutoSwap_PRInt16 sTypoAscender; michael@0: AutoSwap_PRInt16 sTypoDescender; michael@0: AutoSwap_PRInt16 sTypoLineGap; michael@0: AutoSwap_PRUint16 usWinAscent; michael@0: AutoSwap_PRUint16 usWinDescent; michael@0: AutoSwap_PRUint32 codePageRange1; michael@0: AutoSwap_PRUint32 codePageRange2; michael@0: AutoSwap_PRInt16 sxHeight; michael@0: AutoSwap_PRInt16 sCapHeight; michael@0: AutoSwap_PRUint16 usDefaultChar; michael@0: AutoSwap_PRUint16 usBreakChar; michael@0: AutoSwap_PRUint16 usMaxContext; michael@0: }; michael@0: michael@0: struct PostTable { michael@0: AutoSwap_PRUint32 version; michael@0: AutoSwap_PRInt32 italicAngle; michael@0: AutoSwap_PRInt16 underlinePosition; michael@0: AutoSwap_PRUint16 underlineThickness; michael@0: AutoSwap_PRUint32 isFixedPitch; michael@0: AutoSwap_PRUint32 minMemType42; michael@0: AutoSwap_PRUint32 maxMemType42; michael@0: AutoSwap_PRUint32 minMemType1; michael@0: AutoSwap_PRUint32 maxMemType1; michael@0: }; michael@0: michael@0: struct HheaTable { michael@0: AutoSwap_PRUint32 version; michael@0: AutoSwap_PRInt16 ascender; michael@0: AutoSwap_PRInt16 descender; michael@0: AutoSwap_PRInt16 lineGap; michael@0: AutoSwap_PRUint16 advanceWidthMax; michael@0: AutoSwap_PRInt16 minLeftSideBearing; michael@0: AutoSwap_PRInt16 minRightSideBearing; michael@0: AutoSwap_PRInt16 xMaxExtent; michael@0: AutoSwap_PRInt16 caretSlopeRise; michael@0: AutoSwap_PRInt16 caretSlopeRun; michael@0: AutoSwap_PRInt16 caretOffset; michael@0: AutoSwap_PRInt16 reserved1; michael@0: AutoSwap_PRInt16 reserved2; michael@0: AutoSwap_PRInt16 reserved3; michael@0: AutoSwap_PRInt16 reserved4; michael@0: AutoSwap_PRInt16 metricDataFormat; michael@0: AutoSwap_PRUint16 numOfLongHorMetrics; michael@0: }; michael@0: michael@0: struct MaxpTableHeader { michael@0: AutoSwap_PRUint32 version; // CFF: 0x00005000; TrueType: 0x00010000 michael@0: AutoSwap_PRUint16 numGlyphs; michael@0: // truetype version has additional fields that we don't currently use michael@0: }; michael@0: michael@0: // old 'kern' table, supported on Windows michael@0: // see http://www.microsoft.com/typography/otspec/kern.htm michael@0: struct KernTableVersion0 { michael@0: AutoSwap_PRUint16 version; // 0x0000 michael@0: AutoSwap_PRUint16 nTables; michael@0: }; michael@0: michael@0: struct KernTableSubtableHeaderVersion0 { michael@0: AutoSwap_PRUint16 version; michael@0: AutoSwap_PRUint16 length; michael@0: AutoSwap_PRUint16 coverage; michael@0: }; michael@0: michael@0: // newer Mac-only 'kern' table, ignored by Windows michael@0: // see http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6kern.html michael@0: struct KernTableVersion1 { michael@0: AutoSwap_PRUint32 version; // 0x00010000 michael@0: AutoSwap_PRUint32 nTables; michael@0: }; michael@0: michael@0: struct KernTableSubtableHeaderVersion1 { michael@0: AutoSwap_PRUint32 length; michael@0: AutoSwap_PRUint16 coverage; michael@0: AutoSwap_PRUint16 tupleIndex; michael@0: }; michael@0: michael@0: #pragma pack() michael@0: michael@0: // Return just the highest bit of the given value, i.e., the highest michael@0: // power of 2 that is <= value, or zero if the input value is zero. michael@0: inline uint32_t michael@0: FindHighestBit(uint32_t value) michael@0: { michael@0: // propagate highest bit into all lower bits of the value michael@0: value |= (value >> 1); michael@0: value |= (value >> 2); michael@0: value |= (value >> 4); michael@0: value |= (value >> 8); michael@0: value |= (value >> 16); michael@0: // isolate the leftmost bit michael@0: return (value & ~(value >> 1)); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: // used for overlaying name changes without touching original font data michael@0: struct FontDataOverlay { michael@0: // overlaySrc != 0 ==> use overlay michael@0: uint32_t overlaySrc; // src offset from start of font data michael@0: uint32_t overlaySrcLen; // src length michael@0: uint32_t overlayDest; // dest offset from start of font data michael@0: }; michael@0: michael@0: enum gfxUserFontType { michael@0: GFX_USERFONT_UNKNOWN = 0, michael@0: GFX_USERFONT_OPENTYPE = 1, michael@0: GFX_USERFONT_SVG = 2, michael@0: GFX_USERFONT_WOFF = 3 michael@0: }; michael@0: michael@0: extern const uint8_t sCJKCompatSVSTable[]; michael@0: michael@0: class gfxFontUtils { michael@0: michael@0: public: michael@0: // these are public because gfxFont.cpp also looks into the name table michael@0: enum { michael@0: NAME_ID_FAMILY = 1, michael@0: NAME_ID_STYLE = 2, michael@0: NAME_ID_UNIQUE = 3, michael@0: NAME_ID_FULL = 4, michael@0: NAME_ID_VERSION = 5, michael@0: NAME_ID_POSTSCRIPT = 6, michael@0: NAME_ID_PREFERRED_FAMILY = 16, michael@0: NAME_ID_PREFERRED_STYLE = 17, michael@0: michael@0: PLATFORM_ALL = -1, michael@0: PLATFORM_ID_UNICODE = 0, // Mac OS uses this typically michael@0: PLATFORM_ID_MAC = 1, michael@0: PLATFORM_ID_ISO = 2, michael@0: PLATFORM_ID_MICROSOFT = 3, michael@0: michael@0: ENCODING_ID_MAC_ROMAN = 0, // traditional Mac OS script manager encodings michael@0: ENCODING_ID_MAC_JAPANESE = 1, // (there are others defined, but some were never michael@0: ENCODING_ID_MAC_TRAD_CHINESE = 2, // implemented by Apple, and I have never seen them michael@0: ENCODING_ID_MAC_KOREAN = 3, // used in font names) michael@0: ENCODING_ID_MAC_ARABIC = 4, michael@0: ENCODING_ID_MAC_HEBREW = 5, michael@0: ENCODING_ID_MAC_GREEK = 6, michael@0: ENCODING_ID_MAC_CYRILLIC = 7, michael@0: ENCODING_ID_MAC_DEVANAGARI = 9, michael@0: ENCODING_ID_MAC_GURMUKHI = 10, michael@0: ENCODING_ID_MAC_GUJARATI = 11, michael@0: ENCODING_ID_MAC_SIMP_CHINESE = 25, michael@0: michael@0: ENCODING_ID_MICROSOFT_SYMBOL = 0, // Microsoft platform encoding IDs michael@0: ENCODING_ID_MICROSOFT_UNICODEBMP = 1, michael@0: ENCODING_ID_MICROSOFT_SHIFTJIS = 2, michael@0: ENCODING_ID_MICROSOFT_PRC = 3, michael@0: ENCODING_ID_MICROSOFT_BIG5 = 4, michael@0: ENCODING_ID_MICROSOFT_WANSUNG = 5, michael@0: ENCODING_ID_MICROSOFT_JOHAB = 6, michael@0: ENCODING_ID_MICROSOFT_UNICODEFULL = 10, michael@0: michael@0: LANG_ALL = -1, michael@0: LANG_ID_MAC_ENGLISH = 0, // many others are defined, but most don't affect michael@0: LANG_ID_MAC_HEBREW = 10, // the charset; should check all the central/eastern michael@0: LANG_ID_MAC_JAPANESE = 11, // european codes, though michael@0: LANG_ID_MAC_ARABIC = 12, michael@0: LANG_ID_MAC_ICELANDIC = 15, michael@0: LANG_ID_MAC_TURKISH = 17, michael@0: LANG_ID_MAC_TRAD_CHINESE = 19, michael@0: LANG_ID_MAC_URDU = 20, michael@0: LANG_ID_MAC_KOREAN = 23, michael@0: LANG_ID_MAC_POLISH = 25, michael@0: LANG_ID_MAC_FARSI = 31, michael@0: LANG_ID_MAC_SIMP_CHINESE = 33, michael@0: LANG_ID_MAC_ROMANIAN = 37, michael@0: LANG_ID_MAC_CZECH = 38, michael@0: LANG_ID_MAC_SLOVAK = 39, michael@0: michael@0: LANG_ID_MICROSOFT_EN_US = 0x0409, // with Microsoft platformID, EN US lang code michael@0: michael@0: CMAP_MAX_CODEPOINT = 0x10ffff // maximum possible Unicode codepoint michael@0: // contained in a cmap michael@0: }; michael@0: michael@0: // name table has a header, followed by name records, followed by string data michael@0: struct NameHeader { michael@0: mozilla::AutoSwap_PRUint16 format; // Format selector (=0). michael@0: mozilla::AutoSwap_PRUint16 count; // Number of name records. michael@0: mozilla::AutoSwap_PRUint16 stringOffset; // Offset to start of string storage michael@0: // (from start of table) michael@0: }; michael@0: michael@0: struct NameRecord { michael@0: mozilla::AutoSwap_PRUint16 platformID; // Platform ID michael@0: mozilla::AutoSwap_PRUint16 encodingID; // Platform-specific encoding ID michael@0: mozilla::AutoSwap_PRUint16 languageID; // Language ID michael@0: mozilla::AutoSwap_PRUint16 nameID; // Name ID. michael@0: mozilla::AutoSwap_PRUint16 length; // String length (in bytes). michael@0: mozilla::AutoSwap_PRUint16 offset; // String offset from start of storage michael@0: // (in bytes). michael@0: }; michael@0: michael@0: // for reading big-endian font data on either big or little-endian platforms michael@0: michael@0: static inline uint16_t michael@0: ReadShortAt(const uint8_t *aBuf, uint32_t aIndex) michael@0: { michael@0: return (aBuf[aIndex] << 8) | aBuf[aIndex + 1]; michael@0: } michael@0: michael@0: static inline uint16_t michael@0: ReadShortAt16(const uint16_t *aBuf, uint32_t aIndex) michael@0: { michael@0: const uint8_t *buf = reinterpret_cast(aBuf); michael@0: uint32_t index = aIndex << 1; michael@0: return (buf[index] << 8) | buf[index+1]; michael@0: } michael@0: michael@0: static inline uint32_t michael@0: ReadUint24At(const uint8_t *aBuf, uint32_t aIndex) michael@0: { michael@0: return ((aBuf[aIndex] << 16) | (aBuf[aIndex + 1] << 8) | michael@0: (aBuf[aIndex + 2])); michael@0: } michael@0: michael@0: static inline uint32_t michael@0: ReadLongAt(const uint8_t *aBuf, uint32_t aIndex) michael@0: { michael@0: return ((aBuf[aIndex] << 24) | (aBuf[aIndex + 1] << 16) | michael@0: (aBuf[aIndex + 2] << 8) | (aBuf[aIndex + 3])); michael@0: } michael@0: michael@0: static nsresult michael@0: ReadCMAPTableFormat12(const uint8_t *aBuf, uint32_t aLength, michael@0: gfxSparseBitSet& aCharacterMap); michael@0: michael@0: static nsresult michael@0: ReadCMAPTableFormat4(const uint8_t *aBuf, uint32_t aLength, michael@0: gfxSparseBitSet& aCharacterMap); michael@0: michael@0: static nsresult michael@0: ReadCMAPTableFormat14(const uint8_t *aBuf, uint32_t aLength, michael@0: uint8_t*& aTable); michael@0: michael@0: static uint32_t michael@0: FindPreferredSubtable(const uint8_t *aBuf, uint32_t aBufLength, michael@0: uint32_t *aTableOffset, uint32_t *aUVSTableOffset, michael@0: bool *aSymbolEncoding); michael@0: michael@0: static nsresult michael@0: ReadCMAP(const uint8_t *aBuf, uint32_t aBufLength, michael@0: gfxSparseBitSet& aCharacterMap, michael@0: uint32_t& aUVSOffset, michael@0: bool& aUnicodeFont, bool& aSymbolFont); michael@0: michael@0: static uint32_t michael@0: MapCharToGlyphFormat4(const uint8_t *aBuf, char16_t aCh); michael@0: michael@0: static uint32_t michael@0: MapCharToGlyphFormat12(const uint8_t *aBuf, uint32_t aCh); michael@0: michael@0: static uint16_t michael@0: MapUVSToGlyphFormat14(const uint8_t *aBuf, uint32_t aCh, uint32_t aVS); michael@0: michael@0: // sCJKCompatSVSTable is a 'cmap' format 14 subtable that maps michael@0: // pairs to the corresponding Unicode michael@0: // compatibility ideograph codepoints. michael@0: static MOZ_ALWAYS_INLINE uint32_t michael@0: GetUVSFallback(uint32_t aCh, uint32_t aVS) { michael@0: aCh = MapUVSToGlyphFormat14(sCJKCompatSVSTable, aCh, aVS); michael@0: return aCh >= 0xFB00 ? aCh + (0x2F800 - 0xFB00) : aCh; michael@0: } michael@0: michael@0: static uint32_t michael@0: MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength, michael@0: uint32_t aUnicode, uint32_t aVarSelector = 0); michael@0: michael@0: #ifdef XP_WIN michael@0: // determine whether a font (which has already been sanitized, so is known michael@0: // to be a valid sfnt) is CFF format rather than TrueType michael@0: static bool michael@0: IsCffFont(const uint8_t* aFontData); michael@0: #endif michael@0: michael@0: // determine the format of font data michael@0: static gfxUserFontType michael@0: DetermineFontDataType(const uint8_t *aFontData, uint32_t aFontDataLength); michael@0: michael@0: // Read the fullname from the sfnt data (used to save the original name michael@0: // prior to renaming the font for installation). michael@0: // This is called with sfnt data that has already been validated, michael@0: // so it should always succeed in finding the name table. michael@0: static nsresult michael@0: GetFullNameFromSFNT(const uint8_t* aFontData, uint32_t aLength, michael@0: nsAString& aFullName); michael@0: michael@0: // helper to get fullname from name table, constructing from family+style michael@0: // if no explicit fullname is present michael@0: static nsresult michael@0: GetFullNameFromTable(hb_blob_t *aNameTable, michael@0: nsAString& aFullName); michael@0: michael@0: // helper to get family name from name table michael@0: static nsresult michael@0: GetFamilyNameFromTable(hb_blob_t *aNameTable, michael@0: nsAString& aFamilyName); michael@0: michael@0: // create a new name table and build a new font with that name table michael@0: // appended on the end, returns true on success michael@0: static nsresult michael@0: RenameFont(const nsAString& aName, const uint8_t *aFontData, michael@0: uint32_t aFontDataLength, FallibleTArray *aNewFont); michael@0: michael@0: // read all names matching aNameID, returning in aNames array michael@0: static nsresult michael@0: ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID, michael@0: int32_t aPlatformID, nsTArray& aNames); michael@0: michael@0: // reads English or first name matching aNameID, returning in aName michael@0: // platform based on OS michael@0: static nsresult michael@0: ReadCanonicalName(hb_blob_t *aNameTable, uint32_t aNameID, michael@0: nsString& aName); michael@0: michael@0: static nsresult michael@0: ReadCanonicalName(const char *aNameData, uint32_t aDataLen, michael@0: uint32_t aNameID, nsString& aName); michael@0: michael@0: // convert a name from the raw name table data into an nsString, michael@0: // provided we know how; return true if successful, or false michael@0: // if we can't handle the encoding michael@0: static bool michael@0: DecodeFontName(const char *aBuf, int32_t aLength, michael@0: uint32_t aPlatformCode, uint32_t aScriptCode, michael@0: uint32_t aLangCode, nsAString& dest); michael@0: michael@0: static inline bool IsJoinCauser(uint32_t ch) { michael@0: return (ch == 0x200D); michael@0: } michael@0: michael@0: static inline bool IsJoinControl(uint32_t ch) { michael@0: return (ch == 0x200C || ch == 0x200D); michael@0: } michael@0: michael@0: enum { michael@0: kUnicodeVS1 = 0xFE00, michael@0: kUnicodeVS16 = 0xFE0F, michael@0: kUnicodeVS17 = 0xE0100, michael@0: kUnicodeVS256 = 0xE01EF michael@0: }; michael@0: michael@0: static inline bool IsVarSelector(uint32_t ch) { michael@0: return (ch >= kUnicodeVS1 && ch <= kUnicodeVS16) || michael@0: (ch >= kUnicodeVS17 && ch <= kUnicodeVS256); michael@0: } michael@0: michael@0: static inline bool IsInvalid(uint32_t ch) { michael@0: return (ch == 0xFFFD); michael@0: } michael@0: michael@0: // Font code may want to know if there is the potential for bidi behavior michael@0: // to be triggered by any of the characters in a text run; this can be michael@0: // used to test that possibility. michael@0: enum { michael@0: kUnicodeBidiScriptsStart = 0x0590, michael@0: kUnicodeBidiScriptsEnd = 0x08FF, michael@0: kUnicodeBidiPresentationStart = 0xFB1D, michael@0: kUnicodeBidiPresentationEnd = 0xFEFC, michael@0: kUnicodeFirstHighSurrogateBlock = 0xD800, michael@0: kUnicodeRLM = 0x200F, michael@0: kUnicodeRLE = 0x202B, michael@0: kUnicodeRLO = 0x202E michael@0: }; michael@0: michael@0: static inline bool PotentialRTLChar(char16_t aCh) { michael@0: if (aCh >= kUnicodeBidiScriptsStart && aCh <= kUnicodeBidiScriptsEnd) michael@0: // bidi scripts Hebrew, Arabic, Syriac, Thaana, N'Ko are all encoded together michael@0: return true; michael@0: michael@0: if (aCh == kUnicodeRLM || aCh == kUnicodeRLE || aCh == kUnicodeRLO) michael@0: // directional controls that trigger bidi layout michael@0: return true; michael@0: michael@0: if (aCh >= kUnicodeBidiPresentationStart && michael@0: aCh <= kUnicodeBidiPresentationEnd) michael@0: // presentation forms of Arabic and Hebrew letters michael@0: return true; michael@0: michael@0: if ((aCh & 0xFF00) == kUnicodeFirstHighSurrogateBlock) michael@0: // surrogate that could be part of a bidi supplementary char michael@0: // (Cypriot, Aramaic, Phoenecian, etc) michael@0: return true; michael@0: michael@0: // otherwise we know this char cannot trigger bidi reordering michael@0: return false; michael@0: } michael@0: michael@0: // for a given font list pref name, set up a list of font names michael@0: static void GetPrefsFontList(const char *aPrefName, michael@0: nsTArray& aFontList); michael@0: michael@0: // generate a unique font name michael@0: static nsresult MakeUniqueUserFontName(nsAString& aName); michael@0: michael@0: protected: michael@0: static nsresult michael@0: ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID, michael@0: int32_t aLangID, int32_t aPlatformID, nsTArray& aNames); michael@0: michael@0: // convert opentype name-table platform/encoding/language values to a charset name michael@0: // we can use to convert the name data to unicode, or "" if data is UTF16BE michael@0: static const char* michael@0: GetCharsetForFontName(uint16_t aPlatform, uint16_t aScript, uint16_t aLanguage); michael@0: michael@0: struct MacFontNameCharsetMapping { michael@0: uint16_t mEncoding; michael@0: uint16_t mLanguage; michael@0: const char *mCharsetName; michael@0: michael@0: bool operator<(const MacFontNameCharsetMapping& rhs) const { michael@0: return (mEncoding < rhs.mEncoding) || michael@0: ((mEncoding == rhs.mEncoding) && (mLanguage < rhs.mLanguage)); michael@0: } michael@0: }; michael@0: static const MacFontNameCharsetMapping gMacFontNameCharsets[]; michael@0: static const char* gISOFontNameCharsets[]; michael@0: static const char* gMSFontNameCharsets[]; michael@0: }; michael@0: michael@0: michael@0: #endif /* GFX_FONT_UTILS_H */