mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-28 07:33:38 +00:00
180 lines
5.9 KiB
Python
180 lines
5.9 KiB
Python
"""
|
|
X11 Portable Compiled Font (pcf) parser.
|
|
|
|
Documents:
|
|
- Format for X11 pcf bitmap font files
|
|
http://fontforge.sourceforge.net/pcf-format.html
|
|
(file is based on the X11 sources)
|
|
|
|
Author: Victor Stinner
|
|
Creation date: 2007-03-20
|
|
"""
|
|
|
|
from hachoir.parser import Parser
|
|
from hachoir.field import (FieldSet, Enum,
|
|
UInt8, UInt32, Bytes, RawBytes, NullBytes,
|
|
Bit, Bits, PaddingBits, CString)
|
|
from hachoir.core.endian import LITTLE_ENDIAN, BIG_ENDIAN
|
|
from hachoir.core.text_handler import textHandler, hexadecimal, filesizeHandler
|
|
from hachoir.core.tools import paddingSize
|
|
|
|
|
|
class TOC(FieldSet):
|
|
TYPE_NAME = {
|
|
0x00000001: "Properties",
|
|
0x00000002: "Accelerators",
|
|
0x00000004: "Metrics",
|
|
0x00000008: "Bitmaps",
|
|
0x00000010: "Ink metrics",
|
|
0x00000020: "BDF encodings",
|
|
0x00000040: "SWidths",
|
|
0x00000080: "Glyph names",
|
|
0x00000100: "BDF accelerators",
|
|
}
|
|
|
|
FORMAT_NAME = {
|
|
0x00000000: "Default",
|
|
0x00000200: "Ink bounds",
|
|
0x00000100: "Accelerator W ink bounds",
|
|
# 0x00000200: "Compressed metrics",
|
|
}
|
|
|
|
def createFields(self):
|
|
yield Enum(UInt32(self, "type"), self.TYPE_NAME)
|
|
yield UInt32(self, "format")
|
|
yield filesizeHandler(UInt32(self, "size"))
|
|
yield UInt32(self, "offset")
|
|
|
|
def createDescription(self):
|
|
return "%s at %s (%s)" % (
|
|
self["type"].display, self["offset"].value, self["size"].display)
|
|
|
|
|
|
class PropertiesFormat(FieldSet):
|
|
static_size = 32
|
|
endian = LITTLE_ENDIAN
|
|
|
|
def createFields(self):
|
|
yield Bits(self, "reserved[]", 2)
|
|
yield Bit(self, "byte_big_endian")
|
|
yield Bit(self, "bit_big_endian")
|
|
yield Bits(self, "scan_unit", 2)
|
|
yield textHandler(PaddingBits(self, "reserved[]", 26), hexadecimal)
|
|
|
|
|
|
class Property(FieldSet):
|
|
|
|
def createFields(self):
|
|
yield UInt32(self, "name_offset")
|
|
yield UInt8(self, "is_string")
|
|
yield UInt32(self, "value_offset")
|
|
|
|
def createDescription(self):
|
|
# FIXME: Use link or any better way to read name value
|
|
name = self["../name[%s]" % (self.index - 2)].value
|
|
return "Property %s" % name
|
|
|
|
|
|
class GlyphNames(FieldSet):
|
|
|
|
def __init__(self, parent, name, toc, description, size=None):
|
|
FieldSet.__init__(self, parent, name, description, size=size)
|
|
self.toc = toc
|
|
if self["format/byte_big_endian"].value:
|
|
self.endian = BIG_ENDIAN
|
|
else:
|
|
self.endian = LITTLE_ENDIAN
|
|
|
|
def createFields(self):
|
|
yield PropertiesFormat(self, "format")
|
|
yield UInt32(self, "count")
|
|
offsets = []
|
|
for index in range(self["count"].value):
|
|
offset = UInt32(self, "offset[]")
|
|
yield offset
|
|
offsets.append(offset.value)
|
|
yield UInt32(self, "total_str_length")
|
|
offsets.sort()
|
|
offset0 = self.current_size // 8
|
|
for offset in offsets:
|
|
padding = self.seekByte(offset0 + offset)
|
|
if padding:
|
|
yield padding
|
|
yield CString(self, "name[]")
|
|
padding = (self.size - self.current_size) // 8
|
|
if padding:
|
|
yield NullBytes(self, "end_padding", padding)
|
|
|
|
|
|
class Properties(GlyphNames):
|
|
|
|
def createFields(self):
|
|
yield PropertiesFormat(self, "format")
|
|
yield UInt32(self, "nb_prop")
|
|
properties = []
|
|
for index in range(self["nb_prop"].value):
|
|
property = Property(self, "property[]")
|
|
yield property
|
|
properties.append(property)
|
|
padding = paddingSize(self.current_size // 8, 4)
|
|
if padding:
|
|
yield NullBytes(self, "padding", padding)
|
|
yield UInt32(self, "total_str_length")
|
|
properties.sort(key=lambda entry: entry["name_offset"].value)
|
|
offset0 = self.current_size // 8
|
|
for property in properties:
|
|
padding = self.seekByte(offset0 + property["name_offset"].value)
|
|
if padding:
|
|
yield padding
|
|
yield CString(self, "name[]", "Name of %s" % property.name)
|
|
if property["is_string"].value:
|
|
yield CString(self, "value[]", "Value of %s" % property.name)
|
|
padding = (self.size - self.current_size) // 8
|
|
if padding:
|
|
yield NullBytes(self, "end_padding", padding)
|
|
|
|
|
|
class PcfFile(Parser):
|
|
MAGIC = b"\1fcp"
|
|
PARSER_TAGS = {
|
|
"id": "pcf",
|
|
"category": "misc",
|
|
"file_ext": ("pcf",),
|
|
"magic": ((MAGIC, 0),),
|
|
"min_size": 32, # FIXME
|
|
"description": "X11 Portable Compiled Font (pcf)",
|
|
}
|
|
endian = LITTLE_ENDIAN
|
|
|
|
def validate(self):
|
|
if self["signature"].value != self.MAGIC:
|
|
return "Invalid signature"
|
|
return True
|
|
|
|
def createFields(self):
|
|
yield Bytes(self, "signature", 4, r'File signature ("\1pcf")')
|
|
yield UInt32(self, "nb_toc")
|
|
entries = []
|
|
for index in range(self["nb_toc"].value):
|
|
entry = TOC(self, "toc[]")
|
|
yield entry
|
|
entries.append(entry)
|
|
entries.sort(key=lambda entry: entry["offset"].value)
|
|
for entry in entries:
|
|
size = entry["size"].value
|
|
padding = self.seekByte(entry["offset"].value)
|
|
if padding:
|
|
yield padding
|
|
maxsize = (self.size - self.current_size) // 8
|
|
if maxsize < size:
|
|
self.warning("Truncate content of %s to %s bytes (was %s)" % (
|
|
entry.path, maxsize, size))
|
|
size = maxsize
|
|
if not size:
|
|
continue
|
|
if entry["type"].value == 1:
|
|
yield Properties(self, "properties", entry, "Properties", size=size * 8)
|
|
elif entry["type"].value == 128:
|
|
yield GlyphNames(self, "glyph_names", entry, "Glyph names", size=size * 8)
|
|
else:
|
|
yield RawBytes(self, "data[]", size, "Content of %s" % entry.path)
|