Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/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

484 statements  

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

2# 

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

4# 

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

6# 

7# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org 

8# 

9 

10# Standard packages. 

11import os 

12import stat 

13import tempfile 

14from io import BytesIO, StringIO 

15from shutil import copy 

16 

17# Package imports. 

18from .app import App 

19from .comments import Comments 

20from .contenttypes import ContentTypes 

21from .core import Core 

22from .custom import Custom 

23from .exceptions import EmptyChartSeries 

24from .feature_property_bag import FeaturePropertyBag 

25from .metadata import Metadata 

26from .relationships import Relationships 

27from .rich_value import RichValue 

28from .rich_value_rel import RichValueRel 

29from .rich_value_structure import RichValueStructure 

30from .rich_value_types import RichValueTypes 

31from .sharedstrings import SharedStrings 

32from .styles import Styles 

33from .table import Table 

34from .theme import Theme 

35from .vml import Vml 

36 

37 

38class Packager: 

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().__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_feature_bag_property() 

165 self._write_rich_value_files() 

166 

167 return self.filenames 

168 

169 def _filename(self, xml_filename): 

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

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

172 if self.in_memory: 

173 os_filename = StringIO() 

174 else: 

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

176 os.close(fd) 

177 

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

179 

180 return os_filename 

181 

182 def _write_workbook_file(self): 

183 # Write the workbook.xml file. 

184 workbook = self.workbook 

185 

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

187 workbook._assemble_xml_file() 

188 

189 def _write_worksheet_files(self): 

190 # Write the worksheet files. 

191 index = 1 

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

193 if worksheet.is_chartsheet: 

194 continue 

195 

196 if worksheet.constant_memory: 

197 worksheet._opt_reopen() 

198 worksheet._write_single_row() 

199 

200 worksheet._set_xml_writer( 

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

202 ) 

203 worksheet._assemble_xml_file() 

204 index += 1 

205 

206 def _write_chartsheet_files(self): 

207 # Write the chartsheet files. 

208 index = 1 

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

210 if not worksheet.is_chartsheet: 

211 continue 

212 

213 worksheet._set_xml_writer( 

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

215 ) 

216 worksheet._assemble_xml_file() 

217 index += 1 

218 

219 def _write_chart_files(self): 

220 # Write the chart files. 

221 if not self.workbook.charts: 

222 return 

223 

224 index = 1 

225 for chart in self.workbook.charts: 

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

227 if not chart.series: 

228 raise EmptyChartSeries( 

229 f"Chart{index} must contain at least one " 

230 f"data series. See chart.add_series()." 

231 ) 

232 

233 chart._set_xml_writer( 

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

235 ) 

236 chart._assemble_xml_file() 

237 index += 1 

238 

239 def _write_drawing_files(self): 

240 # Write the drawing files. 

241 if not self.drawing_count: 

242 return 

243 

244 index = 1 

245 for drawing in self.workbook.drawings: 

246 drawing._set_xml_writer( 

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

248 ) 

249 drawing._assemble_xml_file() 

250 index += 1 

251 

252 def _write_vml_files(self): 

253 # Write the comment VML files. 

254 index = 1 

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

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

257 continue 

258 if worksheet.has_vml: 

259 vml = Vml() 

260 vml._set_xml_writer( 

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

262 ) 

263 vml._assemble_xml_file( 

264 worksheet.vml_data_id, 

265 worksheet.vml_shape_id, 

266 worksheet.comments_list, 

267 worksheet.buttons_list, 

268 ) 

269 index += 1 

270 

271 if worksheet.has_header_vml: 

272 vml = Vml() 

273 

274 vml._set_xml_writer( 

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

276 ) 

277 vml._assemble_xml_file( 

278 worksheet.vml_header_id, 

279 worksheet.vml_header_id * 1024, 

280 None, 

281 None, 

282 worksheet.header_images_list, 

283 ) 

284 

285 self._write_vml_drawing_rels_file(worksheet, index) 

286 index += 1 

287 

288 def _write_comment_files(self): 

289 # Write the comment files. 

290 index = 1 

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

292 if not worksheet.has_comments: 

293 continue 

294 

295 comment = Comments() 

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

297 comment._assemble_xml_file(worksheet.comments_list) 

298 index += 1 

299 

300 def _write_shared_strings_file(self): 

301 # Write the sharedStrings.xml file. 

302 sst = SharedStrings() 

303 sst.string_table = self.workbook.str_table 

304 

305 if not self.workbook.str_table.count: 

306 return 

307 

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

309 sst._assemble_xml_file() 

310 

311 def _write_app_file(self): 

312 # Write the app.xml file. 

313 properties = self.workbook.doc_properties 

314 app = App() 

315 

316 # Add the Worksheet parts. 

317 worksheet_count = 0 

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

