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