我希望你们中很少有人对 EOT 文件了解很多(我认为没有人知道),但您可能对一般文件格式有足够的了解,以便对这个问题有所了解。
我可以很好地读取 Python 中的 EOT 字体,但是在修改了一些标头字段并重新计算校验和后,Internet Explorer 拒绝了结果文件。
有两个校验和,一个用于 RootString 字段,另一个用于整个字体文件。该规范可以在这里找到:https ://www.w3.org/Submission/EOT/#Version22
当我解析一个 EOT 文件并重新计算它的 ChecksumAdjustment 时,我得到一个与文件中的数字不匹配的数字,但如果我用这个不同的校验和保存文件,它仍然有效。但是,如果我真的对字体进行任何修改,例如更改字体的名称,它将不再起作用。
如果校验和不是问题,我认为这可能与填充字段有关,我正在努力理解其目的。显然,填充是为了确保 ULONG(4 字节)对齐。某些标头字段具有自定义长度,因此当自定义长度字段破坏对齐时,可能会使用填充。但是第一个填充字段的目的是什么?它前面没有自定义长度字段,并且填充发生在 4 个字节的倍数处,那么为什么需要它?此外,我拥有的文件(我知道它有效)具有每个2 字节的填充字段,即使它破坏了对齐。
这是我解析文件的方式:
def __init__(self, file_path):
super().__init__(base.FontType.EOT)
self._file_name = os.path.basename(file_path)
with open(file_path, 'rb') as f:
self.eot_size = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.font_data_size = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.version = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.flags = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.font_panose = struct.unpack('<10s', f.read(struct.calcsize('<10s')))[0]
self.charset = struct.unpack('<s', f.read(struct.calcsize('<s')))[0]
self.italic = struct.unpack('<s', f.read(struct.calcsize('<s')))[0]
self.weight = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.fs_type = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.magic_number = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.unicode_range_1 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.unicode_range_2 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.unicode_range_3 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.unicode_range_4 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.code_page_range_1 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.code_page_range_2 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.checksum_adjustment = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.reserved1 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.reserved2 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.reserved3 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.reserved4 = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self._eat_padding(f)
self.family_name_size = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.family_name = f.read(self.family_name_size).decode('utf-16')
self._eat_padding(f)
self.style_name_size = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.style_name = f.read(self.style_name_size).decode('utf-16')
self._eat_padding(f)
self.version_name_size = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.version_name = f.read(self.version_name_size).decode('utf-16')
self._eat_padding(f)
self.full_name_size = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.full_name = f.read(self.full_name_size).decode('utf-16')
self._eat_padding(f)
self.root_string_size = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.root_string = f.read(self.root_string_size).decode('utf-16')
self.root_string_checksum = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.eudc_code_page = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self._eat_padding(f)
self.signature_size = struct.unpack('<H', f.read(struct.calcsize('<H')))[0]
self.signature = f.read(self.signature_size)
self.eudc_flags = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.eudc_font_size = struct.unpack('<L', f.read(struct.calcsize('<L')))[0]
self.eudc_font_data = f.read(self.eudc_font_size)
self.font_data = f.read(self.font_data_size)
_assert_equal(self.family_name_size, len(self.family_name.encode('utf-16-le')))
_assert_equal(self.style_name_size, len(self.style_name.encode('utf-16-le')))
_assert_equal(self.version_name_size, len(self.version_name.encode('utf-16-le')))
_assert_equal(self.full_name_size, len(self.full_name.encode('utf-16-le')))
_assert_equal(self.root_string_size, len(self.root_string.encode('utf-16-le')))
def _eat_padding(self, f):
L = struct.calcsize('<L')
H = struct.calcsize('<H')
# f.read(H)
# return
excess = f.tell() % L
if excess != 0:
assert excess % H == 0, 'Font not USHORT aligned'
bytes = f.read(L - excess)
assert bytes == b'\x00' * (L - excess), 'Found non-zero padding bytes'
这是我导出文件并计算校验和的方法:
def dump(self, f):
self._compute_root_string_checksum()
buf = io.BytesIO()
buf.write(struct.pack('<L', self.eot_size))
buf.write(struct.pack('<L', self.font_data_size))
buf.write(struct.pack('<L', self.version))
buf.write(struct.pack('<L', self.flags))
buf.write(struct.pack('<10s', self.font_panose))
buf.write(struct.pack('<s', self.charset))
buf.write(struct.pack('<s', self.italic))
buf.write(struct.pack('<L', self.weight))
buf.write(struct.pack('<H', self.fs_type))
buf.write(struct.pack('<H', self.magic_number))
buf.write(struct.pack('<L', self.unicode_range_1))
buf.write(struct.pack('<L', self.unicode_range_2))
buf.write(struct.pack('<L', self.unicode_range_3))
buf.write(struct.pack('<L', self.unicode_range_4))
buf.write(struct.pack('<L', self.code_page_range_1))
buf.write(struct.pack('<L', self.code_page_range_2))
checksum_adj_pos = buf.tell()
buf.write(struct.pack('<L', 0))
buf.write(struct.pack('<L', self.reserved1))
buf.write(struct.pack('<L', self.reserved2))
buf.write(struct.pack('<L', self.reserved3))
buf.write(struct.pack('<L', self.reserved4))
self._add_padding(buf)
buf.write(struct.pack('<H', self.family_name_size))
buf.write(self.family_name.encode('utf-16-le'))
self._add_padding(buf)
buf.write(struct.pack('<H', self.style_name_size))
buf.write(self.style_name.encode('utf-16-le'))
self._add_padding(buf)
buf.write(struct.pack('<H', self.version_name_size))
buf.write(self.version_name.encode('utf-16-le'))
self._add_padding(buf)
buf.write(struct.pack('<H', self.full_name_size))
buf.write(self.full_name.encode('utf-16-le'))
self._add_padding(buf)
buf.write(struct.pack('<H', self.root_string_size))
buf.write(self.root_string.encode('utf-16-le'))
buf.write(struct.pack('<L', self.root_string_checksum))
buf.write(struct.pack('<L', self.eudc_code_page))
self._add_padding(buf)
buf.write(struct.pack('<H', self.signature_size))
buf.write(self.signature)
buf.write(struct.pack('<L', self.eudc_flags))
buf.write(struct.pack('<L', self.eudc_font_size))
buf.write(self.eudc_font_data)
buf.write(self.font_data)
total = 0
buf.seek(0)
while True:
bytes = buf.read(4)
if len(bytes) == 0:
break
assert len(bytes) % 4 == 0, 'Font not padded correctly'
ulong = struct.unpack('<L', bytes)[0]
total = (total + ulong) & 0xffffffff
self.checksum_adjustment = (0xB1B0AFBA - total) & 0xffffffff
buf.seek(checksum_adj_pos)
buf.write(struct.pack('<L', self.checksum_adjustment))
buf.seek(0)
while True:
bytes = buf.read(1)
if len(bytes) == 0:
break
f.write(bytes)
def _compute_root_string_checksum(self):
root_string = self.root_string.encode('utf-16-le')
buf = io.BytesIO(root_string)
total = 0
while True:
bytes = buf.read(4)
if len(bytes) == 0:
break
assert len(bytes) % 4 == 0, 'Root String not padded correctly'
ulong = struct.unpack('<L', bytes)[0]
total = (total + ulong) & 0xffffffff
self.root_string_checksum = total ^ 0x50475342
任何帮助深表感谢。谢谢