diff options
Diffstat (limited to 'src/ports/SkFontHost_tables.cpp')
-rw-r--r-- | src/ports/SkFontHost_tables.cpp | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/ports/SkFontHost_tables.cpp b/src/ports/SkFontHost_tables.cpp new file mode 100644 index 0000000000..f14e085575 --- /dev/null +++ b/src/ports/SkFontHost_tables.cpp @@ -0,0 +1,173 @@ +#include "SkEndian.h" +#include "SkFontHost.h" +#include "SkStream.h" + +struct SkSFNTHeader { + uint32_t fVersion; + uint16_t fNumTables; + uint16_t fSearchRange; + uint16_t fEntrySelector; + uint16_t fRangeShift; +}; + +struct SkTTCFHeader { + uint32_t fTag; + uint32_t fVersion; + uint32_t fNumOffsets; + uint32_t fOffset0; // the first of N (fNumOffsets) +}; + +union SkSharedTTHeader { + SkSFNTHeader fSingle; + SkTTCFHeader fCollection; +}; + +struct SkSFNTDirEntry { + uint32_t fTag; + uint32_t fChecksum; + uint32_t fOffset; + uint32_t fLength; +}; + +static int count_tables(SkStream* stream, size_t* offsetToDir = NULL) { + SkSharedTTHeader shared; + if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) { + return 0; + } + + uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); + if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { + if (shared.fCollection.fNumOffsets == 0) { + return 0; + } + size_t offset = SkEndian_SwapBE32(shared.fCollection.fOffset0); + stream->rewind(); + if (stream->skip(offset) != offset) { + return 0; + } + if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) { + return 0; + } + if (offsetToDir) { + *offsetToDir = offset; + } + } + return SkEndian_SwapBE16(shared.fSingle.fNumTables); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct SfntHeader { + SfntHeader() : fCount(0), fDir(NULL) {} + ~SfntHeader() { sk_free(fDir); } + + bool init(SkStream* stream) { + size_t offsetToDir; + fCount = count_tables(stream, &offsetToDir); + if (0 == fCount) { + return false; + } + + stream->rewind(); + if (stream->skip(offsetToDir) != offsetToDir) { + return false; + } + + size_t size = fCount * sizeof(SkSFNTDirEntry); + fDir = reinterpret_cast<SkSFNTDirEntry*>(sk_malloc_throw(size)); + return stream->read(fDir, size) == size; + } + + int fCount; + SkSFNTDirEntry* fDir; +}; + +/////////////////////////////////////////////////////////////////////////////// + +int SkFontHost::CountTables(SkFontID fontID) { + SkStream* stream = SkFontHost::OpenStream(fontID); + if (NULL == stream) { + return 0; + } + + SkAutoUnref au(stream); + return count_tables(stream); +} + +int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) { + SkStream* stream = SkFontHost::OpenStream(fontID); + if (NULL == stream) { + return 0; + } + + SkAutoUnref au(stream); + SfntHeader header; + if (!header.init(stream)) { + return 0; + } + + for (int i = 0; i < header.fCount; i++) { + tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); + } + return header.fCount; +} + +size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) { + SkStream* stream = SkFontHost::OpenStream(fontID); + if (NULL == stream) { + return 0; + } + + SkAutoUnref au(stream); + SfntHeader header; + if (!header.init(stream)) { + return 0; + } + + for (int i = 0; i < header.fCount; i++) { + if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { + return SkEndian_SwapBE32(header.fDir[i].fLength); + } + } + return 0; +} + +size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag, + size_t offset, size_t length, void* data) { + SkStream* stream = SkFontHost::OpenStream(fontID); + if (NULL == stream) { + return 0; + } + + SkAutoUnref au(stream); + SfntHeader header; + if (!header.init(stream)) { + return 0; + } + + for (int i = 0; i < header.fCount; i++) { + if (SkEndian_SwapBE32(header.fDir[i].fTag) == tag) { + size_t realOffset = SkEndian_SwapBE32(header.fDir[i].fOffset); + size_t realLength = SkEndian_SwapBE32(header.fDir[i].fLength); + // now sanity check the caller's offset/length + if (offset >= realLength) { + return 0; + } + if (offset + length > realLength) { + length = realLength - offset; + } + // skip the stream to the part of the table we want to copy from + stream->rewind(); + size_t bytesToSkip = realOffset + offset; + if (stream->skip(bytesToSkip) != bytesToSkip) { + return 0; + } + if (stream->read(data, length) != length) { + return 0; + } + return length; + } + } + return 0; +} + |