mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-12 18:39:45 +00:00
365 lines
15 KiB
C#
365 lines
15 KiB
C#
//MIT, 2019-present, WinterDev
|
|
//see https://www.w3.org/TR/2012/REC-WOFF-20121213/
|
|
|
|
using System;
|
|
using System.IO;
|
|
using Typography.OpenFont.Tables;
|
|
using Typography.OpenFont.Trimmable;
|
|
|
|
namespace Typography.OpenFont.WebFont
|
|
{
|
|
//NOTE: Web Font file structure is not part of 'Open Font Format'.
|
|
|
|
class WoffHeader
|
|
{
|
|
//WOFFHeader
|
|
//UInt32 signature 0x774F4646 'wOFF'
|
|
//UInt32 flavor The "sfnt version" of the input font.
|
|
//UInt32 length Total size of the WOFF file.
|
|
//UInt16 numTables Number of entries in directory of font tables.
|
|
//UInt16 reserved Reserved; set to zero.
|
|
//UInt32 totalSfntSize Total size needed for the uncompressed font data, including the sfnt header, directory, and font tables(including padding).
|
|
//UInt16 majorVersion Major version of the WOFF file.
|
|
//UInt16 minorVersion Minor version of the WOFF file.
|
|
//UInt32 metaOffset Offset to metadata block, from beginning of WOFF file.
|
|
//UInt32 metaLength Length of compressed metadata block.
|
|
//UInt32 metaOrigLength Uncompressed size of metadata block.
|
|
//UInt32 privOffset Offset to private data block, from beginning of WOFF file.
|
|
//UInt32 privLength Length of private data block.
|
|
|
|
public uint flavor;
|
|
public uint length;
|
|
public uint numTables;
|
|
|
|
//public ushort reserved;
|
|
public uint totalSfntSize;
|
|
|
|
public ushort majorVersion;
|
|
public ushort minorVersion;
|
|
public uint metaOffset;
|
|
public uint metaLength;
|
|
public uint metaOriginalLength;
|
|
public uint privOffset;
|
|
public uint privLength;
|
|
}
|
|
|
|
class WoffTableDirectory
|
|
{
|
|
//WOFF TableDirectoryEntry
|
|
//UInt32 tag 4-byte sfnt table identifier.
|
|
//UInt32 offset Offset to the data, from beginning of WOFF file.
|
|
//UInt32 compLength Length of the compressed data, excluding padding.
|
|
//UInt32 origLength Length of the uncompressed table, excluding padding.
|
|
//UInt32 origChecksum Checksum of the uncompressed table.
|
|
public uint tag;
|
|
|
|
public uint offset;
|
|
public uint compLength;
|
|
public uint origLength;
|
|
public uint origChecksum;
|
|
|
|
//translated values
|
|
//public UnreadTableEntry unreadTableEntry; //simulate
|
|
public string Name { get; set; }
|
|
|
|
public long ExpectedStartAt { get; set; }
|
|
#if DEBUG
|
|
|
|
public WoffTableDirectory()
|
|
{
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Name;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
public delegate bool ZlibDecompressStreamFunc(byte[] compressedInput, byte[] decompressOutput);
|
|
|
|
public static class WoffDefaultZlibDecompressFunc
|
|
{
|
|
public static ZlibDecompressStreamFunc DecompressHandler;
|
|
}
|
|
|
|
class WoffReader
|
|
{
|
|
private WoffHeader _header;
|
|
|
|
public ZlibDecompressStreamFunc DecompressHandler;
|
|
|
|
public PreviewFontInfo ReadPreview(BinaryReader reader)
|
|
{
|
|
//read preview only
|
|
//WOFF File
|
|
//WOFFHeader File header with basic font type and version, along with offsets to metadata and private data blocks.
|
|
//TableDirectory Directory of font tables, indicating the original size, compressed size and location of each table within the WOFF file.
|
|
//FontTables The font data tables from the input sfnt font, compressed to reduce bandwidth requirements.
|
|
//ExtendedMetadata An optional block of extended metadata, represented in XML format and compressed for storage in the WOFF file.
|
|
//PrivateData An optional block of private data for the font designer, foundry, or vendor to use.
|
|
|
|
PreviewFontInfo fontPreviewInfo = null;
|
|
_header = ReadWOFFHeader(reader);
|
|
if (_header == null)
|
|
{
|
|
#if DEBUG
|
|
System.Diagnostics.Debug.WriteLine("can't read ");
|
|
#endif
|
|
return null; //notify user too
|
|
}
|
|
|
|
//
|
|
WoffTableDirectory[] woffTableDirs = ReadTableDirectories(reader);
|
|
if (woffTableDirs == null)
|
|
{
|
|
return null;
|
|
}
|
|
//try read each compressed table
|
|
if (DecompressHandler == null)
|
|
{
|
|
if (WoffDefaultZlibDecompressFunc.DecompressHandler != null)
|
|
{
|
|
DecompressHandler = WoffDefaultZlibDecompressFunc.DecompressHandler;
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG
|
|
System.Diagnostics.Debug.WriteLine("no Zlib DecompressHandler ");
|
|
#endif
|
|
return null; //notify user too
|
|
}
|
|
}
|
|
|
|
TableEntryCollection tableEntryCollection = CreateTableEntryCollection(woffTableDirs);
|
|
//for font preview, we may not need to extract
|
|
using (MemoryStream decompressStream = new MemoryStream())
|
|
{
|
|
if (Extract(reader, woffTableDirs, decompressStream))
|
|
{
|
|
using (ByteOrderSwappingBinaryReader reader2 = new ByteOrderSwappingBinaryReader(decompressStream))
|
|
{
|
|
decompressStream.Position = 0;
|
|
OpenFontReader openFontReader = new OpenFontReader();
|
|
PreviewFontInfo previewFontInfo = openFontReader.ReadPreviewFontInfo(tableEntryCollection, reader2);
|
|
if (previewFontInfo != null)
|
|
{
|
|
//add webfont info to this preview font
|
|
previewFontInfo.IsWebFont = true;
|
|
}
|
|
return previewFontInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fontPreviewInfo;
|
|
}
|
|
|
|
internal bool Read(Typeface typeface, BinaryReader reader, RestoreTicket ticket)
|
|
{
|
|
//WOFF File
|
|
//WOFFHeader File header with basic font type and version, along with offsets to metadata and private data blocks.
|
|
//TableDirectory Directory of font tables, indicating the original size, compressed size and location of each table within the WOFF file.
|
|
//FontTables The font data tables from the input sfnt font, compressed to reduce bandwidth requirements.
|
|
//ExtendedMetadata An optional block of extended metadata, represented in XML format and compressed for storage in the WOFF file.
|
|
//PrivateData An optional block of private data for the font designer, foundry, or vendor to use.
|
|
_header = ReadWOFFHeader(reader);
|
|
if (_header == null)
|
|
{
|
|
#if DEBUG
|
|
System.Diagnostics.Debug.WriteLine("can't read ");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//
|
|
WoffTableDirectory[] woffTableDirs = ReadTableDirectories(reader);
|
|
if (woffTableDirs == null)
|
|
{
|
|
return false;
|
|
}
|
|
//
|
|
//try read each compressed table
|
|
if (DecompressHandler == null)
|
|
{
|
|
if (WoffDefaultZlibDecompressFunc.DecompressHandler != null)
|
|
{
|
|
DecompressHandler = WoffDefaultZlibDecompressFunc.DecompressHandler;
|
|
}
|
|
else
|
|
{
|
|
#if DEBUG
|
|
System.Diagnostics.Debug.WriteLine("no Zlib DecompressHandler ");
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
|
|
TableEntryCollection tableEntryCollection = CreateTableEntryCollection(woffTableDirs);
|
|
|
|
using (MemoryStream decompressStream = new MemoryStream())
|
|
{
|
|
if (Extract(reader, woffTableDirs, decompressStream))
|
|
{
|
|
using (ByteOrderSwappingBinaryReader reader2 = new ByteOrderSwappingBinaryReader(decompressStream))
|
|
{
|
|
decompressStream.Position = 0;
|
|
OpenFontReader openFontReader = new OpenFontReader();
|
|
return openFontReader.ReadTableEntryCollection(typeface, ticket, tableEntryCollection, reader2);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static TableEntryCollection CreateTableEntryCollection(WoffTableDirectory[] woffTableDirs)
|
|
{
|
|
TableEntryCollection tableEntryCollection = new TableEntryCollection();
|
|
for (int i = 0; i < woffTableDirs.Length; ++i)
|
|
{
|
|
WoffTableDirectory woffTableDir = woffTableDirs[i];
|
|
tableEntryCollection.AddEntry(
|
|
new UnreadTableEntry(
|
|
new TableHeader(woffTableDir.tag,
|
|
woffTableDir.origChecksum,
|
|
(uint)woffTableDir.ExpectedStartAt,
|
|
woffTableDir.origLength)));
|
|
}
|
|
|
|
return tableEntryCollection;
|
|
}
|
|
|
|
static WoffHeader ReadWOFFHeader(BinaryReader reader)
|
|
{
|
|
//WOFFHeader
|
|
//UInt32 signature 0x774F4646 'wOFF'
|
|
//UInt32 flavor The "sfnt version" of the input font.
|
|
//UInt32 length Total size of the WOFF file.
|
|
//UInt16 numTables Number of entries in directory of font tables.
|
|
//UInt16 reserved Reserved; set to zero.
|
|
//UInt32 totalSfntSize Total size needed for the uncompressed font data, including the sfnt header, directory, and font tables(including padding).
|
|
//UInt16 majorVersion Major version of the WOFF file.
|
|
//UInt16 minorVersion Minor version of the WOFF file.
|
|
//UInt32 metaOffset Offset to metadata block, from beginning of WOFF file.
|
|
//UInt32 metaLength Length of compressed metadata block.
|
|
//UInt32 metaOrigLength Uncompressed size of metadata block.
|
|
//UInt32 privOffset Offset to private data block, from beginning of WOFF file.
|
|
//UInt32 privLength Length of private data block.
|
|
|
|
//signature
|
|
byte b0 = reader.ReadByte();
|
|
byte b1 = reader.ReadByte();
|
|
byte b2 = reader.ReadByte();
|
|
byte b3 = reader.ReadByte();
|
|
if (!(b0 == 0x77 && b1 == 0x4f && b2 == 0x46 && b3 == 0x46))
|
|
{
|
|
return null;
|
|
}
|
|
WoffHeader header = new WoffHeader();
|
|
header.flavor = reader.ReadUInt32BE();
|
|
header.length = reader.ReadUInt32BE();
|
|
header.numTables = reader.ReadUInt16BE();
|
|
ushort reserved = reader.ReadUInt16BE();
|
|
header.totalSfntSize = reader.ReadUInt32BE();
|
|
|
|
header.majorVersion = reader.ReadUInt16BE();
|
|
header.minorVersion = reader.ReadUInt16BE();
|
|
|
|
header.metaOffset = reader.ReadUInt32BE();
|
|
header.metaLength = reader.ReadUInt32BE();
|
|
header.metaOriginalLength = reader.ReadUInt32BE();
|
|
|
|
header.privOffset = reader.ReadUInt32BE();
|
|
header.privLength = reader.ReadUInt32BE();
|
|
|
|
return header;
|
|
}
|
|
|
|
WoffTableDirectory[] ReadTableDirectories(BinaryReader reader)
|
|
{
|
|
//The table directory is an array of WOFF table directory entries, as defined below.
|
|
//The directory follows immediately after the WOFF file header;
|
|
//therefore, there is no explicit offset in the header pointing to this block.
|
|
//Its size is calculated by multiplying the numTables value in the WOFF header times the size of a single WOFF table directory.
|
|
//Each table directory entry specifies the size and location of a single font data table.
|
|
|
|
uint tableCount = (uint)_header.numTables; //?
|
|
//tableDirs = new WoffTableDirectory[tableCount];
|
|
long expectedStartAt = 0;
|
|
|
|
//simulate table entry collection
|
|
//var tableEntryCollection = new TableEntryCollection((int)tableCount);
|
|
WoffTableDirectory[] tableDirs = new WoffTableDirectory[tableCount];
|
|
|
|
for (int i = 0; i < tableCount; ++i)
|
|
{
|
|
//UInt32 tag 4-byte sfnt table identifier.
|
|
//UInt32 offset Offset to the data, from beginning of WOFF file.
|
|
//UInt32 compLength Length of the compressed data, excluding padding.
|
|
//UInt32 origLength Length of the uncompressed table, excluding padding.
|
|
//UInt32 origChecksum Checksum of the uncompressed table.
|
|
|
|
WoffTableDirectory table = new WoffTableDirectory();
|
|
table.tag = reader.ReadUInt32();
|
|
table.offset = reader.ReadUInt32();
|
|
table.compLength = reader.ReadUInt32();
|
|
table.origLength = reader.ReadUInt32();
|
|
table.origChecksum = reader.ReadUInt32();
|
|
|
|
table.ExpectedStartAt = expectedStartAt;
|
|
table.Name = Utils.TagToString(table.tag);
|
|
//var tableHeader = new TableHeader(tag, origChecksum, (uint)expectedStartAt, origLength);
|
|
//var unreadTable = new UnreadTableEntry(tableHeader);
|
|
//tableEntryCollection.AddEntry(unreadTable);
|
|
//table.unreadTableEntry = unreadTable;
|
|
|
|
tableDirs[i] = table;
|
|
expectedStartAt += table.origLength;
|
|
}
|
|
|
|
return tableDirs;
|
|
}
|
|
|
|
bool Extract(BinaryReader reader, WoffTableDirectory[] tables, Stream newDecompressedStream)
|
|
{
|
|
for (int i = 0; i < tables.Length; ++i)
|
|
{
|
|
//UInt32 tag 4-byte sfnt table identifier.
|
|
//UInt32 offset Offset to the data, from beginning of WOFF file.
|
|
//UInt32 compLength Length of the compressed data, excluding padding.
|
|
//UInt32 origLength Length of the uncompressed table, excluding padding.
|
|
//UInt32 origChecksum Checksum of the uncompressed table.
|
|
|
|
WoffTableDirectory table = tables[i];
|
|
reader.BaseStream.Seek(table.offset, SeekOrigin.Begin);
|
|
|
|
//indeed, table may be compress or not=> check length of before and after ...
|
|
byte[] compressedBuffer = reader.ReadBytes((int)table.compLength);
|
|
|
|
if (compressedBuffer.Length == table.origLength)
|
|
{
|
|
//not a compress buffer
|
|
newDecompressedStream.Write(compressedBuffer, 0, compressedBuffer.Length);
|
|
}
|
|
else
|
|
{
|
|
var decompressedBuffer = new byte[table.origLength];
|
|
if (!DecompressHandler(compressedBuffer, decompressedBuffer))
|
|
{
|
|
//if not pass set to null
|
|
decompressedBuffer = null;
|
|
}
|
|
else
|
|
{
|
|
//pass
|
|
newDecompressedStream.Write(decompressedBuffer, 0, decompressedBuffer.Length);
|
|
}
|
|
}
|
|
}
|
|
newDecompressedStream.Flush();
|
|
return true;
|
|
}
|
|
}
|
|
}
|