diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py b/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py new file mode 100644 index 00000000..41da07f4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py @@ -0,0 +1,194 @@ +# Copyright (c) 2010-2024 openpyxl + +""" +File manifest +""" +from mimetypes import MimeTypes +import os.path + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import String, Sequence +from openpyxl.xml.functions import fromstring +from openpyxl.xml.constants import ( + ARC_CONTENT_TYPES, + ARC_THEME, + ARC_STYLE, + THEME_TYPE, + STYLES_TYPE, + CONTYPES_NS, + ACTIVEX, + CTRL, + VBA, +) +from openpyxl.xml.functions import tostring + +# initialise mime-types +mimetypes = MimeTypes() +mimetypes.add_type('application/xml', ".xml") +mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels") +mimetypes.add_type("application/vnd.ms-office.vbaProject", ".bin") +mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml") +mimetypes.add_type("image/x-emf", ".emf") + + +class FileExtension(Serialisable): + + tagname = "Default" + + Extension = String() + ContentType = String() + + def __init__(self, Extension, ContentType): + self.Extension = Extension + self.ContentType = ContentType + + +class Override(Serialisable): + + tagname = "Override" + + PartName = String() + ContentType = String() + + def __init__(self, PartName, ContentType): + self.PartName = PartName + self.ContentType = ContentType + + +DEFAULT_TYPES = [ + FileExtension("rels", "application/vnd.openxmlformats-package.relationships+xml"), + FileExtension("xml", "application/xml"), +] + +DEFAULT_OVERRIDE = [ + Override("/" + ARC_STYLE, STYLES_TYPE), # Styles + Override("/" + ARC_THEME, THEME_TYPE), # Theme + Override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"), + Override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml") +] + + +class Manifest(Serialisable): + + tagname = "Types" + + Default = Sequence(expected_type=FileExtension, unique=True) + Override = Sequence(expected_type=Override, unique=True) + path = "[Content_Types].xml" + + __elements__ = ("Default", "Override") + + def __init__(self, + Default=(), + Override=(), + ): + if not Default: + Default = DEFAULT_TYPES + self.Default = Default + if not Override: + Override = DEFAULT_OVERRIDE + self.Override = Override + + + @property + def filenames(self): + return [part.PartName for part in self.Override] + + + @property + def extensions(self): + """ + Map content types to file extensions + Skip parts without extensions + """ + exts = {os.path.splitext(part.PartName)[-1] for part in self.Override} + return [(ext[1:], mimetypes.types_map[True][ext]) for ext in sorted(exts) if ext] + + + def to_tree(self): + """ + Custom serialisation method to allow setting a default namespace + """ + defaults = [t.Extension for t in self.Default] + for ext, mime in self.extensions: + if ext not in defaults: + mime = FileExtension(ext, mime) + self.Default.append(mime) + tree = super().to_tree() + tree.set("xmlns", CONTYPES_NS) + return tree + + + def __contains__(self, content_type): + """ + Check whether a particular content type is contained + """ + for t in self.Override: + if t.ContentType == content_type: + return True + + + def find(self, content_type): + """ + Find specific content-type + """ + try: + return next(self.findall(content_type)) + except StopIteration: + return + + + def findall(self, content_type): + """ + Find all elements of a specific content-type + """ + for t in self.Override: + if t.ContentType == content_type: + yield t + + + def append(self, obj): + """ + Add content object to the package manifest + # needs a contract... + """ + ct = Override(PartName=obj.path, ContentType=obj.mime_type) + self.Override.append(ct) + + + def _write(self, archive, workbook): + """ + Write manifest to the archive + """ + self.append(workbook) + self._write_vba(workbook) + self._register_mimetypes(filenames=archive.namelist()) + archive.writestr(self.path, tostring(self.to_tree())) + + + def _register_mimetypes(self, filenames): + """ + Make sure that the mime type for all file extensions is registered + """ + for fn in filenames: + ext = os.path.splitext(fn)[-1] + if not ext: + continue + mime = mimetypes.types_map[True][ext] + fe = FileExtension(ext[1:], mime) + self.Default.append(fe) + + + def _write_vba(self, workbook): + """ + Add content types from cached workbook when keeping VBA + """ + if workbook.vba_archive: + node = fromstring(workbook.vba_archive.read(ARC_CONTENT_TYPES)) + mf = Manifest.from_tree(node) + filenames = self.filenames + for override in mf.Override: + if override.PartName not in (ACTIVEX, CTRL, VBA): + continue + if override.PartName not in filenames: + self.Override.append(override) |