EOT ファイルについてよく知っている人はほとんどいないと思いますが (誰も知らないと思います)、この問題に光を当てるために、一般的なファイル形式について十分に知っているかもしれません。
Python で EOT フォントを問題なく読み取ることができますが、ヘッダー フィールドの一部を変更してチェックサムを再計算すると、結果のファイルが Internet Explorer によって拒否されます。
RootString フィールド用とフォント ファイル全体用の 2 つのチェックサムがあります。仕様はここにあります: 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
どんな助けでも大歓迎です。ありがとう