Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/xlsxwriter/packager.py: 62%

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

473 statements  

1############################################################################### 

2# 

3# Packager - A class for writing the Excel XLSX Worksheet file. 

4# 

5# SPDX-License-Identifier: BSD-2-Clause 

6# Copyright 2013-2024, John McNamara, jmcnamara@cpan.org 

7# 

8 

9# Standard packages. 

10import os 

11import stat 

12import tempfile 

13from shutil import copy 

14 

15from io import StringIO 

16from io import BytesIO 

17 

18# Package imports. 

19from .app import App 

20from .contenttypes import ContentTypes 

21from .core import Core 

22from .custom import Custom 

23from .metadata import Metadata 

24from .relationships import Relationships 

25from .sharedstrings import SharedStrings 

26from .rich_value import RichValue 

27from .rich_value_types import RichValueTypes 

28from .rich_value_rel import RichValueRel 

29from .rich_value_structure import RichValueStructure 

30from .styles import Styles 

31from .theme import Theme 

32from .vml import Vml 

33from .table import Table 

34from .comments import Comments 

35from .exceptions import EmptyChartSeries 

36 

37 

38class Packager(object): 

39 """ 

40 A class for writing the Excel XLSX Packager file. 

41 

42 This module is used in conjunction with XlsxWriter to create an 

43 Excel XLSX container file. 

44 

45 From Wikipedia: The Open Packaging Conventions (OPC) is a 

46 container-file technology initially created by Microsoft to store 

47 a combination of XML and non-XML files that together form a single 

48 entity such as an Open XML Paper Specification (OpenXPS) 

49 document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions. 

50 

51 At its simplest an Excel XLSX file contains the following elements:: 

52 

53 ____ [Content_Types].xml 

54 | 

55 |____ docProps 

56 | |____ app.xml 

57 | |____ core.xml 

58 | 

59 |____ xl 

60 | |____ workbook.xml 

61 | |____ worksheets 

62 | | |____ sheet1.xml 

63 | | 

64 | |____ styles.xml 

65 | | 

66 | |____ theme 

67 | | |____ theme1.xml 

68 | | 

69 | |_____rels 

70 | |____ workbook.xml.rels 

71 | 

72 |_____rels 

73 |____ .rels 

74 

75 The Packager class coordinates the classes that represent the 

76 elements of the package and writes them into the XLSX file. 

77 

78 """ 

79 

80 ########################################################################### 

81 # 

82 # Public API. 

83 # 

84 ########################################################################### 

85 

86 def __init__(self): 

87 """ 

88 Constructor. 

89 

90 """ 

91 

92 super(Packager, self).__init__() 

93 

94 self.tmpdir = "" 

95 self.in_memory = False 

96 self.workbook = None 

97 self.worksheet_count = 0 

98 self.chartsheet_count = 0 

99 self.chart_count = 0 

100 self.drawing_count = 0 

101 self.table_count = 0 

102 self.num_vml_files = 0 

103 self.num_comment_files = 0 

104 self.named_ranges = [] 

105 self.filenames = [] 

106 

107 ########################################################################### 

108 # 

109 # Private API. 

110 # 

111 ########################################################################### 

112 

113 def _set_tmpdir(self, tmpdir): 

114 # Set an optional user defined temp directory. 

115 self.tmpdir = tmpdir 

116 

117 def _set_in_memory(self, in_memory): 

118 # Set the optional 'in_memory' mode. 

119 self.in_memory = in_memory 

120 

121 def _add_workbook(self, workbook): 

122 # Add the Excel::Writer::XLSX::Workbook object to the package. 

123 self.workbook = workbook 

124 self.chart_count = len(workbook.charts) 

125 self.drawing_count = len(workbook.drawings) 

126 self.num_vml_files = workbook.num_vml_files 

127 self.num_comment_files = workbook.num_comment_files 

128 self.named_ranges = workbook.named_ranges 

129 

130 for worksheet in self.workbook.worksheets(): 

131 if worksheet.is_chartsheet: 

132 self.chartsheet_count += 1 

133 else: 

134 self.worksheet_count += 1 

135 

136 def _create_package(self): 

137 # Write the xml files that make up the XLSX OPC package. 

138 self._write_content_types_file() 

139 self._write_root_rels_file() 

