Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/XpmImagePlugin.py: 56%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

86 statements  

1# 

2# The Python Imaging Library. 

3# $Id$ 

4# 

5# XPM File handling 

6# 

7# History: 

8# 1996-12-29 fl Created 

9# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) 

10# 

11# Copyright (c) Secret Labs AB 1997-2001. 

12# Copyright (c) Fredrik Lundh 1996-2001. 

13# 

14# See the README file for information on usage and redistribution. 

15# 

16from __future__ import annotations 

17 

18import re 

19 

20from . import Image, ImageFile, ImagePalette 

21from ._binary import o8 

22 

23# XPM header 

24xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') 

25 

26 

27def _accept(prefix: bytes) -> bool: 

28 return prefix.startswith(b"/* XPM */") 

29 

30 

31## 

32# Image plugin for X11 pixel maps. 

33 

34 

35class XpmImageFile(ImageFile.ImageFile): 

36 format = "XPM" 

37 format_description = "X11 Pixel Map" 

38 

39 def _open(self) -> None: 

40 assert self.fp is not None 

41 if not _accept(self.fp.read(9)): 

42 msg = "not an XPM file" 

43 raise SyntaxError(msg) 

44 

45 # skip forward to next string 

46 while True: 

47 line = self.fp.readline() 

48 if not line: 

49 msg = "broken XPM file" 

50 raise SyntaxError(msg) 

51 m = xpm_head.match(line) 

52 if m: 

53 break 

54 

55 self._size = int(m.group(1)), int(m.group(2)) 

56 

57 palette_length = int(m.group(3)) 

58 bpp = int(m.group(4)) 

59 

60 # 

61 # load palette description 

62 

63 palette = {} 

64 

65 for _ in range(palette_length): 

66 line = self.fp.readline().rstrip() 

67 

68 c = line[1 : bpp + 1] 

69 s = line[bpp + 1 : -2].split() 

70 

71 for i in range(0, len(s), 2): 

72 if s[i] == b"c": 

73 # process colour key 

74 rgb = s[i + 1] 

75 if rgb == b"None": 

76 self.info["transparency"] = c 

77 elif rgb.startswith(b"#"): 

78 rgb_int = int(rgb[1:], 16) 

79 palette[c] = ( 

80 o8((rgb_int >> 16) & 255) 

81 + o8((rgb_int >> 8) & 255) 

82 + o8(rgb_int & 255) 

83 ) 

84 else: 

85 # unknown colour 

86 msg = "cannot read this XPM file" 

87 raise ValueError(msg) 

88 break 

89 

90 else: 

91 # missing colour key 

92 msg = "cannot read this XPM file" 

93 raise ValueError(msg) 

94 

95 args: tuple[int, dict[bytes, bytes] | tuple[bytes, ...]] 

96 if palette_length > 256: 

97 self._mode = "RGB" 

98 args = (bpp, palette) 

99 else: 

100 self._mode = "P" 

101 self.palette = ImagePalette.raw("RGB", b"".join(palette.values())) 

102 args = (bpp, tuple(palette.keys())) 

103 

104 self.tile = [ImageFile._Tile("xpm", (0, 0) + self.size, self.fp.tell(), args)] 

105 

106 def load_read(self, read_bytes: int) -> bytes: 

107 # 

108 # load all image data in one chunk 

109 

110 xsize, ysize = self.size 

111 

112 assert self.fp is not None 

113 s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)] 

114 

115 return b"".join(s) 

116 

117 

118class XpmDecoder(ImageFile.PyDecoder): 

119 _pulls_fd = True 

120 

121 def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: 

122 assert self.fd is not None 

123 

124 data = bytearray() 

125 bpp, palette = self.args 

126 dest_length = self.state.xsize * self.state.ysize 

127 if self.mode == "RGB": 

128 dest_length *= 3 

129 pixel_header = False 

130 while len(data) < dest_length: 

131 line = self.fd.readline() 

132 if not line: 

133 break 

134 if line.rstrip() == b"/* pixels */" and not pixel_header: 

135 pixel_header = True 

136 continue 

137 line = b'"'.join(line.split(b'"')[1:-1]) 

138 for i in range(0, len(line), bpp): 

139 key = line[i : i + bpp] 

140 if self.mode == "RGB": 

141 data += palette[key] 

142 else: 

143 data += o8(palette.index(key)) 

144 self.set_as_raw(bytes(data)) 

145 return -1, 0 

146 

147 

148# 

149# Registry 

150 

151 

152Image.register_open(XpmImageFile.format, XpmImageFile, _accept) 

153Image.register_decoder("xpm", XpmDecoder) 

154 

155Image.register_extension(XpmImageFile.format, ".xpm") 

156 

157Image.register_mime(XpmImageFile.format, "image/xpm")