mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add code to get metrics from fonts, convert QRawFont to an Sfnt and fix curve drawing
This commit is contained in:
parent
391a58f9e9
commit
5e9a943cc0
@ -10,15 +10,16 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import sys, traceback
|
import sys, traceback
|
||||||
from math import sqrt
|
from math import sqrt
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from future_builtins import map
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
|
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
|
||||||
QTransform, QPainterPath, QFontMetricsF)
|
QTransform, QPainterPath, QRawFont)
|
||||||
|
|
||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG
|
||||||
from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path, Text)
|
from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path, Text)
|
||||||
from calibre.ebooks.pdf.render.common import inch, A4
|
from calibre.ebooks.pdf.render.common import inch, A4
|
||||||
|
from calibre.utils.fonts.sfnt.container import Sfnt
|
||||||
|
from calibre.utils.fonts.sfnt.metrics import FontMetrics
|
||||||
|
|
||||||
XDPI = 1200
|
XDPI = 1200
|
||||||
YDPI = 1200
|
YDPI = 1200
|
||||||
@ -309,11 +310,16 @@ class PdfEngine(QPaintEngine):
|
|||||||
elif elem.isLineTo():
|
elif elem.isLineTo():
|
||||||
p.line_to(*em)
|
p.line_to(*em)
|
||||||
elif elem.isCurveTo():
|
elif elem.isCurveTo():
|
||||||
|
added = False
|
||||||
if path.elementCount() > i+1:
|
if path.elementCount() > i+1:
|
||||||
c1, c2 = map(lambda j:(
|
c1, c2 = path.elementAt(i), path.elementAt(i+1)
|
||||||
path.elementAt(j).x, path.elementAt(j).y), (i, i+1))
|
if (c1.type == path.CurveToDataElement and c2.type ==
|
||||||
|
path.CurveToDataElement):
|
||||||
i += 2
|
i += 2
|
||||||
p.curve_to(*(c1 + c2 + em))
|
p.curve_to(em[0], em[1], c1.x, c1.y, c2.x, c2.y)
|
||||||
|
added = True
|
||||||
|
if not added:
|
||||||
|
raise ValueError('Invalid curve to operation')
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@store_error
|
@store_error
|
||||||
@ -355,15 +361,8 @@ class PdfEngine(QPaintEngine):
|
|||||||
else:
|
else:
|
||||||
sz = px
|
sz = px
|
||||||
|
|
||||||
q = self.qt_system
|
r = QRawFont.fromFont(f)
|
||||||
if not q.isIdentity() and q.type() > q.TxShear:
|
metrics = FontMetrics(Sfnt(r))
|
||||||
# We cant map this transform to a PDF text transform operator
|
|
||||||
f, s = self.do_fill, self.do_stroke
|
|
||||||
self.do_fill, self.do_stroke = True, False
|
|
||||||
super(PdfEngine, self).drawTextItem(point, text_item)
|
|
||||||
self.do_fill, self.do_stroke = f, s
|
|
||||||
return
|
|
||||||
|
|
||||||
to = Text()
|
to = Text()
|
||||||
to.size = sz
|
to.size = sz
|
||||||
to.set_transform(1, 0, 0, -1, point.x(), point.y())
|
to.set_transform(1, 0, 0, -1, point.x(), point.y())
|
||||||
@ -375,43 +374,24 @@ class PdfEngine(QPaintEngine):
|
|||||||
to.word_spacing = ws
|
to.word_spacing = ws
|
||||||
spacing = f.letterSpacing()
|
spacing = f.letterSpacing()
|
||||||
st = f.letterSpacingType()
|
st = f.letterSpacingType()
|
||||||
|
text = type(u'')(text_item.text())
|
||||||
if st == f.AbsoluteSpacing and spacing != 0:
|
if st == f.AbsoluteSpacing and spacing != 0:
|
||||||
to.char_space = spacing/self.scale
|
to.char_space = spacing/self.scale
|
||||||
if st == f.PercentageSpacing and spacing not in {100, 0}:
|
if st == f.PercentageSpacing and spacing not in {100, 0}:
|
||||||
# TODO: Implement this with the TJ operator
|
# TODO: Figure out why the results from uncommenting the super
|
||||||
avg_char_width = QFontMetricsF(f).averageCharWidth()
|
# class call above differ. The advance widths are the same as those
|
||||||
to.char_space = (spacing - 100) * avg_char_width / 100
|
# reported by QRawfont, so presumably, Qt use some other
|
||||||
text = type(u'')(text_item.text())
|
# algorithm, I can't be bothered to track it down. This behavior is
|
||||||
|
# correct as per the Qt docs' description of PercentageSpacing
|
||||||
|
widths = [w*-1 for w in metrics.advance_widths(text,
|
||||||
|
sz, f.stretch()/100.)]
|
||||||
|
to.glyph_adjust = ((spacing-100)/100., widths)
|
||||||
to.text = text
|
to.text = text
|
||||||
with self:
|
with self:
|
||||||
self.graphics_state.apply_fill(self.graphics_state.current_state['stroke'],
|
self.graphics_state.apply_fill(self.graphics_state.current_state['stroke'],
|
||||||
self, self.pdf)
|
self, self.pdf)
|
||||||
self.pdf.draw_text(to)
|
self.pdf.draw_text(to)
|
||||||
|
|
||||||
def draw_line(kind='underline'):
|
|
||||||
m = QFontMetricsF(f)
|
|
||||||
tw = m.width(text)
|
|
||||||
p = Path()
|
|
||||||
if kind == 'underline':
|
|
||||||
dy = m.underlinePos()
|
|
||||||
elif kind == 'overline':
|
|
||||||
dy = -m.overlinePos()
|
|
||||||
elif kind == 'strikeout':
|
|
||||||
dy = -m.strikeOutPos()
|
|
||||||
p.move_to(point.x(), point.y()+dy)
|
|
||||||
p.line_to(point.x()+tw, point.y()+dy)
|
|
||||||
with self:
|
|
||||||
self.graphics_state.apply_line_width(m.lineWidth(),
|
|
||||||
self, self.pdf)
|
|
||||||
self.pdf.draw_path(p, stroke=True, fill=False)
|
|
||||||
|
|
||||||
if f.underline():
|
|
||||||
draw_line()
|
|
||||||
if f.overline():
|
|
||||||
draw_line('overline')
|
|
||||||
if f.strikeOut():
|
|
||||||
draw_line('strikeout')
|
|
||||||
|
|
||||||
@store_error
|
@store_error
|
||||||
def drawPolygon(self, points, mode):
|
def drawPolygon(self, points, mode):
|
||||||
if not points: return
|
if not points: return
|
||||||
@ -419,8 +399,7 @@ class PdfEngine(QPaintEngine):
|
|||||||
p.move_to(points[0].x(), points[0].y())
|
p.move_to(points[0].x(), points[0].y())
|
||||||
for point in points[1:]:
|
for point in points[1:]:
|
||||||
p.line_to(point.x(), point.y())
|
p.line_to(point.x(), point.y())
|
||||||
if points[-1] != points[0]:
|
p.close()
|
||||||
p.line_to(points[0].x(), points[0].y())
|
|
||||||
fill_rule = {self.OddEvenMode:'evenodd',
|
fill_rule = {self.OddEvenMode:'evenodd',
|
||||||
self.WindingMode:'winding'}.get(mode, 'evenodd')
|
self.WindingMode:'winding'}.get(mode, 'evenodd')
|
||||||
self.pdf.draw_path(p, stroke=True, fill_rule=fill_rule,
|
self.pdf.draw_path(p, stroke=True, fill_rule=fill_rule,
|
||||||
@ -504,13 +483,16 @@ if __name__ == '__main__':
|
|||||||
p.restore()
|
p.restore()
|
||||||
|
|
||||||
f = p.font()
|
f = p.font()
|
||||||
f.setPointSize(24)
|
f.setPointSize(48)
|
||||||
f.setUnderline(True)
|
f.setLetterSpacing(f.PercentageSpacing, 200)
|
||||||
|
# f.setUnderline(True)
|
||||||
|
# f.setOverline(True)
|
||||||
|
# f.setStrikeOut(True)
|
||||||
f.setFamily('Times New Roman')
|
f.setFamily('Times New Roman')
|
||||||
p.setFont(f)
|
p.setFont(f)
|
||||||
# p.scale(2, 2)
|
# p.scale(2, 2)
|
||||||
p.rotate(45)
|
# p.rotate(45)
|
||||||
p.setPen(QColor(0, 255, 0))
|
p.setPen(QColor(0, 0, 255))
|
||||||
p.drawText(QPoint(100, 300), 'Some text')
|
p.drawText(QPoint(100, 300), 'Some text')
|
||||||
finally:
|
finally:
|
||||||
p.end()
|
p.end()
|
||||||
|
@ -9,6 +9,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from future_builtins import map
|
from future_builtins import map
|
||||||
|
from itertools import izip
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from calibre.constants import (__appname__, __version__)
|
from calibre.constants import (__appname__, __version__)
|
||||||
@ -137,6 +138,9 @@ class Path(object):
|
|||||||
def curve_to(self, x1, y1, x2, y2, x, y):
|
def curve_to(self, x1, y1, x2, y2, x, y):
|
||||||
self.ops.append((x1, y1, x2, y2, x, y, 'c'))
|
self.ops.append((x1, y1, x2, y2, x, y, 'c'))
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.ops.append(('h',))
|
||||||
|
|
||||||
class Text(object):
|
class Text(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -146,6 +150,7 @@ class Text(object):
|
|||||||
self.horizontal_scale = self.default_horizontal_scale = 100
|
self.horizontal_scale = self.default_horizontal_scale = 100
|
||||||
self.word_spacing = self.default_word_spacing = 0
|
self.word_spacing = self.default_word_spacing = 0
|
||||||
self.char_space = self.default_char_space = 0
|
self.char_space = self.default_char_space = 0
|
||||||
|
self.glyph_adjust = self.default_glyph_adjust = None
|
||||||
self.size = 12
|
self.size = 12
|
||||||
self.text = ''
|
self.text = ''
|
||||||
|
|
||||||
@ -170,8 +175,17 @@ class Text(object):
|
|||||||
if self.char_space != self.default_char_space:
|
if self.char_space != self.default_char_space:
|
||||||
stream.write('%g Tc '%self.char_space)
|
stream.write('%g Tc '%self.char_space)
|
||||||
stream.write_line()
|
stream.write_line()
|
||||||
|
if self.glyph_adjust is self.default_glyph_adjust:
|
||||||
serialize(String(self.text), stream)
|
serialize(String(self.text), stream)
|
||||||
stream.write(' Tj ')
|
stream.write(' Tj ')
|
||||||
|
else:
|
||||||
|
chars = Array()
|
||||||
|
frac, widths = self.glyph_adjust
|
||||||
|
for c, width in izip(self.text, widths):
|
||||||
|
chars.append(String(c))
|
||||||
|
chars.append(int(width * frac))
|
||||||
|
serialize(chars, stream)
|
||||||
|
stream.write(' TJ ')
|
||||||
stream.write_line('ET')
|
stream.write_line('ET')
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ from calibre.utils.fonts.utils import (get_tables, checksum_of_block,
|
|||||||
from calibre.utils.fonts.sfnt import align_block, UnknownTable, max_power_of_two
|
from calibre.utils.fonts.sfnt import align_block, UnknownTable, max_power_of_two
|
||||||
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
|
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
|
||||||
|
|
||||||
from calibre.utils.fonts.sfnt.head import HeadTable
|
from calibre.utils.fonts.sfnt.head import (HeadTable, HorizontalHeader,
|
||||||
|
OS2Table, PostTable)
|
||||||
from calibre.utils.fonts.sfnt.maxp import MaxpTable
|
from calibre.utils.fonts.sfnt.maxp import MaxpTable
|
||||||
from calibre.utils.fonts.sfnt.loca import LocaTable
|
from calibre.utils.fonts.sfnt.loca import LocaTable
|
||||||
from calibre.utils.fonts.sfnt.glyf import GlyfTable
|
from calibre.utils.fonts.sfnt.glyf import GlyfTable
|
||||||
@ -29,18 +30,9 @@ from calibre.utils.fonts.sfnt.cff.table import CFFTable
|
|||||||
|
|
||||||
class Sfnt(object):
|
class Sfnt(object):
|
||||||
|
|
||||||
def __init__(self, raw):
|
TABLE_MAP = {
|
||||||
self.sfnt_version = raw[:4]
|
|
||||||
if self.sfnt_version not in {b'\x00\x01\x00\x00', b'OTTO', b'true',
|
|
||||||
b'type1'}:
|
|
||||||
raise UnsupportedFont('Font has unknown sfnt version: %r'%self.sfnt_version)
|
|
||||||
self.read_tables(raw)
|
|
||||||
|
|
||||||
def read_tables(self, raw):
|
|
||||||
self.tables = {}
|
|
||||||
for table_tag, table, table_index, table_offset, table_checksum in get_tables(raw):
|
|
||||||
self.tables[table_tag] = {
|
|
||||||
b'head' : HeadTable,
|
b'head' : HeadTable,
|
||||||
|
b'hhea' : HorizontalHeader,
|
||||||
b'maxp' : MaxpTable,
|
b'maxp' : MaxpTable,
|
||||||
b'loca' : LocaTable,
|
b'loca' : LocaTable,
|
||||||
b'glyf' : GlyfTable,
|
b'glyf' : GlyfTable,
|
||||||
@ -48,7 +40,32 @@ class Sfnt(object):
|
|||||||
b'CFF ' : CFFTable,
|
b'CFF ' : CFFTable,
|
||||||
b'kern' : KernTable,
|
b'kern' : KernTable,
|
||||||
b'GSUB' : GSUBTable,
|
b'GSUB' : GSUBTable,
|
||||||
}.get(table_tag, UnknownTable)(table)
|
b'OS/2' : OS2Table,
|
||||||
|
b'post' : PostTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, raw_or_qrawfont):
|
||||||
|
self.tables = {}
|
||||||
|
if isinstance(raw_or_qrawfont, bytes):
|
||||||
|
raw = raw_or_qrawfont
|
||||||
|
self.sfnt_version = raw[:4]
|
||||||
|
if self.sfnt_version not in {b'\x00\x01\x00\x00', b'OTTO', b'true',
|
||||||
|
b'type1'}:
|
||||||
|
raise UnsupportedFont('Font has unknown sfnt version: %r'%self.sfnt_version)
|
||||||
|
for table_tag, table, table_index, table_offset, table_checksum in get_tables(raw):
|
||||||
|
self.tables[table_tag] = self.TABLE_MAP.get(
|
||||||
|
table_tag, UnknownTable)(table)
|
||||||
|
else:
|
||||||
|
for table_tag in {
|
||||||
|
b'cmap', b'hhea', b'head', b'hmtx', b'maxp', b'name', b'OS/2',
|
||||||
|
b'post', b'cvt ', b'fpgm', b'glyf', b'loca', b'prep', b'CFF ',
|
||||||
|
b'VORG', b'EBDT', b'EBLC', b'EBSC', b'BASE', b'GSUB', b'GPOS',
|
||||||
|
b'GDEF', b'JSTF', b'gasp', b'hdmx', b'kern', b'LTSH', b'PCLT',
|
||||||
|
b'VDMX', b'vhea', b'vmtx', b'MATH'}:
|
||||||
|
table = bytes(raw_or_qrawfont.fontTable(table_tag))
|
||||||
|
if table:
|
||||||
|
self.tables[table_tag] = self.TABLE_MAP.get(
|
||||||
|
table_tag, UnknownTable)(table)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return self.tables[key]
|
return self.tables[key]
|
||||||
@ -140,7 +157,8 @@ def test_roundtrip(ff=None):
|
|||||||
if data[:12] != rd[:12]:
|
if data[:12] != rd[:12]:
|
||||||
raise ValueError('Roundtripping failed, font header not the same')
|
raise ValueError('Roundtripping failed, font header not the same')
|
||||||
if len(data) != len(rd):
|
if len(data) != len(rd):
|
||||||
raise ValueError('Roundtripping failed, size different')
|
raise ValueError('Roundtripping failed, size different (%d vs. %d)'%
|
||||||
|
(len(data), len(rd)))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
|
@ -11,6 +11,7 @@ from itertools import izip
|
|||||||
from struct import unpack_from, pack
|
from struct import unpack_from, pack
|
||||||
|
|
||||||
from calibre.utils.fonts.sfnt import UnknownTable, DateTimeProperty, FixedProperty
|
from calibre.utils.fonts.sfnt import UnknownTable, DateTimeProperty, FixedProperty
|
||||||
|
from calibre.utils.fonts.sfnt.errors import UnsupportedFont
|
||||||
|
|
||||||
class HeadTable(UnknownTable):
|
class HeadTable(UnknownTable):
|
||||||
|
|
||||||
@ -52,4 +53,75 @@ class HeadTable(UnknownTable):
|
|||||||
vals = [getattr(self, f) for f in self._fields]
|
vals = [getattr(self, f) for f in self._fields]
|
||||||
self.raw = pack(self._fmt, *vals)
|
self.raw = pack(self._fmt, *vals)
|
||||||
|
|
||||||
|
class HorizontalHeader(UnknownTable):
|
||||||
|
|
||||||
|
version_number = FixedProperty('_version_number')
|
||||||
|
|
||||||
|
def read_data(self, hmtx):
|
||||||
|
if hasattr(self, 'ascender'): return
|
||||||
|
field_types = (
|
||||||
|
'_version_number' , 'l',
|
||||||
|
'ascender', 'h',
|
||||||
|
'descender', 'h',
|
||||||
|
'line_gap', 'h',
|
||||||
|
'advance_width_max', 'H',
|
||||||
|
'min_left_size_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 izip(self._fields, unpack_from(self._fmt, self.raw)):
|
||||||
|
setattr(self, f, val)
|
||||||
|
|
||||||
|
raw = hmtx.raw
|
||||||
|
num = self.number_of_h_metrics
|
||||||
|
if len(raw) < 4*num:
|
||||||
|
raise UnsupportedFont('The hmtx table has insufficient data')
|
||||||
|
long_hor_metric = raw[:4*num]
|
||||||
|
fmt = '>%dH'%(2*num)
|
||||||
|
entries = unpack_from(fmt.encode('ascii'), long_hor_metric)
|
||||||
|
self.advance_widths = entries[0::2]
|
||||||
|
fmt = '>%dh'%(2*num)
|
||||||
|
entries = unpack_from(fmt.encode('ascii'), long_hor_metric)
|
||||||
|
self.left_side_bearings = entries[1::2]
|
||||||
|
|
||||||
|
class OS2Table(UnknownTable):
|
||||||
|
|
||||||
|
version_number = FixedProperty('_version')
|
||||||
|
|
||||||
|
def read_data(self):
|
||||||
|
if hasattr(self, 'char_width'): return
|
||||||
|
from calibre.utils.fonts.utils import get_font_characteristics
|
||||||
|
vals = get_font_characteristics(self.raw, raw_is_table=True,
|
||||||
|
return_all=True)
|
||||||
|
for i, attr in enumerate((
|
||||||
|
'_version', 'char_width', 'weight', 'width', 'fs_type',
|
||||||
|
'subscript_x_size', 'subscript_y_size', 'subscript_x_offset',
|
||||||
|
'subscript_y_offset', 'superscript_x_size', 'superscript_y_size',
|
||||||
|
'superscript_x_offset', 'superscript_y_offset', 'strikeout_size',
|
||||||
|
'strikeout_position', 'family_class', 'panose', 'selection',
|
||||||
|
'is_italic', 'is_bold', 'is_regular')):
|
||||||
|
setattr(self, attr, vals[i])
|
||||||
|
|
||||||
|
class PostTable(UnknownTable):
|
||||||
|
|
||||||
|
version_number = FixedProperty('_version')
|
||||||
|
italic_angle = FixedProperty('_italic_angle')
|
||||||
|
|
||||||
|
def read_data(self):
|
||||||
|
if hasattr(self, 'underline_position'): return
|
||||||
|
(self._version, self._italic_angle, self.underline_position,
|
||||||
|
self.underline_thickness) = unpack_from(b'>llhh', self.raw)
|
||||||
|
|
||||||
|
86
src/calibre/utils/fonts/sfnt/metrics.py
Normal file
86
src/calibre/utils/fonts/sfnt/metrics.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from future_builtins import map
|
||||||
|
|
||||||
|
class FontMetrics(object):
|
||||||
|
|
||||||
|
'''
|
||||||
|
Get various metrics for the specified sfnt. All the metrics are returned in
|
||||||
|
units of pixels. To calculate a metric you have to specify the font size
|
||||||
|
(in pixels) and the horizontal stretch factor (between 0.0 and 1.0).
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, sfnt):
|
||||||
|
self.sfnt = sfnt
|
||||||
|
|
||||||
|
hhea = self.sfnt[b'hhea']
|
||||||
|
hhea.read_data(self.sfnt[b'hmtx'])
|
||||||
|
self.ascent = hhea.ascender
|
||||||
|
self.descent = hhea.descender
|
||||||
|
self._advance_widths = hhea.advance_widths
|
||||||
|
self.cmap = self.sfnt[b'cmap']
|
||||||
|
self.head = self.sfnt[b'head']
|
||||||
|
self.units_per_em = self.head.units_per_em
|
||||||
|
self.os2 = self.sfnt[b'OS/2']
|
||||||
|
self.os2.read_data()
|
||||||
|
self.post = self.sfnt[b'post']
|
||||||
|
self.post.read_data()
|
||||||
|
|
||||||
|
def underline_thickness(self, pixel_size=12.0):
|
||||||
|
'Thickness for lines (in pixels) at the specified size'
|
||||||
|
yscale = pixel_size / self.units_per_em
|
||||||
|
return self.post.underline_thickness * yscale
|
||||||
|
|
||||||
|
def underline_position(self, pixel_size=12.0):
|
||||||
|
yscale = pixel_size / self.units_per_em
|
||||||
|
return self.post.underline_position * yscale
|
||||||
|
|
||||||
|
def overline_position(self, pixel_size=12.0):
|
||||||
|
yscale = pixel_size / self.units_per_em
|
||||||
|
return (self.ascent + 2) * yscale
|
||||||
|
|
||||||
|
def strikeout_size(self, pixel_size=12.0):
|
||||||
|
'The width of the strikeout line, in pixels'
|
||||||
|
yscale = pixel_size / self.units_per_em
|
||||||
|
return yscale * self.os2.strikeout_size
|
||||||
|
|
||||||
|
def strikeout_position(self, pixel_size=12.0):
|
||||||
|
'The displacement from the baseline to top of the strikeout line, in pixels'
|
||||||
|
yscale = pixel_size / self.units_per_em
|
||||||
|
return yscale * self.os2.strikeout_position
|
||||||
|
|
||||||
|
def advance_widths(self, string, pixel_size=12.0, stretch=1.0):
|
||||||
|
'''
|
||||||
|
Return the advance widths (in pixels) for all glyphs corresponding to
|
||||||
|
the characters in string at the specified pixel_size and stretch factor.
|
||||||
|
'''
|
||||||
|
if not isinstance(string, type(u'')):
|
||||||
|
raise ValueError('Must supply a unicode object')
|
||||||
|
chars = tuple(map(ord, string))
|
||||||
|
cmap = self.cmap.get_character_map(chars)
|
||||||
|
glyph_ids = (cmap[c] for c in chars)
|
||||||
|
last = len(self._advance_widths)
|
||||||
|
pixel_size_x = stretch * pixel_size
|
||||||
|
xscale = pixel_size_x / self.units_per_em
|
||||||
|
return tuple(self._advance_widths[i if i < last else -1]*xscale for i in glyph_ids)
|
||||||
|
|
||||||
|
def width(self, string, pixel_size=12.0, stretch=1.0):
|
||||||
|
'The width of the string at the specified pixel size and stretch, in pixels'
|
||||||
|
return sum(self.advance_widths(string, pixel_size, stretch))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
from calibre.utils.fonts.sfnt.container import Sfnt
|
||||||
|
with open(sys.argv[-2], 'rb') as f:
|
||||||
|
raw = f.read()
|
||||||
|
sfnt = Sfnt(raw)
|
||||||
|
m = FontMetrics(sfnt)
|
||||||
|
print (m.advance_widths(sys.argv[-1]))
|
||||||
|
|
@ -41,7 +41,7 @@ def get_table(raw, name):
|
|||||||
return table, table_index, table_offset, table_checksum
|
return table, table_index, table_offset, table_checksum
|
||||||
return None, None, None, None
|
return None, None, None, None
|
||||||
|
|
||||||
def get_font_characteristics(raw, raw_is_table=False):
|
def get_font_characteristics(raw, raw_is_table=False, return_all=False):
|
||||||
'''
|
'''
|
||||||
Return (weight, is_italic, is_bold, is_regular, fs_type, panose, width,
|
Return (weight, is_italic, is_bold, is_regular, fs_type, panose, width,
|
||||||
is_oblique, is_wws). These
|
is_oblique, is_wws). These
|
||||||
@ -79,6 +79,13 @@ def get_font_characteristics(raw, raw_is_table=False):
|
|||||||
is_regular = (selection & (1 << 6)) != 0
|
is_regular = (selection & (1 << 6)) != 0
|
||||||
is_wws = (selection & (1 << 8)) != 0
|
is_wws = (selection & (1 << 8)) != 0
|
||||||
is_oblique = (selection & (1 << 9)) != 0
|
is_oblique = (selection & (1 << 9)) != 0
|
||||||
|
if return_all:
|
||||||
|
return (version, char_width, weight, width, fs_type, subscript_x_size,
|
||||||
|
subscript_y_size, subscript_x_offset, subscript_y_offset,
|
||||||
|
superscript_x_size, superscript_y_size, superscript_x_offset,
|
||||||
|
superscript_y_offset, strikeout_size, strikeout_position,
|
||||||
|
family_class, panose, selection, is_italic, is_bold, is_regular)
|
||||||
|
|
||||||
return weight, is_italic, is_bold, is_regular, fs_type, panose, width, is_oblique, is_wws, version
|
return weight, is_italic, is_bold, is_regular, fs_type, panose, width, is_oblique, is_wws, version
|
||||||
|
|
||||||
def panose_to_css_generic_family(panose):
|
def panose_to_css_generic_family(panose):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user