Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added gs_binary to control Ghostscript use on all platforms #7392

Merged
merged 4 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions Tests/test_file_eps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
assert_image_similar,
assert_image_similar_tofile,
hopper,
is_win32,
mark_if_feature_version,
skip_unless_feature,
)
Expand Down Expand Up @@ -98,6 +99,20 @@
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

Check warning on line 106 in Tests/test_file_eps.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_eps.py#L106

Added line #L106 was not covered by tests

if not is_win32():
assert EpsImagePlugin.gs_windows_binary is None
elif not HAS_GHOSTSCRIPT:
assert EpsImagePlugin.gs_windows_binary is False

Check warning on line 111 in Tests/test_file_eps.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_eps.py#L111

Added line #L111 was not covered by tests
else:
assert EpsImagePlugin.gs_windows_binary is not None


def test_invalid_file():
invalid_file = "Tests/images/flower.jpg"
with pytest.raises(SyntaxError):
Expand Down
8 changes: 7 additions & 1 deletion docs/handbook/image-file-formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bit more concise:

Suggested change
also searches for "gswin32c" and "gswin64c". If you would like to customise
also searches for "gswin32c" and "gswin64c". To customise

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I've pushed a commit so that I could rearrange the lines ending as well, and shortened another bit of text later on.

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
Expand Down
59 changes: 26 additions & 33 deletions src/PIL/EpsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,33 +37,39 @@
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")

gs_binary = None
gs_windows_binary = None
if sys.platform.startswith("win"):
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


def has_ghostscript():
if gs_windows_binary:
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
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

Check warning on line 56 in src/PIL/EpsImagePlugin.py

View check run for this annotation

Codecov / codecov/patch

src/PIL/EpsImagePlugin.py#L56

Added line #L56 was not covered by tests
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]
Expand Down Expand Up @@ -113,7 +119,7 @@

# 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)
Expand All @@ -132,19 +138,6 @@
"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)
command[0] = gs_windows_binary

# push data through Ghostscript
try:
startupinfo = None
Expand Down