/* * Copyright © 2018 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the * above copyright notice and the following two paragraphs appear in * all copies of this software. * * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #include #include #include "hb-open-type-private.hh" #ifndef HB_NO_VISIBILITY const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {}; #endif template struct LEInt; template struct LEInt { public: inline void set (Type V) { v = V; } inline operator Type (void) const { return v; } private: uint8_t v; }; template struct LEInt { public: inline void set (Type V) { v[1] = (V >> 8) & 0xFF; v[0] = (V ) & 0xFF; } inline operator Type (void) const { return (v[1] << 8) + (v[0] ); } private: uint8_t v[2]; }; template struct LEInt { public: inline void set (Type V) { v[2] = (V >> 16) & 0xFF; v[1] = (V >> 8) & 0xFF; v[0] = (V ) & 0xFF; } inline operator Type (void) const { return (v[2] << 16) + (v[1] << 8) + (v[0] ); } private: uint8_t v[3]; }; template struct LEInt { public: inline void set (Type V) { v[3] = (V >> 24) & 0xFF; v[2] = (V >> 16) & 0xFF; v[1] = (V >> 8) & 0xFF; v[0] = (V ) & 0xFF; } inline operator Type (void) const { return (v[3] << 24) + (v[2] << 16) + (v[1] << 8) + (v[0] ); } private: uint8_t v[4]; }; template struct LEIntType { inline void set (Type i) { v.set (i); } inline operator Type(void) const { return v; } inline bool sanitize (OT::hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this))); } protected: LEInt v; public: DEFINE_SIZE_STATIC (Size); }; typedef LEIntType LEUINT8; /* 8-bit unsigned integer. */ typedef LEIntType LEINT8; /* 8-bit signed integer. */ typedef LEIntType LEUINT16; /* 16-bit unsigned integer. */ typedef LEIntType LEINT16; /* 16-bit signed integer. */ typedef LEIntType LEUINT32; /* 32-bit unsigned integer. */ typedef LEIntType LEINT32; /* 32-bit signed integer. */ typedef LEIntType LEUINT24; /* 24-bit unsigned integer. */ struct LE_FONTINFO16 { inline bool sanitize (OT::hb_sanitize_context_t *c, unsigned int length) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && c->check_range (this, length))); } // https://msdn.microsoft.com/en-us/library/cc194829.aspx enum charset_t { // dfCharSet possible values and the codepage they are indicating to ANSI = 0x00, // 1252 DEFAULT = 0x01, // SYMBOL = 0x02, // SHIFTJIS = 0x80, // 932 HANGUL = 0x81, // 949 GB2312 = 0x86, // 936 CHINESEBIG5 = 0x88, // 950 GREEK = 0xA1, // 1253 TURKISH = 0xA2, // 1254 HEBREW = 0xB1, // 1255 ARABIC = 0xB2, // 1256 BALTIC = 0xBA, // 1257 RUSSIAN = 0xCC, // 1251 THAI = 0xDE, // 874 EE = 0xEE, // 1250 OEM = 0xFF // }; inline const char* get_charset() const { switch (dfCharSet) { case ANSI: return "ISO8859"; case DEFAULT: return "WinDefault"; case SYMBOL: return "Symbol"; case SHIFTJIS: return "JISX0208.1983"; case HANGUL: return "MSHangul"; case GB2312: return "GB2312.1980"; case CHINESEBIG5: return "Big5"; case GREEK: return "CP1253"; case TURKISH: return "CP1254"; case HEBREW: return "CP1255"; case ARABIC: return "CP1256"; case BALTIC: return "CP1257"; case RUSSIAN: return "CP1251"; case THAI: return "CP874"; case EE: return "CP1250"; case OEM: return "OEM"; default: return "Unknown"; } } inline unsigned int get_version () const { return dfVersion; } inline unsigned int get_weight () const { return dfWeight; } enum weight_t { DONTCARE = 0, THIN = 100, EXTRALIGHT = 200, ULTRALIGHT = 200, LIGHT = 300, NORMAL = 400, REGULAR = 400, MEDIUM = 500, SEMIBOLD = 600, DEMIBOLD = 600, BOLD = 700, EXTRABOLD = 800, ULTRABOLD = 800, HEAVY = 900, BLACK = 900 }; inline void dump () const { // With https://github.com/juanitogan/mkwinfont/blob/master/python/dewinfont.py help // Should be implemented differently eventually, but for now unsigned int ctstart; unsigned int ctsize; if (dfVersion == 0x200) { ctstart = 0x76; ctsize = 4; } else { return; // must of ".fon"s are version 2 and even dewinfont V1 implmentation doesn't seem correct ctstart = 0x94; ctsize = 6; } // unsigned int maxwidth = 0; for (unsigned int i = dfFirstChar; i < dfLastChar; ++i) { unsigned int entry = ctstart + ctsize * (i-dfFirstChar); unsigned int w = (uint16_t) OT::StructAtOffset (this, entry); unsigned int off; if (ctsize == 4) off = (uint16_t) OT::StructAtOffset (this, entry+2); else off = (uint32_t) OT::StructAtOffset (this, entry+2); unsigned int widthbytes = (w + 7) / 8; for (unsigned int j = 0; j < dfPixHeight; ++j) { for (unsigned int k = 0; k < widthbytes; ++k) { unsigned int bytepos = off + k * dfPixHeight + j; const uint8_t b = (uint8_t) OT::StructAtOffset (this, bytepos); for (unsigned int a = 128; a > 0; a >>= 1) printf (b & a ? "x" : "."); } printf ("\n"); } printf ("\n\n"); } } protected: LEUINT16 dfVersion; LEUINT32 dfSize; LEUINT8 dfCopyright[60]; LEUINT16 dfType; LEUINT16 dfPoints; LEUINT16 dfVertRes; LEUINT16 dfHorizRes; LEUINT16 dfAscent; LEUINT16 dfInternalLeading; LEUINT16 dfExternalLeading; LEUINT8 dfItalic; LEUINT8 dfUnderline; LEUINT8 dfStrikeOut; LEUINT16 dfWeight; // see weight_t LEUINT8 dfCharSet; // see charset_t LEUINT16 dfPixWidth; LEUINT16 dfPixHeight; LEUINT8 dfPitchAndFamily; LEUINT16 dfAvgWidth; LEUINT16 dfMaxWidth; LEUINT8 dfFirstChar; LEUINT8 dfLastChar; LEUINT8 dfDefaultChar; LEUINT8 dfBreakChar; LEUINT16 dfWidthBytes; LEUINT32 dfDevice; LEUINT32 dfFace; LEUINT32 dfBitsPointer; LEUINT32 dfBitsOffset; LEUINT8 dfReserved; // LEUINT32 dfFlags; // LEUINT16 dfAspace; // LEUINT16 dfBspace; // LEUINT16 dfCspace; // LEUINT32 dfColorPointer; // LEUINT32 dfReserved1[4]; OT::UnsizedArrayOf dataZ; public: DEFINE_SIZE_ARRAY (118, dataZ); }; struct NE_NAMEINFO { friend struct NE_TYPEINFO; inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base, unsigned int shift) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && get_font (base, shift).sanitize (c, length << shift))); } inline const LE_FONTINFO16& get_font (const void *base, int shift) const { return OT::StructAtOffset (base, offset << shift); } enum resource_type_flag_t { NONE = 0x0000, MOVEABLE = 0x0010, PURE = 0x0020, PRELOAD = 0x0040 }; protected: LEUINT16 offset; // Should be shifted with alignmentShiftCount before use LEUINT16 length; // Should be shifted with alignmentShiftCount before use LEUINT16 flags; // resource_type_flag_t LEUINT16 id; LEUINT16 handle; LEUINT16 usage; public: DEFINE_SIZE_STATIC (12); }; struct NE_TYPEINFO { inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base, unsigned int shift) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && resources.sanitize (c, count, base, shift)); } inline unsigned int get_size (void) const { return 8 + count * NE_NAMEINFO::static_size; } inline const NE_TYPEINFO& next () const { const NE_TYPEINFO& next = OT::StructAfter (*this); if (type_id == 0) return OT::Null (NE_TYPEINFO); return next; } inline const LE_FONTINFO16& get_font (unsigned int idx, const void *base, int shift) const { if (idx < count) return resources[idx].get_font (base, shift); return OT::Null (LE_FONTINFO16); } inline unsigned int get_count () const { return count; } inline unsigned int get_type_id () const { return type_id; } enum type_id_t { CURSOR = 0x8001, BITMAP = 0x8002, ICON = 0x8003, MENU = 0x8004, DIALOG = 0x8005, STRING = 0x8006, FONT_DIRECTORY = 0x8007, FONT = 0x8008, ACCELERATOR_TABLE = 0x8009, RESOURCE_DATA = 0x800a, GROUP_CURSOR = 0x800c, GROUP_ICON = 0x800e, VERSION = 0x8010 }; protected: LEUINT16 type_id; // see type_id_t LEUINT16 count; LEUINT32 resloader; OT::UnsizedArrayOf resources; public: DEFINE_SIZE_ARRAY (8, resources); }; struct NE_RESOURCE_TABLE { inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); if (!c->check_struct (this)) return_trace (false); const NE_TYPEINFO* n = &chain; while (n != &OT::Null (NE_TYPEINFO) && c->check_struct (n) && n->get_type_id () != 0) { if (n->get_type_id () == NE_TYPEINFO::FONT) return_trace (n->sanitize (c, base, alignmentShiftCount)); n = &n->next(); } return_trace (false); } inline unsigned int get_shift_value () const { return alignmentShiftCount; } inline const NE_TYPEINFO& get_fonts_entry () const { const NE_TYPEINFO* n = &chain; while (n != &OT::Null (NE_TYPEINFO) && n->get_type_id () != 0) { if (n->get_type_id () == NE_TYPEINFO::FONT) return *n; n = &n->next(); } return OT::Null (NE_TYPEINFO); } protected: LEUINT16 alignmentShiftCount; NE_TYPEINFO chain; // It is followed by an array of OT::ArrayOf chars; public: DEFINE_SIZE_MIN (2); }; // https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L2467 struct LE_IMAGE_OS2_HEADER { inline bool sanitize (OT::hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && (this+rsrctab).sanitize (c, base))); } inline const NE_RESOURCE_TABLE& get_resource_table () const { if (magic != 0x454E) // Only NE containers are support for now, NE == 0x454E return OT::Null (NE_RESOURCE_TABLE); return this+rsrctab; } protected: LEUINT16 magic; /* 00 NE signature 'NE' */ LEUINT8 ver; /* 02 Linker version number */ LEUINT8 rev; /* 03 Linker revision number */ LEUINT16 enttab; /* 04 Offset to entry table relative to NE */ LEUINT16 cbenttab; /* 06 Length of entry table in bytes */ LEUINT32 crc; /* 08 Checksum */ LEUINT16 flags; /* 0c Flags about segments in this file */ LEUINT16 autodata; /* 0e Automatic data segment number */ LEUINT16 heap; /* 10 Initial size of local heap */ LEUINT16 stack; /* 12 Initial size of stack */ LEUINT32 csip; /* 14 Initial CS:IP */ LEUINT32 sssp; /* 18 Initial SS:SP */ LEUINT16 cseg; /* 1c # of entries in segment table */ LEUINT16 cmod; /* 1e # of entries in module reference tab. */ LEUINT16 cbnrestab; /* 20 Length of nonresident-name table */ LEUINT16 segtab; /* 22 Offset to segment table */ OT::OffsetTo rsrctab; /* 24 Offset to resource table */ LEUINT16 restab; /* 26 Offset to resident-name table */ LEUINT16 modtab; /* 28 Offset to module reference table */ LEUINT16 imptab; /* 2a Offset to imported name table */ LEUINT32 nrestab; /* 2c Offset to nonresident-name table */ LEUINT16 cmovent; /* 30 # of movable entry points */ LEUINT16 align; /* 32 Logical sector alignment shift count */ LEUINT16 cres; /* 34 # of resource segments */ LEUINT8 exetyp; /* 36 Flags indicating target OS */ LEUINT8 flagsothers; /* 37 Additional information flags */ LEUINT16 pretthunks; /* 38 Offset to return thunks */ LEUINT16 psegrefbytes; /* 3a Offset to segment ref. bytes */ LEUINT16 swaparea; /* 3c Reserved by Microsoft */ LEUINT16 expver; /* 3e Expected Windows version number */ public: DEFINE_SIZE_STATIC (64); }; struct LE_IMAGE_DOS_HEADER { inline bool sanitize (OT::hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && get_os2_header ().sanitize (c, this))); } inline const LE_IMAGE_OS2_HEADER& get_os2_header () const { return this+e_lfanew; } protected: LEUINT16 e_magic; // Magic number LEUINT16 e_cblp; // Bytes on last page of file LEUINT16 e_cp; // Pages in file LEUINT16 e_crlc; // Relocations LEUINT16 e_cparhdr; // Size of header in paragraphs LEUINT16 e_minalloc; // Minimum extra paragraphs needed LEUINT16 e_maxalloc; // Maximum extra paragraphs needed LEUINT16 e_ss; // Initial (relative) SS value LEUINT16 e_sp; // Initial SP value LEUINT16 e_csum; // Checksum LEUINT16 e_ip; // Initial IP value LEUINT16 e_cs; // Initial (relative) CS value LEUINT16 e_lfarlc; // File address of relocation table LEUINT16 e_ovno; // Overlay number LEUINT16 e_res_0; // Reserved words LEUINT16 e_res_1; // Reserved words LEUINT16 e_res_2; // Reserved words LEUINT16 e_res_3; // Reserved words LEUINT16 e_oemid; // OEM identifier (for e_oeminfo) LEUINT16 e_oeminfo; // OEM information; e_oemid specific LEUINT16 e_res2_0; // Reserved words LEUINT16 e_res2_1; // Reserved words LEUINT16 e_res2_2; // Reserved words LEUINT16 e_res2_3; // Reserved words LEUINT16 e_res2_4; // Reserved words LEUINT16 e_res2_5; // Reserved words LEUINT16 e_res2_6; // Reserved words LEUINT16 e_res2_7; // Reserved words LEUINT16 e_res2_8; // Reserved words LEUINT16 e_res2_9; // Reserved words OT::OffsetTo e_lfanew; // File address of new exe header public: DEFINE_SIZE_STATIC (64); }; int main (int argc, char** argv) { hb_blob_t *blob = hb_blob_create_from_file (argv[1]); OT::Sanitizer sanitizer; hb_blob_t *font_blob = sanitizer.sanitize (blob); const LE_IMAGE_DOS_HEADER* dos_header = OT::Sanitizer::lock_instance (font_blob); const NE_RESOURCE_TABLE &rtable = dos_header->get_os2_header ().get_resource_table (); int shift = rtable.get_shift_value (); const NE_TYPEINFO& entry = rtable.get_fonts_entry (); for (unsigned int i = 0; i < entry.get_count (); ++i) { const LE_FONTINFO16& font = entry.get_font (i, dos_header, shift); printf ("version: %x, weight: %d, charset: %s\n", font.get_version (), font.get_weight (), font.get_charset ()); // font.dump (); } return 0; }