140 self._write_workbook_rels_file() 

141 self._write_worksheet_files() 

142 self._write_chartsheet_files() 

143 self._write_workbook_file() 

144 self._write_chart_files() 

145 self._write_drawing_files() 

146 self._write_vml_files() 

147 self._write_comment_files() 

148 self._write_table_files() 

149 self._write_shared_strings_file() 

150 self._write_styles_file() 

151 self._write_custom_file() 

152 self._write_theme_file() 

153 self._write_worksheet_rels_files() 

154 self._write_chartsheet_rels_files() 

155 self._write_drawing_rels_files() 

156 self._write_rich_value_rels_files() 

157 self._add_image_files() 

158 self._add_vba_project() 

159 self._add_vba_project_signature() 

160 self._write_vba_project_rels_file() 

161 self._write_core_file() 

162 self._write_app_file() 

163 self._write_metadata_file() 

164 self._write_rich_value_files() 

165 

166 return self.filenames 

167 

168 def _filename(self, xml_filename): 

169 # Create a temp filename to write the XML data to and store the Excel 

170 # filename to use as the name in the Zip container. 

171 if self.in_memory: 

172 os_filename = StringIO() 

173 else: 

174 (fd, os_filename) = tempfile.mkstemp(dir=self.tmpdir) 

175 os.close(fd) 

176 

177 self.filenames.append((os_filename, xml_filename, False)) 

178 

179 return os_filename 

180 

181 def _write_workbook_file(self): 

182 # Write the workbook.xml file. 

183 workbook = self.workbook 

184 

185 workbook._set_xml_writer(self._filename("xl/workbook.xml")) 

186 workbook._assemble_xml_file() 

187 

188 def _write_worksheet_files(self): 

189 # Write the worksheet files. 

190 index = 1 

191 for worksheet in self.workbook.worksheets(): 

192 if worksheet.is_chartsheet: 

193 continue 

194 

195 if worksheet.constant_memory: 

196 worksheet._opt_reopen() 

197 worksheet._write_single_row() 

198 

199 worksheet._set_xml_writer( 

200 self._filename("xl/worksheets/sheet" + str(index) + ".xml") 

201 ) 

202 worksheet._assemble_xml_file() 

203 index += 1 

204 

205 def _write_chartsheet_files(self): 

206 # Write the chartsheet files. 

207 index = 1 

208 for worksheet in self.workbook.worksheets(): 

209 if not worksheet.is_chartsheet: 

210 continue 

211 

212 worksheet._set_xml_writer( 

213 self._filename("xl/chartsheets/sheet" + str(index) + ".xml") 

214 ) 

215 worksheet._assemble_xml_file() 

216 index += 1 

217 

218 def _write_chart_files(self): 

219 # Write the chart files. 

220 if not self.workbook.charts: 

221 return 

222 

223 index = 1 

224 for chart in self.workbook.charts: 

225 # Check that the chart has at least one data series. 

226 if not chart.series: 

227 raise EmptyChartSeries( 

228 "Chart%d must contain at least one " 

229 "data series. See chart.add_series()." % index 

230 ) 

231 

232 chart._set_xml_writer( 

233 self._filename("xl/charts/chart" + str(index) + ".xml") 

234 ) 

235 chart._assemble_xml_file() 

236 index += 1 

237 

238 def _write_drawing_files(self): 

239 # Write the drawing files. 

240 if not self.drawing_count: 

241 return 

242 

243 index = 1 

244 for drawing in self.workbook.drawings: 

245 drawing._set_xml_writer( 

246 self._filename("xl/drawings/drawing" + str(index) + ".xml") 

247 ) 

248 drawing._assemble_xml_file() 

249 index += 1 

250 

251 def _write_vml_files(self): 

252 # Write the comment VML files. 

253 index = 1 

254 for worksheet in self.workbook.worksheets(): 

255 if not worksheet.has_vml and not worksheet.has_header_vml: 

256 continue 

257 if worksheet.has_vml: 

258 vml = Vml() 

259 vml._set_xml_writer( 

260 self._filename("xl/drawings/vmlDrawing" + str(index) + ".vml") 

261 ) 

262 vml._assemble_xml_file( 

263 worksheet.vml_data_id, 

264 worksheet.vml_shape_id, 

265 worksheet.comments_list, 

266 worksheet.buttons_list, 

267 ) 

