child.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. # Copyright (c) 2010-2024 openpyxl
  2. import re
  3. import warnings
  4. from openpyxl.worksheet.header_footer import HeaderFooter
  5. """
  6. Base class for worksheets, chartsheets, etc. that can be added to workbooks
  7. """
  8. INVALID_TITLE_REGEX = re.compile(r'[\\*?:/\[\]]')
  9. def avoid_duplicate_name(names, value):
  10. """
  11. Naive check to see whether name already exists.
  12. If name does exist suggest a name using an incrementer
  13. Duplicates are case insensitive
  14. """
  15. # Check for an absolute match in which case we need to find an alternative
  16. match = [n for n in names if n.lower() == value.lower()]
  17. if match:
  18. names = u",".join(names)
  19. sheet_title_regex = re.compile(f'(?P<title>{re.escape(value)})(?P<count>\\d*),?', re.I)
  20. matches = sheet_title_regex.findall(names)
  21. if matches:
  22. # use name, but append with the next highest integer
  23. counts = [int(idx) for (t, idx) in matches if idx.isdigit()]
  24. highest = 0
  25. if counts:
  26. highest = max(counts)
  27. value = u"{0}{1}".format(value, highest + 1)
  28. return value
  29. class _WorkbookChild:
  30. __title = ""
  31. _id = None
  32. _path = "{0}"
  33. _parent = None
  34. _default_title = "Sheet"
  35. def __init__(self, parent=None, title=None):
  36. self._parent = parent
  37. self.title = title or self._default_title
  38. self.HeaderFooter = HeaderFooter()
  39. def __repr__(self):
  40. return '<{0} "{1}">'.format(self.__class__.__name__, self.title)
  41. @property
  42. def parent(self):
  43. return self._parent
  44. @property
  45. def encoding(self):
  46. return self._parent.encoding
  47. @property
  48. def title(self):
  49. return self.__title
  50. @title.setter
  51. def title(self, value):
  52. """
  53. Set a sheet title, ensuring it is valid.
  54. Limited to 31 characters, no special characters.
  55. Duplicate titles will be incremented numerically
  56. """
  57. if not self._parent:
  58. return
  59. if not value:
  60. raise ValueError("Title must have at least one character")
  61. if hasattr(value, "decode"):
  62. if not isinstance(value, str):
  63. try:
  64. value = value.decode("ascii")
  65. except UnicodeDecodeError:
  66. raise ValueError("Worksheet titles must be str")
  67. m = INVALID_TITLE_REGEX.search(value)
  68. if m:
  69. msg = "Invalid character {0} found in sheet title".format(m.group(0))
  70. raise ValueError(msg)
  71. if self.title is not None and self.title != value:
  72. value = avoid_duplicate_name(self.parent.sheetnames, value)
  73. if len(value) > 31:
  74. warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file")
  75. self.__title = value
  76. @property
  77. def oddHeader(self):
  78. return self.HeaderFooter.oddHeader
  79. @oddHeader.setter
  80. def oddHeader(self, value):
  81. self.HeaderFooter.oddHeader = value
  82. @property
  83. def oddFooter(self):
  84. return self.HeaderFooter.oddFooter
  85. @oddFooter.setter
  86. def oddFooter(self, value):
  87. self.HeaderFooter.oddFooter = value
  88. @property
  89. def evenHeader(self):
  90. return self.HeaderFooter.evenHeader
  91. @evenHeader.setter
  92. def evenHeader(self, value):
  93. self.HeaderFooter.evenHeader = value
  94. @property
  95. def evenFooter(self):
  96. return self.HeaderFooter.evenFooter
  97. @evenFooter.setter
  98. def evenFooter(self, value):
  99. self.HeaderFooter.evenFooter = value
  100. @property
  101. def firstHeader(self):
  102. return self.HeaderFooter.firstHeader
  103. @firstHeader.setter
  104. def firstHeader(self, value):
  105. self.HeaderFooter.firstHeader = value
  106. @property
  107. def firstFooter(self):
  108. return self.HeaderFooter.firstFooter
  109. @firstFooter.setter
  110. def firstFooter(self, value):
  111. self.HeaderFooter.firstFooter = value
  112. @property
  113. def path(self):
  114. return self._path.format(self._id)