319 if worksheet.is_chartsheet: 

320 continue 

321 

322 # Don't write/count veryHidden sheets. 

323 if worksheet.hidden != 2: 

324 app._add_part_name(worksheet.name) 

325 worksheet_count += 1 

326 

327 # Add the Worksheet heading pairs. 

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

329 

330 # Add the Chartsheet parts. 

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

332 if not worksheet.is_chartsheet: 

333 continue 

334 app._add_part_name(worksheet.name) 

335 

336 # Add the Chartsheet heading pairs. 

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

338 

339 # Add the Named Range heading pairs. 

340 if self.named_ranges: 

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

342 

343 # Add the Named Ranges parts. 

344 for named_range in self.named_ranges: 

345 app._add_part_name(named_range) 

346 

347 app._set_properties(properties) 

348 app.doc_security = self.workbook.read_only 

349 

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

351 app._assemble_xml_file() 

352 

353 def _write_core_file(self): 

354 # Write the core.xml file. 

355 properties = self.workbook.doc_properties 

356 core = Core() 

357 

358 core._set_properties(properties) 

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

360 core._assemble_xml_file() 

361 

362 def _write_metadata_file(self): 

363 # Write the metadata.xml file. 

364 if not self.workbook.has_metadata: 

365 return 

366 

367 metadata = Metadata() 

368 metadata.has_dynamic_functions = self.workbook.has_dynamic_functions 

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

370 

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

372 metadata._assemble_xml_file() 

373 

374 def _write_feature_bag_property(self): 

375 # Write the featurePropertyBag.xml file. 

376 feature_property_bags = self.workbook._has_feature_property_bags() 

377 if not feature_property_bags: 

378 return 

379 

380 property_bag = FeaturePropertyBag() 

381 property_bag.feature_property_bags = feature_property_bags 

382 

383 property_bag._set_xml_writer( 

384 self._filename("xl/featurePropertyBag/featurePropertyBag.xml") 

385 ) 

386 property_bag._assemble_xml_file() 

387 

388 def _write_rich_value_files(self): 

389 

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

391 return 

392 

393 self._write_rich_value() 

394 self._write_rich_value_types() 

395 self._write_rich_value_structure() 

396 self._write_rich_value_rel() 

397 

398 def _write_rich_value(self): 

399 # Write the rdrichvalue.xml file. 

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

401 xml_file = RichValue() 

402 xml_file.embedded_images = self.workbook.embedded_images.images 

403 xml_file._set_xml_writer(filename) 

404 xml_file._assemble_xml_file() 

405 

406 def _write_rich_value_types(self): 

407 # Write the rdRichValueTypes.xml file. 

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

409 xml_file = RichValueTypes() 

410 xml_file._set_xml_writer(filename) 

411 xml_file._assemble_xml_file() 

412 

413 def _write_rich_value_structure(self): 

414 # Write the rdrichvaluestructure.xml file. 

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

416 xml_file = RichValueStructure() 

417 xml_file.has_embedded_descriptions = self.workbook.has_embedded_descriptions 

418 xml_file._set_xml_writer(filename) 

419 xml_file._assemble_xml_file() 

420 

421 def _write_rich_value_rel(self): 

422 # Write the richValueRel.xml file. 

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

424 xml_file = RichValueRel() 

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

426 xml_file._set_xml_writer(filename) 

427 xml_file._assemble_xml_file() 

428 

429 def _write_custom_file(self): 

430 # Write the custom.xml file. 

431 properties = self.workbook.custom_properties 

432 custom = Custom() 

433 

434 if not properties: 

435 return 

436 

437 custom._set_properties(properties) 

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

439 custom._assemble_xml_file() 

440 

441 def _write_content_types_file(self): 

442 # Write the ContentTypes.xml file. 

443 content = ContentTypes() 

444 content._add_image_types(self.workbook.image_types) 

445 

446 self._get_table_count() 

447 

448 worksheet_index = 1 

449 chartsheet_index = 1 

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

451 if worksheet.is_chartsheet: 

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

453 chartsheet_index += 1 

454 else: 

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

456 worksheet_index += 1 

457 

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

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

460 

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

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

463 

464 if self.num_vml_files: 

465 content._add_vml_name() 

466 

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

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

469 

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

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

472 

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

474 if self.workbook.str_table.count: 

475 content._add_shared_strings() 

476 

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

478 if self.workbook.vba_project: 

479 content._add_vba_project() 

480 if self.workbook.vba_project_signature: 

481 content._add_vba_project_signature() 

482 

483 # Add the custom properties if present. 

484 if self.workbook.custom_properties: 

485 content._add_custom_properties() 

486 

487 # Add the metadata file if present. 

488 if self.workbook.has_metadata: 

489 content._add_metadata() 

490 

491 # Add the metadata file if present. 

