Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/xlwt/Bitmap.py: 12%

149 statements  

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

1# -*- coding: windows-1251 -*- 

2 

3# Portions are Copyright (C) 2005 Roman V. Kiseliov 

4# Portions are Copyright (c) 2004 Evgeny Filatov <fufff@users.sourceforge.net> 

5# Portions are Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel) 

6 

7from .BIFFRecords import BiffRecord 

8from struct import pack, unpack 

9 

10 

11def _size_col(sheet, col): 

12 return sheet.col_width(col) 

13 

14 

15def _size_row(sheet, row): 

16 return sheet.row_height(row) 

17 

18 

19def _position_image(sheet, row_start, col_start, x1, y1, width, height): 

20 """Calculate the vertices that define the position of the image as required by 

21 the OBJ record. 

22 

23 +------------+------------+ 

24 | A | B | 

25 +-----+------------+------------+ 

26 | |(x1,y1) | | 

27 | 1 |(A1)._______|______ | 

28 | | | | | 

29 | | | | | 

30 +-----+----| BITMAP |-----+ 

31 | | | | | 

32 | 2 | |______________. | 

33 | | | (B2)| 

34 | | | (x2,y2)| 

35 +---- +------------+------------+ 

36 

37 Example of a bitmap that covers some of the area from cell A1 to cell B2. 

38 

39 Based on the width and height of the bitmap we need to calculate 8 vars: 

40 col_start, row_start, col_end, row_end, x1, y1, x2, y2. 

41 The width and height of the cells are also variable and have to be taken into 

42 account. 

43 The values of col_start and row_start are passed in from the calling 

44 function. The values of col_end and row_end are calculated by subtracting 

45 the width and height of the bitmap from the width and height of the 

46 underlying cells. 

47 The vertices are expressed as a percentage of the underlying cell width as 

48 follows (rhs values are in pixels): 

49 

50 x1 = X / W *1024 

51 y1 = Y / H *256 

52 x2 = (X-1) / W *1024 

53 y2 = (Y-1) / H *256 

54 

55 Where: X is distance from the left side of the underlying cell 

56 Y is distance from the top of the underlying cell 

57 W is the width of the cell 

58 H is the height of the cell 

59 

60 Note: the SDK incorrectly states that the height should be expressed as a 

61 percentage of 1024. 

62 

63 col_start - Col containing upper left corner of object 

64 row_start - Row containing top left corner of object 

65 x1 - Distance to left side of object 

66 y1 - Distance to top of object 

67 width - Width of image frame 

68 height - Height of image frame 

69 

70 """ 

71 # Adjust start column for offsets that are greater than the col width 

72 while x1 >= _size_col(sheet, col_start): 

73 x1 -= _size_col(sheet, col_start) 

74 col_start += 1 

75 # Adjust start row for offsets that are greater than the row height 

76 while y1 >= _size_row(sheet, row_start): 

77 y1 -= _size_row(sheet, row_start) 

78 row_start += 1 

79 # Initialise end cell to the same as the start cell 

80 row_end = row_start # Row containing bottom right corner of object 

81 col_end = col_start # Col containing lower right corner of object 

82 width = width + x1 - 1 

83 height = height + y1 - 1 

84 # Subtract the underlying cell widths to find the end cell of the image 

85 while (width >= _size_col(sheet, col_end)): 

86 width -= _size_col(sheet, col_end) 

87 col_end += 1 

88 # Subtract the underlying cell heights to find the end cell of the image 

89 while (height >= _size_row(sheet, row_end)): 

90 height -= _size_row(sheet, row_end) 

91 row_end += 1 

92 # Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell 

93 # with zero height or width. 

94 if ((_size_col(sheet, col_start) == 0) or (_size_col(sheet, col_end) == 0) 

95 or (_size_row(sheet, row_start) == 0) or (_size_row(sheet, row_end) == 0)): 

96 return 

97 # Convert the pixel values to the percentage value expected by Excel 

