Updated script that can be controled by Nodejs web app
This commit is contained in:
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Stuff related to Office OpenXML packaging: relationships, archive, content types.
|
||||
"""
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
115
lib/python3.13/site-packages/openpyxl/packaging/core.py
Normal file
115
lib/python3.13/site-packages/openpyxl/packaging/core.py
Normal file
@ -0,0 +1,115 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
import datetime
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
DateTime,
|
||||
Alias,
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors.nested import NestedText
|
||||
from openpyxl.xml.functions import (
|
||||
Element,
|
||||
QName,
|
||||
)
|
||||
from openpyxl.xml.constants import (
|
||||
COREPROPS_NS,
|
||||
DCORE_NS,
|
||||
XSI_NS,
|
||||
DCTERMS_NS,
|
||||
)
|
||||
|
||||
|
||||
class NestedDateTime(DateTime, NestedText):
|
||||
|
||||
expected_type = datetime.datetime
|
||||
|
||||
def to_tree(self, tagname=None, value=None, namespace=None):
|
||||
namespace = getattr(self, "namespace", namespace)
|
||||
if namespace is not None:
|
||||
tagname = "{%s}%s" % (namespace, tagname)
|
||||
el = Element(tagname)
|
||||
if value is not None:
|
||||
value = value.replace(tzinfo=None)
|
||||
el.text = value.isoformat(timespec="seconds") + 'Z'
|
||||
return el
|
||||
|
||||
|
||||
class QualifiedDateTime(NestedDateTime):
|
||||
|
||||
"""In certain situations Excel will complain if the additional type
|
||||
attribute isn't set"""
|
||||
|
||||
def to_tree(self, tagname=None, value=None, namespace=None):
|
||||
el = super().to_tree(tagname, value, namespace)
|
||||
el.set("{%s}type" % XSI_NS, QName(DCTERMS_NS, "W3CDTF"))
|
||||
return el
|
||||
|
||||
|
||||
class DocumentProperties(Serialisable):
|
||||
"""High-level properties of the document.
|
||||
Defined in ECMA-376 Par2 Annex D
|
||||
"""
|
||||
|
||||
tagname = "coreProperties"
|
||||
namespace = COREPROPS_NS
|
||||
|
||||
category = NestedText(expected_type=str, allow_none=True)
|
||||
contentStatus = NestedText(expected_type=str, allow_none=True)
|
||||
keywords = NestedText(expected_type=str, allow_none=True)
|
||||
lastModifiedBy = NestedText(expected_type=str, allow_none=True)
|
||||
lastPrinted = NestedDateTime(allow_none=True)
|
||||
revision = NestedText(expected_type=str, allow_none=True)
|
||||
version = NestedText(expected_type=str, allow_none=True)
|
||||
last_modified_by = Alias("lastModifiedBy")
|
||||
|
||||
# Dublin Core Properties
|
||||
subject = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
|
||||
title = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
|
||||
creator = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
|
||||
description = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
|
||||
identifier = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
|
||||
language = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS)
|
||||
# Dublin Core Terms
|
||||
created = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) # assumed to be UTC
|
||||
modified = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) # assumed to be UTC
|
||||
|
||||
__elements__ = ("creator", "title", "description", "subject","identifier",
|
||||
"language", "created", "modified", "lastModifiedBy", "category",
|
||||
"contentStatus", "version", "revision", "keywords", "lastPrinted",
|
||||
)
|
||||
|
||||
|
||||
def __init__(self,
|
||||
category=None,
|
||||
contentStatus=None,
|
||||
keywords=None,
|
||||
lastModifiedBy=None,
|
||||
lastPrinted=None,
|
||||
revision=None,
|
||||
version=None,
|
||||
created=None,
|
||||
creator="openpyxl",
|
||||
description=None,
|
||||
identifier=None,
|
||||
language=None,
|
||||
modified=None,
|
||||
subject=None,
|
||||
title=None,
|
||||
):
|
||||
now = datetime.datetime.now(tz=datetime.timezone.utc).replace(tzinfo=None)
|
||||
self.contentStatus = contentStatus
|
||||
self.lastPrinted = lastPrinted
|
||||
self.revision = revision
|
||||
self.version = version
|
||||
self.creator = creator
|
||||
self.lastModifiedBy = lastModifiedBy
|
||||
self.modified = modified or now
|
||||
self.created = created or now
|
||||
self.title = title
|
||||
self.subject = subject
|
||||
self.description = description
|
||||
self.identifier = identifier
|
||||
self.language = language
|
||||
self.keywords = keywords
|
||||
self.category = category
|
289
lib/python3.13/site-packages/openpyxl/packaging/custom.py
Normal file
289
lib/python3.13/site-packages/openpyxl/packaging/custom.py
Normal file
@ -0,0 +1,289 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
"""Implementation of custom properties see § 22.3 in the specification"""
|
||||
|
||||
|
||||
from warnings import warn
|
||||
|
||||
from openpyxl.descriptors import Strict
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors.sequence import Sequence
|
||||
from openpyxl.descriptors import (
|
||||
Alias,
|
||||
String,
|
||||
Integer,
|
||||
Float,
|
||||
DateTime,
|
||||
Bool,
|
||||
)
|
||||
from openpyxl.descriptors.nested import (
|
||||
NestedText,
|
||||
)
|
||||
|
||||
from openpyxl.xml.constants import (
|
||||
CUSTPROPS_NS,
|
||||
VTYPES_NS,
|
||||
CPROPS_FMTID,
|
||||
)
|
||||
|
||||
from .core import NestedDateTime
|
||||
|
||||
|
||||
class NestedBoolText(Bool, NestedText):
|
||||
"""
|
||||
Descriptor for handling nested elements with the value stored in the text part
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class _CustomDocumentProperty(Serialisable):
|
||||
|
||||
"""
|
||||
Low-level representation of a Custom Document Property.
|
||||
Not used directly
|
||||
Must always contain a child element, even if this is empty
|
||||
"""
|
||||
|
||||
tagname = "property"
|
||||
_typ = None
|
||||
|
||||
name = String(allow_none=True)
|
||||
lpwstr = NestedText(expected_type=str, allow_none=True, namespace=VTYPES_NS)
|
||||
i4 = NestedText(expected_type=int, allow_none=True, namespace=VTYPES_NS)
|
||||
r8 = NestedText(expected_type=float, allow_none=True, namespace=VTYPES_NS)
|
||||
filetime = NestedDateTime(allow_none=True, namespace=VTYPES_NS)
|
||||
bool = NestedBoolText(expected_type=bool, allow_none=True, namespace=VTYPES_NS)
|
||||
linkTarget = String(expected_type=str, allow_none=True)
|
||||
fmtid = String()
|
||||
pid = Integer()
|
||||
|
||||
def __init__(self,
|
||||
name=None,
|
||||
pid=0,
|
||||
fmtid=CPROPS_FMTID,
|
||||
linkTarget=None,
|
||||
**kw):
|
||||
self.fmtid = fmtid
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self._typ = None
|
||||
self.linkTarget = linkTarget
|
||||
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
setattr(self, "_typ", k) # ugh!
|
||||
for e in self.__elements__:
|
||||
if e not in kw:
|
||||
setattr(self, e, None)
|
||||
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
if self._typ is not None:
|
||||
return self._typ
|
||||
for a in self.__elements__:
|
||||
if getattr(self, a) is not None:
|
||||
return a
|
||||
if self.linkTarget is not None:
|
||||
return "linkTarget"
|
||||
|
||||
|
||||
def to_tree(self, tagname=None, idx=None, namespace=None):
|
||||
child = getattr(self, self._typ, None)
|
||||
if child is None:
|
||||
setattr(self, self._typ, "")
|
||||
|
||||
return super().to_tree(tagname=None, idx=None, namespace=None)
|
||||
|
||||
|
||||
class _CustomDocumentPropertyList(Serialisable):
|
||||
|
||||
"""
|
||||
Parses and seriliases property lists but is not used directly
|
||||
"""
|
||||
|
||||
tagname = "Properties"
|
||||
|
||||
property = Sequence(expected_type=_CustomDocumentProperty, namespace=CUSTPROPS_NS)
|
||||
customProps = Alias("property")
|
||||
|
||||
|
||||
def __init__(self, property=()):
|
||||
self.property = property
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.property)
|
||||
|
||||
|
||||
def to_tree(self, tagname=None, idx=None, namespace=None):
|
||||
for idx, p in enumerate(self.property, 2):
|
||||
p.pid = idx
|
||||
tree = super().to_tree(tagname, idx, namespace)
|
||||
tree.set("xmlns", CUSTPROPS_NS)
|
||||
|
||||
return tree
|
||||
|
||||
|
||||
class _TypedProperty(Strict):
|
||||
|
||||
name = String()
|
||||
|
||||
def __init__(self,
|
||||
name,
|
||||
value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name and self.value == other.value
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}, name={self.name}, value={self.value}"
|
||||
|
||||
|
||||
class IntProperty(_TypedProperty):
|
||||
|
||||
value = Integer()
|
||||
|
||||
|
||||
class FloatProperty(_TypedProperty):
|
||||
|
||||
value = Float()
|
||||
|
||||
|
||||
class StringProperty(_TypedProperty):
|
||||
|
||||
value = String(allow_none=True)
|
||||
|
||||
|
||||
class DateTimeProperty(_TypedProperty):
|
||||
|
||||
value = DateTime()
|
||||
|
||||
|
||||
class BoolProperty(_TypedProperty):
|
||||
|
||||
value = Bool()
|
||||
|
||||
|
||||
class LinkProperty(_TypedProperty):
|
||||
|
||||
value = String()
|
||||
|
||||
|
||||
# from Python
|
||||
CLASS_MAPPING = {
|
||||
StringProperty: "lpwstr",
|
||||
IntProperty: "i4",
|
||||
FloatProperty: "r8",
|
||||
DateTimeProperty: "filetime",
|
||||
BoolProperty: "bool",
|
||||
LinkProperty: "linkTarget"
|
||||
}
|
||||
|
||||
XML_MAPPING = {v:k for k,v in CLASS_MAPPING.items()}
|
||||
|
||||
|
||||
class CustomPropertyList(Strict):
|
||||
|
||||
|
||||
props = Sequence(expected_type=_TypedProperty)
|
||||
|
||||
def __init__(self):
|
||||
self.props = []
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_tree(cls, tree):
|
||||
"""
|
||||
Create list from OOXML element
|
||||
"""
|
||||
prop_list = _CustomDocumentPropertyList.from_tree(tree)
|
||||
props = []
|
||||
|
||||
for prop in prop_list.property:
|
||||
attr = prop.type
|
||||
|
||||
typ = XML_MAPPING.get(attr, None)
|
||||
if not typ:
|
||||
warn(f"Unknown type for {prop.name}")
|
||||
continue
|
||||
value = getattr(prop, attr)
|
||||
link = prop.linkTarget
|
||||
if link is not None:
|
||||
typ = LinkProperty
|
||||
value = prop.linkTarget
|
||||
|
||||
new_prop = typ(name=prop.name, value=value)
|
||||
props.append(new_prop)
|
||||
|
||||
new_prop_list = cls()
|
||||
new_prop_list.props = props
|
||||
return new_prop_list
|
||||
|
||||
|
||||
def append(self, prop):
|
||||
if prop.name in self.names:
|
||||
raise ValueError(f"Property with name {prop.name} already exists")
|
||||
|
||||
self.props.append(prop)
|
||||
|
||||
|
||||
def to_tree(self):
|
||||
props = []
|
||||
|
||||
for p in self.props:
|
||||
attr = CLASS_MAPPING.get(p.__class__, None)
|
||||
if not attr:
|
||||
raise TypeError("Unknown adapter for {p}")
|
||||
np = _CustomDocumentProperty(name=p.name, **{attr:p.value})
|
||||
if isinstance(p, LinkProperty):
|
||||
np._typ = "lpwstr"
|
||||
#np.lpwstr = ""
|
||||
props.append(np)
|
||||
|
||||
prop_list = _CustomDocumentPropertyList(property=props)
|
||||
return prop_list.to_tree()
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.props)
|
||||
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
"""List of property names"""
|
||||
return [p.name for p in self.props]
|
||||
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""
|
||||
Get property by name
|
||||
"""
|
||||
for p in self.props:
|
||||
if p.name == name:
|
||||
return p
|
||||
raise KeyError(f"Property with name {name} not found")
|
||||
|
||||
|
||||
def __delitem__(self, name):
|
||||
"""
|
||||
Delete a propery by name
|
||||
"""
|
||||
for idx, p in enumerate(self.props):
|
||||
if p.name == name:
|
||||
self.props.pop(idx)
|
||||
return
|
||||
raise KeyError(f"Property with name {name} not found")
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__} containing {self.props}"
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.props)
|
137
lib/python3.13/site-packages/openpyxl/packaging/extended.py
Normal file
137
lib/python3.13/site-packages/openpyxl/packaging/extended.py
Normal file
@ -0,0 +1,137 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
)
|
||||
from openpyxl.descriptors.nested import (
|
||||
NestedText,
|
||||
)
|
||||
|
||||
from openpyxl.xml.constants import XPROPS_NS
|
||||
from openpyxl import __version__
|
||||
|
||||
|
||||
class DigSigBlob(Serialisable):
|
||||
|
||||
__elements__ = __attrs__ = ()
|
||||
|
||||
|
||||
class VectorLpstr(Serialisable):
|
||||
|
||||
__elements__ = __attrs__ = ()
|
||||
|
||||
|
||||
class VectorVariant(Serialisable):
|
||||
|
||||
__elements__ = __attrs__ = ()
|
||||
|
||||
|
||||
class ExtendedProperties(Serialisable):
|
||||
|
||||
"""
|
||||
See 22.2
|
||||
|
||||
Most of this is irrelevant but Excel is very picky about the version number
|
||||
|
||||
It uses XX.YYYY (Version.Build) and expects everyone else to
|
||||
|
||||
We provide Major.Minor and the full version in the application name
|
||||
"""
|
||||
|
||||
tagname = "Properties"
|
||||
|
||||
Template = NestedText(expected_type=str, allow_none=True)
|
||||
Manager = NestedText(expected_type=str, allow_none=True)
|
||||
Company = NestedText(expected_type=str, allow_none=True)
|
||||
Pages = NestedText(expected_type=int, allow_none=True)
|
||||
Words = NestedText(expected_type=int,allow_none=True)
|
||||
Characters = NestedText(expected_type=int, allow_none=True)
|
||||
PresentationFormat = NestedText(expected_type=str, allow_none=True)
|
||||
Lines = NestedText(expected_type=int, allow_none=True)
|
||||
Paragraphs = NestedText(expected_type=int, allow_none=True)
|
||||
Slides = NestedText(expected_type=int, allow_none=True)
|
||||
Notes = NestedText(expected_type=int, allow_none=True)
|
||||
TotalTime = NestedText(expected_type=int, allow_none=True)
|
||||
HiddenSlides = NestedText(expected_type=int, allow_none=True)
|
||||
MMClips = NestedText(expected_type=int, allow_none=True)
|
||||
ScaleCrop = NestedText(expected_type=bool, allow_none=True)
|
||||
HeadingPairs = Typed(expected_type=VectorVariant, allow_none=True)
|
||||
TitlesOfParts = Typed(expected_type=VectorLpstr, allow_none=True)
|
||||
LinksUpToDate = NestedText(expected_type=bool, allow_none=True)
|
||||
CharactersWithSpaces = NestedText(expected_type=int, allow_none=True)
|
||||
SharedDoc = NestedText(expected_type=bool, allow_none=True)
|
||||
HyperlinkBase = NestedText(expected_type=str, allow_none=True)
|
||||
HLinks = Typed(expected_type=VectorVariant, allow_none=True)
|
||||
HyperlinksChanged = NestedText(expected_type=bool, allow_none=True)
|
||||
DigSig = Typed(expected_type=DigSigBlob, allow_none=True)
|
||||
Application = NestedText(expected_type=str, allow_none=True)
|
||||
AppVersion = NestedText(expected_type=str, allow_none=True)
|
||||
DocSecurity = NestedText(expected_type=int, allow_none=True)
|
||||
|
||||
__elements__ = ('Application', 'AppVersion', 'DocSecurity', 'ScaleCrop',
|
||||
'LinksUpToDate', 'SharedDoc', 'HyperlinksChanged')
|
||||
|
||||
def __init__(self,
|
||||
Template=None,
|
||||
Manager=None,
|
||||
Company=None,
|
||||
Pages=None,
|
||||
Words=None,
|
||||
Characters=None,
|
||||
PresentationFormat=None,
|
||||
Lines=None,
|
||||
Paragraphs=None,
|
||||
Slides=None,
|
||||
Notes=None,
|
||||
TotalTime=None,
|
||||
HiddenSlides=None,
|
||||
MMClips=None,
|
||||
ScaleCrop=None,
|
||||
HeadingPairs=None,
|
||||
TitlesOfParts=None,
|
||||
LinksUpToDate=None,
|
||||
CharactersWithSpaces=None,
|
||||
SharedDoc=None,
|
||||
HyperlinkBase=None,
|
||||
HLinks=None,
|
||||
HyperlinksChanged=None,
|
||||
DigSig=None,
|
||||
Application=None,
|
||||
AppVersion=None,
|
||||
DocSecurity=None,
|
||||
):
|
||||
self.Template = Template
|
||||
self.Manager = Manager
|
||||
self.Company = Company
|
||||
self.Pages = Pages
|
||||
self.Words = Words
|
||||
self.Characters = Characters
|
||||
self.PresentationFormat = PresentationFormat
|
||||
self.Lines = Lines
|
||||
self.Paragraphs = Paragraphs
|
||||
self.Slides = Slides
|
||||
self.Notes = Notes
|
||||
self.TotalTime = TotalTime
|
||||
self.HiddenSlides = HiddenSlides
|
||||
self.MMClips = MMClips
|
||||
self.ScaleCrop = ScaleCrop
|
||||
self.HeadingPairs = None
|
||||
self.TitlesOfParts = None
|
||||
self.LinksUpToDate = LinksUpToDate
|
||||
self.CharactersWithSpaces = CharactersWithSpaces
|
||||
self.SharedDoc = SharedDoc
|
||||
self.HyperlinkBase = HyperlinkBase
|
||||
self.HLinks = None
|
||||
self.HyperlinksChanged = HyperlinksChanged
|
||||
self.DigSig = None
|
||||
self.Application = f"Microsoft Excel Compatible / Openpyxl {__version__}"
|
||||
self.AppVersion = ".".join(__version__.split(".")[:-1])
|
||||
self.DocSecurity = DocSecurity
|
||||
|
||||
|
||||
def to_tree(self):
|
||||
tree = super().to_tree()
|
||||
tree.set("xmlns", XPROPS_NS)
|
||||
return tree
|
56
lib/python3.13/site-packages/openpyxl/packaging/interface.py
Normal file
56
lib/python3.13/site-packages/openpyxl/packaging/interface.py
Normal file
@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
from abc import abstractproperty
|
||||
from openpyxl.compat.abc import ABC
|
||||
|
||||
|
||||
class ISerialisableFile(ABC):
|
||||
|
||||
"""
|
||||
Interface for Serialisable classes that represent files in the archive
|
||||
"""
|
||||
|
||||
|
||||
@abstractproperty
|
||||
def id(self):
|
||||
"""
|
||||
Object id making it unique
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@abstractproperty
|
||||
def _path(self):
|
||||
"""
|
||||
File path in the archive
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@abstractproperty
|
||||
def _namespace(self):
|
||||
"""
|
||||
Qualified namespace when serialised
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@abstractproperty
|
||||
def _type(self):
|
||||
"""
|
||||
The content type for the manifest
|
||||
"""
|
||||
|
||||
|
||||
@abstractproperty
|
||||
def _rel_type(self):
|
||||
"""
|
||||
The content type for relationships
|
||||
"""
|
||||
|
||||
|
||||
@abstractproperty
|
||||
def _rel_id(self):
|
||||
"""
|
||||
Links object with parent
|
||||
"""
|
194
lib/python3.13/site-packages/openpyxl/packaging/manifest.py
Normal file
194
lib/python3.13/site-packages/openpyxl/packaging/manifest.py
Normal file
@ -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)
|
158
lib/python3.13/site-packages/openpyxl/packaging/relationship.py
Normal file
158
lib/python3.13/site-packages/openpyxl/packaging/relationship.py
Normal file
@ -0,0 +1,158 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
import posixpath
|
||||
from warnings import warn
|
||||
|
||||
from openpyxl.descriptors import (
|
||||
String,
|
||||
Alias,
|
||||
Sequence,
|
||||
)
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors.container import ElementList
|
||||
|
||||
from openpyxl.xml.constants import REL_NS, PKG_REL_NS
|
||||
from openpyxl.xml.functions import (
|
||||
Element,
|
||||
fromstring,
|
||||
)
|
||||
|
||||
|
||||
class Relationship(Serialisable):
|
||||
"""Represents many kinds of relationships."""
|
||||
|
||||
tagname = "Relationship"
|
||||
|
||||
Type = String()
|
||||
Target = String()
|
||||
target = Alias("Target")
|
||||
TargetMode = String(allow_none=True)
|
||||
Id = String(allow_none=True)
|
||||
id = Alias("Id")
|
||||
|
||||
|
||||
def __init__(self,
|
||||
Id=None,
|
||||
Type=None,
|
||||
type=None,
|
||||
Target=None,
|
||||
TargetMode=None
|
||||
):
|
||||
"""
|
||||
`type` can be used as a shorthand with the default relationships namespace
|
||||
otherwise the `Type` must be a fully qualified URL
|
||||
"""
|
||||
if type is not None:
|
||||
Type = "{0}/{1}".format(REL_NS, type)
|
||||
self.Type = Type
|
||||
self.Target = Target
|
||||
self.TargetMode = TargetMode
|
||||
self.Id = Id
|
||||
|
||||
|
||||
class RelationshipList(ElementList):
|
||||
|
||||
tagname = "Relationships"
|
||||
expected_type = Relationship
|
||||
|
||||
|
||||
def append(self, value):
|
||||
super().append(value)
|
||||
if not value.Id:
|
||||
value.Id = f"rId{len(self)}"
|
||||
|
||||
|
||||
def find(self, content_type):
|
||||
"""
|
||||
Find relationships by content-type
|
||||
NB. these content-types namespaced objects and different to the MIME-types
|
||||
in the package manifest :-(
|
||||
"""
|
||||
for r in self:
|
||||
if r.Type == content_type:
|
||||
yield r
|
||||
|
||||
|
||||
def get(self, key):
|
||||
for r in self:
|
||||
if r.Id == key:
|
||||
return r
|
||||
raise KeyError("Unknown relationship: {0}".format(key))
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Return a dictionary of relations keyed by id"""
|
||||
return {r.id:r for r in self}
|
||||
|
||||
|
||||
def to_tree(self):
|
||||
tree = super().to_tree()
|
||||
tree.set("xmlns", PKG_REL_NS)
|
||||
return tree
|
||||
|
||||
|
||||
def get_rels_path(path):
|
||||
"""
|
||||
Convert relative path to absolutes that can be loaded from a zip
|
||||
archive.
|
||||
The path to be passed in is that of containing object (workbook,
|
||||
worksheet, etc.)
|
||||
"""
|
||||
folder, obj = posixpath.split(path)
|
||||
filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj))
|
||||
return filename
|
||||
|
||||
|
||||
def get_dependents(archive, filename):
|
||||
"""
|
||||
Normalise dependency file paths to absolute ones
|
||||
|
||||
Relative paths are relative to parent object
|
||||
"""
|
||||
src = archive.read(filename)
|
||||
node = fromstring(src)
|
||||
try:
|
||||
rels = RelationshipList.from_tree(node)
|
||||
except TypeError:
|
||||
msg = "{0} contains invalid dependency definitions".format(filename)
|
||||
warn(msg)
|
||||
rels = RelationshipList()
|
||||
folder = posixpath.dirname(filename)
|
||||
parent = posixpath.split(folder)[0]
|
||||
for r in rels:
|
||||
if r.TargetMode == "External":
|
||||
continue
|
||||
elif r.target.startswith("/"):
|
||||
r.target = r.target[1:]
|
||||
else:
|
||||
pth = posixpath.join(parent, r.target)
|
||||
r.target = posixpath.normpath(pth)
|
||||
return rels
|
||||
|
||||
|
||||
def get_rel(archive, deps, id=None, cls=None):
|
||||
"""
|
||||
Get related object based on id or rel_type
|
||||
"""
|
||||
if not any([id, cls]):
|
||||
raise ValueError("Either the id or the content type are required")
|
||||
if id is not None:
|
||||
rel = deps.get(id)
|
||||
else:
|
||||
try:
|
||||
rel = next(deps.find(cls.rel_type))
|
||||
except StopIteration: # no known dependency
|
||||
return
|
||||
|
||||
path = rel.target
|
||||
src = archive.read(path)
|
||||
tree = fromstring(src)
|
||||
obj = cls.from_tree(tree)
|
||||
|
||||
rels_path = get_rels_path(path)
|
||||
try:
|
||||
obj.deps = get_dependents(archive, rels_path)
|
||||
except KeyError:
|
||||
obj.deps = []
|
||||
|
||||
return obj
|
185
lib/python3.13/site-packages/openpyxl/packaging/workbook.py
Normal file
185
lib/python3.13/site-packages/openpyxl/packaging/workbook.py
Normal file
@ -0,0 +1,185 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Alias,
|
||||
Typed,
|
||||
String,
|
||||
Integer,
|
||||
Bool,
|
||||
NoneSet,
|
||||
)
|
||||
from openpyxl.descriptors.excel import ExtensionList, Relation
|
||||
from openpyxl.descriptors.sequence import NestedSequence
|
||||
from openpyxl.descriptors.nested import NestedString
|
||||
|
||||
from openpyxl.xml.constants import SHEET_MAIN_NS
|
||||
|
||||
from openpyxl.workbook.defined_name import DefinedNameList
|
||||
from openpyxl.workbook.external_reference import ExternalReference
|
||||
from openpyxl.workbook.function_group import FunctionGroupList
|
||||
from openpyxl.workbook.properties import WorkbookProperties, CalcProperties, FileVersion
|
||||
from openpyxl.workbook.protection import WorkbookProtection, FileSharing
|
||||
from openpyxl.workbook.smart_tags import SmartTagList, SmartTagProperties
|
||||
from openpyxl.workbook.views import CustomWorkbookView, BookView
|
||||
from openpyxl.workbook.web import WebPublishing, WebPublishObjectList
|
||||
|
||||
|
||||
class FileRecoveryProperties(Serialisable):
|
||||
|
||||
tagname = "fileRecoveryPr"
|
||||
|
||||
autoRecover = Bool(allow_none=True)
|
||||
crashSave = Bool(allow_none=True)
|
||||
dataExtractLoad = Bool(allow_none=True)
|
||||
repairLoad = Bool(allow_none=True)
|
||||
|
||||
def __init__(self,
|
||||
autoRecover=None,
|
||||
crashSave=None,
|
||||
dataExtractLoad=None,
|
||||
repairLoad=None,
|
||||
):
|
||||
self.autoRecover = autoRecover
|
||||
self.crashSave = crashSave
|
||||
self.dataExtractLoad = dataExtractLoad
|
||||
self.repairLoad = repairLoad
|
||||
|
||||
|
||||
class ChildSheet(Serialisable):
|
||||
"""
|
||||
Represents a reference to a worksheet or chartsheet in workbook.xml
|
||||
|
||||
It contains the title, order and state but only an indirect reference to
|
||||
the objects themselves.
|
||||
"""
|
||||
|
||||
tagname = "sheet"
|
||||
|
||||
name = String()
|
||||
sheetId = Integer()
|
||||
state = NoneSet(values=(['visible', 'hidden', 'veryHidden']))
|
||||
id = Relation()
|
||||
|
||||
def __init__(self,
|
||||
name=None,
|
||||
sheetId=None,
|
||||
state="visible",
|
||||
id=None,
|
||||
):
|
||||
self.name = name
|
||||
self.sheetId = sheetId
|
||||
self.state = state
|
||||
self.id = id
|
||||
|
||||
|
||||
class PivotCache(Serialisable):
|
||||
|
||||
tagname = "pivotCache"
|
||||
|
||||
cacheId = Integer()
|
||||
id = Relation()
|
||||
|
||||
def __init__(self,
|
||||
cacheId=None,
|
||||
id=None
|
||||
):
|
||||
self.cacheId = cacheId
|
||||
self.id = id
|
||||
|
||||
|
||||
class WorkbookPackage(Serialisable):
|
||||
|
||||
"""
|
||||
Represent the workbook file in the archive
|
||||
"""
|
||||
|
||||
tagname = "workbook"
|
||||
|
||||
conformance = NoneSet(values=['strict', 'transitional'])
|
||||
fileVersion = Typed(expected_type=FileVersion, allow_none=True)
|
||||
fileSharing = Typed(expected_type=FileSharing, allow_none=True)
|
||||
workbookPr = Typed(expected_type=WorkbookProperties, allow_none=True)
|
||||
properties = Alias("workbookPr")
|
||||
workbookProtection = Typed(expected_type=WorkbookProtection, allow_none=True)
|
||||
bookViews = NestedSequence(expected_type=BookView)
|
||||
sheets = NestedSequence(expected_type=ChildSheet)
|
||||
functionGroups = Typed(expected_type=FunctionGroupList, allow_none=True)
|
||||
externalReferences = NestedSequence(expected_type=ExternalReference)
|
||||
definedNames = Typed(expected_type=DefinedNameList, allow_none=True)
|
||||
calcPr = Typed(expected_type=CalcProperties, allow_none=True)
|
||||
oleSize = NestedString(allow_none=True, attribute="ref")
|
||||
customWorkbookViews = NestedSequence(expected_type=CustomWorkbookView)
|
||||
pivotCaches = NestedSequence(expected_type=PivotCache, allow_none=True)
|
||||
smartTagPr = Typed(expected_type=SmartTagProperties, allow_none=True)
|
||||
smartTagTypes = Typed(expected_type=SmartTagList, allow_none=True)
|
||||
webPublishing = Typed(expected_type=WebPublishing, allow_none=True)
|
||||
fileRecoveryPr = Typed(expected_type=FileRecoveryProperties, allow_none=True)
|
||||
webPublishObjects = Typed(expected_type=WebPublishObjectList, allow_none=True)
|
||||
extLst = Typed(expected_type=ExtensionList, allow_none=True)
|
||||
Ignorable = NestedString(namespace="http://schemas.openxmlformats.org/markup-compatibility/2006", allow_none=True)
|
||||
|
||||
__elements__ = ('fileVersion', 'fileSharing', 'workbookPr',
|
||||
'workbookProtection', 'bookViews', 'sheets', 'functionGroups',
|
||||
'externalReferences', 'definedNames', 'calcPr', 'oleSize',
|
||||
'customWorkbookViews', 'pivotCaches', 'smartTagPr', 'smartTagTypes',
|
||||
'webPublishing', 'fileRecoveryPr', 'webPublishObjects')
|
||||
|
||||
def __init__(self,
|
||||
conformance=None,
|
||||
fileVersion=None,
|
||||
fileSharing=None,
|
||||
workbookPr=None,
|
||||
workbookProtection=None,
|
||||
bookViews=(),
|
||||
sheets=(),
|
||||
functionGroups=None,
|
||||
externalReferences=(),
|
||||
definedNames=None,
|
||||
calcPr=None,
|
||||
oleSize=None,
|
||||
customWorkbookViews=(),
|
||||
pivotCaches=(),
|
||||
smartTagPr=None,
|
||||
smartTagTypes=None,
|
||||
webPublishing=None,
|
||||
fileRecoveryPr=None,
|
||||
webPublishObjects=None,
|
||||
extLst=None,
|
||||
Ignorable=None,
|
||||
):
|
||||
self.conformance = conformance
|
||||
self.fileVersion = fileVersion
|
||||
self.fileSharing = fileSharing
|
||||
if workbookPr is None:
|
||||
workbookPr = WorkbookProperties()
|
||||
self.workbookPr = workbookPr
|
||||
self.workbookProtection = workbookProtection
|
||||
self.bookViews = bookViews
|
||||
self.sheets = sheets
|
||||
self.functionGroups = functionGroups
|
||||
self.externalReferences = externalReferences
|
||||
self.definedNames = definedNames
|
||||
self.calcPr = calcPr
|
||||
self.oleSize = oleSize
|
||||
self.customWorkbookViews = customWorkbookViews
|
||||
self.pivotCaches = pivotCaches
|
||||
self.smartTagPr = smartTagPr
|
||||
self.smartTagTypes = smartTagTypes
|
||||
self.webPublishing = webPublishing
|
||||
self.fileRecoveryPr = fileRecoveryPr
|
||||
self.webPublishObjects = webPublishObjects
|
||||
|
||||
|
||||
def to_tree(self):
|
||||
tree = super().to_tree()
|
||||
tree.set("xmlns", SHEET_MAIN_NS)
|
||||
return tree
|
||||
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
for view in self.bookViews:
|
||||
if view.activeTab is not None:
|
||||
return view.activeTab
|
||||
return 0
|
Reference in New Issue
Block a user