Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pillow-10.4.0-py3.8-linux-x86_64.egg/PIL/IptcImagePlugin.py: 24%

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

130 statements  

1# 

2# The Python Imaging Library. 

3# $Id$ 

4# 

5# IPTC/NAA file handling 

6# 

7# history: 

8# 1995-10-01 fl Created 

9# 1998-03-09 fl Cleaned up and added to PIL 

10# 2002-06-18 fl Added getiptcinfo helper 

11# 

12# Copyright (c) Secret Labs AB 1997-2002. 

13# Copyright (c) Fredrik Lundh 1995. 

14# 

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

16# 

17from __future__ import annotations 

18 

19from io import BytesIO 

20from typing import Sequence 

21 

22from . import Image, ImageFile 

23from ._binary import i16be as i16 

24from ._binary import i32be as i32 

25from ._deprecate import deprecate 

26 

27COMPRESSION = {1: "raw", 5: "jpeg"} 

28 

29 

30def __getattr__(name: str) -> bytes: 

31 if name == "PAD": 

32 deprecate("IptcImagePlugin.PAD", 12) 

33 return b"\0\0\0\0" 

34 msg = f"module '{__name__}' has no attribute '{name}'" 

35 raise AttributeError(msg) 

36 

37 

38# 

39# Helpers 

40 

41 

42def _i(c: bytes) -> int: 

43 return i32((b"\0\0\0\0" + c)[-4:]) 

44 

45 

46def _i8(c: int | bytes) -> int: 

47 return c if isinstance(c, int) else c[0] 

48 

49 

50def i(c: bytes) -> int: 

51 """.. deprecated:: 10.2.0""" 

52 deprecate("IptcImagePlugin.i", 12) 

53 return _i(c) 

54 

55 

56def dump(c: Sequence[int | bytes]) -> None: 

57 """.. deprecated:: 10.2.0""" 

58 deprecate("IptcImagePlugin.dump", 12) 

59 for i in c: 

60 print(f"{_i8(i):02x}", end=" ") 

61 print() 

62 

63 

64## 

65# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields 

66# from TIFF and JPEG files, use the <b>getiptcinfo</b> function. 

67 

68 

69class IptcImageFile(ImageFile.ImageFile): 

70 format = "IPTC" 

71 format_description = "IPTC/NAA" 

72 

73 def getint(self, key: tuple[int, int]) -> int: 

74 return _i(self.info[key]) 

75 

76 def field(self) -> tuple[tuple[int, int] | None, int]: 

77 # 

78 # get a IPTC field header 

79 s = self.fp.read(5) 

80 if not s.strip(b"\x00"): 

81 return None, 0 

82 

83 tag = s[1], s[2] 

84 

85 # syntax 

86 if s[0] != 0x1C or tag[0] not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 240]: 

87 msg = "invalid IPTC/NAA file" 

88 raise SyntaxError(msg) 

89 

90 # field size 

91 size = s[3] 

92 if size > 132: 

93 msg = "illegal field length in IPTC/NAA file" 

94 raise OSError(msg) 

95 elif size == 128: 

96 size = 0 

97 elif size > 128: 

98 size = _i(self.fp.read(size - 128)) 

99 else: 

100 size = i16(s, 3) 

101 

102 return tag, size 

103 

104 def _open(self) -> None: 

105 # load descriptive fields 

106 while True: 

107 offset = self.fp.tell() 

108 tag, size = self.field() 

109 if not tag or tag == (8, 10): 

110 break 

111 if size: 

112 tagdata = self.fp.read(size) 

113 else: 

114 tagdata = None 

115 if tag in self.info: 

116 if isinstance(self.info[tag], list): 

117 self.info[tag].append(tagdata) 

118 else: 

119 self.info[tag] = [self.info[tag], tagdata] 

120 else: 

121 self.info[tag] = tagdata 

122 

123 # mode 

124 layers = self.info[(3, 60)][0] 

125 component = self.info[(3, 60)][1] 

126 if (3, 65) in self.info: 

127 id = self.info[(3, 65)][0] - 1 

128 else: 

129 id = 0 

130 if layers == 1 and not component: 

131 self._mode = "L" 

132 elif layers == 3 and component: 

133 self._mode = "RGB"[id] 

134 elif layers == 4 and component: 

135 self._mode = "CMYK"[id] 

136 

137 # size 

138 self._size = self.getint((3, 20)), self.getint((3, 30)) 

139 

140 # compression 

141 try: 

142 compression = COMPRESSION[self.getint((3, 120))] 

143 except KeyError as e: 

144 msg = "Unknown IPTC image compression" 

145 raise OSError(msg) from e 

146 

147 # tile 

148 if tag == (8, 10): 

149 self.tile = [("iptc", (0, 0) + self.size, offset, compression)] 

150 

151 def load(self): 

152 if len(self.tile) != 1 or self.tile[0][0] != "iptc": 

153 return ImageFile.ImageFile.load(self) 

154 

155 offset, compression = self.tile[0][2:] 

156 

157 self.fp.seek(offset) 

158 

159 # Copy image data to temporary file 

160 o = BytesIO() 

161 if compression == "raw": 

162 # To simplify access to the extracted file, 

163 # prepend a PPM header 

164 o.write(b"P5\n%d %d\n255\n" % self.size) 

165 while True: 

166 type, size = self.field() 

167 if type != (8, 10): 

168 break 

169 while size > 0: 

170 s = self.fp.read(min(size, 8192)) 

171 if not s: 

172 break 

173 o.write(s) 

174 size -= len(s) 

175 

176 with Image.open(o) as _im: 

177 _im.load() 

178 self.im = _im.im 

179 

180 

181Image.register_open(IptcImageFile.format, IptcImageFile) 

182 

183Image.register_extension(IptcImageFile.format, ".iim") 

184 

185 

186def getiptcinfo(im): 

187 """ 

188 Get IPTC information from TIFF, JPEG, or IPTC file. 

189 

190 :param im: An image containing IPTC data. 

191 :returns: A dictionary containing IPTC information, or None if 

192 no IPTC information block was found. 

193 """ 

194 from . import JpegImagePlugin, TiffImagePlugin 

195 

196 data = None 

197 

198 if isinstance(im, IptcImageFile): 

199 # return info dictionary right away 

200 return im.info 

201 

202 elif isinstance(im, JpegImagePlugin.JpegImageFile): 

203 # extract the IPTC/NAA resource 

204 photoshop = im.info.get("photoshop") 

205 if photoshop: 

206 data = photoshop.get(0x0404) 

207 

208 elif isinstance(im, TiffImagePlugin.TiffImageFile): 

209 # get raw data from the IPTC/NAA tag (PhotoShop tags the data 

210 # as 4-byte integers, so we cannot use the get method...) 

211 try: 

212 data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] 

213 except (AttributeError, KeyError): 

214 pass 

215 

216 if data is None: 

217 return None # no properties 

218 

219 # create an IptcImagePlugin object without initializing it 

220 class FakeImage: 

221 pass 

222 

223 im = FakeImage() 

224 im.__class__ = IptcImageFile 

225 

226 # parse the IPTC information chunk 

227 im.info = {} 

228 im.fp = BytesIO(data) 

229 

230 try: 

231 im._open() 

232 except (IndexError, KeyError): 

233 pass # expected failure 

234 

235 return im.info