Coverage for pyguymer3/image/image2png.py: 4%
28 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-16 08:31 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-16 08:31 +0000
1#!/usr/bin/env python3
3# Define function ...
4def image2png(
5 img,
6 png,
7 /,
8 *,
9 chunksize = 1048576,
10 debug = __debug__,
11 exif = None,
12 exiftoolPath = None,
13 gifsiclePath = None,
14 jpegtranPath = None,
15 mode = "RGB",
16 optimise = True,
17 optipngPath = None,
18 screenHeight = -1,
19 screenWidth = -1,
20 strip = False,
21 timeout = 60.0,
22):
23 """Save an image as a PNG
25 This function accepts either a PIL Image or a file path and saves the image
26 as a PNG.
28 Parameters
29 ----------
30 img : PIL.Image.Image or str
31 the input PIL Image or path to the input image
32 png : str
33 the path to the output PNG
34 chunksize : int, optional
35 the size of the chunks of any files which are read in (in bytes)
36 debug : bool, optional
37 print debug messages (default False)
38 exif : dict, optional
39 a dictionary of EXIF data to save in the output PNG (default None)
40 exiftoolPath : str, optional
41 the path to the "exiftool" binary (if not provided then Python will attempt to
42 find the binary itself)
43 gifsiclePath : str, optional
44 the path to the "gifsicle" binary (if not provided then Python will attempt to
45 find the binary itself)
46 jpegtranPath : str, optional
47 the path to the "jpegtran" binary (if not provided then Python will attempt to
48 find the binary itself)
49 mode : str, optional
50 the mode of the outout PNG (default "RGB")
51 optimise : bool, optional
52 optimise the output PNG (default True)
53 optipngPath : str, optional
54 the path to the "optipng" binary (if not provided then Python will attempt to
55 find the binary itself)
56 screenHeight : int, optional
57 the height of the screen to downscale the input image to fit within,
58 currently only implemented if "img" is a str (default -1; integers less
59 than 100 imply no downscaling)
60 screenWidth : int, optional
61 the width of the screen to downscale the input image to fit within,
62 currently only implemented if "img" is a str (default -1; integers less
63 than 100 imply no downscaling)
64 strip : bool, optional
65 strip metadata from the output PNG (default False)
66 timeout : float, optional
67 the timeout for any requests/subprocess calls
69 Notes
70 -----
71 Copyright 2017 Thomas Guymer [1]_
73 References
74 ----------
75 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
76 """
78 # Import special modules ...
79 try:
80 import PIL
81 import PIL.Image
82 PIL.Image.MAX_IMAGE_PIXELS = 1024 * 1024 * 1024 # [px]
83 except:
84 raise Exception("\"PIL\" is not installed; run \"pip install --user Pillow\"") from None
86 # Import sub-functions ...
87 from .dict2exif import dict2exif
88 from .optimise_image import optimise_image
90 # Check input ...
91 if exif is not None and strip:
92 print("WARNING: You have provided EXIF data but then asked to strip metadata.")
94 # Find out what the user supplied ...
95 match img:
96 case str():
97 # Open image as RGB (even if it is paletted) ...
98 with PIL.Image.open(img) as iObj:
99 tmpImg = iObj.convert("RGB")
101 # Check if the user wants to scale the image down to fit within a
102 # screen size ...
103 if screenWidth >= 100 and screenHeight >= 100:
104 # Resize image in place ...
105 tmpImg.thumbnail(
106 (screenWidth, screenHeight),
107 resample = PIL.Image.Resampling.LANCZOS,
108 )
110 # Convert image to whatever mode the user asked for ...
111 tmpImg = tmpImg.convert(mode)
112 case PIL.Image.Image():
113 # Copy image as RGB (even if it is paletted) ...
114 tmpImg = img.convert("RGB")
116 # Check if the user wants to scale the image down to fit within a
117 # screen size ...
118 if screenWidth >= 100 and screenHeight >= 100:
119 # Resize image in place ...
120 tmpImg.thumbnail(
121 (screenWidth, screenHeight),
122 resample = PIL.Image.Resampling.LANCZOS,
123 )
125 # Convert image to whatever mode the user asked for ...
126 tmpImg = tmpImg.convert(mode)
127 case _:
128 # Crash ...
129 raise TypeError(f"\"img\" is an unexpected type ({repr(type(img))})") from None
131 # Save it as a PNG ...
132 # NOTE: See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#png
133 tmpImg.save(
134 png,
135 exif = dict2exif(exif, mode = mode),
136 optimise = optimise,
137 )
139 # Optimise PNG ...
140 if optimise or strip:
141 optimise_image(
142 png,
143 chunksize = chunksize,
144 debug = debug,
145 exiftoolPath = exiftoolPath,
146 gifsiclePath = gifsiclePath,
147 jpegtranPath = jpegtranPath,
148 optipngPath = optipngPath,
149 pool = None,
150 strip = strip,
151 timeout = timeout,
152 )