ImageGrab.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #
  2. # The Python Imaging Library
  3. # $Id$
  4. #
  5. # screen grabber
  6. #
  7. # History:
  8. # 2001-04-26 fl created
  9. # 2001-09-17 fl use builtin driver, if present
  10. # 2002-11-19 fl added grabclipboard support
  11. #
  12. # Copyright (c) 2001-2002 by Secret Labs AB
  13. # Copyright (c) 2001-2002 by Fredrik Lundh
  14. #
  15. # See the README file for information on usage and redistribution.
  16. #
  17. from __future__ import annotations
  18. import io
  19. import os
  20. import shutil
  21. import subprocess
  22. import sys
  23. import tempfile
  24. from . import Image
  25. def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None):
  26. if xdisplay is None:
  27. if sys.platform == "darwin":
  28. fh, filepath = tempfile.mkstemp(".png")
  29. os.close(fh)
  30. args = ["screencapture"]
  31. if bbox:
  32. left, top, right, bottom = bbox
  33. args += ["-R", f"{left},{top},{right-left},{bottom-top}"]
  34. subprocess.call(args + ["-x", filepath])
  35. im = Image.open(filepath)
  36. im.load()
  37. os.unlink(filepath)
  38. if bbox:
  39. im_resized = im.resize((right - left, bottom - top))
  40. im.close()
  41. return im_resized
  42. return im
  43. elif sys.platform == "win32":
  44. offset, size, data = Image.core.grabscreen_win32(
  45. include_layered_windows, all_screens
  46. )
  47. im = Image.frombytes(
  48. "RGB",
  49. size,
  50. data,
  51. # RGB, 32-bit line padding, origin lower left corner
  52. "raw",
  53. "BGR",
  54. (size[0] * 3 + 3) & -4,
  55. -1,
  56. )
  57. if bbox:
  58. x0, y0 = offset
  59. left, top, right, bottom = bbox
  60. im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
  61. return im
  62. try:
  63. if not Image.core.HAVE_XCB:
  64. msg = "Pillow was built without XCB support"
  65. raise OSError(msg)
  66. size, data = Image.core.grabscreen_x11(xdisplay)
  67. except OSError:
  68. if (
  69. xdisplay is None
  70. and sys.platform not in ("darwin", "win32")
  71. and shutil.which("gnome-screenshot")
  72. ):
  73. fh, filepath = tempfile.mkstemp(".png")
  74. os.close(fh)
  75. subprocess.call(["gnome-screenshot", "-f", filepath])
  76. im = Image.open(filepath)
  77. im.load()
  78. os.unlink(filepath)
  79. if bbox:
  80. im_cropped = im.crop(bbox)
  81. im.close()
  82. return im_cropped
  83. return im
  84. else:
  85. raise
  86. else:
  87. im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
  88. if bbox:
  89. im = im.crop(bbox)
  90. return im
  91. def grabclipboard():
  92. if sys.platform == "darwin":
  93. fh, filepath = tempfile.mkstemp(".png")
  94. os.close(fh)
  95. commands = [
  96. 'set theFile to (open for access POSIX file "'
  97. + filepath
  98. + '" with write permission)',
  99. "try",
  100. " write (the clipboard as «class PNGf») to theFile",
  101. "end try",
  102. "close access theFile",
  103. ]
  104. script = ["osascript"]
  105. for command in commands:
  106. script += ["-e", command]
  107. subprocess.call(script)
  108. im = None
  109. if os.stat(filepath).st_size != 0:
  110. im = Image.open(filepath)
  111. im.load()
  112. os.unlink(filepath)
  113. return im
  114. elif sys.platform == "win32":
  115. fmt, data = Image.core.grabclipboard_win32()
  116. if fmt == "file": # CF_HDROP
  117. import struct
  118. o = struct.unpack_from("I", data)[0]
  119. if data[16] != 0:
  120. files = data[o:].decode("utf-16le").split("\0")
  121. else:
  122. files = data[o:].decode("mbcs").split("\0")
  123. return files[: files.index("")]
  124. if isinstance(data, bytes):
  125. data = io.BytesIO(data)
  126. if fmt == "png":
  127. from . import PngImagePlugin
  128. return PngImagePlugin.PngImageFile(data)
  129. elif fmt == "DIB":
  130. from . import BmpImagePlugin
  131. return BmpImagePlugin.DibImageFile(data)
  132. return None
  133. else:
  134. if os.getenv("WAYLAND_DISPLAY"):
  135. session_type = "wayland"
  136. elif os.getenv("DISPLAY"):
  137. session_type = "x11"
  138. else: # Session type check failed
  139. session_type = None
  140. if shutil.which("wl-paste") and session_type in ("wayland", None):
  141. output = subprocess.check_output(["wl-paste", "-l"]).decode()
  142. mimetypes = output.splitlines()
  143. if "image/png" in mimetypes:
  144. mimetype = "image/png"
  145. elif mimetypes:
  146. mimetype = mimetypes[0]
  147. else:
  148. mimetype = None
  149. args = ["wl-paste"]
  150. if mimetype:
  151. args.extend(["-t", mimetype])
  152. elif shutil.which("xclip") and session_type in ("x11", None):
  153. args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"]
  154. else:
  155. msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
  156. raise NotImplementedError(msg)
  157. p = subprocess.run(args, capture_output=True)
  158. err = p.stderr
  159. if err:
  160. msg = f"{args[0]} error: {err.strip().decode()}"
  161. raise ChildProcessError(msg)
  162. data = io.BytesIO(p.stdout)
  163. im = Image.open(data)
  164. im.load()
  165. return im