ImageGrab.py 5.5 KB

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