/kiva/fonttools/font.py

https://github.com/pib/enable
Python | 161 lines | 112 code | 16 blank | 33 comment | 25 complexity | 2daf2f26419c194399d7f483ce86f04f MD5 | raw file
  1. """
  2. Defines the Kiva Font class and a utility method to parse free-form font
  3. specification strings into Font instances.
  4. """
  5. import copy
  6. from kiva.constants import DEFAULT, DECORATIVE, ROMAN, SCRIPT, \
  7. SWISS, MODERN, TELETYPE, NORMAL, ITALIC, BOLD, BOLD_ITALIC
  8. from font_manager import FontProperties, fontManager
  9. # Various maps used by str_to_font
  10. font_families = {
  11. 'default': DEFAULT,
  12. 'decorative': DECORATIVE,
  13. 'roman': ROMAN,
  14. 'script': SCRIPT,
  15. 'swiss': SWISS,
  16. 'modern': MODERN
  17. }
  18. font_styles = { 'italic': ITALIC }
  19. font_weights = { 'bold': BOLD }
  20. font_noise = [ 'pt', 'point', 'family' ]
  21. def str_to_font(fontspec):
  22. """
  23. Converts a string specification of a font into a Font instance.
  24. string specifications are of the form: "modern 12", "9 roman italic",
  25. and so on.
  26. """
  27. point_size = 10
  28. family = DEFAULT
  29. style = NORMAL
  30. weight = NORMAL
  31. underline = 0
  32. facename = []
  33. for word in fontspec.split():
  34. lword = word.lower()
  35. if font_families.has_key( lword ):
  36. family = font_families[ lword ]
  37. elif font_styles.has_key( lword ):
  38. style = font_styles[ lword ]
  39. elif font_weights.has_key( lword ):
  40. weight = font_weights[ lword ]
  41. elif lword == 'underline':
  42. underline = 1
  43. elif lword not in font_noise:
  44. try:
  45. point_size = int( lword )
  46. except:
  47. facename.append( word )
  48. return Font( size=point_size, family=family, weight=weight, style=style,
  49. underline=underline, face_name=' '.join( facename ) )
  50. class Font(object):
  51. """ Font class for device independent font specification.
  52. It is primarily based on wxPython, but looks to be similar to
  53. the needs of Mac OS X, etc.
  54. The family defaults to SWISS so that font rotation will work
  55. correctly under wxPython. Revisit as we get more platforms
  56. defined.
  57. """
  58. # Maps the constants for font families to names to use when searching for
  59. # fonts.
  60. familymap = {DEFAULT : "serif",
  61. SWISS : "sans-serif",
  62. ROMAN : "serif",
  63. MODERN : "sans-serif",
  64. DECORATIVE : "fantasy",
  65. SCRIPT : "script",
  66. TELETYPE : "monospace" }
  67. def __init__(self, face_name="", size = 12,family = SWISS, weight=NORMAL,
  68. style=NORMAL, underline = 0, encoding=DEFAULT):
  69. if (type(size) != int) or (type(family) != type(SWISS)) or \
  70. (type(weight) != type(NORMAL)) or (type(style) != type(NORMAL)) or \
  71. (type(underline) != int) or (not isinstance(face_name, basestring)) or \
  72. (type(encoding) != type(DEFAULT)):
  73. raise RuntimeError, "Bad value in Font() constructor."
  74. ### HACK: C++ stuff expects a string (not unicode) for the face_name, so fix
  75. ### if needed. See ticket #2111 in the CP Trac.
  76. if isinstance(face_name, unicode):
  77. face_name = face_name.encode("latin1")
  78. self.size = size
  79. self.family = family
  80. self.weight = weight
  81. self.style = style
  82. self.underline = underline
  83. self.face_name = face_name
  84. self.encoding = encoding
  85. def findfont(self):
  86. """ Returns the file name containing the font that most closely matches
  87. our font properties.
  88. """
  89. fp = self._make_font_props()
  90. return str(fontManager.findfont(fp))
  91. def findfontname(self):
  92. """ Returns the name of the font that most closely matches our font
  93. properties
  94. """
  95. fp = self._make_font_props()
  96. return fp.get_name()
  97. def _make_font_props(self):
  98. """ Returns a font_manager.FontProperties object that encapsulates our
  99. font properties
  100. """
  101. # XXX: change the weight to a numerical value
  102. if self.style == BOLD or self.style == BOLD_ITALIC:
  103. weight = "bold"
  104. else:
  105. weight = "normal"
  106. if self.style == ITALIC or self.style == BOLD_ITALIC:
  107. style = "italic"
  108. else:
  109. style = "normal"
  110. fp = FontProperties(family = self.familymap[self.family], style=style, weight=weight,
  111. size = self.size)
  112. if self.face_name != "":
  113. fp.set_name(self.face_name)
  114. return fp
  115. def _get_name(self):
  116. return self.face_name
  117. def _set_name(self, val):
  118. self.face_name = val
  119. name = property(_get_name, _set_name)
  120. def copy(self):
  121. """ Returns a copy of the font object.
  122. """
  123. return copy.deepcopy(self)
  124. def __eq__(self, other):
  125. result = False
  126. try:
  127. if (self.family == other.family and
  128. self.size == other.size and
  129. self.weight == other.weight and
  130. self.style == other.style and
  131. self.underline == other.underline and
  132. self.face_name == other.face_name and
  133. self.encoding == other.encoding):
  134. result = True
  135. except AttributeError:
  136. pass
  137. return result
  138. def __ne__(self, other):
  139. return not self.__eq__(other)
  140. def __repr__(self):
  141. return "Font(size=%d,family=%d,weight=%d, style=%d, face_name='%s',encoding=%d )" % \
  142. (self.size, self.family, self.weight, self.style, self.face_name, self.encoding)