####################################################################################################
#
# PyDvi - A Python Library to Process DVI Stream
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
####################################################################################################
#
# Audit
#
# - 11/12/2011 fabrice
# - why ref Tfm, self registration
#
# - why int is scaled_...
# - TfmChar -> Char
# - Check and complete lig kern table and for special fonts
# - add_lig_kern
#
####################################################################################################
""" This module handles TeX Font Metric.
The class :class:`PyDvi.Tfm` handles the font's metric. To get a :class:`PyDvi.Tfm` instance for a
particular font use the static method :meth:`PyDvi.TfmParser.TfmParser.parse`. For example use this
code for the font "cmr10"::
tfm = TfmParser.parse('cmr10', '/usr/share/texmf/fonts/tfm/public/cm/cmr10.tfm')
The number of characters in the font can be obtained using the function :func:`len`::
>>> len(tfm)
128
Each character's metric is stored in a :class:`TfmChar` instance that can be accessed using the char
code as index on the :class:`Tfm` class instance. For example to get the metric of the character
"A" use::
tfm[ord('A')]
"""
####################################################################################################
__all__ = ['Tfm', 'TfmChar', 'TfmExtensibleChar', 'TfmKern', 'TfmLigature']
####################################################################################################
import string
####################################################################################################
from ..Tools.Logging import print_card
####################################################################################################
[docs]class TfmChar(object):
""" This class encapsulates a TeX Font Metric for a Glyph.
Public attributes:
:attr:`char_code`
:attr:`width`
:attr:`height`
:attr:`depth`
:attr:`italic_correction`
"""
#: List of the printable characters.
printable = string.digits + string.letters + string.punctuation
##############################################
def __init__(self,
tfm,
char_code,
width,
height,
depth,
italic_correction,
lig_kern_program_index=None,
next_larger_char=None):
self.tfm = tfm
tfm[char_code] = self
self.char_code = char_code
self.width = width
self.height = height
self.depth = depth
self.italic_correction = italic_correction
self.lig_kern_program_index = lig_kern_program_index
self.next_larger_char = next_larger_char
##############################################
[docs] def scaled_width(self, scale_factor):
""" Return the scaled width by *scale_factor*. """
return int(self.width * scale_factor)
##############################################
[docs] def scaled_height(self, scale_factor):
""" Return the scaled height by *scale_factor*. """
return int(self.height * scale_factor)
##############################################
[docs] def scaled_depth(self, scale_factor):
""" Return the scaled depth by *scale_factor*. """
return int(self.depth * scale_factor)
##############################################
[docs] def scaled_dimensions(self, scale_factor):
""" Return the 3-tuple made of the scaled width, height and depth by *scale_factor*. """
return [int(x * scale_factor) for x in self.width, self.height, self.depth]
##############################################
[docs] def next_larger_tfm_char(self):
""" Return the :class:`TfmChar` instance for the next larger char if it exists else return
:obj:`None`."""
if self.next_larger_char is not None:
return self.tfm[self.next_larger_char]
else:
return None
##############################################
[docs] def get_lig_kern_program(self):
""" Get the ligature/kern program of the character. """
if self.lig_kern_program_index is not None:
return self.tfm.get_lig_kern_program(self.lig_kern_program_index)
else:
return None
##############################################
[docs] def chr(self):
""" Return the character string from its char code if it is printable. """
char = chr(self.char_code)
if char in self.printable:
return char
else:
return self.char_code
##############################################
[docs] def print_summary(self):
string_format = '''TFM Char %u %s
- width %.3f
- height %.3f
- depth %.3f
- italic correction %.3f
- lig kern program index %s
- next larger char %s'''
message = string_format % (self.char_code, self.chr(),
self.width,
self.height,
self.depth,
self.italic_correction,
str(self.lig_kern_program_index),
str(self.next_larger_char),
)
first_lig_kern = self.get_lig_kern_program()
if first_lig_kern is not None:
for lig_kern in first_lig_kern:
message += '\n' + str(lig_kern)
print_card(message)
####################################################################################################
[docs]class TfmExtensibleChar(TfmChar):
""" This class encapsulates a TeX Font Metric for an extensible Glyph.
Public attributes:
:attr:`top`
:attr:`mid`
:attr:`bot`
:attr:`rep`
"""
##############################################
def __init__(self,
tfm,
char_code,
width,
height,
depth,
italic_correction,
extensible_recipe,
lig_kern_program_index=None,
next_larger_char=None):
super(TfmExtensibleChar, self).__init__(tfm,
char_code,
width,
height,
depth,
italic_correction,
lig_kern_program_index,
next_larger_char)
self.top, self.mid, self.bot, self.rep = extensible_recipe
####################################################################################################
class TfmLigKern(object):
""" This classe serves of base class for ligature and kern program instruction.
"""
##############################################
def __init__(self, tfm, index, stop, next_char):
self.tfm = tfm
self.stop = stop
self.index = index
self.next_char = next_char
self.tfm.add_lig_kern(self)
##############################################
def __iter__(self):
""" Iterate of the ligature/kern program. """
i = self.index
while True:
lig_kern = self.tfm.get_lig_kern_program(i)
yield lig_kern
if lig_kern.stop:
break
else:
i += 1
####################################################################################################
[docs]class TfmKern(TfmLigKern):
""" This class represents a Kerning Program Instruction.
Public Attributes:
:attr:`next_char`
next character
:attr:`kern`
kerning value
"""
##############################################
def __init__(self, tfm, index, stop, next_char, kern):
super(TfmKern, self).__init__(tfm, index, stop, next_char)
self.kern = kern
##############################################
def __str__(self):
return 'Kern char code %3u %s %0.6f' % (
self.next_char,
self.tfm[self.next_char].chr(),
self.kern,
)
####################################################################################################
[docs]class TfmLigature(TfmLigKern):
""" This class represents a Ligature Program Instruction.
Public Attributes:
:attr:`next_char`
next character
:attr:`ligature_char_code`
ligature character code
:attr:`current_char_is_deleted`
the current characters must be deleted of the stream
:attr:`next_char_is_deleted`
the next characters must be deleted of the stream
:attr:`number_of_chars_to_pass_over`
number of characters to pass over
"""
##############################################
def __init__(self,
tfm,
index,
stop,
next_char,
ligature_char_code,
number_of_chars_to_pass_over,
current_char_is_deleted,
next_char_is_deleted):
super(TfmLigature, self).__init__(tfm, index, stop, next_char)
self.ligature_char_code = ligature_char_code
self.number_of_chars_to_pass_over = number_of_chars_to_pass_over
self.current_char_is_deleted = current_char_is_deleted
self.next_char_is_deleted = next_char_is_deleted
##############################################
def __str__(self):
return 'Lig char code %3u %s ligature char code %3u' % (
self.next_char,
self.tfm[self.next_char].chr(),
self.ligature_char_code,
)
####################################################################################################
[docs]class Tfm(object):
""" This class encapsulates a TeX Font Metric for a font.
Public attributes:
:attr:`font_name`
font's name
:attr:`filename`
".tfm" filename
:attr:`smallest_character_code`
smallest character code of the font
:attr:`largest_character_code`
largest character code of the font
:attr:`checksum`
checksum of the tfm file
:attr:`design_font_size`
design font size
:attr:`character_coding_scheme`
character coding scheme
:attr:`family`
font's family
:attr:`slant`
:attr:`spacing`
:attr:`space_stretch`
:attr:`space_shrink`
:attr:`x_height`
:attr:`quad`
:attr:`extra_space`
In addition for Math font, the following public attributes are available:
:attr:`um1`
:attr:`num2`
:attr:`num3`
:attr:`denom1`
:attr:`denom2`
:attr:`sup1`
:attr:`sup2`
:attr:`sup3`
:attr:`sub1`
:attr:`sub2`
:attr:`supdrop`
:attr:`subdrop`
:attr:`delim1`
:attr:`delim2`
:attr:`axis_height`
:attr:`default_rule_thickness`
:attr:`big_op_spacing`
The number of characters can be queried using :func:`len`. The :class:`TfmChar` instance for a
character code *char_code* can be set or get using the operator [].
"""
##############################################
def __init__(self,
font_name,
filename,
smallest_character_code,
largest_character_code,
checksum,
design_font_size,
character_coding_scheme,
family):
self.font_name = font_name
self.filename = filename
self.smallest_character_code = smallest_character_code
self.largest_character_code = largest_character_code
self.checksum = checksum
self.design_font_size = design_font_size
self.character_coding_scheme = character_coding_scheme
self.family = family
self._lig_kerns = []
self._chars = {}
##############################################
def __setitem__(self, char_code, value):
""" Set the :class:`TfmChar` instance for the character code *char_code*. """
self._chars[char_code] = value
##############################################
def __getitem__(self, char_code):
""" Return the :class:`TfmChar` instance for the character code *char_code*. """
return self._chars[char_code]
##############################################
def __len__(self):
""" Return the number of characters. """
# return self.largest_character_code - self.smallest_character_code +1
return len(self._chars)
##############################################
[docs] def set_font_parameters(self, parameters):
""" Set the font parameters. """
(self.slant,
self.spacing,
self.space_stretch,
self.space_shrink,
self.x_height,
self.quad,
self.extra_space) = parameters
##############################################
[docs] def set_math_symbols_parameters(self, parameters):
""" Set the math symbols parameters. """
(self.num1,
self.num2,
self.num3,
self.denom1,
self.denom2,
self.sup1,
self.sup2,
self.sup3,
self.sub1,
self.sub2,
self.supdrop,
self.subdrop,
self.delim1,
self.delim2,
self.axis_height) = parameters
##############################################
[docs] def set_math_extension_parameters(self, parameters):
""" Set the math extension parameters. """
self.default_rule_thickness = parameters[0]
self.big_op_spacing = parameters[1:]
##############################################
[docs] def add_lig_kern(self, obj):
""" Add a ligature/kern program *obj*. """
self._lig_kerns.append(obj)
##############################################
[docs] def get_lig_kern_program(self, i):
""" Return the ligature/kern program at index *i*. """
return self._lig_kerns[i]
##############################################
[docs] def print_summary(self):
string_format = '''TFM %s
- Smallest character code in the font: %u
- Largest character code in the font: %u
- Checksum: %u
- Design Font Size: %f
- Character coding scheme: "%s"
- Family: "%s"
Font Parameters:
- Slant: %f
- Spacing: %f
- Space Stretch: %f
- Space Shrink: %f
- X Height: %f
- Quad: %f
- Extra Space: %f'''
message = string_format % (self.font_name,
self.smallest_character_code,
self.largest_character_code,
self.checksum,
self.design_font_size,
self.character_coding_scheme,
self.family,
self.slant,
self.spacing,
self.space_stretch,
self.space_shrink,
self.x_height,
self.quad,
self.extra_space,
)
print_card(message)
for char in self._chars.values():
char.print_summary()
####################################################################################################
#
# End
#
####################################################################################################