venv
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.
Binary file not shown.
Binary file not shown.
115
venv/lib/python3.12/site-packages/openpyxl/packaging/core.py
Normal file
115
venv/lib/python3.12/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
venv/lib/python3.12/site-packages/openpyxl/packaging/custom.py
Normal file
289
venv/lib/python3.12/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
venv/lib/python3.12/site-packages/openpyxl/packaging/extended.py
Normal file
137
venv/lib/python3.12/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
|
@ -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
venv/lib/python3.12/site-packages/openpyxl/packaging/manifest.py
Normal file
194
venv/lib/python3.12/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)
|
@ -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
venv/lib/python3.12/site-packages/openpyxl/packaging/workbook.py
Normal file
185
venv/lib/python3.12/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