268 index += 1 

269 

270 if worksheet.has_header_vml: 

271 vml = Vml() 

272 

273 vml._set_xml_writer( 

274 self._filename("xl/drawings/vmlDrawing" + str(index) + ".vml") 

275 ) 

276 vml._assemble_xml_file( 

277 worksheet.vml_header_id, 

278 worksheet.vml_header_id * 1024, 

279 None, 

280 None, 

281 worksheet.header_images_list, 

282 ) 

283 

284 self._write_vml_drawing_rels_file(worksheet, index) 

285 index += 1 

286 

287 def _write_comment_files(self): 

288 # Write the comment files. 

289 index = 1 

290 for worksheet in self.workbook.worksheets(): 

291 if not worksheet.has_comments: 

292 continue 

293 

294 comment = Comments() 

295 comment._set_xml_writer(self._filename("xl/comments" + str(index) + ".xml")) 

296 comment._assemble_xml_file(worksheet.comments_list) 

297 index += 1 

298 

299 def _write_shared_strings_file(self): 

300 # Write the sharedStrings.xml file. 

301 sst = SharedStrings() 

302 sst.string_table = self.workbook.str_table 

303 

304 if not self.workbook.str_table.count: 

305 return 

306 

307 sst._set_xml_writer(self._filename("xl/sharedStrings.xml")) 

308 sst._assemble_xml_file() 

309 

310 def _write_app_file(self): 

311 # Write the app.xml file. 

312 properties = self.workbook.doc_properties 

313 app = App() 

314 

315 # Add the Worksheet parts. 

316 worksheet_count = 0 

317 for worksheet in self.workbook.worksheets(): 

318 if worksheet.is_chartsheet: 

319 continue 

320 

321 # Don't write/count veryHidden sheets. 

322 if worksheet.hidden != 2: 

323 app._add_part_name(worksheet.name) 

324 worksheet_count += 1 

325 

326 # Add the Worksheet heading pairs. 

327 app._add_heading_pair(["Worksheets", worksheet_count]) 

328 

329 # Add the Chartsheet parts. 

330 for worksheet in self.workbook.worksheets(): 

331 if not worksheet.is_chartsheet: 

332 continue 

333 app._add_part_name(worksheet.name) 

334 

335 # Add the Chartsheet heading pairs. 

336 app._add_heading_pair(["Charts", self.chartsheet_count]) 

337 

338 # Add the Named Range heading pairs. 

339 if self.named_ranges: 

340 app._add_heading_pair(["Named Ranges", len(self.named_ranges)]) 

341 

342 # Add the Named Ranges parts. 

343 for named_range in self.named_ranges: 

344 app._add_part_name(named_range) 

345 

346 app._set_properties(properties) 

347 app.doc_security = self.workbook.read_only 

348 

349 app._set_xml_writer(self._filename("docProps/app.xml")) 

350 app._assemble_xml_file() 

351 

352 def _write_core_file(self): 

353 # Write the core.xml file. 

354 properties = self.workbook.doc_properties 

355 core = Core() 

356 

357 core._set_properties(properties) 

358 core._set_xml_writer(self._filename("docProps/core.xml")) 

359 core._assemble_xml_file() 

360 

361 def _write_metadata_file(self): 

362 # Write the metadata.xml file. 

363 if not self.workbook.has_metadata: 

364 return 

365 

366 metadata = Metadata() 

367 metadata.has_dynamic_functions = self.workbook.has_dynamic_functions 

368 metadata.num_embedded_images = len(self.workbook.embedded_images.images) 

369 

370 metadata._set_xml_writer(self._filename("xl/metadata.xml")) 

371 metadata._assemble_xml_file() 

372 

373 def _write_rich_value_files(self): 

374 

375 if not self.workbook.embedded_images.has_images(): 

376 return 

377 

378 self._write_rich_value() 

379 self._write_rich_value_types() 

380 self._write_rich_value_structure() 

381 self._write_rich_value_rel() 

382 

383 def _write_rich_value(self): 

384 # Write the rdrichvalue.xml file. 

385 filename = self._filename("xl/richData/rdrichvalue.xml") 

386 xml_file = RichValue() 