492 if self.workbook._has_feature_property_bags(): 

493 content._add_feature_bag_property() 

494 

495 # Add the RichValue file if present. 

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

497 content._add_rich_value() 

498 

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

500 content._assemble_xml_file() 

501 

502 def _write_styles_file(self): 

503 # Write the style xml file. 

504 xf_formats = self.workbook.xf_formats 

505 palette = self.workbook.palette 

506 font_count = self.workbook.font_count 

507 num_formats = self.workbook.num_formats 

508 border_count = self.workbook.border_count 

509 fill_count = self.workbook.fill_count 

510 custom_colors = self.workbook.custom_colors 

511 dxf_formats = self.workbook.dxf_formats 

512 has_comments = self.workbook.has_comments 

513 

514 styles = Styles() 

515 styles._set_style_properties( 

516 [ 

517 xf_formats, 

518 palette, 

519 font_count, 

520 num_formats, 

521 border_count, 

522 fill_count, 

523 custom_colors, 

524 dxf_formats, 

525 has_comments, 

526 ] 

527 ) 

528 

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

530 styles._assemble_xml_file() 

531 

532 def _write_theme_file(self): 

533 # Write the theme xml file. 

534 theme = Theme() 

535 

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

537 theme._assemble_xml_file() 

538 

539 def _write_table_files(self): 

540 # Write the table files. 

541 index = 1 

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

543 table_props = worksheet.tables 

544 

545 if not table_props: 

546 continue 

547 

548 for table_props in table_props: 

549 table = Table() 

550 table._set_xml_writer( 

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

552 ) 

553 table._set_properties(table_props) 

554 table._assemble_xml_file() 

555 index += 1 

556 

557 def _get_table_count(self): 

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

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

560 for _ in worksheet.tables: 

561 self.table_count += 1 

562 

563 def _write_root_rels_file(self): 

564 # Write the _rels/.rels xml file. 

565 rels = Relationships() 

566 

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

568 

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

570 

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

572 

573 if self.workbook.custom_properties: 

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

575 

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

577 

578 rels._assemble_xml_file() 

579 

580 def _write_workbook_rels_file(self): 

581 # Write the _rels/.rels xml file. 

582 rels = Relationships() 

583 

584 worksheet_index = 1 

585 chartsheet_index = 1 

586 

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

588 if worksheet.is_chartsheet: 

589 rels._add_document_relationship( 

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

591 ) 

592 chartsheet_index += 1 

593 else: 

594 rels._add_document_relationship( 

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

596 ) 

597 worksheet_index += 1 

598 

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

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

601 

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

603 if self.workbook.str_table.count: 

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

605 

606 # Add vbaProject if present. 

607 if self.workbook.vba_project: 

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

609 

610 # Add the metadata file if required. 

611 if self.workbook.has_metadata: 

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

613 

614 # Add the RichValue files if present. 

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

616 rels._add_rich_value_relationship() 

617 

618 # Add the checkbox/FeaturePropertyBag file if present. 

619 if self.workbook._has_feature_property_bags(): 

620 rels._add_feature_bag_relationship() 

621 

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

623 rels._assemble_xml_file() 

624 

625 def _write_worksheet_rels_files(self): 

626 # Write data such as hyperlinks or drawings. 

627 index = 0 

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

629 if worksheet.is_chartsheet: 

630 continue 

631 

632 index += 1 

633 

634 external_links = ( 

635 worksheet.external_hyper_links 

636 + worksheet.external_drawing_links 

637 + worksheet.external_vml_links 

638 + worksheet.external_background_links 

639 + worksheet.external_table_links 

640 + worksheet.external_comment_links 

641 ) 

642 

643 if not external_links: 

644 continue 

645 

646 # Create the worksheet .rels dirs. 

647 rels = Relationships() 

648 

649 for link_data in external_links: 

650 rels._add_document_relationship(*link_data) 

651 

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

653 rels._set_xml_writer( 

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

655 ) 

656 rels._assemble_xml_file() 

657 

658 def _write_chartsheet_rels_files(self): 

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

660 index = 0 

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

662 if not worksheet.is_chartsheet: 

663 continue 

664 

665 index += 1 

666 

667 external_links = ( 

668 worksheet.external_drawing_links + worksheet.external_vml_links 

669 ) 

670 

671 if not external_links: 

672 continue 

673 

674 # Create the chartsheet .rels xlsx_dir. 

675 rels = Relationships() 

676 

677 for link_data in external_links: 

678 rels._add_document_relationship(*link_data) 

679 

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

681 rels._set_xml_writer( 

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

683 ) 

684 rels._assemble_xml_file() 

685 

686 def _write_drawing_rels_files(self): 

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

688 index = 0 

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

690 if worksheet.drawing: 

691 index += 1 

692 

693 if not worksheet.drawing_links: 

