Coverage for pyguymer3/image/image2png.py: 4%

25 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-08 18:47 +0000

1#!/usr/bin/env python3 

2 

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 

24 

25 This function accepts either a PIL Image or a file path and saves the image 

26 as a PNG. 

27 

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 

68 

69 Notes 

70 ----- 

71 Copyright 2017 Thomas Guymer [1]_ 

72 

73 References 

74 ---------- 

75 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3 

76 """ 

77 

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 

85 

86 # Import sub-functions ... 

87 from .dict2exif import dict2exif 

88 from .optimise_image import optimise_image 

89 

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.") 

93 

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") 

100 

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 ) 

109 

110 # Convert it to whatever mode the user asked for ... 

111 tmpImg = tmpImg.convert(mode) 

112 case PIL.Image.Image(): 

113 # Convert image to whatever mode the user asked for ... 

114 tmpImg = img.convert(mode) 

115 case _: 

116 # Crash ... 

117 raise TypeError(f"\"img\" is an unexpected type ({repr(type(img))})") from None 

118 

119 # Save it as a PNG ... 

120 # NOTE: See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#png 

121 tmpImg.save( 

122 png, 

123 exif = dict2exif(exif, mode = mode), 

124 optimise = optimise, 

125 ) 

126 

127 # Optimise PNG ... 

128 if optimise or strip: 

129 optimise_image( 

130 png, 

131 chunksize = chunksize, 

132 debug = debug, 

133 exiftoolPath = exiftoolPath, 

134 gifsiclePath = gifsiclePath, 

135 jpegtranPath = jpegtranPath, 

136 optipngPath = optipngPath, 

137 pool = None, 

138 strip = strip, 

139 timeout = timeout, 

140 )