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
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1# -*- coding: windows-1251 -*-
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)
7from .BIFFRecords import BiffRecord
8from struct import pack, unpack
11def _size_col(sheet, col):
12 return sheet.col_width(col)
15def _size_row(sheet, row):
16 return sheet.row_height(row)
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.
23 +------------+------------+
24 | A | B |
25 +-----+------------+------------+
26 | |(x1,y1) | |
27 | 1 |(A1)._______|______ |
28 | | | | |
29 | | | | |
30 +-----+----| BITMAP |-----+
31 | | | | |
32 | 2 | |______________. |
33 | | | (B2)|
34 | | | (x2,y2)|
35 +---- +------------+------------+
37 Example of a bitmap that covers some of the area from cell A1 to cell B2.
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):
50 x1 = X / W *1024
51 y1 = Y / H *256
52 x2 = (X-1) / W *1024
53 y2 = (Y-1) / H *256
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
60 Note: the SDK incorrectly states that the height should be expressed as a
61 percentage of 1024.
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
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)
107class ObjBmpRecord(BiffRecord):
108 _REC_ID = 0x005D # Record identifier
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
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
120 """Store the OBJ record that precedes an IMDATA record. This could be generalise
121 to support other Excel objects.
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
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)
186 self._rec_data = data
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.
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)
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)
245class ImRawDataBmpRecord(BiffRecord):
246 _REC_ID = 0x007F
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.
252 """
253 BiffRecord.__init__(self)
255 self.width, self.height, self.size, data = _process_bitmap_data(data)
256 self._write_imdata(data)
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
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.
270 """
271 BiffRecord.__init__(self)
273 self.width, self.height, self.size, data = _process_bitmap(filename)
274 self._write_imdata(data)