From f225130ee52f8b93b9e4a51c1ee779f544fcca51 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Sep 2023 19:08:30 +1000 Subject: [PATCH 1/4] Only search for Ghostscript Windows binary when needed --- src/PIL/EpsImagePlugin.py | 50 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index b96ce9603a3..84177aaaa88 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -38,7 +38,14 @@ field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") gs_windows_binary = None -if sys.platform.startswith("win"): + + +def _get_windows_binary(): + global gs_windows_binary + if gs_windows_binary is not None: + return gs_windows_binary + if not sys.platform.startswith("win"): + return import shutil for binary in ("gswin32c", "gswin64c", "gs"): @@ -47,19 +54,19 @@ break else: gs_windows_binary = False + return gs_windows_binary def has_ghostscript(): - if gs_windows_binary: + gs_windows_binary = _get_windows_binary() + if gs_windows_binary is not None: + return gs_windows_binary is not False + try: + subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) return True - if not sys.platform.startswith("win"): - try: - subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) - return True - except OSError: - # No Ghostscript - pass - return False + except OSError: + # No Ghostscript + return False def Ghostscript(tile, size, fp, scale=1, transparency=False): @@ -132,17 +139,18 @@ def Ghostscript(tile, size, fp, scale=1, transparency=False): "showpage", ] - if gs_windows_binary is not None: - if not gs_windows_binary: - try: - os.unlink(outfile) - if infile_temp: - os.unlink(infile_temp) - except OSError: - pass - - msg = "Unable to locate Ghostscript on paths" - raise OSError(msg) + gs_windows_binary = _get_windows_binary() + if gs_windows_binary is False: + try: + os.unlink(outfile) + if infile_temp: + os.unlink(infile_temp) + except OSError: + pass + + msg = "Unable to locate Ghostscript on paths" + raise OSError(msg) + elif gs_windows_binary is not None: command[0] = gs_windows_binary # push data through Ghostscript From a84a43528ebf8756369a3b7248312c4122ef3bc9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Sep 2023 19:46:46 +1000 Subject: [PATCH 2/4] Added gs_binary to control Ghostscript use on all platforms --- Tests/test_file_eps.py | 15 +++++++++ src/PIL/EpsImagePlugin.py | 67 +++++++++++++++------------------------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 26adfff8786..4e3954ecb63 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -8,6 +8,7 @@ assert_image_similar, assert_image_similar_tofile, hopper, + is_win32, mark_if_feature_version, skip_unless_feature, ) @@ -98,6 +99,20 @@ def test_load(): assert im.load()[0, 0] == (255, 255, 255) +def test_binary(): + if HAS_GHOSTSCRIPT: + assert EpsImagePlugin.gs_binary is not None + else: + assert EpsImagePlugin.gs_binary is False + + if not is_win32(): + assert EpsImagePlugin.gs_windows_binary is None + elif not HAS_GHOSTSCRIPT: + assert EpsImagePlugin.gs_windows_binary is False + else: + assert EpsImagePlugin.gs_windows_binary is not None + + def test_invalid_file(): invalid_file = "Tests/images/flower.jpg" with pytest.raises(SyntaxError): diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 84177aaaa88..ec4e822d109 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -37,40 +37,39 @@ split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") +gs_binary = None gs_windows_binary = None -def _get_windows_binary(): - global gs_windows_binary - if gs_windows_binary is not None: - return gs_windows_binary - if not sys.platform.startswith("win"): - return - import shutil - - for binary in ("gswin32c", "gswin64c", "gs"): - if shutil.which(binary) is not None: - gs_windows_binary = binary - break - else: - gs_windows_binary = False - return gs_windows_binary - - def has_ghostscript(): - gs_windows_binary = _get_windows_binary() - if gs_windows_binary is not None: - return gs_windows_binary is not False - try: - subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) - return True - except OSError: - # No Ghostscript - return False + global gs_binary, gs_windows_binary + if gs_binary is None: + if sys.platform.startswith("win"): + if gs_windows_binary is None: + import shutil + + for binary in ("gswin32c", "gswin64c", "gs"): + if shutil.which(binary) is not None: + gs_windows_binary = binary + break + else: + gs_windows_binary = False + gs_binary = gs_windows_binary + else: + try: + subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) + gs_binary = "gs" + except OSError: + gs_binary = False + return gs_binary is not False def Ghostscript(tile, size, fp, scale=1, transparency=False): """Render an image using Ghostscript""" + global gs_binary + if not has_ghostscript(): + msg = "Unable to locate Ghostscript on paths" + raise OSError(msg) # Unpack decoder tile decoder, tile, offset, data = tile[0] @@ -120,7 +119,7 @@ def Ghostscript(tile, size, fp, scale=1, transparency=False): # Build Ghostscript command command = [ - "gs", + gs_binary, "-q", # quiet mode "-g%dx%d" % size, # set output geometry (pixels) "-r%fx%f" % res, # set input DPI (dots per inch) @@ -139,20 +138,6 @@ def Ghostscript(tile, size, fp, scale=1, transparency=False): "showpage", ] - gs_windows_binary = _get_windows_binary() - if gs_windows_binary is False: - try: - os.unlink(outfile) - if infile_temp: - os.unlink(infile_temp) - except OSError: - pass - - msg = "Unable to locate Ghostscript on paths" - raise OSError(msg) - elif gs_windows_binary is not None: - command[0] = gs_windows_binary - # push data through Ghostscript try: startupinfo = None From 3ee800956116da85cc7df9cd19a72e287900f492 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Sep 2023 22:22:42 +1000 Subject: [PATCH 3/4] Document gs_binary property --- docs/handbook/image-file-formats.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 2a42bdacba7..7f4eb3a71cc 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -88,8 +88,14 @@ in ``L``, ``RGB`` and ``CMYK`` modes. Loading ~~~~~~~ +To use Ghostscript, Pillow searches for the "gs" executable. On Windows, it +also searches for "gswin32c" and "gswin64c". If you would like to customise +this behaviour, ``EpsImagePlugin.gs_binary = "gswin64"`` will set the name of +the executable to use. ``EpsImagePlugin.gs_binary = False`` will prevent +Ghostscript from being used altogether. + If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load` -method with the following parameters to affect how Ghostscript renders the EPS +method with the following parameters to affect how Ghostscript renders the EPS. **scale** Affects the scale of the resultant rasterized image. If the EPS suggests From ffe2076fd67260f722cbbe9731bc00e3ff28616c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 Sep 2023 08:03:23 +1000 Subject: [PATCH 4/4] Shortened property description --- docs/handbook/image-file-formats.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 7f4eb3a71cc..f13475a898a 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -89,10 +89,9 @@ Loading ~~~~~~~ To use Ghostscript, Pillow searches for the "gs" executable. On Windows, it -also searches for "gswin32c" and "gswin64c". If you would like to customise -this behaviour, ``EpsImagePlugin.gs_binary = "gswin64"`` will set the name of -the executable to use. ``EpsImagePlugin.gs_binary = False`` will prevent -Ghostscript from being used altogether. +also searches for "gswin32c" and "gswin64c". To customise this behaviour, +``EpsImagePlugin.gs_binary = "gswin64"`` will set the name of the executable to +use. ``EpsImagePlugin.gs_binary = False`` will prevent Ghostscript use. If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load` method with the following parameters to affect how Ghostscript renders the EPS.