PSDraw.py 6.4 KB

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