############################################################################### # # ContentTypes - A class for writing the Excel XLSX ContentTypes file. # # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org # import copy from . import xmlwriter # Long namespace strings used in the class. APP_PACKAGE = "application/vnd.openxmlformats-package." APP_DOCUMENT = "application/vnd.openxmlformats-officedocument." defaults = [ ["rels", APP_PACKAGE + "relationships+xml"], ["xml", "application/xml"], ] overrides = [ ["/docProps/app.xml", APP_DOCUMENT + "extended-properties+xml"], ["/docProps/core.xml", APP_PACKAGE + "core-properties+xml"], ["/xl/styles.xml", APP_DOCUMENT + "spreadsheetml.styles+xml"], ["/xl/theme/theme1.xml", APP_DOCUMENT + "theme+xml"], ["/xl/workbook.xml", APP_DOCUMENT + "spreadsheetml.sheet.main+xml"], ] class ContentTypes(xmlwriter.XMLwriter): """ A class for writing the Excel XLSX ContentTypes file. """ ########################################################################### # # Public API. # ########################################################################### def __init__(self): """ Constructor. """ super().__init__() # Copy the defaults in case we need to change them. self.defaults = copy.deepcopy(defaults) self.overrides = copy.deepcopy(overrides) ########################################################################### # # Private API. # ########################################################################### def _assemble_xml_file(self): # Assemble and write the XML file. # Write the XML declaration. self._xml_declaration() self._write_types() self._write_defaults() self._write_overrides() self._xml_end_tag("Types") # Close the file. self._xml_close() def _add_default(self, default): # Add elements to the ContentTypes defaults. self.defaults.append(default) def _add_override(self, override): # Add elements to the ContentTypes overrides. self.overrides.append(override) def _add_worksheet_name(self, worksheet_name): # Add the name of a worksheet to the ContentTypes overrides. worksheet_name = "/xl/worksheets/" + worksheet_name + ".xml" self._add_override( (worksheet_name, APP_DOCUMENT + "spreadsheetml.worksheet+xml") ) def _add_chartsheet_name(self, chartsheet_name): # Add the name of a chartsheet to the ContentTypes overrides. chartsheet_name = "/xl/chartsheets/" + chartsheet_name + ".xml" self._add_override( (chartsheet_name, APP_DOCUMENT + "spreadsheetml.chartsheet+xml") ) def _add_chart_name(self, chart_name): # Add the name of a chart to the ContentTypes overrides. chart_name = "/xl/charts/" + chart_name + ".xml" self._add_override((chart_name, APP_DOCUMENT + "drawingml.chart+xml")) def _add_drawing_name(self, drawing_name): # Add the name of a drawing to the ContentTypes overrides. drawing_name = "/xl/drawings/" + drawing_name + ".xml" self._add_override((drawing_name, APP_DOCUMENT + "drawing+xml")) def _add_vml_name(self): # Add the name of a VML drawing to the ContentTypes defaults. self._add_default(("vml", APP_DOCUMENT + "vmlDrawing")) def _add_comment_name(self, comment_name): # Add the name of a comment to the ContentTypes overrides. comment_name = "/xl/" + comment_name + ".xml" self._add_override((comment_name, APP_DOCUMENT + "spreadsheetml.comments+xml")) def _add_shared_strings(self): # Add the sharedStrings link to the ContentTypes overrides. self._add_override( ("/xl/sharedStrings.xml", APP_DOCUMENT + "spreadsheetml.sharedStrings+xml") ) def _add_calc_chain(self): # Add the calcChain link to the ContentTypes overrides. self._add_override( ("/xl/calcChain.xml", APP_DOCUMENT + "spreadsheetml.calcChain+xml") ) def _add_image_types(self, image_types): # Add the image default types. for image_type in image_types: extension = image_type if image_type in ("wmf", "emf"): image_type = "x-" + image_type self._add_default((extension, "image/" + image_type)) def _add_table_name(self, table_name): # Add the name of a table to the ContentTypes overrides. table_name = "/xl/tables/" + table_name + ".xml" self._add_override((table_name, APP_DOCUMENT + "spreadsheetml.table+xml")) def _add_vba_project(self): # Add a vbaProject to the ContentTypes defaults. # Change the workbook.xml content-type from xlsx to xlsm. for i, override in enumerate(self.overrides): if override[0] == "/xl/workbook.xml": xlsm = "application/vnd.ms-excel.sheet.macroEnabled.main+xml" self.overrides[i][1] = xlsm self._add_default(("bin", "application/vnd.ms-office.vbaProject")) def _add_vba_project_signature(self): # Add a vbaProjectSignature to the ContentTypes overrides. self._add_override( ( "/xl/vbaProjectSignature.bin", "application/vnd.ms-office.vbaProjectSignature", ) ) def _add_custom_properties(self): # Add the custom properties to the ContentTypes overrides. self._add_override( ("/docProps/custom.xml", APP_DOCUMENT + "custom-properties+xml") ) def _add_metadata(self): # Add the metadata file to the ContentTypes overrides. self._add_override( ("/xl/metadata.xml", APP_DOCUMENT + "spreadsheetml.sheetMetadata+xml") ) def _add_feature_bag_property(self): # Add the featurePropertyBag file to the ContentTypes overrides. self._add_override( ( "/xl/featurePropertyBag/featurePropertyBag.xml", "application/vnd.ms-excel.featurepropertybag+xml", ) ) def _add_rich_value(self): # Add the richValue files to the ContentTypes overrides. self._add_override( ( "/xl/richData/rdRichValueTypes.xml", "application/vnd.ms-excel.rdrichvaluetypes+xml", ) ) self._add_override( ("/xl/richData/rdrichvalue.xml", "application/vnd.ms-excel.rdrichvalue+xml") ) self._add_override( ( "/xl/richData/rdrichvaluestructure.xml", "application/vnd.ms-excel.rdrichvaluestructure+xml", ) ) self._add_override( ( "/xl/richData/richValueRel.xml", "application/vnd.ms-excel.richvaluerel+xml", ) ) ########################################################################### # # XML methods. # ########################################################################### def _write_defaults(self): # Write out all of the <Default> types. for extension, content_type in self.defaults: self._xml_empty_tag( "Default", [("Extension", extension), ("ContentType", content_type)] ) def _write_overrides(self): # Write out all of the <Override> types. for part_name, content_type in self.overrides: self._xml_empty_tag( "Override", [("PartName", part_name), ("ContentType", content_type)] ) def _write_types(self): # Write the <Types> element. xmlns = "http://schemas.openxmlformats.org/package/2006/content-types" attributes = [ ( "xmlns", xmlns, ) ] self._xml_start_tag("Types", attributes) def _write_default(self, extension, content_type): # Write the <Default> element. attributes = [ ("Extension", extension), ("ContentType", content_type), ] self._xml_empty_tag("Default", attributes) def _write_override(self, part_name, content_type): # Write the <Override> element. attributes = [ ("PartName", part_name), ("ContentType", content_type), ] self._xml_empty_tag("Override", attributes)