387 xml_file.embedded_images = self.workbook.embedded_images.images 

388 xml_file._set_xml_writer(filename) 

389 xml_file._assemble_xml_file() 

390 

391 def _write_rich_value_types(self): 

392 # Write the rdRichValueTypes.xml file. 

393 filename = self._filename("xl/richData/rdRichValueTypes.xml") 

394 xml_file = RichValueTypes() 

395 xml_file._set_xml_writer(filename) 

396 xml_file._assemble_xml_file() 

397 

398 def _write_rich_value_structure(self): 

399 # Write the rdrichvaluestructure.xml file. 

400 filename = self._filename("xl/richData/rdrichvaluestructure.xml") 

401 xml_file = RichValueStructure() 

402 xml_file.has_embedded_descriptions = self.workbook.has_embedded_descriptions 

403 xml_file._set_xml_writer(filename) 

404 xml_file._assemble_xml_file() 

405 

406 def _write_rich_value_rel(self): 

407 # Write the richValueRel.xml file. 

408 filename = self._filename("xl/richData/richValueRel.xml") 

409 xml_file = RichValueRel() 

410 xml_file.num_embedded_images = len(self.workbook.embedded_images.images) 

411 xml_file._set_xml_writer(filename) 

412 xml_file._assemble_xml_file() 

413 

414 def _write_custom_file(self): 

415 # Write the custom.xml file. 

416 properties = self.workbook.custom_properties 

417 custom = Custom() 

418 

419 if not len(properties): 

420 return 

421 

422 custom._set_properties(properties) 

423 custom._set_xml_writer(self._filename("docProps/custom.xml")) 

424 custom._assemble_xml_file() 

425 

426 def _write_content_types_file(self): 

427 # Write the ContentTypes.xml file. 

428 content = ContentTypes() 

429 content._add_image_types(self.workbook.image_types) 

430 

431 self._get_table_count() 

432 

433 worksheet_index = 1 

434 chartsheet_index = 1 

435 for worksheet in self.workbook.worksheets(): 

436 if worksheet.is_chartsheet: 

437 content._add_chartsheet_name("sheet" + str(chartsheet_index)) 

438 chartsheet_index += 1 

439 else: 

440 content._add_worksheet_name("sheet" + str(worksheet_index)) 

441 worksheet_index += 1 

442 

443 for i in range(1, self.chart_count + 1): 

444 content._add_chart_name("chart" + str(i)) 

445 

446 for i in range(1, self.drawing_count + 1): 

447 content._add_drawing_name("drawing" + str(i)) 

448 

449 if self.num_vml_files: 

450 content._add_vml_name() 

451 

452 for i in range(1, self.table_count + 1): 

453 content._add_table_name("table" + str(i)) 

454 

455 for i in range(1, self.num_comment_files + 1): 

456 content._add_comment_name("comments" + str(i)) 

457 

458 # Add the sharedString rel if there is string data in the workbook. 

459 if self.workbook.str_table.count: 

460 content._add_shared_strings() 

461 

462 # Add vbaProject (and optionally vbaProjectSignature) if present. 

463 if self.workbook.vba_project: 

464 content._add_vba_project() 

465 if self.workbook.vba_project_signature: 

466 content._add_vba_project_signature() 

467 

468 # Add the custom properties if present. 

469 if self.workbook.custom_properties: 

470 content._add_custom_properties() 

471 

472 # Add the metadata file if present. 

473 if self.workbook.has_metadata: 

474 content._add_metadata() 

475 

476 # Add the RichValue file if present. 

477 if self.workbook.embedded_images.has_images(): 

478 content._add_rich_value() 

479 

480 content._set_xml_writer(self._filename("[Content_Types].xml")) 

481 content._assemble_xml_file() 

482 

483 def _write_styles_file(self): 

484 # Write the style xml file. 

485 xf_formats = self.workbook.xf_formats 

486 palette = self.workbook.palette 

487 font_count = self.workbook.font_count 

488 num_formats = self.workbook.num_formats 

489 border_count = self.workbook.border_count 

490 fill_count = self.workbook.fill_count 

491 custom_colors = self.workbook.custom_colors 

492 dxf_formats = self.workbook.dxf_formats 

493 has_comments = self.workbook.has_comments 

494 

495 styles = Styles() 