694 continue 

695 

696 # Create the drawing .rels xlsx_dir. 

697 rels = Relationships() 

698 

699 for drawing_data in worksheet.drawing_links: 

700 rels._add_document_relationship(*drawing_data) 

701 

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

703 rels._set_xml_writer( 

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

705 ) 

706 rels._assemble_xml_file() 

707 

708 def _write_vml_drawing_rels_file(self, worksheet, index): 

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

710 # headers or footers. 

711 

712 # Create the drawing .rels dir. 

713 rels = Relationships() 

714 

715 for drawing_data in worksheet.vml_drawing_links: 

716 rels._add_document_relationship(*drawing_data) 

717 

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

719 rels._set_xml_writer( 

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

721 ) 

722 rels._assemble_xml_file() 

723 

724 def _write_vba_project_rels_file(self): 

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

726 vba_project_signature = self.workbook.vba_project_signature 

727 

728 if not vba_project_signature: 

729 return 

730 

731 # Create the vbaProject .rels dir. 

732 rels = Relationships() 

733 

734 rels._add_ms_package_relationship( 

735 "/vbaProjectSignature", "vbaProjectSignature.bin" 

736 ) 

737 

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

739 rels._assemble_xml_file() 

740 

741 def _write_rich_value_rels_files(self): 

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

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

744 return 

745 

746 # Create the worksheet .rels dirs. 

747 rels = Relationships() 

748 

749 index = 1 

750 for image in self.workbook.embedded_images.images: 

751 image_extension = image.image_type.lower() 

752 image_file = f"../media/image{index}.{image_extension}" 

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

754 index += 1 

755 

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

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

758 

759 rels._assemble_xml_file() 

760 

761 def _add_image_files(self): 

762 # pylint: disable=consider-using-with 

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

764 workbook = self.workbook 

765 index = 1 

766 

767 images = workbook.embedded_images.images + workbook.images 

768 

769 for image in images: 

770 xml_image_name = ( 

771 "xl/media/image" + str(index) + "." + image._image_extension 

772 ) 

773 

774 if not self.in_memory: 

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

776 os_filename = self._filename(xml_image_name) 

777 

778 if image.image_data: 

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

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

781 os_file.write(image.image_data.getvalue()) 

782 os_file.close() 

783 else: 

784 copy(image.filename, os_filename) 

785 

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

787 try: 

788 os.chmod( 

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

790 ) 

791 except OSError: 

792 pass 

793 else: 

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

795 if image.image_data: 

796 # The data is already in a byte stream. 

797 os_filename = image.image_data 

798 else: 

799 image_file = open(image.filename, mode="rb") 

800 image_data = image_file.read() 

801 os_filename = BytesIO(image_data) 

802 image_file.close() 

803 

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

805 

806 index += 1 

807 

808 def _add_vba_project_signature(self): 

809 # pylint: disable=consider-using-with 

810 # Copy in a vbaProjectSignature.bin file. 

811 vba_project_signature = self.workbook.vba_project_signature 

812 vba_project_signature_is_stream = self.workbook.vba_project_signature_is_stream 

813 

814 if not vba_project_signature: 

815 return 

816 

817 xml_vba_signature_name = "xl/vbaProjectSignature.bin" 

818 

819 if not self.in_memory: 

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

821 os_filename = self._filename(xml_vba_signature_name) 

822 

823 if vba_project_signature_is_stream: 

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

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

826 os_file.write(vba_project_signature.getvalue()) 

827 os_file.close() 

828 else: 

829 copy(vba_project_signature, os_filename) 

830 

831 else: 

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

833 if vba_project_signature_is_stream: 

834 # The data is already in a byte stream. 

835 os_filename = vba_project_signature 

836 else: 

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

838 vba_data = vba_file.read() 

839 os_filename = BytesIO(vba_data) 

840 vba_file.close() 

841 

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

843 

844 def _add_vba_project(self): 

845 # pylint: disable=consider-using-with 

846 # Copy in a vbaProject.bin file. 

847 vba_project = self.workbook.vba_project 

848 vba_project_is_stream = self.workbook.vba_project_is_stream 

849 

850 if not vba_project: 

851 return 

852 

853 xml_vba_name = "xl/vbaProject.bin" 

854 

855 if not self.in_memory: 

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

857 os_filename = self._filename(xml_vba_name) 

858 

859 if vba_project_is_stream: 

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

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

862 os_file.write(vba_project.getvalue()) 

863 os_file.close() 

864 else: 

865 copy(vba_project, os_filename) 

866 

867 else: 

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

869 if vba_project_is_stream: 

870 # The data is already in a byte stream. 

871 os_filename = vba_project 

872 else: 

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

874 vba_data = vba_file.read() 

875 os_filename = BytesIO(vba_data) 

876 vba_file.close() 

877 

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