about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py
diff options
context:
space:
mode:
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.py194
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)