aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/xlsxwriter/packager.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/xlsxwriter/packager.py')
-rw-r--r--.venv/lib/python3.12/site-packages/xlsxwriter/packager.py880
1 files changed, 880 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/xlsxwriter/packager.py b/.venv/lib/python3.12/site-packages/xlsxwriter/packager.py
new file mode 100644
index 00000000..17587f0a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/xlsxwriter/packager.py
@@ -0,0 +1,880 @@
+###############################################################################
+#
+# Packager - A class for writing the Excel XLSX Worksheet file.
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org
+#
+
+# Standard packages.
+import os
+import stat
+import tempfile
+from io import BytesIO, StringIO
+from shutil import copy
+
+# Package imports.
+from .app import App
+from .comments import Comments
+from .contenttypes import ContentTypes
+from .core import Core
+from .custom import Custom
+from .exceptions import EmptyChartSeries
+from .feature_property_bag import FeaturePropertyBag
+from .metadata import Metadata
+from .relationships import Relationships
+from .rich_value import RichValue
+from .rich_value_rel import RichValueRel
+from .rich_value_structure import RichValueStructure
+from .rich_value_types import RichValueTypes
+from .sharedstrings import SharedStrings
+from .styles import Styles
+from .table import Table
+from .theme import Theme
+from .vml import Vml
+
+
+class Packager:
+ """
+ A class for writing the Excel XLSX Packager file.
+
+ This module is used in conjunction with XlsxWriter to create an
+ Excel XLSX container file.
+
+ From Wikipedia: The Open Packaging Conventions (OPC) is a
+ container-file technology initially created by Microsoft to store
+ a combination of XML and non-XML files that together form a single
+ entity such as an Open XML Paper Specification (OpenXPS)
+ document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions.
+
+ At its simplest an Excel XLSX file contains the following elements::
+
+ ____ [Content_Types].xml
+ |
+ |____ docProps
+ | |____ app.xml
+ | |____ core.xml
+ |
+ |____ xl
+ | |____ workbook.xml
+ | |____ worksheets
+ | | |____ sheet1.xml
+ | |
+ | |____ styles.xml
+ | |
+ | |____ theme
+ | | |____ theme1.xml
+ | |
+ | |_____rels
+ | |____ workbook.xml.rels
+ |
+ |_____rels
+ |____ .rels
+
+ The Packager class coordinates the classes that represent the
+ elements of the package and writes them into the XLSX file.
+
+ """
+
+ ###########################################################################
+ #
+ # Public API.
+ #
+ ###########################################################################
+
+ def __init__(self):
+ """
+ Constructor.
+
+ """
+
+ super().__init__()
+
+ self.tmpdir = ""
+ self.in_memory = False
+ self.workbook = None
+ self.worksheet_count = 0
+ self.chartsheet_count = 0
+ self.chart_count = 0
+ self.drawing_count = 0
+ self.table_count = 0
+ self.num_vml_files = 0
+ self.num_comment_files = 0
+ self.named_ranges = []
+ self.filenames = []
+
+ ###########################################################################
+ #
+ # Private API.
+ #
+ ###########################################################################
+
+ def _set_tmpdir(self, tmpdir):
+ # Set an optional user defined temp directory.
+ self.tmpdir = tmpdir
+
+ def _set_in_memory(self, in_memory):
+ # Set the optional 'in_memory' mode.
+ self.in_memory = in_memory
+
+ def _add_workbook(self, workbook):
+ # Add the Excel::Writer::XLSX::Workbook object to the package.
+ self.workbook = workbook
+ self.chart_count = len(workbook.charts)
+ self.drawing_count = len(workbook.drawings)
+ self.num_vml_files = workbook.num_vml_files
+ self.num_comment_files = workbook.num_comment_files
+ self.named_ranges = workbook.named_ranges
+
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ self.chartsheet_count += 1
+ else:
+ self.worksheet_count += 1
+
+ def _create_package(self):
+ # Write the xml files that make up the XLSX OPC package.
+ self._write_content_types_file()
+ self._write_root_rels_file()
+ self._write_workbook_rels_file()
+ self._write_worksheet_files()
+ self._write_chartsheet_files()
+ self._write_workbook_file()
+ self._write_chart_files()
+ self._write_drawing_files()
+ self._write_vml_files()
+ self._write_comment_files()
+ self._write_table_files()
+ self._write_shared_strings_file()
+ self._write_styles_file()
+ self._write_custom_file()
+ self._write_theme_file()
+ self._write_worksheet_rels_files()
+ self._write_chartsheet_rels_files()
+ self._write_drawing_rels_files()
+ self._write_rich_value_rels_files()
+ self._add_image_files()
+ self._add_vba_project()
+ self._add_vba_project_signature()
+ self._write_vba_project_rels_file()
+ self._write_core_file()
+ self._write_app_file()
+ self._write_metadata_file()
+ self._write_feature_bag_property()
+ self._write_rich_value_files()
+
+ return self.filenames
+
+ def _filename(self, xml_filename):
+ # Create a temp filename to write the XML data to and store the Excel
+ # filename to use as the name in the Zip container.
+ if self.in_memory:
+ os_filename = StringIO()
+ else:
+ (fd, os_filename) = tempfile.mkstemp(dir=self.tmpdir)
+ os.close(fd)
+
+ self.filenames.append((os_filename, xml_filename, False))
+
+ return os_filename
+
+ def _write_workbook_file(self):
+ # Write the workbook.xml file.
+ workbook = self.workbook
+
+ workbook._set_xml_writer(self._filename("xl/workbook.xml"))
+ workbook._assemble_xml_file()
+
+ def _write_worksheet_files(self):
+ # Write the worksheet files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ continue
+
+ if worksheet.constant_memory:
+ worksheet._opt_reopen()
+ worksheet._write_single_row()
+
+ worksheet._set_xml_writer(
+ self._filename("xl/worksheets/sheet" + str(index) + ".xml")
+ )
+ worksheet._assemble_xml_file()
+ index += 1
+
+ def _write_chartsheet_files(self):
+ # Write the chartsheet files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.is_chartsheet:
+ continue
+
+ worksheet._set_xml_writer(
+ self._filename("xl/chartsheets/sheet" + str(index) + ".xml")
+ )
+ worksheet._assemble_xml_file()
+ index += 1
+
+ def _write_chart_files(self):
+ # Write the chart files.
+ if not self.workbook.charts:
+ return
+
+ index = 1
+ for chart in self.workbook.charts:
+ # Check that the chart has at least one data series.
+ if not chart.series:
+ raise EmptyChartSeries(
+ f"Chart{index} must contain at least one "
+ f"data series. See chart.add_series()."
+ )
+
+ chart._set_xml_writer(
+ self._filename("xl/charts/chart" + str(index) + ".xml")
+ )
+ chart._assemble_xml_file()
+ index += 1
+
+ def _write_drawing_files(self):
+ # Write the drawing files.
+ if not self.drawing_count:
+ return
+
+ index = 1
+ for drawing in self.workbook.drawings:
+ drawing._set_xml_writer(
+ self._filename("xl/drawings/drawing" + str(index) + ".xml")
+ )
+ drawing._assemble_xml_file()
+ index += 1
+
+ def _write_vml_files(self):
+ # Write the comment VML files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.has_vml and not worksheet.has_header_vml:
+ continue
+ if worksheet.has_vml:
+ vml = Vml()
+ vml._set_xml_writer(
+ self._filename("xl/drawings/vmlDrawing" + str(index) + ".vml")
+ )
+ vml._assemble_xml_file(
+ worksheet.vml_data_id,
+ worksheet.vml_shape_id,
+ worksheet.comments_list,
+ worksheet.buttons_list,
+ )
+ index += 1
+
+ if worksheet.has_header_vml:
+ vml = Vml()
+
+ vml._set_xml_writer(
+ self._filename("xl/drawings/vmlDrawing" + str(index) + ".vml")
+ )
+ vml._assemble_xml_file(
+ worksheet.vml_header_id,
+ worksheet.vml_header_id * 1024,
+ None,
+ None,
+ worksheet.header_images_list,
+ )
+
+ self._write_vml_drawing_rels_file(worksheet, index)
+ index += 1
+
+ def _write_comment_files(self):
+ # Write the comment files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.has_comments:
+ continue
+
+ comment = Comments()
+ comment._set_xml_writer(self._filename("xl/comments" + str(index) + ".xml"))
+ comment._assemble_xml_file(worksheet.comments_list)
+ index += 1
+
+ def _write_shared_strings_file(self):
+ # Write the sharedStrings.xml file.
+ sst = SharedStrings()
+ sst.string_table = self.workbook.str_table
+
+ if not self.workbook.str_table.count:
+ return
+
+ sst._set_xml_writer(self._filename("xl/sharedStrings.xml"))
+ sst._assemble_xml_file()
+
+ def _write_app_file(self):
+ # Write the app.xml file.
+ properties = self.workbook.doc_properties
+ app = App()
+
+ # Add the Worksheet parts.
+ worksheet_count = 0
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ continue
+
+ # Don't write/count veryHidden sheets.
+ if worksheet.hidden != 2:
+ app._add_part_name(worksheet.name)
+ worksheet_count += 1
+
+ # Add the Worksheet heading pairs.
+ app._add_heading_pair(["Worksheets", worksheet_count])
+
+ # Add the Chartsheet parts.
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.is_chartsheet:
+ continue
+ app._add_part_name(worksheet.name)
+
+ # Add the Chartsheet heading pairs.
+ app._add_heading_pair(["Charts", self.chartsheet_count])
+
+ # Add the Named Range heading pairs.
+ if self.named_ranges:
+ app._add_heading_pair(["Named Ranges", len(self.named_ranges)])
+
+ # Add the Named Ranges parts.
+ for named_range in self.named_ranges:
+ app._add_part_name(named_range)
+
+ app._set_properties(properties)
+ app.doc_security = self.workbook.read_only
+
+ app._set_xml_writer(self._filename("docProps/app.xml"))
+ app._assemble_xml_file()
+
+ def _write_core_file(self):
+ # Write the core.xml file.
+ properties = self.workbook.doc_properties
+ core = Core()
+
+ core._set_properties(properties)
+ core._set_xml_writer(self._filename("docProps/core.xml"))
+ core._assemble_xml_file()
+
+ def _write_metadata_file(self):
+ # Write the metadata.xml file.
+ if not self.workbook.has_metadata:
+ return
+
+ metadata = Metadata()
+ metadata.has_dynamic_functions = self.workbook.has_dynamic_functions
+ metadata.num_embedded_images = len(self.workbook.embedded_images.images)
+
+ metadata._set_xml_writer(self._filename("xl/metadata.xml"))
+ metadata._assemble_xml_file()
+
+ def _write_feature_bag_property(self):
+ # Write the featurePropertyBag.xml file.
+ feature_property_bags = self.workbook._has_feature_property_bags()
+ if not feature_property_bags:
+ return
+
+ property_bag = FeaturePropertyBag()
+ property_bag.feature_property_bags = feature_property_bags
+
+ property_bag._set_xml_writer(
+ self._filename("xl/featurePropertyBag/featurePropertyBag.xml")
+ )
+ property_bag._assemble_xml_file()
+
+ def _write_rich_value_files(self):
+
+ if not self.workbook.embedded_images.has_images():
+ return
+
+ self._write_rich_value()
+ self._write_rich_value_types()
+ self._write_rich_value_structure()
+ self._write_rich_value_rel()
+
+ def _write_rich_value(self):
+ # Write the rdrichvalue.xml file.
+ filename = self._filename("xl/richData/rdrichvalue.xml")
+ xml_file = RichValue()
+ xml_file.embedded_images = self.workbook.embedded_images.images
+ xml_file._set_xml_writer(filename)
+ xml_file._assemble_xml_file()
+
+ def _write_rich_value_types(self):
+ # Write the rdRichValueTypes.xml file.
+ filename = self._filename("xl/richData/rdRichValueTypes.xml")
+ xml_file = RichValueTypes()
+ xml_file._set_xml_writer(filename)
+ xml_file._assemble_xml_file()
+
+ def _write_rich_value_structure(self):
+ # Write the rdrichvaluestructure.xml file.
+ filename = self._filename("xl/richData/rdrichvaluestructure.xml")
+ xml_file = RichValueStructure()
+ xml_file.has_embedded_descriptions = self.workbook.has_embedded_descriptions
+ xml_file._set_xml_writer(filename)
+ xml_file._assemble_xml_file()
+
+ def _write_rich_value_rel(self):
+ # Write the richValueRel.xml file.
+ filename = self._filename("xl/richData/richValueRel.xml")
+ xml_file = RichValueRel()
+ xml_file.num_embedded_images = len(self.workbook.embedded_images.images)
+ xml_file._set_xml_writer(filename)
+ xml_file._assemble_xml_file()
+
+ def _write_custom_file(self):
+ # Write the custom.xml file.
+ properties = self.workbook.custom_properties
+ custom = Custom()
+
+ if not properties:
+ return
+
+ custom._set_properties(properties)
+ custom._set_xml_writer(self._filename("docProps/custom.xml"))
+ custom._assemble_xml_file()
+
+ def _write_content_types_file(self):
+ # Write the ContentTypes.xml file.
+ content = ContentTypes()
+ content._add_image_types(self.workbook.image_types)
+
+ self._get_table_count()
+
+ worksheet_index = 1
+ chartsheet_index = 1
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ content._add_chartsheet_name("sheet" + str(chartsheet_index))
+ chartsheet_index += 1
+ else:
+ content._add_worksheet_name("sheet" + str(worksheet_index))
+ worksheet_index += 1
+
+ for i in range(1, self.chart_count + 1):
+ content._add_chart_name("chart" + str(i))
+
+ for i in range(1, self.drawing_count + 1):
+ content._add_drawing_name("drawing" + str(i))
+
+ if self.num_vml_files:
+ content._add_vml_name()
+
+ for i in range(1, self.table_count + 1):
+ content._add_table_name("table" + str(i))
+
+ for i in range(1, self.num_comment_files + 1):
+ content._add_comment_name("comments" + str(i))
+
+ # Add the sharedString rel if there is string data in the workbook.
+ if self.workbook.str_table.count:
+ content._add_shared_strings()
+
+ # Add vbaProject (and optionally vbaProjectSignature) if present.
+ if self.workbook.vba_project:
+ content._add_vba_project()
+ if self.workbook.vba_project_signature:
+ content._add_vba_project_signature()
+
+ # Add the custom properties if present.
+ if self.workbook.custom_properties:
+ content._add_custom_properties()
+
+ # Add the metadata file if present.
+ if self.workbook.has_metadata:
+ content._add_metadata()
+
+ # Add the metadata file if present.
+ if self.workbook._has_feature_property_bags():
+ content._add_feature_bag_property()
+
+ # Add the RichValue file if present.
+ if self.workbook.embedded_images.has_images():
+ content._add_rich_value()
+
+ content._set_xml_writer(self._filename("[Content_Types].xml"))
+ content._assemble_xml_file()
+
+ def _write_styles_file(self):
+ # Write the style xml file.
+ xf_formats = self.workbook.xf_formats
+ palette = self.workbook.palette
+ font_count = self.workbook.font_count
+ num_formats = self.workbook.num_formats
+ border_count = self.workbook.border_count
+ fill_count = self.workbook.fill_count
+ custom_colors = self.workbook.custom_colors
+ dxf_formats = self.workbook.dxf_formats
+ has_comments = self.workbook.has_comments
+
+ styles = Styles()
+ styles._set_style_properties(
+ [
+ xf_formats,
+ palette,
+ font_count,
+ num_formats,
+ border_count,
+ fill_count,
+ custom_colors,
+ dxf_formats,
+ has_comments,
+ ]
+ )
+
+ styles._set_xml_writer(self._filename("xl/styles.xml"))
+ styles._assemble_xml_file()
+
+ def _write_theme_file(self):
+ # Write the theme xml file.
+ theme = Theme()
+
+ theme._set_xml_writer(self._filename("xl/theme/theme1.xml"))
+ theme._assemble_xml_file()
+
+ def _write_table_files(self):
+ # Write the table files.
+ index = 1
+ for worksheet in self.workbook.worksheets():
+ table_props = worksheet.tables
+
+ if not table_props:
+ continue
+
+ for table_props in table_props:
+ table = Table()
+ table._set_xml_writer(
+ self._filename("xl/tables/table" + str(index) + ".xml")
+ )
+ table._set_properties(table_props)
+ table._assemble_xml_file()
+ index += 1
+
+ def _get_table_count(self):
+ # Count the table files. Required for the [Content_Types] file.
+ for worksheet in self.workbook.worksheets():
+ for _ in worksheet.tables:
+ self.table_count += 1
+
+ def _write_root_rels_file(self):
+ # Write the _rels/.rels xml file.
+ rels = Relationships()
+
+ rels._add_document_relationship("/officeDocument", "xl/workbook.xml")
+
+ rels._add_package_relationship("/metadata/core-properties", "docProps/core.xml")
+
+ rels._add_document_relationship("/extended-properties", "docProps/app.xml")
+
+ if self.workbook.custom_properties:
+ rels._add_document_relationship("/custom-properties", "docProps/custom.xml")
+
+ rels._set_xml_writer(self._filename("_rels/.rels"))
+
+ rels._assemble_xml_file()
+
+ def _write_workbook_rels_file(self):
+ # Write the _rels/.rels xml file.
+ rels = Relationships()
+
+ worksheet_index = 1
+ chartsheet_index = 1
+
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ rels._add_document_relationship(
+ "/chartsheet", "chartsheets/sheet" + str(chartsheet_index) + ".xml"
+ )
+ chartsheet_index += 1
+ else:
+ rels._add_document_relationship(
+ "/worksheet", "worksheets/sheet" + str(worksheet_index) + ".xml"
+ )
+ worksheet_index += 1
+
+ rels._add_document_relationship("/theme", "theme/theme1.xml")
+ rels._add_document_relationship("/styles", "styles.xml")
+
+ # Add the sharedString rel if there is string data in the workbook.
+ if self.workbook.str_table.count:
+ rels._add_document_relationship("/sharedStrings", "sharedStrings.xml")
+
+ # Add vbaProject if present.
+ if self.workbook.vba_project:
+ rels._add_ms_package_relationship("/vbaProject", "vbaProject.bin")
+
+ # Add the metadata file if required.
+ if self.workbook.has_metadata:
+ rels._add_document_relationship("/sheetMetadata", "metadata.xml")
+
+ # Add the RichValue files if present.
+ if self.workbook.embedded_images.has_images():
+ rels._add_rich_value_relationship()
+
+ # Add the checkbox/FeaturePropertyBag file if present.
+ if self.workbook._has_feature_property_bags():
+ rels._add_feature_bag_relationship()
+
+ rels._set_xml_writer(self._filename("xl/_rels/workbook.xml.rels"))
+ rels._assemble_xml_file()
+
+ def _write_worksheet_rels_files(self):
+ # Write data such as hyperlinks or drawings.
+ index = 0
+ for worksheet in self.workbook.worksheets():
+ if worksheet.is_chartsheet:
+ continue
+
+ index += 1
+
+ external_links = (
+ worksheet.external_hyper_links
+ + worksheet.external_drawing_links
+ + worksheet.external_vml_links
+ + worksheet.external_background_links
+ + worksheet.external_table_links
+ + worksheet.external_comment_links
+ )
+
+ if not external_links:
+ continue
+
+ # Create the worksheet .rels dirs.
+ rels = Relationships()
+
+ for link_data in external_links:
+ rels._add_document_relationship(*link_data)
+
+ # Create .rels file such as /xl/worksheets/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(
+ self._filename("xl/worksheets/_rels/sheet" + str(index) + ".xml.rels")
+ )
+ rels._assemble_xml_file()
+
+ def _write_chartsheet_rels_files(self):
+ # Write the chartsheet .rels files for links to drawing files.
+ index = 0
+ for worksheet in self.workbook.worksheets():
+ if not worksheet.is_chartsheet:
+ continue
+
+ index += 1
+
+ external_links = (
+ worksheet.external_drawing_links + worksheet.external_vml_links
+ )
+
+ if not external_links:
+ continue
+
+ # Create the chartsheet .rels xlsx_dir.
+ rels = Relationships()
+
+ for link_data in external_links:
+ rels._add_document_relationship(*link_data)
+
+ # Create .rels file such as /xl/chartsheets/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(
+ self._filename("xl/chartsheets/_rels/sheet" + str(index) + ".xml.rels")
+ )
+ rels._assemble_xml_file()
+
+ def _write_drawing_rels_files(self):
+ # Write the drawing .rels files for worksheets with charts or drawings.
+ index = 0
+ for worksheet in self.workbook.worksheets():
+ if worksheet.drawing:
+ index += 1
+
+ if not worksheet.drawing_links:
+ continue
+
+ # Create the drawing .rels xlsx_dir.
+ rels = Relationships()
+
+ for drawing_data in worksheet.drawing_links:
+ rels._add_document_relationship(*drawing_data)
+
+ # Create .rels file such as /xl/drawings/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(
+ self._filename("xl/drawings/_rels/drawing" + str(index) + ".xml.rels")
+ )
+ rels._assemble_xml_file()
+
+ def _write_vml_drawing_rels_file(self, worksheet, index):
+ # Write the vmlDdrawing .rels files for worksheets with images in
+ # headers or footers.
+
+ # Create the drawing .rels dir.
+ rels = Relationships()
+
+ for drawing_data in worksheet.vml_drawing_links:
+ rels._add_document_relationship(*drawing_data)
+
+ # Create .rels file such as /xl/drawings/_rels/vmlDrawing1.vml.rels.
+ rels._set_xml_writer(
+ self._filename("xl/drawings/_rels/vmlDrawing" + str(index) + ".vml.rels")
+ )
+ rels._assemble_xml_file()
+
+ def _write_vba_project_rels_file(self):
+ # Write the vbaProject.rels xml file if signed macros exist.
+ vba_project_signature = self.workbook.vba_project_signature
+
+ if not vba_project_signature:
+ return
+
+ # Create the vbaProject .rels dir.
+ rels = Relationships()
+
+ rels._add_ms_package_relationship(
+ "/vbaProjectSignature", "vbaProjectSignature.bin"
+ )
+
+ rels._set_xml_writer(self._filename("xl/_rels/vbaProject.bin.rels"))
+ rels._assemble_xml_file()
+
+ def _write_rich_value_rels_files(self):
+ # Write the richValueRel.xml.rels for embedded images.
+ if not self.workbook.embedded_images.has_images():
+ return
+
+ # Create the worksheet .rels dirs.
+ rels = Relationships()
+
+ index = 1
+ for image_data in self.workbook.embedded_images.images:
+ file_type = image_data[1]
+ image_file = f"../media/image{index}.{file_type}"
+ rels._add_document_relationship("/image", image_file)
+ index += 1
+
+ # Create .rels file such as /xl/worksheets/_rels/sheet1.xml.rels.
+ rels._set_xml_writer(self._filename("/xl/richData/_rels/richValueRel.xml.rels"))
+
+ rels._assemble_xml_file()
+
+ def _add_image_files(self):
+ # pylint: disable=consider-using-with
+ # Write the /xl/media/image?.xml files.
+ workbook = self.workbook
+ index = 1
+
+ images = workbook.embedded_images.images + workbook.images
+
+ for image in images:
+ filename = image[0]
+ ext = "." + image[1]
+ image_data = image[2]
+
+ xml_image_name = "xl/media/image" + str(index) + ext
+
+ if not self.in_memory:
+ # In file mode we just write or copy the image file.
+ os_filename = self._filename(xml_image_name)
+
+ if image_data:
+ # The data is in a byte stream. Write it to the target.
+ os_file = open(os_filename, mode="wb")
+ os_file.write(image_data.getvalue())
+ os_file.close()
+ else:
+ copy(filename, os_filename)
+
+ # Allow copies of Windows read-only images to be deleted.
+ try:
+ os.chmod(
+ os_filename, os.stat(os_filename).st_mode | stat.S_IWRITE
+ )
+ except OSError:
+ pass
+ else:
+ # For in-memory mode we read the image into a stream.
+ if image_data:
+ # The data is already in a byte stream.
+ os_filename = image_data
+ else:
+ image_file = open(filename, mode="rb")
+ image_data = image_file.read()
+ os_filename = BytesIO(image_data)
+ image_file.close()
+
+ self.filenames.append((os_filename, xml_image_name, True))
+
+ index += 1
+
+ def _add_vba_project_signature(self):
+ # pylint: disable=consider-using-with
+ # Copy in a vbaProjectSignature.bin file.
+ vba_project_signature = self.workbook.vba_project_signature
+ vba_project_signature_is_stream = self.workbook.vba_project_signature_is_stream
+
+ if not vba_project_signature:
+ return
+
+ xml_vba_signature_name = "xl/vbaProjectSignature.bin"
+
+ if not self.in_memory:
+ # In file mode we just write or copy the VBA project signature file.
+ os_filename = self._filename(xml_vba_signature_name)
+
+ if vba_project_signature_is_stream:
+ # The data is in a byte stream. Write it to the target.
+ os_file = open(os_filename, mode="wb")
+ os_file.write(vba_project_signature.getvalue())
+ os_file.close()
+ else:
+ copy(vba_project_signature, os_filename)
+
+ else:
+ # For in-memory mode we read the vba into a stream.
+ if vba_project_signature_is_stream:
+ # The data is already in a byte stream.
+ os_filename = vba_project_signature
+ else:
+ vba_file = open(vba_project_signature, mode="rb")
+ vba_data = vba_file.read()
+ os_filename = BytesIO(vba_data)
+ vba_file.close()
+
+ self.filenames.append((os_filename, xml_vba_signature_name, True))
+
+ def _add_vba_project(self):
+ # pylint: disable=consider-using-with
+ # Copy in a vbaProject.bin file.
+ vba_project = self.workbook.vba_project
+ vba_project_is_stream = self.workbook.vba_project_is_stream
+
+ if not vba_project:
+ return
+
+ xml_vba_name = "xl/vbaProject.bin"
+
+ if not self.in_memory:
+ # In file mode we just write or copy the VBA file.
+ os_filename = self._filename(xml_vba_name)
+
+ if vba_project_is_stream:
+ # The data is in a byte stream. Write it to the target.
+ os_file = open(os_filename, mode="wb")
+ os_file.write(vba_project.getvalue())
+ os_file.close()
+ else:
+ copy(vba_project, os_filename)
+
+ else:
+ # For in-memory mode we read the vba into a stream.
+ if vba_project_is_stream:
+ # The data is already in a byte stream.
+ os_filename = vba_project
+ else:
+ vba_file = open(vba_project, mode="rb")
+ vba_data = vba_file.read()
+ os_filename = BytesIO(vba_data)
+ vba_file.close()
+
+ self.filenames.append((os_filename, xml_vba_name, True))