aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/openpyxl/styles
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/styles')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/__init__.py11
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/alignment.py62
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/borders.py103
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/builtins.py1397
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/cell_style.py206
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/colors.py172
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/differential.py95
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/fills.py224
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/fonts.py113
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py282
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/numbers.py200
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/protection.py17
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/proxy.py62
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/styleable.py151
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/stylesheet.py274
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/styles/table.py94
16 files changed, 3463 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/__init__.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/__init__.py
new file mode 100644
index 00000000..ea20d0d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/__init__.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2010-2024 openpyxl
+
+
+from .alignment import Alignment
+from .borders import Border, Side
+from .colors import Color
+from .fills import PatternFill, GradientFill, Fill
+from .fonts import Font, DEFAULT_FONT
+from .numbers import NumberFormatDescriptor, is_date_format, is_builtin
+from .protection import Protection
+from .named_styles import NamedStyle
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/alignment.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/alignment.py
new file mode 100644
index 00000000..a727f673
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/alignment.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.compat import safe_string
+
+from openpyxl.descriptors import Bool, MinMax, Min, Alias, NoneSet
+from openpyxl.descriptors.serialisable import Serialisable
+
+
+horizontal_alignments = (
+ "general", "left", "center", "right", "fill", "justify", "centerContinuous",
+ "distributed", )
+vertical_aligments = (
+ "top", "center", "bottom", "justify", "distributed",
+)
+
+class Alignment(Serialisable):
+ """Alignment options for use in styles."""
+
+ tagname = "alignment"
+
+ horizontal = NoneSet(values=horizontal_alignments)
+ vertical = NoneSet(values=vertical_aligments)
+ textRotation = NoneSet(values=range(181))
+ textRotation.values.add(255)
+ text_rotation = Alias('textRotation')
+ wrapText = Bool(allow_none=True)
+ wrap_text = Alias('wrapText')
+ shrinkToFit = Bool(allow_none=True)
+ shrink_to_fit = Alias('shrinkToFit')
+ indent = MinMax(min=0, max=255)
+ relativeIndent = MinMax(min=-255, max=255)
+ justifyLastLine = Bool(allow_none=True)
+ readingOrder = Min(min=0)
+
+ def __init__(self, horizontal=None, vertical=None,
+ textRotation=0, wrapText=None, shrinkToFit=None, indent=0, relativeIndent=0,
+ justifyLastLine=None, readingOrder=0, text_rotation=None,
+ wrap_text=None, shrink_to_fit=None, mergeCell=None):
+ self.horizontal = horizontal
+ self.vertical = vertical
+ self.indent = indent
+ self.relativeIndent = relativeIndent
+ self.justifyLastLine = justifyLastLine
+ self.readingOrder = readingOrder
+ if text_rotation is not None:
+ textRotation = text_rotation
+ if textRotation is not None:
+ self.textRotation = int(textRotation)
+ if wrap_text is not None:
+ wrapText = wrap_text
+ self.wrapText = wrapText
+ if shrink_to_fit is not None:
+ shrinkToFit = shrink_to_fit
+ self.shrinkToFit = shrinkToFit
+ # mergeCell is vestigial
+
+
+ def __iter__(self):
+ for attr in self.__attrs__:
+ value = getattr(self, attr)
+ if value is not None and value != 0:
+ yield attr, safe_string(value)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/borders.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/borders.py
new file mode 100644
index 00000000..f9fce814
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/borders.py
@@ -0,0 +1,103 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.compat import safe_string
+from openpyxl.descriptors import (
+ NoneSet,
+ Typed,
+ Bool,
+ Alias,
+ Sequence,
+ Integer,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+
+from .colors import ColorDescriptor
+
+
+BORDER_NONE = None
+BORDER_DASHDOT = 'dashDot'
+BORDER_DASHDOTDOT = 'dashDotDot'
+BORDER_DASHED = 'dashed'
+BORDER_DOTTED = 'dotted'
+BORDER_DOUBLE = 'double'
+BORDER_HAIR = 'hair'
+BORDER_MEDIUM = 'medium'
+BORDER_MEDIUMDASHDOT = 'mediumDashDot'
+BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot'
+BORDER_MEDIUMDASHED = 'mediumDashed'
+BORDER_SLANTDASHDOT = 'slantDashDot'
+BORDER_THICK = 'thick'
+BORDER_THIN = 'thin'
+
+
+class Side(Serialisable):
+
+ """Border options for use in styles.
+ Caution: if you do not specify a border_style, other attributes will
+ have no effect !"""
+
+
+ color = ColorDescriptor(allow_none=True)
+ style = NoneSet(values=('dashDot','dashDotDot', 'dashed','dotted',
+ 'double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot',
+ 'mediumDashed', 'slantDashDot', 'thick', 'thin')
+ )
+ border_style = Alias('style')
+
+ def __init__(self, style=None, color=None, border_style=None):
+ if border_style is not None:
+ style = border_style
+ self.style = style
+ self.color = color
+
+
+class Border(Serialisable):
+ """Border positioning for use in styles."""
+
+ tagname = "border"
+
+ __elements__ = ('start', 'end', 'left', 'right', 'top', 'bottom',
+ 'diagonal', 'vertical', 'horizontal')
+
+ # child elements
+ start = Typed(expected_type=Side, allow_none=True)
+ end = Typed(expected_type=Side, allow_none=True)
+ left = Typed(expected_type=Side, allow_none=True)
+ right = Typed(expected_type=Side, allow_none=True)
+ top = Typed(expected_type=Side, allow_none=True)
+ bottom = Typed(expected_type=Side, allow_none=True)
+ diagonal = Typed(expected_type=Side, allow_none=True)
+ vertical = Typed(expected_type=Side, allow_none=True)
+ horizontal = Typed(expected_type=Side, allow_none=True)
+ # attributes
+ outline = Bool()
+ diagonalUp = Bool()
+ diagonalDown = Bool()
+
+ def __init__(self, left=None, right=None, top=None,
+ bottom=None, diagonal=None, diagonal_direction=None,
+ vertical=None, horizontal=None, diagonalUp=False, diagonalDown=False,
+ outline=True, start=None, end=None):
+ self.left = left
+ self.right = right
+ self.top = top
+ self.bottom = bottom
+ self.diagonal = diagonal
+ self.vertical = vertical
+ self.horizontal = horizontal
+ self.diagonal_direction = diagonal_direction
+ self.diagonalUp = diagonalUp
+ self.diagonalDown = diagonalDown
+ self.outline = outline
+ self.start = start
+ self.end = end
+
+ def __iter__(self):
+ for attr in self.__attrs__:
+ value = getattr(self, attr)
+ if value and attr != "outline":
+ yield attr, safe_string(value)
+ elif attr == "outline" and not value:
+ yield attr, safe_string(value)
+
+DEFAULT_BORDER = Border(left=Side(), right=Side(), top=Side(), bottom=Side(), diagonal=Side())
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/builtins.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/builtins.py
new file mode 100644
index 00000000..7095eb32
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/builtins.py
@@ -0,0 +1,1397 @@
+# Copyright (c) 2010-2024 openpyxl
+
+# Builtins styles as defined in Part 4 Annex G.2
+
+from .named_styles import NamedStyle
+from openpyxl.xml.functions import fromstring
+
+
+normal = """
+ <namedStyle builtinId="0" name="Normal">
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+comma = """
+ <namedStyle builtinId="3" name="Comma">
+ <alignment/>
+ <number_format>_-* #,##0.00\\ _$_-;\\-* #,##0.00\\ _$_-;_-* "-"??\\ _$_-;_-@_-</number_format>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+comma_0 = """
+ <namedStyle builtinId="6" name="Comma [0]">
+ <alignment/>
+ <number_format>_-* #,##0\\ _$_-;\\-* #,##0\\ _$_-;_-* "-"\\ _$_-;_-@_-</number_format>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+currency = """
+ <namedStyle builtinId="4" name="Currency">
+ <alignment/>
+ <number_format>_-* #,##0.00\\ "$"_-;\\-* #,##0.00\\ "$"_-;_-* "-"??\\ "$"_-;_-@_-</number_format>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+currency_0 = """
+ <namedStyle builtinId="7" name="Currency [0]">
+ <alignment/>
+ <number_format>_-* #,##0\\ "$"_-;\\-* #,##0\\ "$"_-;_-* "-"\\ "$"_-;_-@_-</number_format>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+percent = """
+ <namedStyle builtinId="5" name="Percent">
+ <alignment/>
+ <number_format>0%</number_format>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+hyperlink = """
+ <namedStyle builtinId="8" name="Hyperlink" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="10"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+followed_hyperlink = """
+ <namedStyle builtinId="9" name="Followed Hyperlink" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="11"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+title = """
+ <namedStyle builtinId="15" name="Title">
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Cambria"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="3"/>
+ <sz val="18"/>
+ <scheme val="major"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+headline_1 = """
+ <namedStyle builtinId="16" name="Headline 1" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom style="thick">
+ <color theme="4"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="3"/>
+ <sz val="15"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+headline_2 = """
+ <namedStyle builtinId="17" name="Headline 2" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom style="thick">
+ <color theme="4" tint="0.5"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="3"/>
+ <sz val="13"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+headline_3 = """
+ <namedStyle builtinId="18" name="Headline 3" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom style="medium">
+ <color theme="4" tint="0.4"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="3"/>
+ <sz val="11"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+
+"""
+
+headline_4 = """
+ <namedStyle builtinId="19" name="Headline 4">
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="3"/>
+ <sz val="11"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+good = """
+ <namedStyle builtinId="26" name="Good" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFC6EFCE"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color rgb="FF006100"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+bad = """
+ <namedStyle builtinId="27" name="Bad" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFFC7CE"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color rgb="FF9C0006"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+neutral = """
+ <namedStyle builtinId="28" name="Neutral" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFFEB9C"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color rgb="FF9C6500"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+input = """
+ <namedStyle builtinId="20" name="Input" >
+ <alignment/>
+ <border>
+ <left style="thin">
+ <color rgb="FF7F7F7F"/>
+ </left>
+ <right style="thin">
+ <color rgb="FF7F7F7F"/>
+ </right>
+ <top style="thin">
+ <color rgb="FF7F7F7F"/>
+ </top>
+ <bottom style="thin">
+ <color rgb="FF7F7F7F"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFFCC99"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color rgb="FF3F3F76"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+output = """
+ <namedStyle builtinId="21" name="Output" >
+ <alignment/>
+ <border>
+ <left style="thin">
+ <color rgb="FF3F3F3F"/>
+ </left>
+ <right style="thin">
+ <color rgb="FF3F3F3F"/>
+ </right>
+ <top style="thin">
+ <color rgb="FF3F3F3F"/>
+ </top>
+ <bottom style="thin">
+ <color rgb="FF3F3F3F"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFF2F2F2"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color rgb="FF3F3F3F"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+calculation = """
+ <namedStyle builtinId="22" name="Calculation" >
+ <alignment/>
+ <border>
+ <left style="thin">
+ <color rgb="FF7F7F7F"/>
+ </left>
+ <right style="thin">
+ <color rgb="FF7F7F7F"/>
+ </right>
+ <top style="thin">
+ <color rgb="FF7F7F7F"/>
+ </top>
+ <bottom style="thin">
+ <color rgb="FF7F7F7F"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFF2F2F2"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color rgb="FFFA7D00"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+linked_cell = """
+ <namedStyle builtinId="24" name="Linked Cell" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom style="double">
+ <color rgb="FFFF8001"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color rgb="FFFA7D00"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+check_cell = """
+ <namedStyle builtinId="23" name="Check Cell" >
+ <alignment/>
+ <border>
+ <left style="double">
+ <color rgb="FF3F3F3F"/>
+ </left>
+ <right style="double">
+ <color rgb="FF3F3F3F"/>
+ </right>
+ <top style="double">
+ <color rgb="FF3F3F3F"/>
+ </top>
+ <bottom style="double">
+ <color rgb="FF3F3F3F"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFA5A5A5"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+warning = """
+ <namedStyle builtinId="11" name="Warning Text" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color rgb="FFFF0000"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+note = """
+ <namedStyle builtinId="10" name="Note" >
+ <alignment/>
+ <border>
+ <left style="thin">
+ <color rgb="FFB2B2B2"/>
+ </left>
+ <right style="thin">
+ <color rgb="FFB2B2B2"/>
+ </right>
+ <top style="thin">
+ <color rgb="FFB2B2B2"/>
+ </top>
+ <bottom style="thin">
+ <color rgb="FFB2B2B2"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor rgb="FFFFFFCC"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+explanatory = """
+ <namedStyle builtinId="53" name="Explanatory Text" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <i val="1"/>
+ <color rgb="FF7F7F7F"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+total = """
+ <namedStyle builtinId="25" name="Total" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top style="thin">
+ <color theme="4"/>
+ </top>
+ <bottom style="double">
+ <color theme="4"/>
+ </bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <b val="1"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_1 = """
+ <namedStyle builtinId="29" name="Accent1" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="4"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_1_20 = """
+ <namedStyle builtinId="30" name="20 % - Accent1" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="4" tint="0.7999816888943144"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_1_40 = """
+ <namedStyle builtinId="31" name="40 % - Accent1" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="4" tint="0.5999938962981048"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_1_60 = """
+ <namedStyle builtinId="32" name="60 % - Accent1" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="4" tint="0.3999755851924192"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_2 = """<namedStyle builtinId="33" name="Accent2" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="5"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+accent_2_20 = """
+ <namedStyle builtinId="34" name="20 % - Accent2" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="5" tint="0.7999816888943144"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+accent_2_40 = """
+<namedStyle builtinId="35" name="40 % - Accent2" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="5" tint="0.5999938962981048"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+accent_2_60 = """
+<namedStyle builtinId="36" name="60 % - Accent2" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="5" tint="0.3999755851924192"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+accent_3 = """
+<namedStyle builtinId="37" name="Accent3" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="6"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+accent_3_20 = """
+ <namedStyle builtinId="38" name="20 % - Accent3" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="6" tint="0.7999816888943144"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>"""
+
+accent_3_40 = """
+ <namedStyle builtinId="39" name="40 % - Accent3" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="6" tint="0.5999938962981048"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+accent_3_60 = """
+ <namedStyle builtinId="40" name="60 % - Accent3" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="6" tint="0.3999755851924192"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+accent_4 = """
+ <namedStyle builtinId="41" name="Accent4" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="7"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_4_20 = """
+ <namedStyle builtinId="42" name="20 % - Accent4" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="7" tint="0.7999816888943144"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_4_40 = """
+ <namedStyle builtinId="43" name="40 % - Accent4" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="7" tint="0.5999938962981048"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_4_60 = """
+<namedStyle builtinId="44" name="60 % - Accent4" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="7" tint="0.3999755851924192"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_5 = """
+ <namedStyle builtinId="45" name="Accent5" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="8"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_5_20 = """
+ <namedStyle builtinId="46" name="20 % - Accent5" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="8" tint="0.7999816888943144"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_5_40 = """
+ <namedStyle builtinId="47" name="40 % - Accent5" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="8" tint="0.5999938962981048"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_5_60 = """
+ <namedStyle builtinId="48" name="60 % - Accent5" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="8" tint="0.3999755851924192"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_6 = """
+ <namedStyle builtinId="49" name="Accent6" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="9"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_6_20 = """
+ <namedStyle builtinId="50" name="20 % - Accent6" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="9" tint="0.7999816888943144"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_6_40 = """
+ <namedStyle builtinId="51" name="40 % - Accent6" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="9" tint="0.5999938962981048"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="1"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+accent_6_60 = """
+ <namedStyle builtinId="52" name="60 % - Accent6" >
+ <alignment/>
+ <border>
+ <left/>
+ <right/>
+ <top/>
+ <bottom/>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill patternType="solid">
+ <fgColor theme="9" tint="0.3999755851924192"/>
+ <bgColor indexed="65"/>
+ </patternFill>
+ </fill>
+ <font>
+ <name val="Calibri"/>
+ <family val="2"/>
+ <color theme="0"/>
+ <sz val="12"/>
+ <scheme val="minor"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+pandas_highlight = """
+ <namedStyle hidden="0" name="Pandas">
+ <alignment horizontal="center"/>
+ <border>
+ <left style="thin"><color rgb="00000000"/></left>
+ <right style="thin"><color rgb="00000000"/></right>
+ <top style="thin"><color rgb="00000000"/></top>
+ <bottom style="thin"><color rgb="00000000"/></bottom>
+ <diagonal/>
+ </border>
+ <fill>
+ <patternFill/>
+ </fill>
+ <font>
+ <b val="1"/>
+ </font>
+ <protection hidden="0" locked="1"/>
+ </namedStyle>
+"""
+
+styles = dict(
+ [
+ ('Normal', NamedStyle.from_tree(fromstring(normal))),
+ ('Comma', NamedStyle.from_tree(fromstring(comma))),
+ ('Currency', NamedStyle.from_tree(fromstring(currency))),
+ ('Percent', NamedStyle.from_tree(fromstring(percent))),
+ ('Comma [0]', NamedStyle.from_tree(fromstring(comma_0))),
+ ('Currency [0]', NamedStyle.from_tree(fromstring(currency_0))),
+ ('Hyperlink', NamedStyle.from_tree(fromstring(hyperlink))),
+ ('Followed Hyperlink', NamedStyle.from_tree(fromstring(followed_hyperlink))),
+ ('Note', NamedStyle.from_tree(fromstring(note))),
+ ('Warning Text', NamedStyle.from_tree(fromstring(warning))),
+ ('Title', NamedStyle.from_tree(fromstring(title))),
+ ('Headline 1', NamedStyle.from_tree(fromstring(headline_1))),
+ ('Headline 2', NamedStyle.from_tree(fromstring(headline_2))),
+ ('Headline 3', NamedStyle.from_tree(fromstring(headline_3))),
+ ('Headline 4', NamedStyle.from_tree(fromstring(headline_4))),
+ ('Input', NamedStyle.from_tree(fromstring(input))),
+ ('Output', NamedStyle.from_tree(fromstring(output))),
+ ('Calculation',NamedStyle.from_tree(fromstring(calculation))),
+ ('Check Cell', NamedStyle.from_tree(fromstring(check_cell))),
+ ('Linked Cell', NamedStyle.from_tree(fromstring(linked_cell))),
+ ('Total', NamedStyle.from_tree(fromstring(total))),
+ ('Good', NamedStyle.from_tree(fromstring(good))),
+ ('Bad', NamedStyle.from_tree(fromstring(bad))),
+ ('Neutral', NamedStyle.from_tree(fromstring(neutral))),
+ ('Accent1', NamedStyle.from_tree(fromstring(accent_1))),
+ ('20 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_20))),
+ ('40 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_40))),
+ ('60 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_60))),
+ ('Accent2', NamedStyle.from_tree(fromstring(accent_2))),
+ ('20 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_20))),
+ ('40 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_40))),
+ ('60 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_60))),
+ ('Accent3', NamedStyle.from_tree(fromstring(accent_3))),
+ ('20 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_20))),
+ ('40 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_40))),
+ ('60 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_60))),
+ ('Accent4', NamedStyle.from_tree(fromstring(accent_4))),
+ ('20 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_20))),
+ ('40 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_40))),
+ ('60 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_60))),
+ ('Accent5', NamedStyle.from_tree(fromstring(accent_5))),
+ ('20 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_20))),
+ ('40 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_40))),
+ ('60 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_60))),
+ ('Accent6', NamedStyle.from_tree(fromstring(accent_6))),
+ ('20 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_20))),
+ ('40 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_40))),
+ ('60 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_60))),
+ ('Explanatory Text', NamedStyle.from_tree(fromstring(explanatory))),
+ ('Pandas', NamedStyle.from_tree(fromstring(pandas_highlight)))
+ ]
+)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/cell_style.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/cell_style.py
new file mode 100644
index 00000000..51091aa5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/cell_style.py
@@ -0,0 +1,206 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from array import array
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+ Typed,
+ Float,
+ Bool,
+ Integer,
+ Sequence,
+)
+from openpyxl.descriptors.excel import ExtensionList
+from openpyxl.utils.indexed_list import IndexedList
+
+
+from .alignment import Alignment
+from .protection import Protection
+
+
+class ArrayDescriptor:
+
+ def __init__(self, key):
+ self.key = key
+
+ def __get__(self, instance, cls):
+ return instance[self.key]
+
+ def __set__(self, instance, value):
+ instance[self.key] = value
+
+
+class StyleArray(array):
+ """
+ Simplified named tuple with an array
+ """
+
+ __slots__ = ()
+ tagname = 'xf'
+
+ fontId = ArrayDescriptor(0)
+ fillId = ArrayDescriptor(1)
+ borderId = ArrayDescriptor(2)
+ numFmtId = ArrayDescriptor(3)
+ protectionId = ArrayDescriptor(4)
+ alignmentId = ArrayDescriptor(5)
+ pivotButton = ArrayDescriptor(6)
+ quotePrefix = ArrayDescriptor(7)
+ xfId = ArrayDescriptor(8)
+
+
+ def __new__(cls, args=[0]*9):
+ return array.__new__(cls, 'i', args)
+
+
+ def __hash__(self):
+ return hash(tuple(self))
+
+
+ def __copy__(self):
+ return StyleArray((self))
+
+
+ def __deepcopy__(self, memo):
+ return StyleArray((self))
+
+
+class CellStyle(Serialisable):
+
+ tagname = "xf"
+
+ numFmtId = Integer()
+ fontId = Integer()
+ fillId = Integer()
+ borderId = Integer()
+ xfId = Integer(allow_none=True)
+ quotePrefix = Bool(allow_none=True)
+ pivotButton = Bool(allow_none=True)
+ applyNumberFormat = Bool(allow_none=True)
+ applyFont = Bool(allow_none=True)
+ applyFill = Bool(allow_none=True)
+ applyBorder = Bool(allow_none=True)
+ applyAlignment = Bool(allow_none=True)
+ applyProtection = Bool(allow_none=True)
+ alignment = Typed(expected_type=Alignment, allow_none=True)
+ protection = Typed(expected_type=Protection, allow_none=True)
+ extLst = Typed(expected_type=ExtensionList, allow_none=True)
+
+ __elements__ = ('alignment', 'protection')
+ __attrs__ = ("numFmtId", "fontId", "fillId", "borderId",
+ "applyAlignment", "applyProtection", "pivotButton", "quotePrefix", "xfId")
+
+ def __init__(self,
+ numFmtId=0,
+ fontId=0,
+ fillId=0,
+ borderId=0,
+ xfId=None,
+ quotePrefix=None,
+ pivotButton=None,
+ applyNumberFormat=None,
+ applyFont=None,
+ applyFill=None,
+ applyBorder=None,
+ applyAlignment=None,
+ applyProtection=None,
+ alignment=None,
+ protection=None,
+ extLst=None,
+ ):
+ self.numFmtId = numFmtId
+ self.fontId = fontId
+ self.fillId = fillId
+ self.borderId = borderId
+ self.xfId = xfId
+ self.quotePrefix = quotePrefix
+ self.pivotButton = pivotButton
+ self.applyNumberFormat = applyNumberFormat
+ self.applyFont = applyFont
+ self.applyFill = applyFill
+ self.applyBorder = applyBorder
+ self.alignment = alignment
+ self.protection = protection
+
+
+ def to_array(self):
+ """
+ Convert to StyleArray
+ """
+ style = StyleArray()
+ for k in ("fontId", "fillId", "borderId", "numFmtId", "pivotButton",
+ "quotePrefix", "xfId"):
+ v = getattr(self, k, 0)
+ if v is not None:
+ setattr(style, k, v)
+ return style
+
+
+ @classmethod
+ def from_array(cls, style):
+ """
+ Convert from StyleArray
+ """
+ return cls(numFmtId=style.numFmtId, fontId=style.fontId,
+ fillId=style.fillId, borderId=style.borderId, xfId=style.xfId,
+ quotePrefix=style.quotePrefix, pivotButton=style.pivotButton,)
+
+
+ @property
+ def applyProtection(self):
+ return self.protection is not None or None
+
+
+ @property
+ def applyAlignment(self):
+ return self.alignment is not None or None
+
+
+class CellStyleList(Serialisable):
+
+ tagname = "cellXfs"
+
+ __attrs__ = ("count",)
+
+ count = Integer(allow_none=True)
+ xf = Sequence(expected_type=CellStyle)
+ alignment = Sequence(expected_type=Alignment)
+ protection = Sequence(expected_type=Protection)
+
+ __elements__ = ('xf',)
+
+ def __init__(self,
+ count=None,
+ xf=(),
+ ):
+ self.xf = xf
+
+
+ @property
+ def count(self):
+ return len(self.xf)
+
+
+ def __getitem__(self, idx):
+ try:
+ return self.xf[idx]
+ except IndexError:
+ print((f"{idx} is out of range"))
+ return self.xf[idx]
+
+
+ def _to_array(self):
+ """
+ Extract protection and alignments, convert to style array
+ """
+ self.prots = IndexedList([Protection()])
+ self.alignments = IndexedList([Alignment()])
+ styles = [] # allow duplicates
+ for xf in self.xf:
+ style = xf.to_array()
+ if xf.alignment is not None:
+ style.alignmentId = self.alignments.add(xf.alignment)
+ if xf.protection is not None:
+ style.protectionId = self.prots.add(xf.protection)
+ styles.append(style)
+ return IndexedList(styles)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/colors.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/colors.py
new file mode 100644
index 00000000..6fa7476d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/colors.py
@@ -0,0 +1,172 @@
+# Copyright (c) 2010-2024 openpyxl
+
+import re
+from openpyxl.compat import safe_string
+from openpyxl.descriptors import (
+ String,
+ Bool,
+ MinMax,
+ Integer,
+ Typed,
+)
+from openpyxl.descriptors.sequence import NestedSequence
+from openpyxl.descriptors.serialisable import Serialisable
+
+# Default Color Index as per 18.8.27 of ECMA Part 4
+COLOR_INDEX = (
+ '00000000', '00FFFFFF', '00FF0000', '0000FF00', '000000FF', #0-4
+ '00FFFF00', '00FF00FF', '0000FFFF', '00000000', '00FFFFFF', #5-9
+ '00FF0000', '0000FF00', '000000FF', '00FFFF00', '00FF00FF', #10-14
+ '0000FFFF', '00800000', '00008000', '00000080', '00808000', #15-19
+ '00800080', '00008080', '00C0C0C0', '00808080', '009999FF', #20-24
+ '00993366', '00FFFFCC', '00CCFFFF', '00660066', '00FF8080', #25-29
+ '000066CC', '00CCCCFF', '00000080', '00FF00FF', '00FFFF00', #30-34
+ '0000FFFF', '00800080', '00800000', '00008080', '000000FF', #35-39
+ '0000CCFF', '00CCFFFF', '00CCFFCC', '00FFFF99', '0099CCFF', #40-44
+ '00FF99CC', '00CC99FF', '00FFCC99', '003366FF', '0033CCCC', #45-49
+ '0099CC00', '00FFCC00', '00FF9900', '00FF6600', '00666699', #50-54
+ '00969696', '00003366', '00339966', '00003300', '00333300', #55-59
+ '00993300', '00993366', '00333399', '00333333', #60-63
+)
+# indices 64 and 65 are reserved for the system foreground and background colours respectively
+
+# Will remove these definitions in a future release
+BLACK = COLOR_INDEX[0]
+WHITE = COLOR_INDEX[1]
+#RED = COLOR_INDEX[2]
+#DARKRED = COLOR_INDEX[8]
+BLUE = COLOR_INDEX[4]
+#DARKBLUE = COLOR_INDEX[12]
+#GREEN = COLOR_INDEX[3]
+#DARKGREEN = COLOR_INDEX[9]
+#YELLOW = COLOR_INDEX[5]
+#DARKYELLOW = COLOR_INDEX[19]
+
+
+aRGB_REGEX = re.compile("^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$")
+
+
+class RGB(Typed):
+ """
+ Descriptor for aRGB values
+ If not supplied alpha is 00
+ """
+
+ expected_type = str
+
+ def __set__(self, instance, value):
+ if not self.allow_none:
+ m = aRGB_REGEX.match(value)
+ if m is None:
+ raise ValueError("Colors must be aRGB hex values")
+ if len(value) == 6:
+ value = "00" + value
+ super().__set__(instance, value)
+
+
+class Color(Serialisable):
+ """Named colors for use in styles."""
+
+ tagname = "color"
+
+ rgb = RGB()
+ indexed = Integer()
+ auto = Bool()
+ theme = Integer()
+ tint = MinMax(min=-1, max=1, expected_type=float)
+ type = String()
+
+
+ def __init__(self, rgb=BLACK, indexed=None, auto=None, theme=None, tint=0.0, index=None, type='rgb'):
+ if index is not None:
+ indexed = index
+ if indexed is not None:
+ self.type = 'indexed'
+ self.indexed = indexed
+ elif theme is not None:
+ self.type = 'theme'
+ self.theme = theme
+ elif auto is not None:
+ self.type = 'auto'
+ self.auto = auto
+ else:
+ self.rgb = rgb
+ self.type = 'rgb'
+ self.tint = tint
+
+ @property
+ def value(self):
+ return getattr(self, self.type)
+
+ @value.setter
+ def value(self, value):
+ setattr(self, self.type, value)
+
+ def __iter__(self):
+ attrs = [(self.type, self.value)]
+ if self.tint != 0:
+ attrs.append(('tint', self.tint))
+ for k, v in attrs:
+ yield k, safe_string(v)
+
+ @property
+ def index(self):
+ # legacy
+ return self.value
+
+
+ def __add__(self, other):
+ """
+ Adding colours is undefined behaviour best do nothing
+ """
+ if not isinstance(other, Color):
+ return super().__add__(other)
+ return self
+
+
+class ColorDescriptor(Typed):
+
+ expected_type = Color
+
+ def __set__(self, instance, value):
+ if isinstance(value, str):
+ value = Color(rgb=value)
+ super().__set__(instance, value)
+
+
+class RgbColor(Serialisable):
+
+ tagname = "rgbColor"
+
+ rgb = RGB()
+
+ def __init__(self,
+ rgb=None,
+ ):
+ self.rgb = rgb
+
+
+class ColorList(Serialisable):
+
+ tagname = "colors"
+
+ indexedColors = NestedSequence(expected_type=RgbColor)
+ mruColors = NestedSequence(expected_type=Color)
+
+ __elements__ = ('indexedColors', 'mruColors')
+
+ def __init__(self,
+ indexedColors=(),
+ mruColors=(),
+ ):
+ self.indexedColors = indexedColors
+ self.mruColors = mruColors
+
+
+ def __bool__(self):
+ return bool(self.indexedColors) or bool(self.mruColors)
+
+
+ @property
+ def index(self):
+ return [val.rgb for val in self.indexedColors]
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/differential.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/differential.py
new file mode 100644
index 00000000..109577e4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/differential.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors import (
+ Typed,
+ Sequence,
+ Alias,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.styles import (
+ Font,
+ Fill,
+ Border,
+ Alignment,
+ Protection,
+ )
+from .numbers import NumberFormat
+
+
+class DifferentialStyle(Serialisable):
+
+ tagname = "dxf"
+
+ __elements__ = ("font", "numFmt", "fill", "alignment", "border", "protection")
+
+ font = Typed(expected_type=Font, allow_none=True)
+ numFmt = Typed(expected_type=NumberFormat, allow_none=True)
+ fill = Typed(expected_type=Fill, allow_none=True)
+ alignment = Typed(expected_type=Alignment, allow_none=True)
+ border = Typed(expected_type=Border, allow_none=True)
+ protection = Typed(expected_type=Protection, allow_none=True)
+
+ def __init__(self,
+ font=None,
+ numFmt=None,
+ fill=None,
+ alignment=None,
+ border=None,
+ protection=None,
+ extLst=None,
+ ):
+ self.font = font
+ self.numFmt = numFmt
+ self.fill = fill
+ self.alignment = alignment
+ self.border = border
+ self.protection = protection
+ self.extLst = extLst
+
+
+class DifferentialStyleList(Serialisable):
+ """
+ Dedupable container for differential styles.
+ """
+
+ tagname = "dxfs"
+
+ dxf = Sequence(expected_type=DifferentialStyle)
+ styles = Alias("dxf")
+ __attrs__ = ("count",)
+
+
+ def __init__(self, dxf=(), count=None):
+ self.dxf = dxf
+
+
+ def append(self, dxf):
+ """
+ Check to see whether style already exists and append it if does not.
+ """
+ if not isinstance(dxf, DifferentialStyle):
+ raise TypeError('expected ' + str(DifferentialStyle))
+ if dxf in self.styles:
+ return
+ self.styles.append(dxf)
+
+
+ def add(self, dxf):
+ """
+ Add a differential style and return its index
+ """
+ self.append(dxf)
+ return self.styles.index(dxf)
+
+
+ def __bool__(self):
+ return bool(self.styles)
+
+
+ def __getitem__(self, idx):
+ return self.styles[idx]
+
+
+ @property
+ def count(self):
+ return len(self.dxf)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/fills.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/fills.py
new file mode 100644
index 00000000..7071abd6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/fills.py
@@ -0,0 +1,224 @@
+
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors import (
+ Float,
+ Set,
+ Alias,
+ NoneSet,
+ Sequence,
+ Integer,
+ MinMax,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.compat import safe_string
+
+from .colors import ColorDescriptor, Color
+
+from openpyxl.xml.functions import Element, localname
+from openpyxl.xml.constants import SHEET_MAIN_NS
+
+
+FILL_NONE = 'none'
+FILL_SOLID = 'solid'
+FILL_PATTERN_DARKDOWN = 'darkDown'
+FILL_PATTERN_DARKGRAY = 'darkGray'
+FILL_PATTERN_DARKGRID = 'darkGrid'
+FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal'
+FILL_PATTERN_DARKTRELLIS = 'darkTrellis'
+FILL_PATTERN_DARKUP = 'darkUp'
+FILL_PATTERN_DARKVERTICAL = 'darkVertical'
+FILL_PATTERN_GRAY0625 = 'gray0625'
+FILL_PATTERN_GRAY125 = 'gray125'
+FILL_PATTERN_LIGHTDOWN = 'lightDown'
+FILL_PATTERN_LIGHTGRAY = 'lightGray'
+FILL_PATTERN_LIGHTGRID = 'lightGrid'
+FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal'
+FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis'
+FILL_PATTERN_LIGHTUP = 'lightUp'
+FILL_PATTERN_LIGHTVERTICAL = 'lightVertical'
+FILL_PATTERN_MEDIUMGRAY = 'mediumGray'
+
+fills = (FILL_SOLID, FILL_PATTERN_DARKDOWN, FILL_PATTERN_DARKGRAY,
+ FILL_PATTERN_DARKGRID, FILL_PATTERN_DARKHORIZONTAL, FILL_PATTERN_DARKTRELLIS,
+ FILL_PATTERN_DARKUP, FILL_PATTERN_DARKVERTICAL, FILL_PATTERN_GRAY0625,
+ FILL_PATTERN_GRAY125, FILL_PATTERN_LIGHTDOWN, FILL_PATTERN_LIGHTGRAY,
+ FILL_PATTERN_LIGHTGRID, FILL_PATTERN_LIGHTHORIZONTAL,
+ FILL_PATTERN_LIGHTTRELLIS, FILL_PATTERN_LIGHTUP, FILL_PATTERN_LIGHTVERTICAL,
+ FILL_PATTERN_MEDIUMGRAY)
+
+
+class Fill(Serialisable):
+
+ """Base class"""
+
+ tagname = "fill"
+
+ @classmethod
+ def from_tree(cls, el):
+ children = [c for c in el]
+ if not children:
+ return
+ child = children[0]
+ if "patternFill" in child.tag:
+ return PatternFill._from_tree(child)
+ return super(Fill, GradientFill).from_tree(child)
+
+
+class PatternFill(Fill):
+ """Area fill patterns for use in styles.
+ Caution: if you do not specify a fill_type, other attributes will have
+ no effect !"""
+
+ tagname = "patternFill"
+
+ __elements__ = ('fgColor', 'bgColor')
+
+ patternType = NoneSet(values=fills)
+ fill_type = Alias("patternType")
+ fgColor = ColorDescriptor()
+ start_color = Alias("fgColor")
+ bgColor = ColorDescriptor()
+ end_color = Alias("bgColor")
+
+ def __init__(self, patternType=None, fgColor=Color(), bgColor=Color(),
+ fill_type=None, start_color=None, end_color=None):
+ if fill_type is not None:
+ patternType = fill_type
+ self.patternType = patternType
+ if start_color is not None:
+ fgColor = start_color
+ self.fgColor = fgColor
+ if end_color is not None:
+ bgColor = end_color
+ self.bgColor = bgColor
+
+ @classmethod
+ def _from_tree(cls, el):
+ attrib = dict(el.attrib)
+ for child in el:
+ desc = localname(child)
+ attrib[desc] = Color.from_tree(child)
+ return cls(**attrib)
+
+
+ def to_tree(self, tagname=None, idx=None):
+ parent = Element("fill")
+ el = Element(self.tagname)
+ if self.patternType is not None:
+ el.set('patternType', self.patternType)
+ for c in self.__elements__:
+ value = getattr(self, c)
+ if value != Color():
+ el.append(value.to_tree(c))
+ parent.append(el)
+ return parent
+
+
+DEFAULT_EMPTY_FILL = PatternFill()
+DEFAULT_GRAY_FILL = PatternFill(patternType='gray125')
+
+
+class Stop(Serialisable):
+
+ tagname = "stop"
+
+ position = MinMax(min=0, max=1)
+ color = ColorDescriptor()
+
+ def __init__(self, color, position):
+ self.position = position
+ self.color = color
+
+
+def _assign_position(values):
+ """
+ Automatically assign positions if a list of colours is provided.
+
+ It is not permitted to mix colours and stops
+ """
+ n_values = len(values)
+ n_stops = sum(isinstance(value, Stop) for value in values)
+
+ if n_stops == 0:
+ interval = 1
+ if n_values > 2:
+ interval = 1 / (n_values - 1)
+ values = [Stop(value, i * interval)
+ for i, value in enumerate(values)]
+
+ elif n_stops < n_values:
+ raise ValueError('Cannot interpret mix of Stops and Colors in GradientFill')
+
+ pos = set()
+ for stop in values:
+ if stop.position in pos:
+ raise ValueError("Duplicate position {0}".format(stop.position))
+ pos.add(stop.position)
+
+ return values
+
+
+class StopList(Sequence):
+
+ expected_type = Stop
+
+ def __set__(self, obj, values):
+ values = _assign_position(values)
+ super().__set__(obj, values)
+
+
+class GradientFill(Fill):
+ """Fill areas with gradient
+
+ Two types of gradient fill are supported:
+
+ - A type='linear' gradient interpolates colours between
+ a set of specified Stops, across the length of an area.
+ The gradient is left-to-right by default, but this
+ orientation can be modified with the degree
+ attribute. A list of Colors can be provided instead
+ and they will be positioned with equal distance between them.
+
+ - A type='path' gradient applies a linear gradient from each
+ edge of the area. Attributes top, right, bottom, left specify
+ the extent of fill from the respective borders. Thus top="0.2"
+ will fill the top 20% of the cell.
+
+ """
+
+ tagname = "gradientFill"
+
+ type = Set(values=('linear', 'path'))
+ fill_type = Alias("type")
+ degree = Float()
+ left = Float()
+ right = Float()
+ top = Float()
+ bottom = Float()
+ stop = StopList()
+
+
+ def __init__(self, type="linear", degree=0, left=0, right=0, top=0,
+ bottom=0, stop=()):
+ self.degree = degree
+ self.left = left
+ self.right = right
+ self.top = top
+ self.bottom = bottom
+ self.stop = stop
+ self.type = type
+
+
+ def __iter__(self):
+ for attr in self.__attrs__:
+ value = getattr(self, attr)
+ if value:
+ yield attr, safe_string(value)
+
+
+ def to_tree(self, tagname=None, namespace=None, idx=None):
+ parent = Element("fill")
+ el = super().to_tree()
+ parent.append(el)
+ return parent
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/fonts.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/fonts.py
new file mode 100644
index 00000000..06e343fc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/fonts.py
@@ -0,0 +1,113 @@
+# Copyright (c) 2010-2024 openpyxl
+
+
+from openpyxl.descriptors import (
+ Alias,
+ Sequence,
+ Integer
+)
+from openpyxl.descriptors.serialisable import Serialisable
+
+from openpyxl.descriptors.nested import (
+ NestedValue,
+ NestedBool,
+ NestedNoneSet,
+ NestedMinMax,
+ NestedString,
+ NestedInteger,
+ NestedFloat,
+)
+from .colors import ColorDescriptor, Color, BLACK
+
+from openpyxl.compat import safe_string
+from openpyxl.xml.functions import Element, SubElement
+from openpyxl.xml.constants import SHEET_MAIN_NS
+
+
+def _no_value(tagname, value, namespace=None):
+ if value:
+ return Element(tagname, val=safe_string(value))
+
+
+class Font(Serialisable):
+ """Font options used in styles."""
+
+ UNDERLINE_DOUBLE = 'double'
+ UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting'
+ UNDERLINE_SINGLE = 'single'
+ UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting'
+
+ name = NestedString(allow_none=True)
+ charset = NestedInteger(allow_none=True)
+ family = NestedMinMax(min=0, max=14, allow_none=True)
+ sz = NestedFloat(allow_none=True)
+ size = Alias("sz")
+ b = NestedBool(to_tree=_no_value)
+ bold = Alias("b")
+ i = NestedBool(to_tree=_no_value)
+ italic = Alias("i")
+ strike = NestedBool(allow_none=True)
+ strikethrough = Alias("strike")
+ outline = NestedBool(allow_none=True)
+ shadow = NestedBool(allow_none=True)
+ condense = NestedBool(allow_none=True)
+ extend = NestedBool(allow_none=True)
+ u = NestedNoneSet(values=('single', 'double', 'singleAccounting',
+ 'doubleAccounting'))
+ underline = Alias("u")
+ vertAlign = NestedNoneSet(values=('superscript', 'subscript', 'baseline'))
+ color = ColorDescriptor(allow_none=True)
+ scheme = NestedNoneSet(values=("major", "minor"))
+
+ tagname = "font"
+
+ __elements__ = ('name', 'charset', 'family', 'b', 'i', 'strike', 'outline',
+ 'shadow', 'condense', 'color', 'extend', 'sz', 'u', 'vertAlign',
+ 'scheme')
+
+
+ def __init__(self, name=None, sz=None, b=None, i=None, charset=None,
+ u=None, strike=None, color=None, scheme=None, family=None, size=None,
+ bold=None, italic=None, strikethrough=None, underline=None,
+ vertAlign=None, outline=None, shadow=None, condense=None,
+ extend=None):
+ self.name = name
+ self.family = family
+ if size is not None:
+ sz = size
+ self.sz = sz
+ if bold is not None:
+ b = bold
+ self.b = b
+ if italic is not None:
+ i = italic
+ self.i = i
+ if underline is not None:
+ u = underline
+ self.u = u
+ if strikethrough is not None:
+ strike = strikethrough
+ self.strike = strike
+ self.color = color
+ self.vertAlign = vertAlign
+ self.charset = charset
+ self.outline = outline
+ self.shadow = shadow
+ self.condense = condense
+ self.extend = extend
+ self.scheme = scheme
+
+
+ @classmethod
+ def from_tree(cls, node):
+ """
+ Set default value for underline if child element is present
+ """
+ underline = node.find("{%s}u" % SHEET_MAIN_NS)
+ if underline is not None and underline.get('val') is None:
+ underline.set("val", "single")
+ return super().from_tree(node)
+
+
+DEFAULT_FONT = Font(name="Calibri", sz=11, family=2, b=False, i=False,
+ color=Color(theme=1), scheme="minor")
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py
new file mode 100644
index 00000000..221d333b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/named_styles.py
@@ -0,0 +1,282 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.compat import safe_string
+
+from openpyxl.descriptors import (
+ Typed,
+ Integer,
+ Bool,
+ String,
+ Sequence,
+)
+from openpyxl.descriptors.excel import ExtensionList
+from openpyxl.descriptors.serialisable import Serialisable
+
+from .fills import PatternFill, Fill
+from .fonts import Font
+from .borders import Border
+from .alignment import Alignment
+from .protection import Protection
+from .numbers import (
+ NumberFormatDescriptor,
+ BUILTIN_FORMATS_MAX_SIZE,
+ BUILTIN_FORMATS_REVERSE,
+)
+from .cell_style import (
+ StyleArray,
+ CellStyle,
+)
+
+
+class NamedStyle(Serialisable):
+
+ """
+ Named and editable styles
+ """
+
+ font = Typed(expected_type=Font)
+ fill = Typed(expected_type=Fill)
+ border = Typed(expected_type=Border)
+ alignment = Typed(expected_type=Alignment)
+ number_format = NumberFormatDescriptor()
+ protection = Typed(expected_type=Protection)
+ builtinId = Integer(allow_none=True)
+ hidden = Bool(allow_none=True)
+ name = String()
+ _wb = None
+ _style = StyleArray()
+
+
+ def __init__(self,
+ name="Normal",
+ font=None,
+ fill=None,
+ border=None,
+ alignment=None,
+ number_format=None,
+ protection=None,
+ builtinId=None,
+ hidden=False,
+ ):
+ self.name = name
+ self.font = font or Font()
+ self.fill = fill or PatternFill()
+ self.border = border or Border()
+ self.alignment = alignment or Alignment()
+ self.number_format = number_format
+ self.protection = protection or Protection()
+ self.builtinId = builtinId
+ self.hidden = hidden
+ self._wb = None
+ self._style = StyleArray()
+
+
+ def __setattr__(self, attr, value):
+ super().__setattr__(attr, value)
+ if getattr(self, '_wb', None) and attr in (
+ 'font', 'fill', 'border', 'alignment', 'number_format', 'protection',
+ ):
+ self._recalculate()
+
+
+ def __iter__(self):
+ for key in ('name', 'builtinId', 'hidden', 'xfId'):
+ value = getattr(self, key, None)
+ if value is not None:
+ yield key, safe_string(value)
+
+
+ def bind(self, wb):
+ """
+ Bind a named style to a workbook
+ """
+ self._wb = wb
+ self._recalculate()
+
+
+ def _recalculate(self):
+ self._style.fontId = self._wb._fonts.add(self.font)
+ self._style.borderId = self._wb._borders.add(self.border)
+ self._style.fillId = self._wb._fills.add(self.fill)
+ self._style.protectionId = self._wb._protections.add(self.protection)
+ self._style.alignmentId = self._wb._alignments.add(self.alignment)
+ fmt = self.number_format
+ if fmt in BUILTIN_FORMATS_REVERSE:
+ fmt = BUILTIN_FORMATS_REVERSE[fmt]
+ else:
+ fmt = self._wb._number_formats.add(self.number_format) + (
+ BUILTIN_FORMATS_MAX_SIZE)
+ self._style.numFmtId = fmt
+
+
+ def as_tuple(self):
+ """Return a style array representing the current style"""
+ return self._style
+
+
+ def as_xf(self):
+ """
+ Return equivalent XfStyle
+ """
+ xf = CellStyle.from_array(self._style)
+ xf.xfId = None
+ xf.pivotButton = None
+ xf.quotePrefix = None
+ if self.alignment != Alignment():
+ xf.alignment = self.alignment
+ if self.protection != Protection():
+ xf.protection = self.protection
+ return xf
+
+
+ def as_name(self):
+ """
+ Return relevant named style
+
+ """
+ named = _NamedCellStyle(
+ name=self.name,
+ builtinId=self.builtinId,
+ hidden=self.hidden,
+ xfId=self._style.xfId
+ )
+ return named
+
+
+class NamedStyleList(list):
+ """
+ Named styles are editable and can be applied to multiple objects
+
+ As only the index is stored in referencing objects the order mus
+ be preserved.
+
+ Returns a list of NamedStyles
+ """
+
+ def __init__(self, iterable=()):
+ """
+ Allow a list of named styles to be passed in and index them.
+ """
+
+ for idx, s in enumerate(iterable, len(self)):
+ s._style.xfId = idx
+ super().__init__(iterable)
+
+
+ @property
+ def names(self):
+ return [s.name for s in self]
+
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return super().__getitem__(key)
+
+
+ for idx, name in enumerate(self.names):
+ if name == key:
+ return self[idx]
+
+ raise KeyError("No named style with the name{0} exists".format(key))
+
+ def append(self, style):
+ if not isinstance(style, NamedStyle):
+ raise TypeError("""Only NamedStyle instances can be added""")
+ elif style.name in self.names: # hotspot
+ raise ValueError("""Style {0} exists already""".format(style.name))
+ style._style.xfId = (len(self))
+ super().append(style)
+
+
+class _NamedCellStyle(Serialisable):
+
+ """
+ Pointer-based representation of named styles in XML
+ xfId refers to the corresponding CellStyleXfs
+
+ Not used in client code.
+ """
+
+ tagname = "cellStyle"
+
+ name = String()
+ xfId = Integer()
+ builtinId = Integer(allow_none=True)
+ iLevel = Integer(allow_none=True)
+ hidden = Bool(allow_none=True)
+ customBuiltin = Bool(allow_none=True)
+ extLst = Typed(expected_type=ExtensionList, allow_none=True)
+
+ __elements__ = ()
+
+
+ def __init__(self,
+ name=None,
+ xfId=None,
+ builtinId=None,
+ iLevel=None,
+ hidden=None,
+ customBuiltin=None,
+ extLst=None,
+ ):
+ self.name = name
+ self.xfId = xfId
+ self.builtinId = builtinId
+ self.iLevel = iLevel
+ self.hidden = hidden
+ self.customBuiltin = customBuiltin
+
+
+class _NamedCellStyleList(Serialisable):
+ """
+ Container for named cell style objects
+
+ Not used in client code
+ """
+
+ tagname = "cellStyles"
+
+ count = Integer(allow_none=True)
+ cellStyle = Sequence(expected_type=_NamedCellStyle)
+
+ __attrs__ = ("count",)
+
+ def __init__(self,
+ count=None,
+ cellStyle=(),
+ ):
+ self.cellStyle = cellStyle
+
+
+ @property
+ def count(self):
+ return len(self.cellStyle)
+
+
+ def remove_duplicates(self):
+ """
+ Some applications contain duplicate definitions either by name or
+ referenced style.
+
+ As the references are 0-based indices, styles are sorted by
+ index.
+
+ Returns a list of style references with duplicates removed
+ """
+
+ def sort_fn(v):
+ return v.xfId
+
+ styles = []
+ names = set()
+ ids = set()
+
+ for ns in sorted(self.cellStyle, key=sort_fn):
+ if ns.xfId in ids or ns.name in names: # skip duplicates
+ continue
+ ids.add(ns.xfId)
+ names.add(ns.name)
+
+ styles.append(ns)
+
+ return styles
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/numbers.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/numbers.py
new file mode 100644
index 00000000..b548cc7c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/numbers.py
@@ -0,0 +1,200 @@
+# Copyright (c) 2010-2024 openpyxl
+
+import re
+
+from openpyxl.descriptors import (
+ String,
+ Sequence,
+ Integer,
+)
+from openpyxl.descriptors.serialisable import Serialisable
+
+
+BUILTIN_FORMATS = {
+ 0: 'General',
+ 1: '0',
+ 2: '0.00',
+ 3: '#,##0',
+ 4: '#,##0.00',
+ 5: '"$"#,##0_);("$"#,##0)',
+ 6: '"$"#,##0_);[Red]("$"#,##0)',
+ 7: '"$"#,##0.00_);("$"#,##0.00)',
+ 8: '"$"#,##0.00_);[Red]("$"#,##0.00)',
+ 9: '0%',
+ 10: '0.00%',
+ 11: '0.00E+00',
+ 12: '# ?/?',
+ 13: '# ??/??',
+ 14: 'mm-dd-yy',
+ 15: 'd-mmm-yy',
+ 16: 'd-mmm',
+ 17: 'mmm-yy',
+ 18: 'h:mm AM/PM',
+ 19: 'h:mm:ss AM/PM',
+ 20: 'h:mm',
+ 21: 'h:mm:ss',
+ 22: 'm/d/yy h:mm',
+
+ 37: '#,##0_);(#,##0)',
+ 38: '#,##0_);[Red](#,##0)',
+ 39: '#,##0.00_);(#,##0.00)',
+ 40: '#,##0.00_);[Red](#,##0.00)',
+
+ 41: r'_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)',
+ 42: r'_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)',
+ 43: r'_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)',
+
+ 44: r'_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)',
+ 45: 'mm:ss',
+ 46: '[h]:mm:ss',
+ 47: 'mmss.0',
+ 48: '##0.0E+0',
+ 49: '@', }
+
+BUILTIN_FORMATS_MAX_SIZE = 164
+BUILTIN_FORMATS_REVERSE = dict(
+ [(value, key) for key, value in BUILTIN_FORMATS.items()])
+
+FORMAT_GENERAL = BUILTIN_FORMATS[0]
+FORMAT_TEXT = BUILTIN_FORMATS[49]
+FORMAT_NUMBER = BUILTIN_FORMATS[1]
+FORMAT_NUMBER_00 = BUILTIN_FORMATS[2]
+FORMAT_NUMBER_COMMA_SEPARATED1 = BUILTIN_FORMATS[4]
+FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-'
+FORMAT_PERCENTAGE = BUILTIN_FORMATS[9]
+FORMAT_PERCENTAGE_00 = BUILTIN_FORMATS[10]
+FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd'
+FORMAT_DATE_YYMMDD = 'yy-mm-dd'
+FORMAT_DATE_DDMMYY = 'dd/mm/yy'
+FORMAT_DATE_DMYSLASH = 'd/m/y'
+FORMAT_DATE_DMYMINUS = 'd-m-y'
+FORMAT_DATE_DMMINUS = 'd-m'
+FORMAT_DATE_MYMINUS = 'm-y'
+FORMAT_DATE_XLSX14 = BUILTIN_FORMATS[14]
+FORMAT_DATE_XLSX15 = BUILTIN_FORMATS[15]
+FORMAT_DATE_XLSX16 = BUILTIN_FORMATS[16]
+FORMAT_DATE_XLSX17 = BUILTIN_FORMATS[17]
+FORMAT_DATE_XLSX22 = BUILTIN_FORMATS[22]
+FORMAT_DATE_DATETIME = 'yyyy-mm-dd h:mm:ss'
+FORMAT_DATE_TIME1 = BUILTIN_FORMATS[18]
+FORMAT_DATE_TIME2 = BUILTIN_FORMATS[19]
+FORMAT_DATE_TIME3 = BUILTIN_FORMATS[20]
+FORMAT_DATE_TIME4 = BUILTIN_FORMATS[21]
+FORMAT_DATE_TIME5 = BUILTIN_FORMATS[45]
+FORMAT_DATE_TIME6 = BUILTIN_FORMATS[21]
+FORMAT_DATE_TIME7 = 'i:s.S'
+FORMAT_DATE_TIME8 = 'h:mm:ss@'
+FORMAT_DATE_TIMEDELTA = '[hh]:mm:ss'
+FORMAT_DATE_YYMMDDSLASH = 'yy/mm/dd@'
+FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-'
+FORMAT_CURRENCY_USD = '$#,##0_-'
+FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-'
+
+
+COLORS = r"\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]"
+LITERAL_GROUP = r'".*?"' # anything in quotes
+LOCALE_GROUP = r'\[(?!hh?\]|mm?\]|ss?\])[^\]]*\]' # anything in square brackets, except hours or minutes or seconds
+STRIP_RE = re.compile(f"{LITERAL_GROUP}|{LOCALE_GROUP}")
+TIMEDELTA_RE = re.compile(r'\[hh?\](:mm(:ss(\.0*)?)?)?|\[mm?\](:ss(\.0*)?)?|\[ss?\](\.0*)?', re.I)
+
+
+# Spec 18.8.31 numFmts
+# +ve;-ve;zero;text
+
+def is_date_format(fmt):
+ if fmt is None:
+ return False
+ fmt = fmt.split(";")[0] # only look at the first format
+ fmt = STRIP_RE.sub("", fmt) # ignore some formats
+ return re.search(r"(?<![_\\])[dmhysDMHYS]", fmt) is not None
+
+
+def is_timedelta_format(fmt):
+ if fmt is None:
+ return False
+ fmt = fmt.split(";")[0] # only look at the first format
+ return TIMEDELTA_RE.search(fmt) is not None
+
+
+def is_datetime(fmt):
+ """
+ Return date, time or datetime
+ """
+ if not is_date_format(fmt):
+ return
+
+ DATE = TIME = False
+
+ if any((x in fmt for x in 'dy')):
+ DATE = True
+ if any((x in fmt for x in 'hs')):
+ TIME = True
+
+ if DATE and TIME:
+ return "datetime"
+ if DATE:
+ return "date"
+ return "time"
+
+
+def is_builtin(fmt):
+ return fmt in BUILTIN_FORMATS.values()
+
+
+def builtin_format_code(index):
+ """Return one of the standard format codes by index."""
+ try:
+ fmt = BUILTIN_FORMATS[index]
+ except KeyError:
+ fmt = None
+ return fmt
+
+
+def builtin_format_id(fmt):
+ """Return the id of a standard style."""
+ return BUILTIN_FORMATS_REVERSE.get(fmt)
+
+
+class NumberFormatDescriptor(String):
+
+ def __set__(self, instance, value):
+ if value is None:
+ value = FORMAT_GENERAL
+ super().__set__(instance, value)
+
+
+class NumberFormat(Serialisable):
+
+ numFmtId = Integer()
+ formatCode = String()
+
+ def __init__(self,
+ numFmtId=None,
+ formatCode=None,
+ ):
+ self.numFmtId = numFmtId
+ self.formatCode = formatCode
+
+
+class NumberFormatList(Serialisable):
+
+ count = Integer(allow_none=True)
+ numFmt = Sequence(expected_type=NumberFormat)
+
+ __elements__ = ('numFmt',)
+ __attrs__ = ("count",)
+
+ def __init__(self,
+ count=None,
+ numFmt=(),
+ ):
+ self.numFmt = numFmt
+
+
+ @property
+ def count(self):
+ return len(self.numFmt)
+
+
+ def __getitem__(self, idx):
+ return self.numFmt[idx]
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/protection.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/protection.py
new file mode 100644
index 00000000..7c9238ce
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/protection.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors import Bool
+from openpyxl.descriptors.serialisable import Serialisable
+
+
+class Protection(Serialisable):
+ """Protection options for use in styles."""
+
+ tagname = "protection"
+
+ locked = Bool()
+ hidden = Bool()
+
+ def __init__(self, locked=True, hidden=False):
+ self.locked = locked
+ self.hidden = hidden
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/proxy.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/proxy.py
new file mode 100644
index 00000000..bee780cd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/proxy.py
@@ -0,0 +1,62 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from copy import copy
+
+from openpyxl.compat import deprecated
+
+
+class StyleProxy:
+ """
+ Proxy formatting objects so that they cannot be altered
+ """
+
+ __slots__ = ('__target')
+
+ def __init__(self, target):
+ self.__target = target
+
+
+ def __repr__(self):
+ return repr(self.__target)
+
+
+ def __getattr__(self, attr):
+ return getattr(self.__target, attr)
+
+
+ def __setattr__(self, attr, value):
+ if attr != "_StyleProxy__target":
+ raise AttributeError("Style objects are immutable and cannot be changed."
+ "Reassign the style with a copy")
+ super().__setattr__(attr, value)
+
+
+ def __copy__(self):
+ """
+ Return a copy of the proxied object.
+ """
+ return copy(self.__target)
+
+
+ def __add__(self, other):
+ """
+ Add proxied object to another instance and return the combined object
+ """
+ return self.__target + other
+
+
+ @deprecated("Use copy(obj) or cell.obj = cell.obj + other")
+ def copy(self, **kw):
+ """Return a copy of the proxied object. Keyword args will be passed through"""
+ cp = copy(self.__target)
+ for k, v in kw.items():
+ setattr(cp, k, v)
+ return cp
+
+
+ def __eq__(self, other):
+ return self.__target == other
+
+
+ def __ne__(self, other):
+ return not self == other
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/styleable.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/styleable.py
new file mode 100644
index 00000000..2703096d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/styleable.py
@@ -0,0 +1,151 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from copy import copy
+
+from .numbers import (
+ BUILTIN_FORMATS,
+ BUILTIN_FORMATS_MAX_SIZE,
+ BUILTIN_FORMATS_REVERSE,
+)
+from .proxy import StyleProxy
+from .cell_style import StyleArray
+from .named_styles import NamedStyle
+from .builtins import styles
+
+
+class StyleDescriptor:
+
+ def __init__(self, collection, key):
+ self.collection = collection
+ self.key = key
+
+ def __set__(self, instance, value):
+ coll = getattr(instance.parent.parent, self.collection)
+ if not getattr(instance, "_style"):
+ instance._style = StyleArray()
+ setattr(instance._style, self.key, coll.add(value))
+
+
+ def __get__(self, instance, cls):
+ coll = getattr(instance.parent.parent, self.collection)
+ if not getattr(instance, "_style"):
+ instance._style = StyleArray()
+ idx = getattr(instance._style, self.key)
+ return StyleProxy(coll[idx])
+
+
+class NumberFormatDescriptor:
+
+ key = "numFmtId"
+ collection = '_number_formats'
+
+ def __set__(self, instance, value):
+ coll = getattr(instance.parent.parent, self.collection)
+ if value in BUILTIN_FORMATS_REVERSE:
+ idx = BUILTIN_FORMATS_REVERSE[value]
+ else:
+ idx = coll.add(value) + BUILTIN_FORMATS_MAX_SIZE
+
+ if not getattr(instance, "_style"):
+ instance._style = StyleArray()
+ setattr(instance._style, self.key, idx)
+
+
+ def __get__(self, instance, cls):
+ if not getattr(instance, "_style"):
+ instance._style = StyleArray()
+ idx = getattr(instance._style, self.key)
+ if idx < BUILTIN_FORMATS_MAX_SIZE:
+ return BUILTIN_FORMATS.get(idx, "General")
+ coll = getattr(instance.parent.parent, self.collection)
+ return coll[idx - BUILTIN_FORMATS_MAX_SIZE]
+
+
+class NamedStyleDescriptor:
+
+ key = "xfId"
+ collection = "_named_styles"
+
+
+ def __set__(self, instance, value):
+ if not getattr(instance, "_style"):
+ instance._style = StyleArray()
+ coll = getattr(instance.parent.parent, self.collection)
+ if isinstance(value, NamedStyle):
+ style = value
+ if style not in coll:
+ instance.parent.parent.add_named_style(style)
+ elif value not in coll.names:
+ if value in styles: # is it builtin?
+ style = styles[value]
+ if style not in coll:
+ instance.parent.parent.add_named_style(style)
+ else:
+ raise ValueError("{0} is not a known style".format(value))
+ else:
+ style = coll[value]
+ instance._style = copy(style.as_tuple())
+
+
+ def __get__(self, instance, cls):
+ if not getattr(instance, "_style"):
+ instance._style = StyleArray()
+ idx = getattr(instance._style, self.key)
+ coll = getattr(instance.parent.parent, self.collection)
+ return coll.names[idx]
+
+
+class StyleArrayDescriptor:
+
+ def __init__(self, key):
+ self.key = key
+
+ def __set__(self, instance, value):
+ if instance._style is None:
+ instance._style = StyleArray()
+ setattr(instance._style, self.key, value)
+
+
+ def __get__(self, instance, cls):
+ if instance._style is None:
+ return False
+ return bool(getattr(instance._style, self.key))
+
+
+class StyleableObject:
+ """
+ Base class for styleble objects implementing proxy and lookup functions
+ """
+
+ font = StyleDescriptor('_fonts', "fontId")
+ fill = StyleDescriptor('_fills', "fillId")
+ border = StyleDescriptor('_borders', "borderId")
+ number_format = NumberFormatDescriptor()
+ protection = StyleDescriptor('_protections', "protectionId")
+ alignment = StyleDescriptor('_alignments', "alignmentId")
+ style = NamedStyleDescriptor()
+ quotePrefix = StyleArrayDescriptor('quotePrefix')
+ pivotButton = StyleArrayDescriptor('pivotButton')
+
+ __slots__ = ('parent', '_style')
+
+ def __init__(self, sheet, style_array=None):
+ self.parent = sheet
+ if style_array is not None:
+ style_array = StyleArray(style_array)
+ self._style = style_array
+
+
+ @property
+ def style_id(self):
+ if self._style is None:
+ self._style = StyleArray()
+ return self.parent.parent._cell_styles.add(self._style)
+
+
+ @property
+ def has_style(self):
+ if self._style is None:
+ return False
+ return any(self._style)
+
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/stylesheet.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/stylesheet.py
new file mode 100644
index 00000000..dfaf875d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/stylesheet.py
@@ -0,0 +1,274 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from warnings import warn
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+ Typed,
+)
+from openpyxl.descriptors.sequence import NestedSequence
+from openpyxl.descriptors.excel import ExtensionList
+from openpyxl.utils.indexed_list import IndexedList
+from openpyxl.xml.constants import ARC_STYLE, SHEET_MAIN_NS
+from openpyxl.xml.functions import fromstring
+
+from .builtins import styles
+from .colors import ColorList
+from .differential import DifferentialStyle
+from .table import TableStyleList
+from .borders import Border
+from .fills import Fill
+from .fonts import Font
+from .numbers import (
+ NumberFormatList,
+ BUILTIN_FORMATS,
+ BUILTIN_FORMATS_MAX_SIZE,
+ BUILTIN_FORMATS_REVERSE,
+ is_date_format,
+ is_timedelta_format,
+ builtin_format_code
+)
+from .named_styles import (
+ _NamedCellStyleList,
+ NamedStyleList,
+ NamedStyle,
+)
+from .cell_style import CellStyle, CellStyleList
+
+
+class Stylesheet(Serialisable):
+
+ tagname = "styleSheet"
+
+ numFmts = Typed(expected_type=NumberFormatList)
+ fonts = NestedSequence(expected_type=Font, count=True)
+ fills = NestedSequence(expected_type=Fill, count=True)
+ borders = NestedSequence(expected_type=Border, count=True)
+ cellStyleXfs = Typed(expected_type=CellStyleList)
+ cellXfs = Typed(expected_type=CellStyleList)
+ cellStyles = Typed(expected_type=_NamedCellStyleList)
+ dxfs = NestedSequence(expected_type=DifferentialStyle, count=True)
+ tableStyles = Typed(expected_type=TableStyleList, allow_none=True)
+ colors = Typed(expected_type=ColorList, allow_none=True)
+ extLst = Typed(expected_type=ExtensionList, allow_none=True)
+
+ __elements__ = ('numFmts', 'fonts', 'fills', 'borders', 'cellStyleXfs',
+ 'cellXfs', 'cellStyles', 'dxfs', 'tableStyles', 'colors')
+
+ def __init__(self,
+ numFmts=None,
+ fonts=(),
+ fills=(),
+ borders=(),
+ cellStyleXfs=None,
+ cellXfs=None,
+ cellStyles=None,
+ dxfs=(),
+ tableStyles=None,
+ colors=None,
+ extLst=None,
+ ):
+ if numFmts is None:
+ numFmts = NumberFormatList()
+ self.numFmts = numFmts
+ self.number_formats = IndexedList()
+ self.fonts = fonts
+ self.fills = fills
+ self.borders = borders
+ if cellStyleXfs is None:
+ cellStyleXfs = CellStyleList()
+ self.cellStyleXfs = cellStyleXfs
+ if cellXfs is None:
+ cellXfs = CellStyleList()
+ self.cellXfs = cellXfs
+ if cellStyles is None:
+ cellStyles = _NamedCellStyleList()
+ self.cellStyles = cellStyles
+
+ self.dxfs = dxfs
+ self.tableStyles = tableStyles
+ self.colors = colors
+
+ self.cell_styles = self.cellXfs._to_array()
+ self.alignments = self.cellXfs.alignments
+ self.protections = self.cellXfs.prots
+ self._normalise_numbers()
+ self.named_styles = self._merge_named_styles()
+
+
+ @classmethod
+ def from_tree(cls, node):
+ # strip all attribs
+ attrs = dict(node.attrib)
+ for k in attrs:
+ del node.attrib[k]
+ return super().from_tree(node)
+
+
+ def _merge_named_styles(self):
+ """
+ Merge named style names "cellStyles" with their associated styles
+ "cellStyleXfs"
+ """
+ style_refs = self.cellStyles.remove_duplicates()
+ from_ref = [self._expand_named_style(style_ref) for style_ref in style_refs]
+
+ return NamedStyleList(from_ref)
+
+
+ def _expand_named_style(self, style_ref):
+ """
+ Expand a named style reference element to a
+ named style object by binding the relevant
+ objects from the stylesheet
+ """
+ xf = self.cellStyleXfs[style_ref.xfId]
+ named_style = NamedStyle(
+ name=style_ref.name,
+ hidden=style_ref.hidden,
+ builtinId=style_ref.builtinId,
+ )
+
+ named_style.font = self.fonts[xf.fontId]
+ named_style.fill = self.fills[xf.fillId]
+ named_style.border = self.borders[xf.borderId]
+ if xf.numFmtId < BUILTIN_FORMATS_MAX_SIZE:
+ formats = BUILTIN_FORMATS
+ else:
+ formats = self.custom_formats
+
+ if xf.numFmtId in formats:
+ named_style.number_format = formats[xf.numFmtId]
+ if xf.alignment:
+ named_style.alignment = xf.alignment
+ if xf.protection:
+ named_style.protection = xf.protection
+
+ return named_style
+
+
+ def _split_named_styles(self, wb):
+ """
+ Convert NamedStyle into separate CellStyle and Xf objects
+
+ """
+ for style in wb._named_styles:
+ self.cellStyles.cellStyle.append(style.as_name())
+ self.cellStyleXfs.xf.append(style.as_xf())
+
+
+ @property
+ def custom_formats(self):
+ return dict([(n.numFmtId, n.formatCode) for n in self.numFmts.numFmt])
+
+
+ def _normalise_numbers(self):
+ """
+ Rebase custom numFmtIds with a floor of 164 when reading stylesheet
+ And index datetime formats
+ """
+ date_formats = set()
+ timedelta_formats = set()
+ custom = self.custom_formats
+ formats = self.number_formats
+ for idx, style in enumerate(self.cell_styles):
+ if style.numFmtId in custom:
+ fmt = custom[style.numFmtId]
+ if fmt in BUILTIN_FORMATS_REVERSE: # remove builtins
+ style.numFmtId = BUILTIN_FORMATS_REVERSE[fmt]
+ else:
+ style.numFmtId = formats.add(fmt) + BUILTIN_FORMATS_MAX_SIZE
+ else:
+ fmt = builtin_format_code(style.numFmtId)
+ if is_date_format(fmt):
+ # Create an index of which styles refer to datetimes
+ date_formats.add(idx)
+ if is_timedelta_format(fmt):
+ # Create an index of which styles refer to timedeltas
+ timedelta_formats.add(idx)
+ self.date_formats = date_formats
+ self.timedelta_formats = timedelta_formats
+
+
+ def to_tree(self, tagname=None, idx=None, namespace=None):
+ tree = super().to_tree(tagname, idx, namespace)
+ tree.set("xmlns", SHEET_MAIN_NS)
+ return tree
+
+
+def apply_stylesheet(archive, wb):
+ """
+ Add styles to workbook if present
+ """
+ try:
+ src = archive.read(ARC_STYLE)
+ except KeyError:
+ return wb
+
+ node = fromstring(src)
+ stylesheet = Stylesheet.from_tree(node)
+
+ if stylesheet.cell_styles:
+
+ wb._borders = IndexedList(stylesheet.borders)
+ wb._fonts = IndexedList(stylesheet.fonts)
+ wb._fills = IndexedList(stylesheet.fills)
+ wb._differential_styles.styles = stylesheet.dxfs
+ wb._number_formats = stylesheet.number_formats
+ wb._protections = stylesheet.protections
+ wb._alignments = stylesheet.alignments
+ wb._table_styles = stylesheet.tableStyles
+
+ # need to overwrite openpyxl defaults in case workbook has different ones
+ wb._cell_styles = stylesheet.cell_styles
+ wb._named_styles = stylesheet.named_styles
+ wb._date_formats = stylesheet.date_formats
+ wb._timedelta_formats = stylesheet.timedelta_formats
+
+ for ns in wb._named_styles:
+ ns.bind(wb)
+
+ else:
+ warn("Workbook contains no stylesheet, using openpyxl's defaults")
+
+ if not wb._named_styles:
+ normal = styles['Normal']
+ wb.add_named_style(normal)
+ warn("Workbook contains no default style, apply openpyxl's default")
+
+ if stylesheet.colors is not None:
+ wb._colors = stylesheet.colors.index
+
+
+def write_stylesheet(wb):
+ stylesheet = Stylesheet()
+ stylesheet.fonts = wb._fonts
+ stylesheet.fills = wb._fills
+ stylesheet.borders = wb._borders
+ stylesheet.dxfs = wb._differential_styles.styles
+ stylesheet.colors = ColorList(indexedColors=wb._colors)
+
+ from .numbers import NumberFormat
+ fmts = []
+ for idx, code in enumerate(wb._number_formats, BUILTIN_FORMATS_MAX_SIZE):
+ fmt = NumberFormat(idx, code)
+ fmts.append(fmt)
+
+ stylesheet.numFmts.numFmt = fmts
+
+ xfs = []
+ for style in wb._cell_styles:
+ xf = CellStyle.from_array(style)
+
+ if style.alignmentId:
+ xf.alignment = wb._alignments[style.alignmentId]
+
+ if style.protectionId:
+ xf.protection = wb._protections[style.protectionId]
+ xfs.append(xf)
+ stylesheet.cellXfs = CellStyleList(xf=xfs)
+
+ stylesheet._split_named_styles(wb)
+ stylesheet.tableStyles = wb._table_styles
+
+ return stylesheet.to_tree()
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/styles/table.py b/.venv/lib/python3.12/site-packages/openpyxl/styles/table.py
new file mode 100644
index 00000000..18307198
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/styles/table.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+ Typed,
+ Float,
+ Bool,
+ Set,
+ Integer,
+ NoneSet,
+ String,
+ Sequence
+)
+
+from .colors import Color
+
+
+class TableStyleElement(Serialisable):
+
+ tagname = "tableStyleElement"
+
+ type = Set(values=(['wholeTable', 'headerRow', 'totalRow', 'firstColumn',
+ 'lastColumn', 'firstRowStripe', 'secondRowStripe', 'firstColumnStripe',
+ 'secondColumnStripe', 'firstHeaderCell', 'lastHeaderCell',
+ 'firstTotalCell', 'lastTotalCell', 'firstSubtotalColumn',
+ 'secondSubtotalColumn', 'thirdSubtotalColumn', 'firstSubtotalRow',
+ 'secondSubtotalRow', 'thirdSubtotalRow', 'blankRow',
+ 'firstColumnSubheading', 'secondColumnSubheading',
+ 'thirdColumnSubheading', 'firstRowSubheading', 'secondRowSubheading',
+ 'thirdRowSubheading', 'pageFieldLabels', 'pageFieldValues']))
+ size = Integer(allow_none=True)
+ dxfId = Integer(allow_none=True)
+
+ def __init__(self,
+ type=None,
+ size=None,
+ dxfId=None,
+ ):
+ self.type = type
+ self.size = size
+ self.dxfId = dxfId
+
+
+class TableStyle(Serialisable):
+
+ tagname = "tableStyle"
+
+ name = String()
+ pivot = Bool(allow_none=True)
+ table = Bool(allow_none=True)
+ count = Integer(allow_none=True)
+ tableStyleElement = Sequence(expected_type=TableStyleElement, allow_none=True)
+
+ __elements__ = ('tableStyleElement',)
+
+ def __init__(self,
+ name=None,
+ pivot=None,
+ table=None,
+ count=None,
+ tableStyleElement=(),
+ ):
+ self.name = name
+ self.pivot = pivot
+ self.table = table
+ self.count = count
+ self.tableStyleElement = tableStyleElement
+
+
+class TableStyleList(Serialisable):
+
+ tagname = "tableStyles"
+
+ defaultTableStyle = String(allow_none=True)
+ defaultPivotStyle = String(allow_none=True)
+ tableStyle = Sequence(expected_type=TableStyle, allow_none=True)
+
+ __elements__ = ('tableStyle',)
+ __attrs__ = ("count", "defaultTableStyle", "defaultPivotStyle")
+
+ def __init__(self,
+ count=None,
+ defaultTableStyle="TableStyleMedium9",
+ defaultPivotStyle="PivotStyleLight16",
+ tableStyle=(),
+ ):
+ self.defaultTableStyle = defaultTableStyle
+ self.defaultPivotStyle = defaultPivotStyle
+ self.tableStyle = tableStyle
+
+
+ @property
+ def count(self):
+ return len(self.tableStyle)