private static TypographicFont ParseOpenTypeStream(BigEndianBinaryReader br, long streamOffset, string filename) { br.BaseStream.Seek(streamOffset, SeekOrigin.Begin); // This version check is crucial, since there is no tag at the beginning of the stream. // If it doesn't match either of the known spec version IDs, it's probably a FON file which we don't parse. var version = br.ReadUInt32(); const uint trueTypeOutlinesVersion = 0x00010000; const uint ccfVersion = 0x4F54544F; // "OTTO" if (version != trueTypeOutlinesVersion && version != ccfVersion) { return(null); } var numTables = br.ReadUInt16(); var searchRange = br.ReadUInt16(); var entrySelector = br.ReadUInt16(); var rangeShift = br.ReadUInt16(); var familyNames = default(FamilyNamesInfo?); var os2Info = default(OS2Info?); for (var ti = 0; ti < numTables; ti++) { var tag = Encoding.ASCII.GetString(br.ReadBytes(4)); var checksum = br.ReadUInt32(); var offset = br.ReadUInt32(); var length = br.ReadUInt32(); var position = br.BaseStream.Position; switch (tag) { case "name": familyNames = ReadFamilyNames(br, offset); break; case "OS/2": os2Info = ReadOS2(br, offset); break; } if (familyNames != null && os2Info != null) { break; } br.BaseStream.Seek(position, SeekOrigin.Begin); } if (familyNames == null || os2Info == null) { return(null); } return(new TypographicFont( familyNames.Value.TypographicFamily, familyNames.Value.TypographicSubfamily == string.Empty ? null : familyNames.Value.TypographicSubfamily, familyNames.Value.FontName, os2Info.Value.Weight, (os2Info.Value.Style & FontStyle.Bold) != 0, (os2Info.Value.Style & FontStyle.Italic) != 0, (os2Info.Value.Style & FontStyle.Oblique) != 0, (os2Info.Value.Style & FontStyle.Underscore) != 0, (os2Info.Value.Style & FontStyle.Negative) != 0, (os2Info.Value.Style & FontStyle.Outlined) != 0, (os2Info.Value.Style & FontStyle.Strikeout) != 0, (os2Info.Value.Style & FontStyle.Regular) != 0, filename )); }
// http://www.microsoft.com/typography/otspec/name.htm private static FamilyNamesInfo ReadFamilyNames(BigEndianBinaryReader br, uint offset) { br.BaseStream.Seek(offset, SeekOrigin.Begin); var fSelector = br.ReadUInt16(); var numNameRecords = br.ReadUInt16(); var storageOffset = br.ReadUInt16(); var fontFamilyName = (string)null; var fontSubfamilyName = (string)null; var typographicFamilyName = (string)null; var typographicSubfamilyName = (string)null; for (var ri = 0; ri < numNameRecords; ri++) { var platformId = (PlatformId)br.ReadUInt16(); var encodingId = br.ReadUInt16(); var languageId = br.ReadUInt16(); var nameId = (NameId)br.ReadUInt16(); var stringLength = br.ReadUInt16(); var stringOffset = br.ReadUInt16(); // Assume we are on Windows switch (platformId) { case PlatformId.Unicode: break; case PlatformId.Windows: // We only want en-US if (languageId != 1033) { continue; } break; default: continue; } switch (nameId) { case NameId.TypographicFamilyName: case NameId.TypographicSubfamilyName: case NameId.FontFamilyName: case NameId.FontSubfamilyName: break; default: // Don't bother reading other strings continue; } var position = br.BaseStream.Position; br.BaseStream.Seek(offset + storageOffset + stringOffset, SeekOrigin.Begin); var str = Encoding.BigEndianUnicode.GetString(br.ReadBytes(stringLength)); br.BaseStream.Seek(position, SeekOrigin.Begin); switch (nameId) { case NameId.TypographicFamilyName: typographicFamilyName = str; break; case NameId.TypographicSubfamilyName: typographicSubfamilyName = str; break; case NameId.FontFamilyName: fontFamilyName = str; break; case NameId.FontSubfamilyName: fontSubfamilyName = str; break; } } return(typographicFamilyName == null ? new FamilyNamesInfo(fontFamilyName, fontSubfamilyName, fontFamilyName) : new FamilyNamesInfo(typographicFamilyName, typographicSubfamilyName, fontFamilyName)); }