comment_sheet.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. # Copyright (c) 2010-2024 openpyxl
  2. ## Incomplete!
  3. from openpyxl.descriptors.serialisable import Serialisable
  4. from openpyxl.descriptors import (
  5. Typed,
  6. Integer,
  7. Set,
  8. String,
  9. Bool,
  10. )
  11. from openpyxl.descriptors.excel import Guid, ExtensionList
  12. from openpyxl.descriptors.sequence import NestedSequence
  13. from openpyxl.utils.indexed_list import IndexedList
  14. from openpyxl.xml.constants import SHEET_MAIN_NS
  15. from openpyxl.cell.text import Text
  16. from .author import AuthorList
  17. from .comments import Comment
  18. from .shape_writer import ShapeWriter
  19. class Properties(Serialisable):
  20. locked = Bool(allow_none=True)
  21. defaultSize = Bool(allow_none=True)
  22. _print = Bool(allow_none=True)
  23. disabled = Bool(allow_none=True)
  24. uiObject = Bool(allow_none=True)
  25. autoFill = Bool(allow_none=True)
  26. autoLine = Bool(allow_none=True)
  27. altText = String(allow_none=True)
  28. textHAlign = Set(values=(['left', 'center', 'right', 'justify', 'distributed']))
  29. textVAlign = Set(values=(['top', 'center', 'bottom', 'justify', 'distributed']))
  30. lockText = Bool(allow_none=True)
  31. justLastX = Bool(allow_none=True)
  32. autoScale = Bool(allow_none=True)
  33. rowHidden = Bool(allow_none=True)
  34. colHidden = Bool(allow_none=True)
  35. # anchor = Typed(expected_type=ObjectAnchor, )
  36. __elements__ = ('anchor',)
  37. def __init__(self,
  38. locked=None,
  39. defaultSize=None,
  40. _print=None,
  41. disabled=None,
  42. uiObject=None,
  43. autoFill=None,
  44. autoLine=None,
  45. altText=None,
  46. textHAlign=None,
  47. textVAlign=None,
  48. lockText=None,
  49. justLastX=None,
  50. autoScale=None,
  51. rowHidden=None,
  52. colHidden=None,
  53. anchor=None,
  54. ):
  55. self.locked = locked
  56. self.defaultSize = defaultSize
  57. self._print = _print
  58. self.disabled = disabled
  59. self.uiObject = uiObject
  60. self.autoFill = autoFill
  61. self.autoLine = autoLine
  62. self.altText = altText
  63. self.textHAlign = textHAlign
  64. self.textVAlign = textVAlign
  65. self.lockText = lockText
  66. self.justLastX = justLastX
  67. self.autoScale = autoScale
  68. self.rowHidden = rowHidden
  69. self.colHidden = colHidden
  70. self.anchor = anchor
  71. class CommentRecord(Serialisable):
  72. tagname = "comment"
  73. ref = String()
  74. authorId = Integer()
  75. guid = Guid(allow_none=True)
  76. shapeId = Integer(allow_none=True)
  77. text = Typed(expected_type=Text)
  78. commentPr = Typed(expected_type=Properties, allow_none=True)
  79. author = String(allow_none=True)
  80. __elements__ = ('text', 'commentPr')
  81. __attrs__ = ('ref', 'authorId', 'guid', 'shapeId')
  82. def __init__(self,
  83. ref="",
  84. authorId=0,
  85. guid=None,
  86. shapeId=0,
  87. text=None,
  88. commentPr=None,
  89. author=None,
  90. height=79,
  91. width=144
  92. ):
  93. self.ref = ref
  94. self.authorId = authorId
  95. self.guid = guid
  96. self.shapeId = shapeId
  97. if text is None:
  98. text = Text()
  99. self.text = text
  100. self.commentPr = commentPr
  101. self.author = author
  102. self.height = height
  103. self.width = width
  104. @classmethod
  105. def from_cell(cls, cell):
  106. """
  107. Class method to convert cell comment
  108. """
  109. comment = cell._comment
  110. ref = cell.coordinate
  111. self = cls(ref=ref, author=comment.author)
  112. self.text.t = comment.content
  113. self.height = comment.height
  114. self.width = comment.width
  115. return self
  116. @property
  117. def content(self):
  118. """
  119. Remove all inline formatting and stuff
  120. """
  121. return self.text.content
  122. class CommentSheet(Serialisable):
  123. tagname = "comments"
  124. authors = Typed(expected_type=AuthorList)
  125. commentList = NestedSequence(expected_type=CommentRecord, count=0)
  126. extLst = Typed(expected_type=ExtensionList, allow_none=True)
  127. _id = None
  128. _path = "/xl/comments/comment{0}.xml"
  129. mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"
  130. _rel_type = "comments"
  131. _rel_id = None
  132. __elements__ = ('authors', 'commentList')
  133. def __init__(self,
  134. authors=None,
  135. commentList=None,
  136. extLst=None,
  137. ):
  138. self.authors = authors
  139. self.commentList = commentList
  140. def to_tree(self):
  141. tree = super().to_tree()
  142. tree.set("xmlns", SHEET_MAIN_NS)
  143. return tree
  144. @property
  145. def comments(self):
  146. """
  147. Return a dictionary of comments keyed by coord
  148. """
  149. authors = self.authors.author
  150. for c in self.commentList:
  151. yield c.ref, Comment(c.content, authors[c.authorId], c.height, c.width)
  152. @classmethod
  153. def from_comments(cls, comments):
  154. """
  155. Create a comment sheet from a list of comments for a particular worksheet
  156. """
  157. authors = IndexedList()
  158. # dedupe authors and get indexes
  159. for comment in comments:
  160. comment.authorId = authors.add(comment.author)
  161. return cls(authors=AuthorList(authors), commentList=comments)
  162. def write_shapes(self, vml=None):
  163. """
  164. Create the VML for comments
  165. """
  166. sw = ShapeWriter(self.comments)
  167. return sw.write(vml)
  168. @property
  169. def path(self):
  170. """
  171. Return path within the archive
  172. """
  173. return self._path.format(self._id)