Beispiel #1
0
        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, os2Info.Value, filename));
        }
            public static TypographicFont[] Read(string filename)
            {
                // Do not check the file extension. Files are often misnamed. Detect by contents.

                using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
                    using (var br = new BigEndianBinaryReader(file))
                    {
                        if (Encoding.ASCII.GetString(br.ReadBytes(4)) == "ttcf")
                        {
                            // Font collection detected.

                            // This check is probably not necessary, since future versions are probably going to be back-compatible
                            var version = br.ReadUInt32();
                            // const uint knownVersion1 = 0x00010000, knownVersion2 = 0x00020000;
                            // if (version != knownVersion1 && version != knownVersion2) return new TypographicFont[0];

                            var numContainedFonts = br.ReadUInt32();
                            var fontStreamOffsets = new uint[numContainedFonts];
                            for (var fi = 0; fi < fontStreamOffsets.Length; fi++)
                            {
                                fontStreamOffsets[fi] = br.ReadUInt32();
                            }


                            var r = new List <TypographicFont>();

                            for (var fi = 0; fi < fontStreamOffsets.Length; fi++)
                            {
                                var font = ParseOpenTypeStream(br, fontStreamOffsets[fi], filename);
                                if (font != null)
                                {
                                    r.Add(font);
                                }
                            }

                            return(r.ToArray());
                        }
                        else
                        {
                            // No collection detected, assume this is an OpenType stream.

                            var font = ParseOpenTypeStream(br, 0, filename);
                            return(font == null ? new TypographicFont[0] : new[] { font });
                        }
                    }
            }
            // 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));
            }