496 styles._set_style_properties( 

497 [ 

498 xf_formats, 

499 palette, 

500 font_count, 

501 num_formats, 

502 border_count, 

503 fill_count, 

504 custom_colors, 

505 dxf_formats, 

506 has_comments, 

507 ] 

508 ) 

509 

510 styles._set_xml_writer(self._filename("xl/styles.xml")) 

511 styles._assemble_xml_file() 

512 

513 def _write_theme_file(self): 

514 # Write the theme xml file. 

515 theme = Theme() 

516 

517 theme._set_xml_writer(self._filename("xl/theme/theme1.xml")) 

518 theme._assemble_xml_file() 

519 

520 def _write_table_files(self): 

521 # Write the table files. 

522 index = 1 

523 for worksheet in self.workbook.worksheets(): 

524 table_props = worksheet.tables 

525 

526 if not table_props: 

527 continue 

528 

529 for table_props in table_props: 

530 table = Table() 

531 table._set_xml_writer( 

532 self._filename("xl/tables/table" + str(index) + ".xml") 

533 ) 

534 table._set_properties(table_props) 

535 table._assemble_xml_file() 

536 index += 1 

537 

538 def _get_table_count(self): 

539 # Count the table files. Required for the [Content_Types] file. 

540 for worksheet in self.workbook.worksheets(): 

541 for _ in worksheet.tables: 

542 self.table_count += 1 

543 

544 def _write_root_rels_file(self): 

545 # Write the _rels/.rels xml file. 

546 rels = Relationships() 

547 

548 rels._add_document_relationship("/officeDocument", "xl/workbook.xml") 

549 

550 rels._add_package_relationship("/metadata/core-properties", "docProps/core.xml") 

551 

552 rels._add_document_relationship("/extended-properties", "docProps/app.xml") 

553 

554 if self.workbook.custom_properties: 

555 rels._add_document_relationship("/custom-properties", "docProps/custom.xml") 

556 

557 rels._set_xml_writer(self._filename("_rels/.rels")) 

558 

559 rels._assemble_xml_file() 

560 

561 def _write_workbook_rels_file(self): 

562 # Write the _rels/.rels xml file. 

563 rels = Relationships() 

564 

565 worksheet_index = 1 

566 chartsheet_index = 1 

567 

568 for worksheet in self.workbook.worksheets(): 

569 if worksheet.is_chartsheet: 

570 rels._add_document_relationship( 

571 "/chartsheet", "chartsheets/sheet" + str(chartsheet_index) + ".xml" 

572 ) 

573 chartsheet_index += 1 

574 else: 

575 rels._add_document_relationship( 

576 "/worksheet", "worksheets/sheet" + str(worksheet_index) + ".xml" 

577 ) 

578 worksheet_index += 1 

579 

580 rels._add_document_relationship("/theme", "theme/theme1.xml") 

581 rels._add_document_relationship("/styles", "styles.xml") 

582 

583 # Add the sharedString rel if there is string data in the workbook. 

584 if self.workbook.str_table.count: 

585 rels._add_document_relationship("/sharedStrings", "sharedStrings.xml") 

586 

587 # Add vbaProject if present. 

588 if self.workbook.vba_project: 

589 rels._add_ms_package_relationship("/vbaProject", "vbaProject.bin") 

590 

591 # Add the metadata file if required. 

592 if self.workbook.has_metadata: 

593 rels._add_document_relationship("/sheetMetadata", "metadata.xml") 

594 

595 # Add the RichValue files if present. 

596 if self.workbook.embedded_images.has_images(): 

597 rels._add_rich_value_relationship() 

598 

599 rels._set_xml_writer(self._filename("xl/_rels/workbook.xml.rels")) 

600 rels._assemble_xml_file() 

601 

602 def _write_worksheet_rels_files(self): 

603 # Write data such as hyperlinks or drawings. 

604 index = 0 

605 for worksheet in self.workbook.worksheets(): 

606 if worksheet.is_chartsheet: 

607 continue 

608 

609 index += 1 

610 

611 external_links = ( 

612 worksheet.external_hyper_links 

613 + worksheet.external_drawing_links 

614 + worksheet.external_vml_links 

615 + worksheet.external_background_links 

616 + worksheet.external_table_links 

617 + worksheet.external_comment_links 

618 ) 

