mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement update for sfnt metrics tables
Also fix reading of bearings for fonts that have fewer advances than glyphs
This commit is contained in:
parent
01666d4fff
commit
4cbf6ab7fd
@ -6,6 +6,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import array
|
||||||
from struct import unpack_from, pack, calcsize
|
from struct import unpack_from, pack, calcsize
|
||||||
|
|
||||||
from calibre.utils.fonts.sfnt import UnknownTable, DateTimeProperty, FixedProperty
|
from calibre.utils.fonts.sfnt import UnknownTable, DateTimeProperty, FixedProperty
|
||||||
@ -55,93 +56,132 @@ class HeadTable(UnknownTable):
|
|||||||
self.raw = pack(self._fmt, *vals)
|
self.raw = pack(self._fmt, *vals)
|
||||||
|
|
||||||
|
|
||||||
|
def read_metrics(raw, num_of_metrics, num_of_glyphs, table_name):
|
||||||
|
rawsz = 4 * num_of_metrics
|
||||||
|
if len(raw) < rawsz:
|
||||||
|
raise UnsupportedFont(f'The {table_name} table has insufficient data')
|
||||||
|
long_hor_metric = raw[:rawsz]
|
||||||
|
a = read_array(long_hor_metric)
|
||||||
|
advances = a[0::2]
|
||||||
|
a = read_array(long_hor_metric, 'h')
|
||||||
|
bearings = a[1::2]
|
||||||
|
if num_of_glyphs > num_of_metrics:
|
||||||
|
extra = num_of_glyphs - num_of_metrics
|
||||||
|
raw = raw[rawsz:]
|
||||||
|
rawsz = 2 * extra
|
||||||
|
if len(raw) < rawsz:
|
||||||
|
raise UnsupportedFont(f'The {table_name} table has insufficient data for trailing bearings')
|
||||||
|
bearings += read_array(raw, 'h')
|
||||||
|
return advances, bearings
|
||||||
|
|
||||||
|
|
||||||
|
def update_metrics_table(metrics_map, mtx_table):
|
||||||
|
recs, aw, b = [], array.array('H'), array.array('h')
|
||||||
|
for glyph_id in sorted(metrics_map):
|
||||||
|
adv, bearing = metrics_map[glyph_id]
|
||||||
|
aw.append(adv)
|
||||||
|
b.append(bearing)
|
||||||
|
recs.append(pack('>Hh', adv, bearing))
|
||||||
|
mtx_table.raw = b''.join(recs)
|
||||||
|
return aw, b
|
||||||
|
|
||||||
|
|
||||||
class HorizontalHeader(UnknownTable):
|
class HorizontalHeader(UnknownTable):
|
||||||
|
|
||||||
version_number = FixedProperty('_version_number')
|
version_number = FixedProperty('_version_number')
|
||||||
|
field_types = (
|
||||||
|
'_version_number' , 'l',
|
||||||
|
'ascender', 'h',
|
||||||
|
'descender', 'h',
|
||||||
|
'line_gap', 'h',
|
||||||
|
'advance_width_max', 'H',
|
||||||
|
'min_left_side_bearing', 'h',
|
||||||
|
'min_right_side_bearing', 'h',
|
||||||
|
'x_max_extent', 'h',
|
||||||
|
'caret_slope_rise', 'h',
|
||||||
|
'caret_slop_run', 'h',
|
||||||
|
'caret_offset', 'h',
|
||||||
|
'r1', 'h',
|
||||||
|
'r2', 'h',
|
||||||
|
'r3', 'h',
|
||||||
|
'r4', 'h',
|
||||||
|
'metric_data_format', 'h',
|
||||||
|
'number_of_h_metrics', 'H',
|
||||||
|
)
|
||||||
|
|
||||||
def read_data(self, hmtx):
|
def read_data(self, hmtx, num_glyphs):
|
||||||
if hasattr(self, 'ascender'):
|
self._fmt = ('>%s'%(''.join(self.field_types[1::2]))).encode('ascii')
|
||||||
return
|
self._fields = self.field_types[0::2]
|
||||||
field_types = (
|
|
||||||
'_version_number' , 'l',
|
|
||||||
'ascender', 'h',
|
|
||||||
'descender', 'h',
|
|
||||||
'line_gap', 'h',
|
|
||||||
'advance_width_max', 'H',
|
|
||||||
'min_left_side_bearing', 'h',
|
|
||||||
'min_right_side_bearing', 'h',
|
|
||||||
'x_max_extent', 'h',
|
|
||||||
'caret_slope_rise', 'h',
|
|
||||||
'caret_slop_run', 'h',
|
|
||||||
'caret_offset', 'h',
|
|
||||||
'r1', 'h',
|
|
||||||
'r2', 'h',
|
|
||||||
'r3', 'h',
|
|
||||||
'r4', 'h',
|
|
||||||
'metric_data_format', 'h',
|
|
||||||
'number_of_h_metrics', 'H',
|
|
||||||
)
|
|
||||||
|
|
||||||
self._fmt = ('>%s'%(''.join(field_types[1::2]))).encode('ascii')
|
|
||||||
self._fields = field_types[0::2]
|
|
||||||
|
|
||||||
for f, val in zip(self._fields, unpack_from(self._fmt, self.raw)):
|
for f, val in zip(self._fields, unpack_from(self._fmt, self.raw)):
|
||||||
setattr(self, f, val)
|
setattr(self, f, val)
|
||||||
|
|
||||||
raw = hmtx.raw
|
self.advance_widths, self.left_side_bearings = read_metrics(hmtx.raw, self.number_of_h_metrics, num_glyphs, 'hmtx')
|
||||||
num = self.number_of_h_metrics
|
|
||||||
if len(raw) < 4*num:
|
def metrics_for(self, glyph_id):
|
||||||
raise UnsupportedFont('The hmtx table has insufficient data')
|
lsb = self.left_side_bearings[glyph_id]
|
||||||
long_hor_metric = raw[:4*num]
|
if glyph_id >= len(self.advance_widths):
|
||||||
a = read_array(long_hor_metric)
|
glyph_id = -1
|
||||||
self.advance_widths = a[0::2]
|
return self.advance_widths[glyph_id], lsb
|
||||||
a = read_array(long_hor_metric, 'h')
|
|
||||||
self.left_side_bearings = a[1::2]
|
def update(self, metrics_map, mtx_table):
|
||||||
|
aw, b = update_metrics_table(metrics_map, mtx_table)
|
||||||
|
self.advance_widths = aw
|
||||||
|
self.left_side_bearings = b
|
||||||
|
self.number_of_h_metrics = len(metrics_map)
|
||||||
|
self.advance_width_max = max(aw or (0,))
|
||||||
|
self.min_left_side_bearing = min(b or (0,))
|
||||||
|
data = (getattr(self, x) for x in self._fields)
|
||||||
|
self.raw = pack('>' + ''.join(self.field_types[1::2]), *data)
|
||||||
|
|
||||||
|
|
||||||
class VerticalHeader(UnknownTable):
|
class VerticalHeader(UnknownTable):
|
||||||
|
|
||||||
version_number = FixedProperty('_version_number')
|
version_number = FixedProperty('_version_number')
|
||||||
|
field_types = (
|
||||||
|
'_version_number' , 'l',
|
||||||
|
'ascender', 'h',
|
||||||
|
'descender', 'h',
|
||||||
|
'line_gap', 'h',
|
||||||
|
'advance_height_max', 'H',
|
||||||
|
'min_top_side_bearing', 'h',
|
||||||
|
'min_bottom_side_bearing', 'h',
|
||||||
|
'y_max_extent', 'h',
|
||||||
|
'caret_slope_rise', 'h',
|
||||||
|
'caret_slop_run', 'h',
|
||||||
|
'caret_offset', 'h',
|
||||||
|
'r1', 'h',
|
||||||
|
'r2', 'h',
|
||||||
|
'r3', 'h',
|
||||||
|
'r4', 'h',
|
||||||
|
'metric_data_format', 'h',
|
||||||
|
'number_of_v_metrics', 'H',
|
||||||
|
)
|
||||||
|
|
||||||
def read_data(self, vmtx):
|
def read_data(self, vmtx, num_glyphs):
|
||||||
if hasattr(self, 'ascender'):
|
self._fmt = ('>%s'%(''.join(self.field_types[1::2]))).encode('ascii')
|
||||||
return
|
self._fields = self.field_types[0::2]
|
||||||
field_types = (
|
|
||||||
'_version_number' , 'l',
|
|
||||||
'ascender', 'h',
|
|
||||||
'descender', 'h',
|
|
||||||
'line_gap', 'h',
|
|
||||||
'advance_height_max', 'H',
|
|
||||||
'min_top_side_bearing', 'h',
|
|
||||||
'min_bottom_side_bearing', 'h',
|
|
||||||
'y_max_extent', 'h',
|
|
||||||
'caret_slope_rise', 'h',
|
|
||||||
'caret_slop_run', 'h',
|
|
||||||
'caret_offset', 'h',
|
|
||||||
'r1', 'h',
|
|
||||||
'r2', 'h',
|
|
||||||
'r3', 'h',
|
|
||||||
'r4', 'h',
|
|
||||||
'metric_data_format', 'h',
|
|
||||||
'number_of_v_metrics', 'H',
|
|
||||||
)
|
|
||||||
|
|
||||||
self._fmt = ('>%s'%(''.join(field_types[1::2]))).encode('ascii')
|
|
||||||
self._fields = field_types[0::2]
|
|
||||||
|
|
||||||
for f, val in zip(self._fields, unpack_from(self._fmt, self.raw)):
|
for f, val in zip(self._fields, unpack_from(self._fmt, self.raw)):
|
||||||
setattr(self, f, val)
|
setattr(self, f, val)
|
||||||
|
|
||||||
raw = vmtx.raw
|
self.advance_heights, self.top_side_bearings = read_metrics(vmtx.raw, self.number_of_v_metrics, num_glyphs, 'vmtx')
|
||||||
num = self.number_of_v_metrics
|
|
||||||
if len(raw) < 4*num:
|
def metrics_for(self, glyph_id):
|
||||||
raise UnsupportedFont('The vmtx table has insufficient data')
|
tsb = self.top_side_bearings[glyph_id]
|
||||||
long_hor_metric = raw[:4*num]
|
if glyph_id >= len(self.advance_heights):
|
||||||
long_hor_metric = raw[:4*num]
|
glyph_id = -1
|
||||||
a = read_array(long_hor_metric)
|
return self.advance_heights[glyph_id], tsb
|
||||||
self.advance_heights = a[0::2]
|
|
||||||
a = read_array(long_hor_metric, 'h')
|
def update(self, metrics_map, mtx_table):
|
||||||
self.top_side_bearings = a[1::2]
|
aw, b = update_metrics_table(metrics_map, mtx_table)
|
||||||
|
self.advance_heights = aw
|
||||||
|
self.top_side_bearings = b
|
||||||
|
self.number_of_v_metrics = len(metrics_map)
|
||||||
|
self.advance_height_max = max(aw or (0,))
|
||||||
|
self.min_top_side_bearing = min(b or (0,))
|
||||||
|
data = (getattr(self, x) for x in self._fields)
|
||||||
|
self.raw = pack('>' + ''.join(self.field_types[1::2]), *data)
|
||||||
|
|
||||||
|
|
||||||
class OS2Table(UnknownTable):
|
class OS2Table(UnknownTable):
|
||||||
|
@ -21,14 +21,14 @@ class FontMetrics(object):
|
|||||||
|
|
||||||
def __init__(self, sfnt):
|
def __init__(self, sfnt):
|
||||||
for table in (b'head', b'hhea', b'hmtx', b'cmap', b'OS/2', b'post',
|
for table in (b'head', b'hhea', b'hmtx', b'cmap', b'OS/2', b'post',
|
||||||
b'name'):
|
b'name', b'maxp'):
|
||||||
if table not in sfnt:
|
if table not in sfnt:
|
||||||
raise UnsupportedFont('This font has no %s table'%table)
|
raise UnsupportedFont('This font has no %s table'%table)
|
||||||
self.sfnt = sfnt
|
self.sfnt = sfnt
|
||||||
|
|
||||||
self.head = self.sfnt[b'head']
|
self.head = self.sfnt[b'head']
|
||||||
hhea = self.sfnt[b'hhea']
|
hhea = self.sfnt[b'hhea']
|
||||||
hhea.read_data(self.sfnt[b'hmtx'])
|
hhea.read_data(self.sfnt[b'hmtx'], self.sfnt[b'maxp'].num_glyphs)
|
||||||
self.ascent = hhea.ascender
|
self.ascent = hhea.ascender
|
||||||
self.descent = hhea.descender
|
self.descent = hhea.descender
|
||||||
self.bbox = (self.head.x_min, self.head.y_min, self.head.x_max,
|
self.bbox = (self.head.x_min, self.head.y_min, self.head.x_max,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user