PSDraw.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #
  2. # The Python Imaging Library
  3. # $Id$
  4. #
  5. # Simple PostScript graphics interface
  6. #
  7. # History:
  8. # 1996-04-20 fl Created
  9. # 1999-01-10 fl Added gsave/grestore to image method
  10. # 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge)
  11. #
  12. # Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved.
  13. # Copyright (c) 1996 by Fredrik Lundh.
  14. #
  15. # See the README file for information on usage and redistribution.
  16. #
  17. import sys
  18. from . import EpsImagePlugin
  19. ##
  20. # Simple PostScript graphics interface.
  21. class PSDraw:
  22. """
  23. Sets up printing to the given file. If ``fp`` is omitted,
  24. ``sys.stdout.buffer`` or ``sys.stdout`` is assumed.
  25. """
  26. def __init__(self, fp=None):
  27. if not fp:
  28. try:
  29. fp = sys.stdout.buffer
  30. except AttributeError:
  31. fp = sys.stdout
  32. self.fp = fp
  33. def begin_document(self, id=None):
  34. """Set up printing of a document. (Write PostScript DSC header.)"""
  35. # FIXME: incomplete
  36. self.fp.write(
  37. b"%!PS-Adobe-3.0\n"
  38. b"save\n"
  39. b"/showpage { } def\n"
  40. b"%%EndComments\n"
  41. b"%%BeginDocument\n"
  42. )
  43. # self.fp.write(ERROR_PS) # debugging!
  44. self.fp.write(EDROFF_PS)
  45. self.fp.write(VDI_PS)
  46. self.fp.write(b"%%EndProlog\n")
  47. self.isofont = {}
  48. def end_document(self):
  49. """Ends printing. (Write PostScript DSC footer.)"""
  50. self.fp.write(b"%%EndDocument\nrestore showpage\n%%End\n")
  51. if hasattr(self.fp, "flush"):
  52. self.fp.flush()
  53. def setfont(self, font, size):
  54. """
  55. Selects which font to use.
  56. :param font: A PostScript font name
  57. :param size: Size in points.
  58. """
  59. font = bytes(font, "UTF-8")
  60. if font not in self.isofont:
  61. # reencode font
  62. self.fp.write(b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font, font))
  63. self.isofont[font] = 1
  64. # rough
  65. self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font))
  66. def line(self, xy0, xy1):
  67. """
  68. Draws a line between the two points. Coordinates are given in
  69. PostScript point coordinates (72 points per inch, (0, 0) is the lower
  70. left corner of the page).
  71. """
  72. self.fp.write(b"%d %d %d %d Vl\n" % (*xy0, *xy1))
  73. def rectangle(self, box):
  74. """
  75. Draws a rectangle.
  76. :param box: A 4-tuple of integers whose order and function is currently
  77. undocumented.
  78. Hint: the tuple is passed into this format string:
  79. .. code-block:: python
  80. %d %d M %d %d 0 Vr\n
  81. """
  82. self.fp.write(b"%d %d M %d %d 0 Vr\n" % box)
  83. def text(self, xy, text):
  84. """
  85. Draws text at the given position. You must use
  86. :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method.
  87. """
  88. text = bytes(text, "UTF-8")
  89. text = b"\\(".join(text.split(b"("))
  90. text = b"\\)".join(text.split(b")"))
  91. xy += (text,)
  92. self.fp.write(b"%d %d M (%s) S\n" % xy)
  93. def image(self, box, im, dpi=None):
  94. """Draw a PIL image, centered in the given box."""
  95. # default resolution depends on mode
  96. if not dpi:
  97. if im.mode == "1":
  98. dpi = 200 # fax
  99. else:
  100. dpi = 100 # greyscale
  101. # image size (on paper)
  102. x = im.size[0] * 72 / dpi
  103. y = im.size[1] * 72 / dpi
  104. # max allowed size
  105. xmax = float(box[2] - box[0])
  106. ymax = float(box[3] - box[1])
  107. if x > xmax:
  108. y = y * xmax / x
  109. x = xmax
  110. if y > ymax:
  111. x = x * ymax / y
  112. y = ymax
  113. dx = (xmax - x) / 2 + box[0]
  114. dy = (ymax - y) / 2 + box[1]
  115. self.fp.write(b"gsave\n%f %f translate\n" % (dx, dy))
  116. if (x, y) != im.size:
  117. # EpsImagePlugin._save prints the image at (0,0,xsize,ysize)
  118. sx = x / im.size[0]
  119. sy = y / im.size[1]
  120. self.fp.write(b"%f %f scale\n" % (sx, sy))
  121. EpsImagePlugin._save(im, self.fp, None, 0)
  122. self.fp.write(b"\ngrestore\n")
  123. # --------------------------------------------------------------------
  124. # PostScript driver
  125. #
  126. # EDROFF.PS -- PostScript driver for Edroff 2
  127. #
  128. # History:
  129. # 94-01-25 fl: created (edroff 2.04)
  130. #
  131. # Copyright (c) Fredrik Lundh 1994.
  132. #
  133. EDROFF_PS = b"""\
  134. /S { show } bind def
  135. /P { moveto show } bind def
  136. /M { moveto } bind def
  137. /X { 0 rmoveto } bind def
  138. /Y { 0 exch rmoveto } bind def
  139. /E { findfont
  140. dup maxlength dict begin
  141. {
  142. 1 index /FID ne { def } { pop pop } ifelse
  143. } forall
  144. /Encoding exch def
  145. dup /FontName exch def
  146. currentdict end definefont pop
  147. } bind def
  148. /F { findfont exch scalefont dup setfont
  149. [ exch /setfont cvx ] cvx bind def
  150. } bind def
  151. """
  152. #
  153. # VDI.PS -- PostScript driver for VDI meta commands
  154. #
  155. # History:
  156. # 94-01-25 fl: created (edroff 2.04)
  157. #
  158. # Copyright (c) Fredrik Lundh 1994.
  159. #
  160. VDI_PS = b"""\
  161. /Vm { moveto } bind def
  162. /Va { newpath arcn stroke } bind def
  163. /Vl { moveto lineto stroke } bind def
  164. /Vc { newpath 0 360 arc closepath } bind def
  165. /Vr { exch dup 0 rlineto
  166. exch dup neg 0 exch rlineto
  167. exch neg 0 rlineto
  168. 0 exch rlineto
  169. 100 div setgray fill 0 setgray } bind def
  170. /Tm matrix def
  171. /Ve { Tm currentmatrix pop
  172. translate scale newpath 0 0 .5 0 360 arc closepath
  173. Tm setmatrix
  174. } bind def
  175. /Vf { currentgray exch setgray fill setgray } bind def
  176. """
  177. #
  178. # ERROR.PS -- Error handler
  179. #
  180. # History:
  181. # 89-11-21 fl: created (pslist 1.10)
  182. #
  183. ERROR_PS = b"""\
  184. /landscape false def
  185. /errorBUF 200 string def
  186. /errorNL { currentpoint 10 sub exch pop 72 exch moveto } def
  187. errordict begin /handleerror {
  188. initmatrix /Courier findfont 10 scalefont setfont
  189. newpath 72 720 moveto $error begin /newerror false def
  190. (PostScript Error) show errorNL errorNL
  191. (Error: ) show
  192. /errorname load errorBUF cvs show errorNL errorNL
  193. (Command: ) show
  194. /command load dup type /stringtype ne { errorBUF cvs } if show
  195. errorNL errorNL
  196. (VMstatus: ) show
  197. vmstatus errorBUF cvs show ( bytes available, ) show
  198. errorBUF cvs show ( bytes used at level ) show
  199. errorBUF cvs show errorNL errorNL
  200. (Operand stargck: ) show errorNL /ostargck load {
  201. dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL
  202. } forall errorNL
  203. (Execution stargck: ) show errorNL /estargck load {
  204. dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL
  205. } forall
  206. end showpage
  207. } def end
  208. """