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