ImageDraw2.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. (Experimental) WCK-style drawing interface operations
  20. .. seealso:: :py:mod:`PIL.ImageDraw`
  21. """
  22. from __future__ import annotations
  23. from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
  24. class Pen:
  25. """Stores an outline color and width."""
  26. def __init__(self, color, width=1, opacity=255):
  27. self.color = ImageColor.getrgb(color)
  28. self.width = width
  29. class Brush:
  30. """Stores a fill color"""
  31. def __init__(self, color, opacity=255):
  32. self.color = ImageColor.getrgb(color)
  33. class Font:
  34. """Stores a TrueType font and color"""
  35. def __init__(self, color, file, size=12):
  36. # FIXME: add support for bitmap fonts
  37. self.color = ImageColor.getrgb(color)
  38. self.font = ImageFont.truetype(file, size)
  39. class Draw:
  40. """
  41. (Experimental) WCK-style drawing interface
  42. """
  43. def __init__(self, image, size=None, color=None):
  44. if not hasattr(image, "im"):
  45. image = Image.new(image, size, color)
  46. self.draw = ImageDraw.Draw(image)
  47. self.image = image
  48. self.transform = None
  49. def flush(self):
  50. return self.image
  51. def render(self, op, xy, pen, brush=None):
  52. # handle color arguments
  53. outline = fill = None
  54. width = 1
  55. if isinstance(pen, Pen):
  56. outline = pen.color
  57. width = pen.width
  58. elif isinstance(brush, Pen):
  59. outline = brush.color
  60. width = brush.width
  61. if isinstance(brush, Brush):
  62. fill = brush.color
  63. elif isinstance(pen, Brush):
  64. fill = pen.color
  65. # handle transformation
  66. if self.transform:
  67. xy = ImagePath.Path(xy)
  68. xy.transform(self.transform)
  69. # render the item
  70. if op == "line":
  71. self.draw.line(xy, fill=outline, width=width)
  72. else:
  73. getattr(self.draw, op)(xy, fill=fill, outline=outline)
  74. def settransform(self, offset):
  75. """Sets a transformation offset."""
  76. (xoffset, yoffset) = offset
  77. self.transform = (1, 0, xoffset, 0, 1, yoffset)
  78. def arc(self, xy, start, end, *options):
  79. """
  80. Draws an arc (a portion of a circle outline) between the start and end
  81. angles, inside the given bounding box.
  82. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc`
  83. """
  84. self.render("arc", xy, start, end, *options)
  85. def chord(self, xy, start, end, *options):
  86. """
  87. Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points
  88. with a straight line.
  89. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord`
  90. """
  91. self.render("chord", xy, start, end, *options)
  92. def ellipse(self, xy, *options):
  93. """
  94. Draws an ellipse inside the given bounding box.
  95. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse`
  96. """
  97. self.render("ellipse", xy, *options)
  98. def line(self, xy, *options):
  99. """
  100. Draws a line between the coordinates in the ``xy`` list.
  101. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line`
  102. """
  103. self.render("line", xy, *options)
  104. def pieslice(self, xy, start, end, *options):
  105. """
  106. Same as arc, but also draws straight lines between the end points and the
  107. center of the bounding box.
  108. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice`
  109. """
  110. self.render("pieslice", xy, start, end, *options)
  111. def polygon(self, xy, *options):
  112. """
  113. Draws a polygon.
  114. The polygon outline consists of straight lines between the given
  115. coordinates, plus a straight line between the last and the first
  116. coordinate.
  117. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon`
  118. """
  119. self.render("polygon", xy, *options)
  120. def rectangle(self, xy, *options):
  121. """
  122. Draws a rectangle.
  123. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle`
  124. """
  125. self.render("rectangle", xy, *options)
  126. def text(self, xy, text, font):
  127. """
  128. Draws the string at the given position.
  129. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text`
  130. """
  131. if self.transform:
  132. xy = ImagePath.Path(xy)
  133. xy.transform(self.transform)
  134. self.draw.text(xy, text, font=font.font, fill=font.color)
  135. def textbbox(self, xy, text, font):
  136. """
  137. Returns bounding box (in pixels) of given text.
  138. :return: ``(left, top, right, bottom)`` bounding box
  139. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox`
  140. """
  141. if self.transform:
  142. xy = ImagePath.Path(xy)
  143. xy.transform(self.transform)
  144. return self.draw.textbbox(xy, text, font=font.font)
  145. def textlength(self, text, font):
  146. """
  147. Returns length (in pixels) of given text.
  148. This is the amount by which following text should be offset.
  149. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength`
  150. """
  151. return self.draw.textlength(text, font=font.font)