619 

620 if not external_links: 

621 continue 

622 

623 # Create the worksheet .rels dirs. 

624 rels = Relationships() 

625 

626 for link_data in external_links: 

627 rels._add_document_relationship(*link_data) 

628 

629 # Create .rels file such as /xl/worksheets/_rels/sheet1.xml.rels. 

630 rels._set_xml_writer( 

631 self._filename("xl/worksheets/_rels/sheet" + str(index) + ".xml.rels") 

632 ) 

633 rels._assemble_xml_file() 

634 

635 def _write_chartsheet_rels_files(self): 

636 # Write the chartsheet .rels files for links to drawing files. 

637 index = 0 

638 for worksheet in self.workbook.worksheets(): 

639 if not worksheet.is_chartsheet: 

640 continue 

641 

642 index += 1 

643 

644 external_links = ( 

645 worksheet.external_drawing_links + worksheet.external_vml_links 

646 ) 

647 

648 if not external_links: 

649 continue 

650 

651 # Create the chartsheet .rels xlsx_dir. 

652 rels = Relationships() 

653 

654 for link_data in external_links: 

655 rels._add_document_relationship(*link_data) 

656 

657 # Create .rels file such as /xl/chartsheets/_rels/sheet1.xml.rels. 

658 rels._set_xml_writer( 

659 self._filename("xl/chartsheets/_rels/sheet" + str(index) + ".xml.rels") 

660 ) 

661 rels._assemble_xml_file() 

662 

663 def _write_drawing_rels_files(self): 

664 # Write the drawing .rels files for worksheets with charts or drawings. 

665 index = 0 

666 for worksheet in self.workbook.worksheets(): 

667 if worksheet.drawing: 

668 index += 1 

669 

670 if not worksheet.drawing_links: 

671 continue 

672 

673 # Create the drawing .rels xlsx_dir. 

674 rels = Relationships() 

675 

676 for drawing_data in worksheet.drawing_links: 

677 rels._add_document_relationship(*drawing_data) 

678 

679 # Create .rels file such as /xl/drawings/_rels/sheet1.xml.rels. 

680 rels._set_xml_writer( 

681 self._filename("xl/drawings/_rels/drawing" + str(index) + ".xml.rels") 

682 ) 

683 rels._assemble_xml_file() 

684 

685 def _write_vml_drawing_rels_file(self, worksheet, index): 

686 # Write the vmlDdrawing .rels files for worksheets with images in 

687 # headers or footers. 

688 

689 # Create the drawing .rels dir. 

690 rels = Relationships() 

691 

692 for drawing_data in worksheet.vml_drawing_links: 

693 rels._add_document_relationship(*drawing_data) 

694 

695 # Create .rels file such as /xl/drawings/_rels/vmlDrawing1.vml.rels. 

696 rels._set_xml_writer( 

697 self._filename("xl/drawings/_rels/vmlDrawing" + str(index) + ".vml.rels") 

698 ) 

699 rels._assemble_xml_file() 

700 

701 def _write_vba_project_rels_file(self): 

702 # Write the vbaProject.rels xml file if signed macros exist. 

703 vba_project_signature = self.workbook.vba_project_signature 

704 

705 if not vba_project_signature: 

706 return 

707 

708 # Create the vbaProject .rels dir. 

709 rels = Relationships() 

710 

711 rels._add_ms_package_relationship( 

712 "/vbaProjectSignature", "vbaProjectSignature.bin" 

713 ) 

714 

715 rels._set_xml_writer(self._filename("xl/_rels/vbaProject.bin.rels")) 

716 rels._assemble_xml_file() 

717 

718 def _write_rich_value_rels_files(self): 

719 # Write the richValueRel.xml.rels for embedded images. 

720 if not self.workbook.embedded_images.has_images(): 

721 return 

722 

723 # Create the worksheet .rels dirs. 

724 rels = Relationships() 

725 

726 index = 1 

727 for image_data in self.workbook.embedded_images.images: 

728 file_type = image_data[1] 

729 image_file = f"../media/image{index}.{file_type}" 

730 rels._add_document_relationship("/image", image_file) 

731 index += 1 

732 

733 # Create .rels file such as /xl/worksheets/_rels/sheet1.xml.rels. 

