Updated script that can be controled by Nodejs web app
This commit is contained in:
@ -0,0 +1,4 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
|
||||
from .comments import Comment
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
21
lib/python3.13/site-packages/openpyxl/comments/author.py
Normal file
21
lib/python3.13/site-packages/openpyxl/comments/author.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Sequence,
|
||||
Alias
|
||||
)
|
||||
|
||||
|
||||
class AuthorList(Serialisable):
|
||||
|
||||
tagname = "authors"
|
||||
|
||||
author = Sequence(expected_type=str)
|
||||
authors = Alias("author")
|
||||
|
||||
def __init__(self,
|
||||
author=(),
|
||||
):
|
||||
self.author = author
|
211
lib/python3.13/site-packages/openpyxl/comments/comment_sheet.py
Normal file
211
lib/python3.13/site-packages/openpyxl/comments/comment_sheet.py
Normal file
@ -0,0 +1,211 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
## Incomplete!
|
||||
from openpyxl.descriptors.serialisable import Serialisable
|
||||
from openpyxl.descriptors import (
|
||||
Typed,
|
||||
Integer,
|
||||
Set,
|
||||
String,
|
||||
Bool,
|
||||
)
|
||||
from openpyxl.descriptors.excel import Guid, ExtensionList
|
||||
from openpyxl.descriptors.sequence import NestedSequence
|
||||
|
||||
from openpyxl.utils.indexed_list import IndexedList
|
||||
from openpyxl.xml.constants import SHEET_MAIN_NS
|
||||
|
||||
from openpyxl.cell.text import Text
|
||||
from .author import AuthorList
|
||||
from .comments import Comment
|
||||
from .shape_writer import ShapeWriter
|
||||
|
||||
|
||||
class Properties(Serialisable):
|
||||
|
||||
locked = Bool(allow_none=True)
|
||||
defaultSize = Bool(allow_none=True)
|
||||
_print = Bool(allow_none=True)
|
||||
disabled = Bool(allow_none=True)
|
||||
uiObject = Bool(allow_none=True)
|
||||
autoFill = Bool(allow_none=True)
|
||||
autoLine = Bool(allow_none=True)
|
||||
altText = String(allow_none=True)
|
||||
textHAlign = Set(values=(['left', 'center', 'right', 'justify', 'distributed']))
|
||||
textVAlign = Set(values=(['top', 'center', 'bottom', 'justify', 'distributed']))
|
||||
lockText = Bool(allow_none=True)
|
||||
justLastX = Bool(allow_none=True)
|
||||
autoScale = Bool(allow_none=True)
|
||||
rowHidden = Bool(allow_none=True)
|
||||
colHidden = Bool(allow_none=True)
|
||||
# anchor = Typed(expected_type=ObjectAnchor, )
|
||||
|
||||
__elements__ = ('anchor',)
|
||||
|
||||
def __init__(self,
|
||||
locked=None,
|
||||
defaultSize=None,
|
||||
_print=None,
|
||||
disabled=None,
|
||||
uiObject=None,
|
||||
autoFill=None,
|
||||
autoLine=None,
|
||||
altText=None,
|
||||
textHAlign=None,
|
||||
textVAlign=None,
|
||||
lockText=None,
|
||||
justLastX=None,
|
||||
autoScale=None,
|
||||
rowHidden=None,
|
||||
colHidden=None,
|
||||
anchor=None,
|
||||
):
|
||||
self.locked = locked
|
||||
self.defaultSize = defaultSize
|
||||
self._print = _print
|
||||
self.disabled = disabled
|
||||
self.uiObject = uiObject
|
||||
self.autoFill = autoFill
|
||||
self.autoLine = autoLine
|
||||
self.altText = altText
|
||||
self.textHAlign = textHAlign
|
||||
self.textVAlign = textVAlign
|
||||
self.lockText = lockText
|
||||
self.justLastX = justLastX
|
||||
self.autoScale = autoScale
|
||||
self.rowHidden = rowHidden
|
||||
self.colHidden = colHidden
|
||||
self.anchor = anchor
|
||||
|
||||
|
||||
class CommentRecord(Serialisable):
|
||||
|
||||
tagname = "comment"
|
||||
|
||||
ref = String()
|
||||
authorId = Integer()
|
||||
guid = Guid(allow_none=True)
|
||||
shapeId = Integer(allow_none=True)
|
||||
text = Typed(expected_type=Text)
|
||||
commentPr = Typed(expected_type=Properties, allow_none=True)
|
||||
author = String(allow_none=True)
|
||||
|
||||
__elements__ = ('text', 'commentPr')
|
||||
__attrs__ = ('ref', 'authorId', 'guid', 'shapeId')
|
||||
|
||||
def __init__(self,
|
||||
ref="",
|
||||
authorId=0,
|
||||
guid=None,
|
||||
shapeId=0,
|
||||
text=None,
|
||||
commentPr=None,
|
||||
author=None,
|
||||
height=79,
|
||||
width=144
|
||||
):
|
||||
self.ref = ref
|
||||
self.authorId = authorId
|
||||
self.guid = guid
|
||||
self.shapeId = shapeId
|
||||
if text is None:
|
||||
text = Text()
|
||||
self.text = text
|
||||
self.commentPr = commentPr
|
||||
self.author = author
|
||||
self.height = height
|
||||
self.width = width
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_cell(cls, cell):
|
||||
"""
|
||||
Class method to convert cell comment
|
||||
"""
|
||||
comment = cell._comment
|
||||
ref = cell.coordinate
|
||||
self = cls(ref=ref, author=comment.author)
|
||||
self.text.t = comment.content
|
||||
self.height = comment.height
|
||||
self.width = comment.width
|
||||
return self
|
||||
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
"""
|
||||
Remove all inline formatting and stuff
|
||||
"""
|
||||
return self.text.content
|
||||
|
||||
|
||||
class CommentSheet(Serialisable):
|
||||
|
||||
tagname = "comments"
|
||||
|
||||
authors = Typed(expected_type=AuthorList)
|
||||
commentList = NestedSequence(expected_type=CommentRecord, count=0)
|
||||
extLst = Typed(expected_type=ExtensionList, allow_none=True)
|
||||
|
||||
_id = None
|
||||
_path = "/xl/comments/comment{0}.xml"
|
||||
mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
|
||||
_rel_type = "comments"
|
||||
_rel_id = None
|
||||
|
||||
__elements__ = ('authors', 'commentList')
|
||||
|
||||
def __init__(self,
|
||||
authors=None,
|
||||
commentList=None,
|
||||
extLst=None,
|
||||
):
|
||||
self.authors = authors
|
||||
self.commentList = commentList
|
||||
|
||||
|
||||
def to_tree(self):
|
||||
tree = super().to_tree()
|
||||
tree.set("xmlns", SHEET_MAIN_NS)
|
||||
return tree
|
||||
|
||||
|
||||
@property
|
||||
def comments(self):
|
||||
"""
|
||||
Return a dictionary of comments keyed by coord
|
||||
"""
|
||||
authors = self.authors.author
|
||||
|
||||
for c in self.commentList:
|
||||
yield c.ref, Comment(c.content, authors[c.authorId], c.height, c.width)
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_comments(cls, comments):
|
||||
"""
|
||||
Create a comment sheet from a list of comments for a particular worksheet
|
||||
"""
|
||||
authors = IndexedList()
|
||||
|
||||
# dedupe authors and get indexes
|
||||
for comment in comments:
|
||||
comment.authorId = authors.add(comment.author)
|
||||
|
||||
return cls(authors=AuthorList(authors), commentList=comments)
|
||||
|
||||
|
||||
def write_shapes(self, vml=None):
|
||||
"""
|
||||
Create the VML for comments
|
||||
"""
|
||||
sw = ShapeWriter(self.comments)
|
||||
return sw.write(vml)
|
||||
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""
|
||||
Return path within the archive
|
||||
"""
|
||||
return self._path.format(self._id)
|
62
lib/python3.13/site-packages/openpyxl/comments/comments.py
Normal file
62
lib/python3.13/site-packages/openpyxl/comments/comments.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
|
||||
class Comment:
|
||||
|
||||
_parent = None
|
||||
|
||||
def __init__(self, text, author, height=79, width=144):
|
||||
self.content = text
|
||||
self.author = author
|
||||
self.height = height
|
||||
self.width = width
|
||||
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
return self._parent
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
self.content == other.content
|
||||
and self.author == other.author
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "Comment: {0} by {1}".format(self.content, self.author)
|
||||
|
||||
|
||||
def __copy__(self):
|
||||
"""Create a detached copy of this comment."""
|
||||
clone = self.__class__(self.content, self.author, self.height, self.width)
|
||||
return clone
|
||||
|
||||
|
||||
def bind(self, cell):
|
||||
"""
|
||||
Bind comment to a particular cell
|
||||
"""
|
||||
if cell is not None and self._parent is not None and self._parent != cell:
|
||||
fmt = "Comment already assigned to {0} in worksheet {1}. Cannot assign a comment to more than one cell"
|
||||
raise AttributeError(fmt.format(cell.coordinate, cell.parent.title))
|
||||
self._parent = cell
|
||||
|
||||
|
||||
def unbind(self):
|
||||
"""
|
||||
Unbind a comment from a cell
|
||||
"""
|
||||
self._parent = None
|
||||
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
"""
|
||||
Any comment text stripped of all formatting.
|
||||
"""
|
||||
return self.content
|
||||
|
||||
@text.setter
|
||||
def text(self, value):
|
||||
self.content = value
|
112
lib/python3.13/site-packages/openpyxl/comments/shape_writer.py
Normal file
112
lib/python3.13/site-packages/openpyxl/comments/shape_writer.py
Normal file
@ -0,0 +1,112 @@
|
||||
# Copyright (c) 2010-2024 openpyxl
|
||||
|
||||
from openpyxl.xml.functions import (
|
||||
Element,
|
||||
SubElement,
|
||||
tostring,
|
||||
)
|
||||
|
||||
from openpyxl.utils import coordinate_to_tuple
|
||||
|
||||
vmlns = "urn:schemas-microsoft-com:vml"
|
||||
officens = "urn:schemas-microsoft-com:office:office"
|
||||
excelns = "urn:schemas-microsoft-com:office:excel"
|
||||
|
||||
|
||||
class ShapeWriter:
|
||||
"""
|
||||
Create VML for comments
|
||||
"""
|
||||
|
||||
vml = None
|
||||
vml_path = None
|
||||
|
||||
|
||||
def __init__(self, comments):
|
||||
self.comments = comments
|
||||
|
||||
|
||||
def add_comment_shapetype(self, root):
|
||||
shape_layout = SubElement(root, "{%s}shapelayout" % officens,
|
||||
{"{%s}ext" % vmlns: "edit"})
|
||||
SubElement(shape_layout,
|
||||
"{%s}idmap" % officens,
|
||||
{"{%s}ext" % vmlns: "edit", "data": "1"})
|
||||
shape_type = SubElement(root,
|
||||
"{%s}shapetype" % vmlns,
|
||||
{"id": "_x0000_t202",
|
||||
"coordsize": "21600,21600",
|
||||
"{%s}spt" % officens: "202",
|
||||
"path": "m,l,21600r21600,l21600,xe"})
|
||||
SubElement(shape_type, "{%s}stroke" % vmlns, {"joinstyle": "miter"})
|
||||
SubElement(shape_type,
|
||||
"{%s}path" % vmlns,
|
||||
{"gradientshapeok": "t",
|
||||
"{%s}connecttype" % officens: "rect"})
|
||||
|
||||
|
||||
def add_comment_shape(self, root, idx, coord, height, width):
|
||||
row, col = coordinate_to_tuple(coord)
|
||||
row -= 1
|
||||
col -= 1
|
||||
shape = _shape_factory(row, col, height, width)
|
||||
|
||||
shape.set('id', "_x0000_s%04d" % idx)
|
||||
root.append(shape)
|
||||
|
||||
|
||||
def write(self, root):
|
||||
|
||||
if not hasattr(root, "findall"):
|
||||
root = Element("xml")
|
||||
|
||||
# Remove any existing comment shapes
|
||||
comments = root.findall("{%s}shape[@type='#_x0000_t202']" % vmlns)
|
||||
for c in comments:
|
||||
root.remove(c)
|
||||
|
||||
# check whether comments shape type already exists
|
||||
shape_types = root.find("{%s}shapetype[@id='_x0000_t202']" % vmlns)
|
||||
if shape_types is None:
|
||||
self.add_comment_shapetype(root)
|
||||
|
||||
for idx, (coord, comment) in enumerate(self.comments, 1026):
|
||||
self.add_comment_shape(root, idx, coord, comment.height, comment.width)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
|
||||
def _shape_factory(row, column, height, width):
|
||||
style = ("position:absolute; "
|
||||
"margin-left:59.25pt;"
|
||||
"margin-top:1.5pt;"
|
||||
"width:{width}px;"
|
||||
"height:{height}px;"
|
||||
"z-index:1;"
|
||||
"visibility:hidden").format(height=height,
|
||||
width=width)
|
||||
attrs = {
|
||||
"type": "#_x0000_t202",
|
||||
"style": style,
|
||||
"fillcolor": "#ffffe1",
|
||||
"{%s}insetmode" % officens: "auto"
|
||||
}
|
||||
shape = Element("{%s}shape" % vmlns, attrs)
|
||||
|
||||
SubElement(shape, "{%s}fill" % vmlns,
|
||||
{"color2": "#ffffe1"})
|
||||
SubElement(shape, "{%s}shadow" % vmlns,
|
||||
{"color": "black", "obscured": "t"})
|
||||
SubElement(shape, "{%s}path" % vmlns,
|
||||
{"{%s}connecttype" % officens: "none"})
|
||||
textbox = SubElement(shape, "{%s}textbox" % vmlns,
|
||||
{"style": "mso-direction-alt:auto"})
|
||||
SubElement(textbox, "div", {"style": "text-align:left"})
|
||||
client_data = SubElement(shape, "{%s}ClientData" % excelns,
|
||||
{"ObjectType": "Note"})
|
||||
SubElement(client_data, "{%s}MoveWithCells" % excelns)
|
||||
SubElement(client_data, "{%s}SizeWithCells" % excelns)
|
||||
SubElement(client_data, "{%s}AutoFill" % excelns).text = "False"
|
||||
SubElement(client_data, "{%s}Row" % excelns).text = str(row)
|
||||
SubElement(client_data, "{%s}Column" % excelns).text = str(column)
|
||||
return shape
|
Reference in New Issue
Block a user