Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
ImageDraw2.py245 linesDownload Raw Back to PIL
1#
2# The Python Imaging Library
3# $Id$
4#
5# WCK-style drawing interface operations
6#
7# History:
8# 2003-12-07 fl   created
9# 2005-05-15 fl   updated; added to PIL as ImageDraw2
10# 2005-05-15 fl   added text support
11# 2005-05-20 fl   added arc/chord/pieslice support
12#
13# Copyright (c) 2003-2005 by Secret Labs AB
14# Copyright (c) 2003-2005 by Fredrik Lundh
15#
16# See the README file for information on usage and redistribution.
17#
18
19
20"""
21(Experimental) WCK-style drawing interface operations
22
23.. seealso:: :py:mod:`PIL.ImageDraw`
24"""
25
26from __future__ import annotations
27
28from typing import Any, AnyStr, BinaryIO
29
30from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
31from ._typing import Coords, StrOrBytesPath
32
33
34class Pen:
35    """Stores an outline color and width."""
36
37    def __init__(self, color: str, width: int = 1, opacity: int = 255) -> None:
38        self.color = ImageColor.getrgb(color)
39        self.width = width
40
41
42class Brush:
43    """Stores a fill color"""
44
45    def __init__(self, color: str, opacity: int = 255) -> None:
46        self.color = ImageColor.getrgb(color)
47
48
49class Font:
50    """Stores a TrueType font and color"""
51
52    def __init__(
53        self, color: str, file: StrOrBytesPath | BinaryIO, size: float = 12
54    ) -> None:
55        # FIXME: add support for bitmap fonts
56        self.color = ImageColor.getrgb(color)
57        self.font = ImageFont.truetype(file, size)
58
59
60class Draw:
61    """
62    (Experimental) WCK-style drawing interface
63    """
64
65    def __init__(
66        self,
67        image: Image.Image | str,
68        size: tuple[int, int] | list[int] | None = None,
69        color: float | tuple[float, ...] | str | None = None,
70    ) -> None:
71        if isinstance(image, str):
72            if size is None:
73                msg = "If image argument is mode string, size must be a list or tuple"
74                raise ValueError(msg)
75            image = Image.new(image, size, color)
76        self.draw = ImageDraw.Draw(image)
77        self.image = image
78        self.transform: tuple[float, float, float, float, float, float] | None = None
79
80    def flush(self) -> Image.Image:
81        return self.image
82
83    def render(
84        self,
85        op: str,
86        xy: Coords,
87        pen: Pen | Brush | None,
88        brush: Brush | Pen | None = None,
89        **kwargs: Any,
90    ) -> None:
91        # handle color arguments
92        outline = fill = None
93        width = 1
94        if isinstance(pen, Pen):
95            outline = pen.color
96            width = pen.width
97        elif isinstance(brush, Pen):
98            outline = brush.color
99            width = brush.width
100        if isinstance(brush, Brush):
101            fill = brush.color
102        elif isinstance(pen, Brush):
103            fill = pen.color
104        # handle transformation
105        if self.transform:
106            path = ImagePath.Path(xy)
107            path.transform(self.transform)
108            xy = path
109        # render the item
110        if op in ("arc", "line"):
111            kwargs.setdefault("fill", outline)
112        else:
113            kwargs.setdefault("fill", fill)
114            kwargs.setdefault("outline", outline)
115        if op == "line":
116            kwargs.setdefault("width", width)
117        getattr(self.draw, op)(xy, **kwargs)
118
119    def settransform(self, offset: tuple[float, float]) -> None:
120        """Sets a transformation offset."""
121        xoffset, yoffset = offset
122        self.transform = (1, 0, xoffset, 0, 1, yoffset)
123
124    def arc(
125        self,
126        xy: Coords,
127        pen: Pen | Brush | None,
128        start: float,
129        end: float,
130        *options: Any,
131    ) -> None:
132        """
133        Draws an arc (a portion of a circle outline) between the start and end
134        angles, inside the given bounding box.
135
136        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc`
137        """
138        self.render("arc", xy, pen, *options, start=start, end=end)
139
140    def chord(
141        self,
142        xy: Coords,
143        pen: Pen | Brush | None,
144        start: float,
145        end: float,
146        *options: Any,
147    ) -> None:
148        """
149        Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points
150        with a straight line.
151
152        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord`
153        """
154        self.render("chord", xy, pen, *options, start=start, end=end)
155
156    def ellipse(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None:
157        """
158        Draws an ellipse inside the given bounding box.
159
160        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse`
161        """
162        self.render("ellipse", xy, pen, *options)
163
164    def line(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None:
165        """
166        Draws a line between the coordinates in the ``xy`` list.
167
168        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line`
169        """
170        self.render("line", xy, pen, *options)
171
172    def pieslice(
173        self,
174        xy: Coords,
175        pen: Pen | Brush | None,
176        start: float,
177        end: float,
178        *options: Any,
179    ) -> None:
180        """
181        Same as arc, but also draws straight lines between the end points and the
182        center of the bounding box.
183
184        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice`
185        """
186        self.render("pieslice", xy, pen, *options, start=start, end=end)
187
188    def polygon(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None:
189        """
190        Draws a polygon.
191
192        The polygon outline consists of straight lines between the given
193        coordinates, plus a straight line between the last and the first
194        coordinate.
195
196
197        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon`
198        """
199        self.render("polygon", xy, pen, *options)
200
201    def rectangle(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None:
202        """
203        Draws a rectangle.
204
205        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle`
206        """
207        self.render("rectangle", xy, pen, *options)
208
209    def text(self, xy: tuple[float, float], text: AnyStr, font: Font) -> None:
210        """
211        Draws the string at the given position.
212
213        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text`
214        """
215        if self.transform:
216            path = ImagePath.Path(xy)
217            path.transform(self.transform)
218            xy = path
219        self.draw.text(xy, text, font=font.font, fill=font.color)
220
221    def textbbox(
222        self, xy: tuple[float, float], text: AnyStr, font: Font
223    ) -> tuple[float, float, float, float]:
224        """
225        Returns bounding box (in pixels) of given text.
226
227        :return: ``(left, top, right, bottom)`` bounding box
228
229        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox`
230        """
231        if self.transform:
232            path = ImagePath.Path(xy)
233            path.transform(self.transform)
234            xy = path
235        return self.draw.textbbox(xy, text, font=font.font)
236
237    def textlength(self, text: AnyStr, font: Font) -> float:
238        """
239        Returns length (in pixels) of given text.
240        This is the amount by which following text should be offset.
241
242        .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength`
243        """
244        return self.draw.textlength(text, font=font.font)
245 
codekingpro/portable-devtools · Team Ai