BdfFontFile.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #
  2. # The Python Imaging Library
  3. # $Id$
  4. #
  5. # bitmap distribution font (bdf) file parser
  6. #
  7. # history:
  8. # 1996-05-16 fl created (as bdf2pil)
  9. # 1997-08-25 fl converted to FontFile driver
  10. # 2001-05-25 fl removed bogus __init__ call
  11. # 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev)
  12. # 2003-04-22 fl more robustification (from Graham Dumpleton)
  13. #
  14. # Copyright (c) 1997-2003 by Secret Labs AB.
  15. # Copyright (c) 1997-2003 by Fredrik Lundh.
  16. #
  17. # See the README file for information on usage and redistribution.
  18. #
  19. """
  20. Parse X Bitmap Distribution Format (BDF)
  21. """
  22. from . import FontFile, Image
  23. bdf_slant = {
  24. "R": "Roman",
  25. "I": "Italic",
  26. "O": "Oblique",
  27. "RI": "Reverse Italic",
  28. "RO": "Reverse Oblique",
  29. "OT": "Other",
  30. }
  31. bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"}
  32. def bdf_char(f):
  33. # skip to STARTCHAR
  34. while True:
  35. s = f.readline()
  36. if not s:
  37. return None
  38. if s[:9] == b"STARTCHAR":
  39. break
  40. id = s[9:].strip().decode("ascii")
  41. # load symbol properties
  42. props = {}
  43. while True:
  44. s = f.readline()
  45. if not s or s[:6] == b"BITMAP":
  46. break
  47. i = s.find(b" ")
  48. props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
  49. # load bitmap
  50. bitmap = []
  51. while True:
  52. s = f.readline()
  53. if not s or s[:7] == b"ENDCHAR":
  54. break
  55. bitmap.append(s[:-1])
  56. bitmap = b"".join(bitmap)
  57. # The word BBX
  58. # followed by the width in x (BBw), height in y (BBh),
  59. # and x and y displacement (BBxoff0, BByoff0)
  60. # of the lower left corner from the origin of the character.
  61. width, height, x_disp, y_disp = (int(p) for p in props["BBX"].split())
  62. # The word DWIDTH
  63. # followed by the width in x and y of the character in device pixels.
  64. dwx, dwy = (int(p) for p in props["DWIDTH"].split())
  65. bbox = (
  66. (dwx, dwy),
  67. (x_disp, -y_disp - height, width + x_disp, -y_disp),
  68. (0, 0, width, height),
  69. )
  70. try:
  71. im = Image.frombytes("1", (width, height), bitmap, "hex", "1")
  72. except ValueError:
  73. # deal with zero-width characters
  74. im = Image.new("1", (width, height))
  75. return id, int(props["ENCODING"]), bbox, im
  76. class BdfFontFile(FontFile.FontFile):
  77. """Font file plugin for the X11 BDF format."""
  78. def __init__(self, fp):
  79. super().__init__()
  80. s = fp.readline()
  81. if s[:13] != b"STARTFONT 2.1":
  82. msg = "not a valid BDF file"
  83. raise SyntaxError(msg)
  84. props = {}
  85. comments = []
  86. while True:
  87. s = fp.readline()
  88. if not s or s[:13] == b"ENDPROPERTIES":
  89. break
  90. i = s.find(b" ")
  91. props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
  92. if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
  93. if s.find(b"LogicalFontDescription") < 0:
  94. comments.append(s[i + 1 : -1].decode("ascii"))
  95. while True:
  96. c = bdf_char(fp)
  97. if not c:
  98. break
  99. id, ch, (xy, dst, src), im = c
  100. if 0 <= ch < len(self.glyph):
  101. self.glyph[ch] = xy, dst, src, im