diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/styles')
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) |