98 x1 = int(float(x1) / _size_col(sheet, col_start) * 1024) 

99 y1 = int(float(y1) / _size_row(sheet, row_start) * 256) 

100 # Distance to right side of object 

101 x2 = int(float(width) / _size_col(sheet, col_end) * 1024) 

102 # Distance to bottom of object 

103 y2 = int(float(height) / _size_row(sheet, row_end) * 256) 

104 return (col_start, x1, row_start, y1, col_end, x2, row_end, y2) 

105 

106 

107class ObjBmpRecord(BiffRecord): 

108 _REC_ID = 0x005D # Record identifier 

109 

110 def __init__(self, row, col, sheet, im_data_bmp, x, y, scale_x, scale_y): 

111 # Scale the frame of the image. 

112 width = im_data_bmp.width * scale_x 

113 height = im_data_bmp.height * scale_y 

114 

115 # Calculate the vertices of the image and write the OBJ record 

116 coordinates = _position_image(sheet, row, col, x, y, width, height) 

117 # print coordinates 

118 col_start, x1, row_start, y1, col_end, x2, row_end, y2 = coordinates 

119 

120 """Store the OBJ record that precedes an IMDATA record. This could be generalise 

121 to support other Excel objects. 

122 

123 """ 

124 cObj = 0x0001 # Count of objects in file (set to 1) 

125 OT = 0x0008 # Object type. 8 = Picture 

126 id = 0x0001 # Object ID 

127 grbit = 0x0614 # Option flags 

128 colL = col_start # Col containing upper left corner of object 

129 dxL = x1 # Distance from left side of cell 

130 rwT = row_start # Row containing top left corner of object 

131 dyT = y1 # Distance from top of cell 

132 colR = col_end # Col containing lower right corner of object 

133 dxR = x2 # Distance from right of cell 

134 rwB = row_end # Row containing bottom right corner of object 

135 dyB = y2 # Distance from bottom of cell 

136 cbMacro = 0x0000 # Length of FMLA structure 

137 Reserved1 = 0x0000 # Reserved 

138 Reserved2 = 0x0000 # Reserved 

139 icvBack = 0x09 # Background colour 

140 icvFore = 0x09 # Foreground colour 

141 fls = 0x00 # Fill pattern 

142 fAuto = 0x00 # Automatic fill 

143 icv = 0x08 # Line colour 

144 lns = 0xff # Line style 

145 lnw = 0x01 # Line weight 

146 fAutoB = 0x00 # Automatic border 

147 frs = 0x0000 # Frame style 

148 cf = 0x0009 # Image format, 9 = bitmap 

149 Reserved3 = 0x0000 # Reserved 

150 cbPictFmla = 0x0000 # Length of FMLA structure 

151 Reserved4 = 0x0000 # Reserved 

152 grbit2 = 0x0001 # Option flags 

153 Reserved5 = 0x0000 # Reserved 

154 

155 data = pack("<L", cObj) 

156 data += pack("<H", OT) 

157 data += pack("<H", id) 

158 data += pack("<H", grbit) 

159 data += pack("<H", colL) 

160 data += pack("<H", dxL) 

161 data += pack("<H", rwT) 

162 data += pack("<H", dyT) 

163 data += pack("<H", colR) 

164 data += pack("<H", dxR) 

165 data += pack("<H", rwB) 

166 data += pack("<H", dyB) 

167 data += pack("<H", cbMacro) 

168 data += pack("<L", Reserved1) 

169 data += pack("<H", Reserved2) 

170 data += pack("<B", icvBack) 

171 data += pack("<B", icvFore) 

172 data += pack("<B", fls) 

173 data += pack("<B", fAuto) 

174 data += pack("<B", icv) 

175 data += pack("<B", lns) 

176 data += pack("<B", lnw) 

177 data += pack("<B", fAutoB) 

178 data += pack("<H", frs) 

179 data += pack("<L", cf) 

180 data += pack("<H", Reserved3) 

181 data += pack("<H", cbPictFmla) 