734 rels._set_xml_writer(self._filename("/xl/richData/_rels/richValueRel.xml.rels")) 

735 

736 rels._assemble_xml_file() 

737 

738 def _add_image_files(self): 

739 # Write the /xl/media/image?.xml files. 

740 workbook = self.workbook 

741 index = 1 

742 

743 images = workbook.embedded_images.images + workbook.images 

744 

745 for image in images: 

746 filename = image[0] 

747 ext = "." + image[1] 

748 image_data = image[2] 

749 

750 xml_image_name = "xl/media/image" + str(index) + ext 

751 

752 if not self.in_memory: 

753 # In file mode we just write or copy the image file. 

754 os_filename = self._filename(xml_image_name) 

755 

756 if image_data: 

757 # The data is in a byte stream. Write it to the target. 

758 os_file = open(os_filename, mode="wb") 

759 os_file.write(image_data.getvalue()) 

760 os_file.close() 

761 else: 

762 copy(filename, os_filename) 

763 

764 # Allow copies of Windows read-only images to be deleted. 

765 try: 

766 os.chmod( 

767 os_filename, os.stat(os_filename).st_mode | stat.S_IWRITE 

768 ) 

769 except OSError: 

770 pass 

771 else: 

772 # For in-memory mode we read the image into a stream. 

773 if image_data: 

774 # The data is already in a byte stream. 

775 os_filename = image_data 

776 else: 

777 image_file = open(filename, mode="rb") 

778 image_data = image_file.read() 

779 os_filename = BytesIO(image_data) 

780 image_file.close() 

781 

782 self.filenames.append((os_filename, xml_image_name, True)) 

783 

784 index += 1 

785 

786 def _add_vba_project_signature(self): 

787 # Copy in a vbaProjectSignature.bin file. 

788 vba_project_signature = self.workbook.vba_project_signature 

789 vba_project_signature_is_stream = self.workbook.vba_project_signature_is_stream 

790 

791 if not vba_project_signature: 

792 return 

793 

794 xml_vba_signature_name = "xl/vbaProjectSignature.bin" 

795 

796 if not self.in_memory: 

797 # In file mode we just write or copy the VBA project signature file. 

798 os_filename = self._filename(xml_vba_signature_name) 

799 

800 if vba_project_signature_is_stream: 

801 # The data is in a byte stream. Write it to the target. 

802 os_file = open(os_filename, mode="wb") 

803 os_file.write(vba_project_signature.getvalue()) 

804 os_file.close() 

805 else: 

806 copy(vba_project_signature, os_filename) 

807 

808 else: 

809 # For in-memory mode we read the vba into a stream. 

810 if vba_project_signature_is_stream: 

811 # The data is already in a byte stream. 

812 os_filename = vba_project_signature 

813 else: 

814 vba_file = open(vba_project_signature, mode="rb") 

815 vba_data = vba_file.read() 

816 os_filename = BytesIO(vba_data) 

817 vba_file.close() 

818 

819 self.filenames.append((os_filename, xml_vba_signature_name, True)) 

820 

821 def _add_vba_project(self): 

822 # Copy in a vbaProject.bin file. 

823 vba_project = self.workbook.vba_project 

824 vba_project_is_stream = self.workbook.vba_project_is_stream 

825 

826 if not vba_project: 

827 return 

828 

829 xml_vba_name = "xl/vbaProject.bin" 

830 

831 if not self.in_memory: 

832 # In file mode we just write or copy the VBA file. 

833 os_filename = self._filename(xml_vba_name) 

834 

835 if vba_project_is_stream: 

836 # The data is in a byte stream. Write it to the target. 

837 os_file = open(os_filename, mode="wb") 

838 os_file.write(vba_project.getvalue()) 

839 os_file.close() 

840 else: 

841 copy(vba_project, os_filename) 

842 

843 else: 

844 # For in-memory mode we read the vba into a stream. 

845 if vba_project_is_stream: 

846 # The data is already in a byte stream. 

847 os_filename = vba_project 

848 else: 

849 vba_file = open(vba_project, mode="rb") 

850 vba_data = vba_file.read() 

851 os_filename = BytesIO(vba_data) 

852 vba_file.close() 

853 

854 self.filenames.append((os_filename, xml_vba_name, True))