public static Woff2Header ReadHeader(Woff2VersionReader version, BigEndianReader reader)
        {
            uint   flavour        = reader.ReadUInt32();
            uint   length         = reader.ReadUInt32();
            ushort numTables      = reader.ReadUInt16();
            var    reserved       = reader.ReadUInt16();
            var    totalFontSize  = reader.ReadUInt32();
            var    compressedSize = reader.ReadUInt32();

            var major = reader.ReadUInt16();
            var minor = reader.ReadUInt16();

            Woff2Header header = new Woff2Header(version, numTables);


            header.Flavour             = flavour;
            header.Length              = length;
            header.TotalFontSize       = totalFontSize;
            header.TotalCompressedSize = compressedSize;
            header.WoffInnerVersion    = new Version(major, minor);

            header.MetaDataOffset         = reader.ReadUInt32();
            header.MetaDataLength         = reader.ReadUInt32();
            header.MetaDataOriginalLength = reader.ReadUInt32();

            header.PrivateDataOffset = reader.ReadUInt32();
            header.PrivateDataLength = reader.ReadUInt32();

            return(header);
        }
        private Woff2CacheData ReadCachableDataAfterVersion(BigEndianReader reader, string source)
        {
            Woff2CacheData cache = null;

            Woff2Header header = Woff2Header.ReadHeader(this, reader);

            Woff2TableEntryList list = new Woff2TableEntryList();

            uint startAt = (uint)reader.Position;

            uint offset = 0;

            bool hasOs2   = false;
            bool hasFHead = false;
            bool hasName  = false;

            for (var i = 0; i < header.NumberOfTables; i++)
            {
                Woff2TableEntry entry = new Woff2TableEntry(offset);
                entry.Read(reader);

                if (entry.Tag == TrueTypeTableNames.WindowsMetrics)
                {
                    hasOs2 = true;
                }
                else if (entry.Tag == TrueTypeTableNames.FontHeader)
                {
                    hasFHead = true;
                }
                else if (entry.Tag == TrueTypeTableNames.NamingTable)
                {
                    hasName = true;
                }

                // If the table data has been transformed,
                // then that will be the offset of the next table

                if (entry.HasTransformation)
                {
                    offset += entry.TransformedLength;
                }
                else
                {
                    offset += entry.Length;
                }

                list.Add(entry);
            }

            if (!(hasOs2 || hasName))
            {
                return(null);// new Utility.UnknownTypefaceInfo(source, "Not all the required tables (head with OS/2 or name) were found in the font file");
            }
            if (!hasFHead)
            {
                return(null);// new Utility.UnknownTypefaceInfo(source, "Not all the required tables (head with OS/2 or name) were found in the font file");
            }
            //After the table entries, the entire table data is compressed using the Brotli alogorythm
            offset = (uint)reader.Position;

            var compressedData   = reader.Read((int)header.TotalCompressedSize);
            var decompressedData = Woff2Brotli.DecompressData(compressedData);

            using (var uncompressed = new MemoryStream(decompressedData))
            {
#if RANGE_CHECK
                if (uncompressed.Length + offset != header.TotalFontSize)
                {
                    throw new ArgumentOutOfRangeException("The header uncompressed size is not the same");
                }
#endif


                using (var newReader = new BigEndianReader(uncompressed))
                {
                    var info = ReadInfoFromTables(list, newReader, source, hasOs2);

                    if (null != info && info.FontCount > 0)
                    {
                        cache = new Woff2CacheData()
                        {
                            Entries          = list,
                            Header           = header,
                            Info             = info,
                            Source           = source,
                            UncompressedData = decompressedData
                        };
                    }
                }
            }

            return(cache);
        }