/* Modified for use with ttf2eot, originally from WebKit. */ /* * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #ifndef _MSC_VER # include #else typedef unsigned char uint8_t; #endif #include "OpenTypeUtilities.h" using std::vector; typedef unsigned Fixed; #define DEFAULT_CHARSET 1 struct BigEndianUShort { operator unsigned short() const { return (v & 0x00ff) << 8 | v >> 8; } BigEndianUShort(unsigned short u) : v((u & 0x00ff) << 8 | u >> 8) { } unsigned short v; }; struct BigEndianULong { operator unsigned() const { return (v & 0xff) << 24 | (v & 0xff00) << 8 | (v & 0xff0000) >> 8 | v >> 24; } BigEndianULong(unsigned u) : v((u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24) { } unsigned v; }; #pragma pack(1) struct EOTPrefix { unsigned eotSize; unsigned fontDataSize; unsigned version; unsigned flags; uint8_t fontPANOSE[10]; uint8_t charset; uint8_t italic; unsigned weight; unsigned short fsType; unsigned short magicNumber; unsigned unicodeRange[4]; unsigned codePageRange[2]; unsigned checkSumAdjustment; unsigned reserved[4]; unsigned short padding1; }; struct TableDirectoryEntry { BigEndianULong tag; BigEndianULong checkSum; BigEndianULong offset; BigEndianULong length; }; struct sfntHeader { Fixed version; BigEndianUShort numTables; BigEndianUShort searchRange; BigEndianUShort entrySelector; BigEndianUShort rangeShift; TableDirectoryEntry tables[1]; }; struct OS2Table { BigEndianUShort version; BigEndianUShort avgCharWidth; BigEndianUShort weightClass; BigEndianUShort widthClass; BigEndianUShort fsType; BigEndianUShort subscriptXSize; BigEndianUShort subscriptYSize; BigEndianUShort subscriptXOffset; BigEndianUShort subscriptYOffset; BigEndianUShort superscriptXSize; BigEndianUShort superscriptYSize; BigEndianUShort superscriptXOffset; BigEndianUShort superscriptYOffset; BigEndianUShort strikeoutSize; BigEndianUShort strikeoutPosition; BigEndianUShort familyClass; uint8_t panose[10]; BigEndianULong unicodeRange[4]; uint8_t vendID[4]; BigEndianUShort fsSelection; BigEndianUShort firstCharIndex; BigEndianUShort lastCharIndex; BigEndianUShort typoAscender; BigEndianUShort typoDescender; BigEndianUShort typoLineGap; BigEndianUShort winAscent; BigEndianUShort winDescent; BigEndianULong codePageRange[2]; BigEndianUShort xHeight; BigEndianUShort capHeight; BigEndianUShort defaultChar; BigEndianUShort breakChar; BigEndianUShort maxContext; }; struct headTable { Fixed version; Fixed fontRevision; BigEndianULong checkSumAdjustment; BigEndianULong magicNumber; BigEndianUShort flags; BigEndianUShort unitsPerEm; long long created; long long modified; BigEndianUShort xMin; BigEndianUShort xMax; BigEndianUShort yMin; BigEndianUShort yMax; BigEndianUShort macStyle; BigEndianUShort lowestRectPPEM; BigEndianUShort fontDirectionHint; BigEndianUShort indexToLocFormat; BigEndianUShort glyphDataFormat; }; struct nameRecord { BigEndianUShort platformID; BigEndianUShort encodingID; BigEndianUShort languageID; BigEndianUShort nameID; BigEndianUShort length; BigEndianUShort offset; }; struct nameTable { BigEndianUShort format; BigEndianUShort count; BigEndianUShort stringOffset; nameRecord nameRecords[1]; }; #pragma pack() static void appendBigEndianStringToEOTHeader(vector&eotHeader, const BigEndianUShort* string, unsigned short length) { size_t size = eotHeader.size(); eotHeader.resize(size + length + 2 * sizeof(unsigned short)); unsigned short* dst = reinterpret_cast(&eotHeader[0] + size); unsigned i = 0; dst[i++] = length; unsigned numCharacters = length / 2; for (unsigned j = 0; j < numCharacters; j++) dst[i++] = string[j]; dst[i] = 0; } bool getEOTHeader(unsigned char* fontData, size_t fontSize, vector& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) { overlayDst = 0; overlaySrc = 0; overlayLength = 0; size_t dataLength = fontSize; const char* data = (const char *) fontData; eotHeader.resize(sizeof(EOTPrefix)); EOTPrefix* prefix = reinterpret_cast(&eotHeader[0]); prefix->fontDataSize = dataLength; prefix->version = 0x00020001; prefix->flags = 0; if (dataLength < offsetof(sfntHeader, tables)) return false; const sfntHeader* sfnt = reinterpret_cast(data); if (dataLength < offsetof(sfntHeader, tables) + sfnt->numTables * sizeof(TableDirectoryEntry)) return false; bool haveOS2 = false; bool haveHead = false; bool haveName = false; const BigEndianUShort* familyName = 0; unsigned short familyNameLength = 0; const BigEndianUShort* subfamilyName = 0; unsigned short subfamilyNameLength = 0; const BigEndianUShort* fullName = 0; unsigned short fullNameLength = 0; const BigEndianUShort* versionString = 0; unsigned short versionStringLength = 0; for (unsigned i = 0; i < sfnt->numTables; i++) { unsigned tableOffset = sfnt->tables[i].offset; unsigned tableLength = sfnt->tables[i].length; if (dataLength < tableOffset || dataLength < tableLength || dataLength < tableOffset + tableLength) return false; unsigned tableTag = sfnt->tables[i].tag; switch (tableTag) { case 'OS/2': { if (dataLength < tableOffset + sizeof(OS2Table)) return false; haveOS2 = true; const OS2Table* OS2 = reinterpret_cast(data + tableOffset); for (unsigned j = 0; j < 10; j++) prefix->fontPANOSE[j] = OS2->panose[j]; prefix->italic = OS2->fsSelection & 0x01; prefix->weight = OS2->weightClass; // FIXME: Should use OS2->fsType, but some TrueType fonts set it to an over-restrictive value. // Since ATS does not enforce this on Mac OS X, we do not enforce it either. prefix->fsType = 0; for (unsigned j = 0; j < 4; j++) prefix->unicodeRange[j] = OS2->unicodeRange[j]; for (unsigned j = 0; j < 2; j++) prefix->codePageRange[j] = OS2->codePageRange[j]; break; } case 'head': { if (dataLength < tableOffset + sizeof(headTable)) return false; haveHead = true; const headTable* head = reinterpret_cast(data + tableOffset); prefix->checkSumAdjustment = head->checkSumAdjustment; break; } case 'name': { if (dataLength < tableOffset + offsetof(nameTable, nameRecords)) return false; haveName = true; const nameTable* name = reinterpret_cast(data + tableOffset); for (int j = 0; j < name->count; j++) { if (dataLength < tableOffset + offsetof(nameTable, nameRecords) + (j + 1) * sizeof(nameRecord)) return false; if (name->nameRecords[j].platformID == 3 && name->nameRecords[j].encodingID == 1 && name->nameRecords[j].languageID == 0x0409) { if (dataLength < tableOffset + name->stringOffset + name->nameRecords[j].offset + name->nameRecords[j].length) return false; unsigned short nameLength = name->nameRecords[j].length; const BigEndianUShort* nameString = reinterpret_cast(data + tableOffset + name->stringOffset + name->nameRecords[j].offset); switch (name->nameRecords[j].nameID) { case 1: familyNameLength = nameLength; familyName = nameString; break; case 2: subfamilyNameLength = nameLength; subfamilyName = nameString; break; case 4: fullNameLength = nameLength; fullName = nameString; break; case 5: versionStringLength = nameLength; versionString = nameString; break; default: break; } } } break; } default: break; } if (haveOS2 && haveHead && haveName) break; } prefix->charset = DEFAULT_CHARSET; prefix->magicNumber = 0x504c; prefix->reserved[0] = 0; prefix->reserved[1] = 0; prefix->reserved[2] = 0; prefix->reserved[3] = 0; prefix->padding1 = 0; appendBigEndianStringToEOTHeader(eotHeader, familyName, familyNameLength); appendBigEndianStringToEOTHeader(eotHeader, subfamilyName, subfamilyNameLength); appendBigEndianStringToEOTHeader(eotHeader, versionString, versionStringLength); // If possible, ensure that the family name is a prefix of the full name. if (fullNameLength >= familyNameLength && memcmp(familyName, fullName, familyNameLength)) { overlaySrc = reinterpret_cast(fullName) - data; overlayDst = reinterpret_cast(familyName) - data; overlayLength = familyNameLength; } appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength); unsigned short padding = 0; eotHeader.push_back(padding); eotHeader.push_back(padding); prefix = reinterpret_cast(&eotHeader[0]); prefix->eotSize = eotHeader.size() + fontSize; return true; }