182 data += pack("<H", Reserved4) 

183 data += pack("<H", grbit2) 

184 data += pack("<L", Reserved5) 

185 

186 self._rec_data = data 

187 

188def _process_bitmap(bitmap): 

189 """Convert a 24 bit bitmap into the modified internal format used by Windows. 

190 This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the 

191 MSDN library. 

192 

193 """ 

194 # Open file and binmode the data in case the platform needs it. 

195 with open(bitmap, "rb") as fh: 

196 # Slurp the file into a string. 

197 data = fh.read() 

198 return _process_bitmap_data(data) 

199 

200def _process_bitmap_data(data): 

201 # Check that the file is big enough to be a bitmap. 

202 if len(data) <= 0x36: 

203 raise Exception("bitmap doesn't contain enough data.") 

204 # The first 2 bytes are used to identify the bitmap. 

205 if (data[:2] != b"BM"): 

206 raise Exception("bitmap doesn't appear to to be a valid bitmap image.") 

207 # Remove bitmap data: ID. 

208 data = data[2:] 

209 # Read and remove the bitmap size. This is more reliable than reading 

210 # the data size at offset 0x22. 

211 # 

212 size = unpack("<L", data[:4])[0] 

213 size -= 0x36 # Subtract size of bitmap header. 

214 size += 0x0C # Add size of BIFF header. 

215 data = data[4:] 

216 # Remove bitmap data: reserved, offset, header length. 

217 data = data[12:] 

218 # Read and remove the bitmap width and height. Verify the sizes. 

219 width, height = unpack("<LL", data[:8]) 

220 data = data[8:] 

221 if (width > 0xFFFF): 

222 raise Exception("bitmap: largest image width supported is 65k.") 

223 if (height > 0xFFFF): 

224 raise Exception("bitmap: largest image height supported is 65k.") 

225 # Read and remove the bitmap planes and bpp data. Verify them. 

226 planes, bitcount = unpack("<HH", data[:4]) 

227 data = data[4:] 

228 if (bitcount != 24): 

229 raise Exception("bitmap isn't a 24bit true color bitmap.") 

230 if (planes != 1): 

231 raise Exception("bitmap: only 1 plane supported in bitmap image.") 

232 # Read and remove the bitmap compression. Verify compression. 

233 compression = unpack("<L", data[:4])[0] 

234 data = data[4:] 

235 if (compression != 0): 

236 raise Exception("bitmap: compression not supported in bitmap image.") 

237 # Remove bitmap data: data size, hres, vres, colours, imp. colours. 

238 data = data[20:] 

239 # Add the BITMAPCOREHEADER data 

240 header = pack("<LHHHH", 0x000c, width, height, 0x01, 0x18) 

241 data = header + data 

242 return (width, height, size, data) 

243 

244 

245class ImRawDataBmpRecord(BiffRecord): 

246 _REC_ID = 0x007F 

247 

248 def __init__(self, data): 

249 """Insert a 24bit bitmap image in a worksheet. The main record required is 

250 IMDATA but it must be proceeded by a OBJ record to define its position. 

251 

252 """ 

253 BiffRecord.__init__(self) 

254 

255 self.width, self.height, self.size, data = _process_bitmap_data(data) 

256 self._write_imdata(data) 

257 

258 def _write_imdata(self, data): 

259 # Write the IMDATA record to store the bitmap data 

260 cf = 0x09 

261 env = 0x01 

262 lcb = self.size 

263 self._rec_data = pack("<HHL", cf, env, lcb) + data 

264 

265class ImDataBmpRecord(ImRawDataBmpRecord): 

266 def __init__(self, filename): 

267 """Insert a 24bit bitmap image in a worksheet. The main record required is 

268 IMDATA but it must be proceeded by a OBJ record to define its position. 

269 

270 """ 

271 BiffRecord.__init__(self) 

272 

273 self.width, self.height, self.size, data = _process_bitmap(filename) 

274 self._write_imdata(data) 

275