Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/PIL/MpoImagePlugin.py: 24%

62 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1# 

2# The Python Imaging Library. 

3# $Id$ 

4# 

5# MPO file handling 

6# 

7# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the 

8# Camera & Imaging Products Association) 

9# 

10# The multi-picture object combines multiple JPEG images (with a modified EXIF 

11# data format) into a single file. While it can theoretically be used much like 

12# a GIF animation, it is commonly used to represent 3D photographs and is (as 

13# of this writing) the most commonly used format by 3D cameras. 

14# 

15# History: 

16# 2014-03-13 Feneric Created 

17# 

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

19# 

20 

21from . import Image, ImageFile, JpegImagePlugin 

22from ._binary import i16be as i16 

23 

24# def _accept(prefix): 

25# return JpegImagePlugin._accept(prefix) 

26 

27 

28def _save(im, fp, filename): 

29 # Note that we can only save the current frame at present 

30 return JpegImagePlugin._save(im, fp, filename) 

31 

32 

33## 

34# Image plugin for MPO images. 

35 

36 

37class MpoImageFile(JpegImagePlugin.JpegImageFile): 

38 

39 format = "MPO" 

40 format_description = "MPO (CIPA DC-007)" 

41 _close_exclusive_fp_after_loading = False 

42 

43 def _open(self): 

44 self.fp.seek(0) # prep the fp in order to pass the JPEG test 

45 JpegImagePlugin.JpegImageFile._open(self) 

46 self._after_jpeg_open() 

47 

48 def _after_jpeg_open(self, mpheader=None): 

49 self._initial_size = self.size 

50 self.mpinfo = mpheader if mpheader is not None else self._getmp() 

51 self.n_frames = self.mpinfo[0xB001] 

52 self.__mpoffsets = [ 

53 mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002] 

54 ] 

55 self.__mpoffsets[0] = 0 

56 # Note that the following assertion will only be invalid if something 

57 # gets broken within JpegImagePlugin. 

58 assert self.n_frames == len(self.__mpoffsets) 

59 del self.info["mpoffset"] # no longer needed 

60 self.is_animated = self.n_frames > 1 

61 self._fp = self.fp # FIXME: hack 

62 self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame 

63 self.__frame = 0 

64 self.offset = 0 

65 # for now we can only handle reading and individual frame extraction 

66 self.readonly = 1 

67 

68 def load_seek(self, pos): 

69 self._fp.seek(pos) 

70 

71 def seek(self, frame): 

72 if not self._seek_check(frame): 

73 return 

74 self.fp = self._fp 

75 self.offset = self.__mpoffsets[frame] 

76 

77 self.fp.seek(self.offset + 2) # skip SOI marker 

78 segment = self.fp.read(2) 

79 if not segment: 

80 raise ValueError("No data found for frame") 

81 self._size = self._initial_size 

82 if i16(segment) == 0xFFE1: # APP1 

83 n = i16(self.fp.read(2)) - 2 

84 self.info["exif"] = ImageFile._safe_read(self.fp, n) 

85 self._reload_exif() 

86 

87 mptype = self.mpinfo[0xB002][frame]["Attribute"]["MPType"] 

88 if mptype.startswith("Large Thumbnail"): 

89 exif = self.getexif().get_ifd(0x8769) 

90 if 40962 in exif and 40963 in exif: 

91 self._size = (exif[40962], exif[40963]) 

92 elif "exif" in self.info: 

93 del self.info["exif"] 

94 self._reload_exif() 

95 

96 self.tile = [("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))] 

97 self.__frame = frame 

98 

99 def tell(self): 

100 return self.__frame 

101 

102 @staticmethod 

103 def adopt(jpeg_instance, mpheader=None): 

104 """ 

105 Transform the instance of JpegImageFile into 

106 an instance of MpoImageFile. 

107 After the call, the JpegImageFile is extended 

108 to be an MpoImageFile. 

109 

110 This is essentially useful when opening a JPEG 

111 file that reveals itself as an MPO, to avoid 

112 double call to _open. 

113 """ 

114 jpeg_instance.__class__ = MpoImageFile 

115 jpeg_instance._after_jpeg_open(mpheader) 

116 return jpeg_instance 

117 

118 

119# --------------------------------------------------------------------- 

120# Registry stuff 

121 

122# Note that since MPO shares a factory with JPEG, we do not need to do a 

123# separate registration for it here. 

124# Image.register_open(MpoImageFile.format, 

125# JpegImagePlugin.jpeg_factory, _accept) 

126Image.register_save(MpoImageFile.format, _save) 

127 

128Image.register_extension(MpoImageFile.format, ".mpo") 

129 

130Image.register_mime(MpoImageFile.format, "image/mpo")