about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/openpyxl/drawing
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/openpyxl/drawing')
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/__init__.py4
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/colors.py435
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/connector.py144
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/drawing.py92
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/effect.py407
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/fill.py425
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/geometry.py584
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/graphic.py177
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/image.py65
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/line.py144
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/picture.py144
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/properties.py174
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/relation.py17
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/spreadsheet_drawing.py382
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/text.py717
-rw-r--r--.venv/lib/python3.12/site-packages/openpyxl/drawing/xdr.py33
16 files changed, 3944 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/__init__.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/__init__.py
new file mode 100644
index 00000000..02f05876
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/__init__.py
@@ -0,0 +1,4 @@
+# Copyright (c) 2010-2024 openpyxl
+
+
+from .drawing import Drawing
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/colors.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/colors.py
new file mode 100644
index 00000000..19fa5e84
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/colors.py
@@ -0,0 +1,435 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Alias,
+    Typed,
+    Integer,
+    Set,
+    MinMax,
+)
+from openpyxl.descriptors.excel import Percentage
+from openpyxl.descriptors.nested import (
+    NestedNoneSet,
+    NestedValue,
+    NestedInteger,
+    EmptyTag,
+)
+
+from openpyxl.styles.colors import RGB
+from openpyxl.xml.constants import DRAWING_NS
+
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+
+PRESET_COLORS = [
+        'aliceBlue', 'antiqueWhite', 'aqua', 'aquamarine',
+        'azure', 'beige', 'bisque', 'black', 'blanchedAlmond', 'blue',
+        'blueViolet', 'brown', 'burlyWood', 'cadetBlue', 'chartreuse',
+        'chocolate', 'coral', 'cornflowerBlue', 'cornsilk', 'crimson', 'cyan',
+        'darkBlue', 'darkCyan', 'darkGoldenrod', 'darkGray', 'darkGrey',
+        'darkGreen', 'darkKhaki', 'darkMagenta', 'darkOliveGreen', 'darkOrange',
+        'darkOrchid', 'darkRed', 'darkSalmon', 'darkSeaGreen', 'darkSlateBlue',
+        'darkSlateGray', 'darkSlateGrey', 'darkTurquoise', 'darkViolet',
+        'dkBlue', 'dkCyan', 'dkGoldenrod', 'dkGray', 'dkGrey', 'dkGreen',
+        'dkKhaki', 'dkMagenta', 'dkOliveGreen', 'dkOrange', 'dkOrchid', 'dkRed',
+        'dkSalmon', 'dkSeaGreen', 'dkSlateBlue', 'dkSlateGray', 'dkSlateGrey',
+        'dkTurquoise', 'dkViolet', 'deepPink', 'deepSkyBlue', 'dimGray',
+        'dimGrey', 'dodgerBlue', 'firebrick', 'floralWhite', 'forestGreen',
+        'fuchsia', 'gainsboro', 'ghostWhite', 'gold', 'goldenrod', 'gray',
+        'grey', 'green', 'greenYellow', 'honeydew', 'hotPink', 'indianRed',
+        'indigo', 'ivory', 'khaki', 'lavender', 'lavenderBlush', 'lawnGreen',
+        'lemonChiffon', 'lightBlue', 'lightCoral', 'lightCyan',
+        'lightGoldenrodYellow', 'lightGray', 'lightGrey', 'lightGreen',
+        'lightPink', 'lightSalmon', 'lightSeaGreen', 'lightSkyBlue',
+        'lightSlateGray', 'lightSlateGrey', 'lightSteelBlue', 'lightYellow',
+        'ltBlue', 'ltCoral', 'ltCyan', 'ltGoldenrodYellow', 'ltGray', 'ltGrey',
+        'ltGreen', 'ltPink', 'ltSalmon', 'ltSeaGreen', 'ltSkyBlue',
+        'ltSlateGray', 'ltSlateGrey', 'ltSteelBlue', 'ltYellow', 'lime',
+        'limeGreen', 'linen', 'magenta', 'maroon', 'medAquamarine', 'medBlue',
+        'medOrchid', 'medPurple', 'medSeaGreen', 'medSlateBlue',
+        'medSpringGreen', 'medTurquoise', 'medVioletRed', 'mediumAquamarine',
+        'mediumBlue', 'mediumOrchid', 'mediumPurple', 'mediumSeaGreen',
+        'mediumSlateBlue', 'mediumSpringGreen', 'mediumTurquoise',
+        'mediumVioletRed', 'midnightBlue', 'mintCream', 'mistyRose', 'moccasin',
+        'navajoWhite', 'navy', 'oldLace', 'olive', 'oliveDrab', 'orange',
+        'orangeRed', 'orchid', 'paleGoldenrod', 'paleGreen', 'paleTurquoise',
+        'paleVioletRed', 'papayaWhip', 'peachPuff', 'peru', 'pink', 'plum',
+        'powderBlue', 'purple', 'red', 'rosyBrown', 'royalBlue', 'saddleBrown',
+        'salmon', 'sandyBrown', 'seaGreen', 'seaShell', 'sienna', 'silver',
+        'skyBlue', 'slateBlue', 'slateGray', 'slateGrey', 'snow', 'springGreen',
+        'steelBlue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet',
+        'wheat', 'white', 'whiteSmoke', 'yellow', 'yellowGreen'
+    ]
+
+
+SCHEME_COLORS= ['bg1', 'tx1', 'bg2', 'tx2', 'accent1', 'accent2', 'accent3',
+                'accent4', 'accent5', 'accent6', 'hlink', 'folHlink', 'phClr', 'dk1', 'lt1',
+                'dk2', 'lt2'
+                ]
+
+
+class Transform(Serialisable):
+
+    pass
+
+
+class SystemColor(Serialisable):
+
+    tagname = "sysClr"
+    namespace = DRAWING_NS
+
+    # color transform options
+    tint = NestedInteger(allow_none=True)
+    shade = NestedInteger(allow_none=True)
+    comp = Typed(expected_type=Transform, allow_none=True)
+    inv = Typed(expected_type=Transform, allow_none=True)
+    gray = Typed(expected_type=Transform, allow_none=True)
+    alpha = NestedInteger(allow_none=True)
+    alphaOff = NestedInteger(allow_none=True)
+    alphaMod = NestedInteger(allow_none=True)
+    hue = NestedInteger(allow_none=True)
+    hueOff = NestedInteger(allow_none=True)
+    hueMod = NestedInteger(allow_none=True)
+    sat = NestedInteger(allow_none=True)
+    satOff = NestedInteger(allow_none=True)
+    satMod = NestedInteger(allow_none=True)
+    lum = NestedInteger(allow_none=True)
+    lumOff = NestedInteger(allow_none=True)
+    lumMod = NestedInteger(allow_none=True)
+    red = NestedInteger(allow_none=True)
+    redOff = NestedInteger(allow_none=True)
+    redMod = NestedInteger(allow_none=True)
+    green = NestedInteger(allow_none=True)
+    greenOff = NestedInteger(allow_none=True)
+    greenMod = NestedInteger(allow_none=True)
+    blue = NestedInteger(allow_none=True)
+    blueOff = NestedInteger(allow_none=True)
+    blueMod = NestedInteger(allow_none=True)
+    gamma = Typed(expected_type=Transform, allow_none=True)
+    invGamma = Typed(expected_type=Transform, allow_none=True)
+
+    val = Set(values=( ['scrollBar', 'background', 'activeCaption',
+                        'inactiveCaption', 'menu', 'window', 'windowFrame', 'menuText',
+                        'windowText', 'captionText', 'activeBorder', 'inactiveBorder',
+                        'appWorkspace', 'highlight', 'highlightText', 'btnFace', 'btnShadow',
+                        'grayText', 'btnText', 'inactiveCaptionText', 'btnHighlight',
+                        '3dDkShadow', '3dLight', 'infoText', 'infoBk', 'hotLight',
+                        'gradientActiveCaption', 'gradientInactiveCaption', 'menuHighlight',
+                        'menuBar'] )
+              )
+    lastClr = RGB(allow_none=True)
+
+    __elements__ = ('tint', 'shade', 'comp', 'inv', 'gray', "alpha",
+                    "alphaOff", "alphaMod", "hue", "hueOff", "hueMod", "hueOff", "sat",
+                    "satOff", "satMod", "lum", "lumOff", "lumMod", "red", "redOff", "redMod",
+                    "green", "greenOff", "greenMod", "blue", "blueOff", "blueMod", "gamma",
+                    "invGamma")
+
+    def __init__(self,
+                 val="windowText",
+                 lastClr=None,
+                 tint=None,
+                 shade=None,
+                 comp=None,
+                 inv=None,
+                 gray=None,
+                 alpha=None,
+                 alphaOff=None,
+                 alphaMod=None,
+                 hue=None,
+                 hueOff=None,
+                 hueMod=None,
+                 sat=None,
+                 satOff=None,
+                 satMod=None,
+                 lum=None,
+                 lumOff=None,
+                 lumMod=None,
+                 red=None,
+                 redOff=None,
+                 redMod=None,
+                 green=None,
+                 greenOff=None,
+                 greenMod=None,
+                 blue=None,
+                 blueOff=None,
+                 blueMod=None,
+                 gamma=None,
+                 invGamma=None
+                ):
+        self.val = val
+        self.lastClr = lastClr
+        self.tint = tint
+        self.shade = shade
+        self.comp = comp
+        self.inv = inv
+        self.gray = gray
+        self.alpha = alpha
+        self.alphaOff = alphaOff
+        self.alphaMod = alphaMod
+        self.hue = hue
+        self.hueOff = hueOff
+        self.hueMod = hueMod
+        self.sat = sat
+        self.satOff = satOff
+        self.satMod = satMod
+        self.lum = lum
+        self.lumOff = lumOff
+        self.lumMod = lumMod
+        self.red = red
+        self.redOff = redOff
+        self.redMod = redMod
+        self.green = green
+        self.greenOff = greenOff
+        self.greenMod = greenMod
+        self.blue = blue
+        self.blueOff = blueOff
+        self.blueMod = blueMod
+        self.gamma = gamma
+        self.invGamma = invGamma
+
+
+class HSLColor(Serialisable):
+
+    tagname = "hslClr"
+
+    hue = Integer()
+    sat = MinMax(min=0, max=100)
+    lum = MinMax(min=0, max=100)
+
+    #TODO add color transform options
+
+    def __init__(self,
+                 hue=None,
+                 sat=None,
+                 lum=None,
+                ):
+        self.hue = hue
+        self.sat = sat
+        self.lum = lum
+
+
+
+class RGBPercent(Serialisable):
+
+    tagname = "rgbClr"
+
+    r = MinMax(min=0, max=100)
+    g = MinMax(min=0, max=100)
+    b = MinMax(min=0, max=100)
+
+    #TODO add color transform options
+
+    def __init__(self,
+                 r=None,
+                 g=None,
+                 b=None,
+                ):
+        self.r = r
+        self.g = g
+        self.b = b
+
+
+class SchemeColor(Serialisable):
+
+    tagname = "schemeClr"
+    namespace = DRAWING_NS
+
+    tint = NestedInteger(allow_none=True)
+    shade = NestedInteger(allow_none=True)
+    comp = EmptyTag(allow_none=True)
+    inv = NestedInteger(allow_none=True)
+    gray = NestedInteger(allow_none=True)
+    alpha = NestedInteger(allow_none=True)
+    alphaOff = NestedInteger(allow_none=True)
+    alphaMod = NestedInteger(allow_none=True)
+    hue = NestedInteger(allow_none=True)
+    hueOff = NestedInteger(allow_none=True)
+    hueMod = NestedInteger(allow_none=True)
+    sat = NestedInteger(allow_none=True)
+    satOff = NestedInteger(allow_none=True)
+    satMod = NestedInteger(allow_none=True)
+    lum = NestedInteger(allow_none=True)
+    lumOff = NestedInteger(allow_none=True)
+    lumMod = NestedInteger(allow_none=True)
+    red = NestedInteger(allow_none=True)
+    redOff = NestedInteger(allow_none=True)
+    redMod = NestedInteger(allow_none=True)
+    green = NestedInteger(allow_none=True)
+    greenOff = NestedInteger(allow_none=True)
+    greenMod = NestedInteger(allow_none=True)
+    blue = NestedInteger(allow_none=True)
+    blueOff = NestedInteger(allow_none=True)
+    blueMod = NestedInteger(allow_none=True)
+    gamma = EmptyTag(allow_none=True)
+    invGamma = EmptyTag(allow_none=True)
+    val = Set(values=(['bg1', 'tx1', 'bg2', 'tx2', 'accent1', 'accent2',
+                       'accent3', 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink', 'phClr',
+                       'dk1', 'lt1', 'dk2', 'lt2']))
+
+    __elements__ = ('tint', 'shade', 'comp', 'inv', 'gray', 'alpha',
+                    'alphaOff', 'alphaMod', 'hue', 'hueOff', 'hueMod', 'sat', 'satOff',
+                    'satMod', 'lum', 'lumMod', 'lumOff', 'red', 'redOff', 'redMod', 'green',
+                    'greenOff', 'greenMod', 'blue', 'blueOff', 'blueMod', 'gamma',
+                    'invGamma')
+
+    def __init__(self,
+                 tint=None,
+                 shade=None,
+                 comp=None,
+                 inv=None,
+                 gray=None,
+                 alpha=None,
+                 alphaOff=None,
+                 alphaMod=None,
+                 hue=None,
+                 hueOff=None,
+                 hueMod=None,
+                 sat=None,
+                 satOff=None,
+                 satMod=None,
+                 lum=None,
+                 lumOff=None,
+                 lumMod=None,
+                 red=None,
+                 redOff=None,
+                 redMod=None,
+                 green=None,
+                 greenOff=None,
+                 greenMod=None,
+                 blue=None,
+                 blueOff=None,
+                 blueMod=None,
+                 gamma=None,
+                 invGamma=None,
+                 val=None,
+                ):
+        self.tint = tint
+        self.shade = shade
+        self.comp = comp
+        self.inv = inv
+        self.gray = gray
+        self.alpha = alpha
+        self.alphaOff = alphaOff
+        self.alphaMod = alphaMod
+        self.hue = hue
+        self.hueOff = hueOff
+        self.hueMod = hueMod
+        self.sat = sat
+        self.satOff = satOff
+        self.satMod = satMod
+        self.lum = lum
+        self.lumOff = lumOff
+        self.lumMod = lumMod
+        self.red = red
+        self.redOff = redOff
+        self.redMod = redMod
+        self.green = green
+        self.greenOff = greenOff
+        self.greenMod = greenMod
+        self.blue = blue
+        self.blueOff = blueOff
+        self.blueMod = blueMod
+        self.gamma = gamma
+        self.invGamma = invGamma
+        self.val = val
+
+class ColorChoice(Serialisable):
+
+    tagname = "colorChoice"
+    namespace = DRAWING_NS
+
+    scrgbClr = Typed(expected_type=RGBPercent, allow_none=True)
+    RGBPercent = Alias('scrgbClr')
+    srgbClr = NestedValue(expected_type=str, allow_none=True) # needs pattern and can have transform
+    RGB = Alias('srgbClr')
+    hslClr = Typed(expected_type=HSLColor, allow_none=True)
+    sysClr = Typed(expected_type=SystemColor, allow_none=True)
+    schemeClr = Typed(expected_type=SchemeColor, allow_none=True)
+    prstClr = NestedNoneSet(values=PRESET_COLORS)
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 scrgbClr=None,
+                 srgbClr=None,
+                 hslClr=None,
+                 sysClr=None,
+                 schemeClr=None,
+                 prstClr=None,
+                ):
+        self.scrgbClr = scrgbClr
+        self.srgbClr = srgbClr
+        self.hslClr = hslClr
+        self.sysClr = sysClr
+        self.schemeClr = schemeClr
+        self.prstClr = prstClr
+
+_COLOR_SET = ('dk1', 'lt1', 'dk2', 'lt2', 'accent1', 'accent2', 'accent3',
+               'accent4', 'accent5', 'accent6', 'hlink', 'folHlink')
+
+
+class ColorMapping(Serialisable):
+
+    tagname = "clrMapOvr"
+
+    bg1 = Set(values=_COLOR_SET)
+    tx1 = Set(values=_COLOR_SET)
+    bg2 = Set(values=_COLOR_SET)
+    tx2 = Set(values=_COLOR_SET)
+    accent1 = Set(values=_COLOR_SET)
+    accent2 = Set(values=_COLOR_SET)
+    accent3 = Set(values=_COLOR_SET)
+    accent4 = Set(values=_COLOR_SET)
+    accent5 = Set(values=_COLOR_SET)
+    accent6 = Set(values=_COLOR_SET)
+    hlink = Set(values=_COLOR_SET)
+    folHlink = Set(values=_COLOR_SET)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 bg1="lt1",
+                 tx1="dk1",
+                 bg2="lt2",
+                 tx2="dk2",
+                 accent1="accent1",
+                 accent2="accent2",
+                 accent3="accent3",
+                 accent4="accent4",
+                 accent5="accent5",
+                 accent6="accent6",
+                 hlink="hlink",
+                 folHlink="folHlink",
+                 extLst=None,
+                ):
+        self.bg1 = bg1
+        self.tx1 = tx1
+        self.bg2 = bg2
+        self.tx2 = tx2
+        self.accent1 = accent1
+        self.accent2 = accent2
+        self.accent3 = accent3
+        self.accent4 = accent4
+        self.accent5 = accent5
+        self.accent6 = accent6
+        self.hlink = hlink
+        self.folHlink = folHlink
+        self.extLst = extLst
+
+
+class ColorChoiceDescriptor(Typed):
+    """
+    Objects can choose from 7 different kinds of color system.
+    Assume RGBHex if a string is passed in.
+    """
+
+    expected_type = ColorChoice
+    allow_none = True
+
+    def __set__(self, instance, value):
+        if isinstance(value, str):
+            value = ColorChoice(srgbClr=value)
+        else:
+            if hasattr(self, "namespace") and value is not None:
+                value.namespace = self.namespace
+        super().__set__(instance, value)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/connector.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/connector.py
new file mode 100644
index 00000000..d25bcf71
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/connector.py
@@ -0,0 +1,144 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Bool,
+    Integer,
+    String,
+    Alias,
+)
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+from openpyxl.chart.shapes import GraphicalProperties
+from openpyxl.chart.text import RichText
+
+from .properties import (
+    NonVisualDrawingProps,
+    NonVisualDrawingShapeProps,
+)
+from .geometry import ShapeStyle
+
+class Connection(Serialisable):
+
+    id = Integer()
+    idx = Integer()
+
+    def __init__(self,
+                 id=None,
+                 idx=None,
+                ):
+        self.id = id
+        self.idx = idx
+
+
+class ConnectorLocking(Serialisable):
+
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 extLst=None,
+                ):
+        self.extLst = extLst
+
+
+class NonVisualConnectorProperties(Serialisable):
+
+    cxnSpLocks = Typed(expected_type=ConnectorLocking, allow_none=True)
+    stCxn = Typed(expected_type=Connection, allow_none=True)
+    endCxn = Typed(expected_type=Connection, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 cxnSpLocks=None,
+                 stCxn=None,
+                 endCxn=None,
+                 extLst=None,
+                ):
+        self.cxnSpLocks = cxnSpLocks
+        self.stCxn = stCxn
+        self.endCxn = endCxn
+        self.extLst = extLst
+
+
+class ConnectorNonVisual(Serialisable):
+
+    cNvPr = Typed(expected_type=NonVisualDrawingProps, )
+    cNvCxnSpPr = Typed(expected_type=NonVisualConnectorProperties, )
+
+    __elements__ = ("cNvPr", "cNvCxnSpPr",)
+
+    def __init__(self,
+                 cNvPr=None,
+                 cNvCxnSpPr=None,
+                ):
+        self.cNvPr = cNvPr
+        self.cNvCxnSpPr = cNvCxnSpPr
+
+
+class ConnectorShape(Serialisable):
+
+    tagname = "cxnSp"
+
+    nvCxnSpPr = Typed(expected_type=ConnectorNonVisual)
+    spPr = Typed(expected_type=GraphicalProperties)
+    style = Typed(expected_type=ShapeStyle, allow_none=True)
+    macro = String(allow_none=True)
+    fPublished = Bool(allow_none=True)
+
+    def __init__(self,
+                 nvCxnSpPr=None,
+                 spPr=None,
+                 style=None,
+                 macro=None,
+                 fPublished=None,
+                 ):
+        self.nvCxnSpPr = nvCxnSpPr
+        self.spPr = spPr
+        self.style = style
+        self.macro = macro
+        self.fPublished = fPublished
+
+
+class ShapeMeta(Serialisable):
+
+    tagname = "nvSpPr"
+
+    cNvPr = Typed(expected_type=NonVisualDrawingProps)
+    cNvSpPr = Typed(expected_type=NonVisualDrawingShapeProps)
+
+    def __init__(self, cNvPr=None, cNvSpPr=None):
+        self.cNvPr = cNvPr
+        self.cNvSpPr = cNvSpPr
+
+
+class Shape(Serialisable):
+
+    macro = String(allow_none=True)
+    textlink = String(allow_none=True)
+    fPublished = Bool(allow_none=True)
+    fLocksText = Bool(allow_none=True)
+    nvSpPr = Typed(expected_type=ShapeMeta, allow_none=True)
+    meta = Alias("nvSpPr")
+    spPr = Typed(expected_type=GraphicalProperties)
+    graphicalProperties = Alias("spPr")
+    style = Typed(expected_type=ShapeStyle, allow_none=True)
+    txBody = Typed(expected_type=RichText, allow_none=True)
+
+    def __init__(self,
+                 macro=None,
+                 textlink=None,
+                 fPublished=None,
+                 fLocksText=None,
+                 nvSpPr=None,
+                 spPr=None,
+                 style=None,
+                 txBody=None,
+                ):
+        self.macro = macro
+        self.textlink = textlink
+        self.fPublished = fPublished
+        self.fLocksText = fLocksText
+        self.nvSpPr = nvSpPr
+        self.spPr = spPr
+        self.style = style
+        self.txBody = txBody
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/drawing.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/drawing.py
new file mode 100644
index 00000000..45acdfe5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/drawing.py
@@ -0,0 +1,92 @@
+
+# Copyright (c) 2010-2024 openpyxl
+
+import math
+
+from openpyxl.utils.units import pixels_to_EMU
+
+
+class Drawing:
+    """ a drawing object - eg container for shapes or charts
+        we assume user specifies dimensions in pixels; units are
+        converted to EMU in the drawing part
+    """
+
+    count = 0
+
+    def __init__(self):
+
+        self.name = ''
+        self.description = ''
+        self.coordinates = ((1, 2), (16, 8))
+        self.left = 0
+        self.top = 0
+        self._width = 21 # default in px
+        self._height = 192 #default in px
+        self.resize_proportional = False
+        self.rotation = 0
+        self.anchortype = "absolute"
+        self.anchorcol = 0 # left cell
+        self.anchorrow = 0 # top row
+
+
+    @property
+    def width(self):
+        return self._width
+
+
+    @width.setter
+    def width(self, w):
+        if self.resize_proportional and w:
+            ratio = self._height / self._width
+            self._height = round(ratio * w)
+        self._width = w
+
+
+    @property
+    def height(self):
+        return self._height
+
+
+    @height.setter
+    def height(self, h):
+        if self.resize_proportional and h:
+            ratio = self._width / self._height
+            self._width = round(ratio * h)
+        self._height = h
+
+
+    def set_dimension(self, w=0, h=0):
+
+        xratio = w / self._width
+        yratio = h / self._height
+
+        if self.resize_proportional and w and h:
+            if (xratio * self._height) < h:
+                self._height = math.ceil(xratio * self._height)
+                self._width = w
+            else:
+                self._width = math.ceil(yratio * self._width)
+                self._height = h
+
+
+    @property
+    def anchor(self):
+        from .spreadsheet_drawing import (
+            OneCellAnchor,
+            TwoCellAnchor,
+            AbsoluteAnchor)
+        if self.anchortype == "absolute":
+            anchor = AbsoluteAnchor()
+            anchor.pos.x = pixels_to_EMU(self.left)
+            anchor.pos.y = pixels_to_EMU(self.top)
+
+        elif self.anchortype == "oneCell":
+            anchor = OneCellAnchor()
+            anchor._from.col = self.anchorcol
+            anchor._from.row = self.anchorrow
+
+        anchor.ext.width = pixels_to_EMU(self._width)
+        anchor.ext.height = pixels_to_EMU(self._height)
+
+        return anchor
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/effect.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/effect.py
new file mode 100644
index 00000000..9edae342
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/effect.py
@@ -0,0 +1,407 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    String,
+    Set,
+    Bool,
+    Integer,
+    Float,
+)
+
+from .colors import ColorChoice
+
+
+class TintEffect(Serialisable):
+
+    tagname = "tint"
+
+    hue = Integer()
+    amt = Integer()
+
+    def __init__(self,
+                 hue=0,
+                 amt=0,
+                ):
+        self.hue = hue
+        self.amt = amt
+
+
+class LuminanceEffect(Serialisable):
+
+    tagname = "lum"
+
+    bright = Integer() #Pct ?
+    contrast = Integer() #Pct#
+
+    def __init__(self,
+                 bright=0,
+                 contrast=0,
+                ):
+        self.bright = bright
+        self.contrast = contrast
+
+
+class HSLEffect(Serialisable):
+
+    hue = Integer()
+    sat = Integer()
+    lum = Integer()
+
+    def __init__(self,
+                 hue=None,
+                 sat=None,
+                 lum=None,
+                ):
+        self.hue = hue
+        self.sat = sat
+        self.lum = lum
+
+
+class GrayscaleEffect(Serialisable):
+
+    tagname = "grayscl"
+
+
+class FillOverlayEffect(Serialisable):
+
+    blend = Set(values=(['over', 'mult', 'screen', 'darken', 'lighten']))
+
+    def __init__(self,
+                 blend=None,
+                ):
+        self.blend = blend
+
+
+class DuotoneEffect(Serialisable):
+
+    pass
+
+class ColorReplaceEffect(Serialisable):
+
+    pass
+
+class Color(Serialisable):
+
+    pass
+
+class ColorChangeEffect(Serialisable):
+
+    useA = Bool(allow_none=True)
+    clrFrom = Typed(expected_type=Color, )
+    clrTo = Typed(expected_type=Color, )
+
+    def __init__(self,
+                 useA=None,
+                 clrFrom=None,
+                 clrTo=None,
+                ):
+        self.useA = useA
+        self.clrFrom = clrFrom
+        self.clrTo = clrTo
+
+
+class BlurEffect(Serialisable):
+
+    rad = Float()
+    grow = Bool(allow_none=True)
+
+    def __init__(self,
+                 rad=None,
+                 grow=None,
+                ):
+        self.rad = rad
+        self.grow = grow
+
+
+class BiLevelEffect(Serialisable):
+
+    thresh = Integer()
+
+    def __init__(self,
+                 thresh=None,
+                ):
+        self.thresh = thresh
+
+
+class AlphaReplaceEffect(Serialisable):
+
+    a = Integer()
+
+    def __init__(self,
+                 a=None,
+                ):
+        self.a = a
+
+
+class AlphaModulateFixedEffect(Serialisable):
+
+    amt = Integer()
+
+    def __init__(self,
+                 amt=None,
+                ):
+        self.amt = amt
+
+
+class EffectContainer(Serialisable):
+
+    type = Set(values=(['sib', 'tree']))
+    name = String(allow_none=True)
+
+    def __init__(self,
+                 type=None,
+                 name=None,
+                ):
+        self.type = type
+        self.name = name
+
+
+class AlphaModulateEffect(Serialisable):
+
+    cont = Typed(expected_type=EffectContainer, )
+
+    def __init__(self,
+                 cont=None,
+                ):
+        self.cont = cont
+
+
+class AlphaInverseEffect(Serialisable):
+
+    pass
+
+class AlphaFloorEffect(Serialisable):
+
+    pass
+
+class AlphaCeilingEffect(Serialisable):
+
+    pass
+
+class AlphaBiLevelEffect(Serialisable):
+
+    thresh = Integer()
+
+    def __init__(self,
+                 thresh=None,
+                ):
+        self.thresh = thresh
+
+
+class GlowEffect(ColorChoice):
+
+    rad = Float()
+    # uses element group EG_ColorChoice
+    scrgbClr = ColorChoice.scrgbClr
+    srgbClr = ColorChoice.srgbClr
+    hslClr = ColorChoice.hslClr
+    sysClr = ColorChoice.sysClr
+    schemeClr = ColorChoice.schemeClr
+    prstClr = ColorChoice.prstClr
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 rad=None,
+                 **kw
+                ):
+        self.rad = rad
+        super().__init__(**kw)
+
+
+class InnerShadowEffect(ColorChoice):
+
+    blurRad = Float()
+    dist = Float()
+    dir = Integer()
+    # uses element group EG_ColorChoice
+    scrgbClr = ColorChoice.scrgbClr
+    srgbClr = ColorChoice.srgbClr
+    hslClr = ColorChoice.hslClr
+    sysClr = ColorChoice.sysClr
+    schemeClr = ColorChoice.schemeClr
+    prstClr = ColorChoice.prstClr
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 blurRad=None,
+                 dist=None,
+                 dir=None,
+                 **kw
+                 ):
+        self.blurRad = blurRad
+        self.dist = dist
+        self.dir = dir
+        super().__init__(**kw)
+
+
+class OuterShadow(ColorChoice):
+
+    tagname = "outerShdw"
+
+    blurRad = Float(allow_none=True)
+    dist = Float(allow_none=True)
+    dir = Integer(allow_none=True)
+    sx = Integer(allow_none=True)
+    sy = Integer(allow_none=True)
+    kx = Integer(allow_none=True)
+    ky = Integer(allow_none=True)
+    algn = Set(values=['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br'])
+    rotWithShape = Bool(allow_none=True)
+    # uses element group EG_ColorChoice
+    scrgbClr = ColorChoice.scrgbClr
+    srgbClr = ColorChoice.srgbClr
+    hslClr = ColorChoice.hslClr
+    sysClr = ColorChoice.sysClr
+    schemeClr = ColorChoice.schemeClr
+    prstClr = ColorChoice.prstClr
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 blurRad=None,
+                 dist=None,
+                 dir=None,
+                 sx=None,
+                 sy=None,
+                 kx=None,
+                 ky=None,
+                 algn=None,
+                 rotWithShape=None,
+                 **kw
+                ):
+        self.blurRad = blurRad
+        self.dist = dist
+        self.dir = dir
+        self.sx = sx
+        self.sy = sy
+        self.kx = kx
+        self.ky = ky
+        self.algn = algn
+        self.rotWithShape = rotWithShape
+        super().__init__(**kw)
+
+
+class PresetShadowEffect(ColorChoice):
+
+    prst = Set(values=(['shdw1', 'shdw2', 'shdw3', 'shdw4', 'shdw5', 'shdw6',
+                        'shdw7', 'shdw8', 'shdw9', 'shdw10', 'shdw11', 'shdw12', 'shdw13',
+                        'shdw14', 'shdw15', 'shdw16', 'shdw17', 'shdw18', 'shdw19', 'shdw20']))
+    dist = Float()
+    dir = Integer()
+    # uses element group EG_ColorChoice
+    scrgbClr = ColorChoice.scrgbClr
+    srgbClr = ColorChoice.srgbClr
+    hslClr = ColorChoice.hslClr
+    sysClr = ColorChoice.sysClr
+    schemeClr = ColorChoice.schemeClr
+    prstClr = ColorChoice.prstClr
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 prst=None,
+                 dist=None,
+                 dir=None,
+                 **kw
+                ):
+        self.prst = prst
+        self.dist = dist
+        self.dir = dir
+        super().__init__(**kw)
+
+
+class ReflectionEffect(Serialisable):
+
+    blurRad = Float()
+    stA = Integer()
+    stPos = Integer()
+    endA = Integer()
+    endPos = Integer()
+    dist = Float()
+    dir = Integer()
+    fadeDir = Integer()
+    sx = Integer()
+    sy = Integer()
+    kx = Integer()
+    ky = Integer()
+    algn = Set(values=(['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br']))
+    rotWithShape = Bool(allow_none=True)
+
+    def __init__(self,
+                 blurRad=None,
+                 stA=None,
+                 stPos=None,
+                 endA=None,
+                 endPos=None,
+                 dist=None,
+                 dir=None,
+                 fadeDir=None,
+                 sx=None,
+                 sy=None,
+                 kx=None,
+                 ky=None,
+                 algn=None,
+                 rotWithShape=None,
+                ):
+        self.blurRad = blurRad
+        self.stA = stA
+        self.stPos = stPos
+        self.endA = endA
+        self.endPos = endPos
+        self.dist = dist
+        self.dir = dir
+        self.fadeDir = fadeDir
+        self.sx = sx
+        self.sy = sy
+        self.kx = kx
+        self.ky = ky
+        self.algn = algn
+        self.rotWithShape = rotWithShape
+
+
+class SoftEdgesEffect(Serialisable):
+
+    rad = Float()
+
+    def __init__(self,
+                 rad=None,
+                ):
+        self.rad = rad
+
+
+class EffectList(Serialisable):
+
+    blur = Typed(expected_type=BlurEffect, allow_none=True)
+    fillOverlay = Typed(expected_type=FillOverlayEffect, allow_none=True)
+    glow = Typed(expected_type=GlowEffect, allow_none=True)
+    innerShdw = Typed(expected_type=InnerShadowEffect, allow_none=True)
+    outerShdw = Typed(expected_type=OuterShadow, allow_none=True)
+    prstShdw = Typed(expected_type=PresetShadowEffect, allow_none=True)
+    reflection = Typed(expected_type=ReflectionEffect, allow_none=True)
+    softEdge = Typed(expected_type=SoftEdgesEffect, allow_none=True)
+
+    __elements__ = ('blur', 'fillOverlay', 'glow', 'innerShdw', 'outerShdw',
+                    'prstShdw', 'reflection', 'softEdge')
+
+    def __init__(self,
+                 blur=None,
+                 fillOverlay=None,
+                 glow=None,
+                 innerShdw=None,
+                 outerShdw=None,
+                 prstShdw=None,
+                 reflection=None,
+                 softEdge=None,
+                ):
+        self.blur = blur
+        self.fillOverlay = fillOverlay
+        self.glow = glow
+        self.innerShdw = innerShdw
+        self.outerShdw = outerShdw
+        self.prstShdw = prstShdw
+        self.reflection = reflection
+        self.softEdge = softEdge
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/fill.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/fill.py
new file mode 100644
index 00000000..580e0db2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/fill.py
@@ -0,0 +1,425 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Alias,
+    Bool,
+    Integer,
+    Set,
+    NoneSet,
+    Typed,
+    MinMax,
+)
+from openpyxl.descriptors.excel import (
+    Relation,
+    Percentage,
+)
+from openpyxl.descriptors.nested import NestedNoneSet, NestedValue
+from openpyxl.descriptors.sequence import NestedSequence
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+from openpyxl.xml.constants import DRAWING_NS
+
+from .colors import (
+    ColorChoice,
+    HSLColor,
+    SystemColor,
+    SchemeColor,
+    PRESET_COLORS,
+    RGBPercent,
+)
+
+from .effect import (
+    AlphaBiLevelEffect,
+    AlphaCeilingEffect,
+    AlphaFloorEffect,
+    AlphaInverseEffect,
+    AlphaModulateEffect,
+    AlphaModulateFixedEffect,
+    AlphaReplaceEffect,
+    BiLevelEffect,
+    BlurEffect,
+    ColorChangeEffect,
+    ColorReplaceEffect,
+    DuotoneEffect,
+    FillOverlayEffect,
+    GrayscaleEffect,
+    HSLEffect,
+    LuminanceEffect,
+    TintEffect,
+)
+
+"""
+Fill elements from drawing main schema
+"""
+
+class PatternFillProperties(Serialisable):
+
+    tagname = "pattFill"
+    namespace = DRAWING_NS
+
+    prst = NoneSet(values=(['pct5', 'pct10', 'pct20', 'pct25', 'pct30',
+                            'pct40', 'pct50', 'pct60', 'pct70', 'pct75', 'pct80', 'pct90', 'horz',
+                            'vert', 'ltHorz', 'ltVert', 'dkHorz', 'dkVert', 'narHorz', 'narVert',
+                            'dashHorz', 'dashVert', 'cross', 'dnDiag', 'upDiag', 'ltDnDiag',
+                            'ltUpDiag', 'dkDnDiag', 'dkUpDiag', 'wdDnDiag', 'wdUpDiag', 'dashDnDiag',
+                            'dashUpDiag', 'diagCross', 'smCheck', 'lgCheck', 'smGrid', 'lgGrid',
+                            'dotGrid', 'smConfetti', 'lgConfetti', 'horzBrick', 'diagBrick',
+                            'solidDmnd', 'openDmnd', 'dotDmnd', 'plaid', 'sphere', 'weave', 'divot',
+                            'shingle', 'wave', 'trellis', 'zigZag']))
+    preset = Alias("prst")
+    fgClr = Typed(expected_type=ColorChoice, allow_none=True)
+    foreground = Alias("fgClr")
+    bgClr = Typed(expected_type=ColorChoice, allow_none=True)
+    background = Alias("bgClr")
+
+    __elements__ = ("fgClr", "bgClr")
+
+    def __init__(self,
+                 prst=None,
+                 fgClr=None,
+                 bgClr=None,
+                ):
+        self.prst = prst
+        self.fgClr = fgClr
+        self.bgClr = bgClr
+
+
+class RelativeRect(Serialisable):
+
+    tagname = "rect"
+    namespace = DRAWING_NS
+
+    l = Percentage(allow_none=True)
+    left = Alias('l')
+    t = Percentage(allow_none=True)
+    top = Alias('t')
+    r = Percentage(allow_none=True)
+    right = Alias('r')
+    b = Percentage(allow_none=True)
+    bottom = Alias('b')
+
+    def __init__(self,
+                 l=None,
+                 t=None,
+                 r=None,
+                 b=None,
+                ):
+        self.l = l
+        self.t = t
+        self.r = r
+        self.b = b
+
+
+class StretchInfoProperties(Serialisable):
+
+    tagname = "stretch"
+    namespace = DRAWING_NS
+
+    fillRect = Typed(expected_type=RelativeRect, allow_none=True)
+
+    def __init__(self,
+                 fillRect=RelativeRect(),
+                ):
+        self.fillRect = fillRect
+
+
+class GradientStop(Serialisable):
+
+    tagname = "gs"
+    namespace = DRAWING_NS
+
+    pos = MinMax(min=0, max=100000, allow_none=True)
+    # Color Choice Group
+    scrgbClr = Typed(expected_type=RGBPercent, allow_none=True)
+    RGBPercent = Alias('scrgbClr')
+    srgbClr = NestedValue(expected_type=str, allow_none=True) # needs pattern and can have transform
+    RGB = Alias('srgbClr')
+    hslClr = Typed(expected_type=HSLColor, allow_none=True)
+    sysClr = Typed(expected_type=SystemColor, allow_none=True)
+    schemeClr = Typed(expected_type=SchemeColor, allow_none=True)
+    prstClr = NestedNoneSet(values=PRESET_COLORS)
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 pos=None,
+                 scrgbClr=None,
+                 srgbClr=None,
+                 hslClr=None,
+                 sysClr=None,
+                 schemeClr=None,
+                 prstClr=None,
+                ):
+        if pos is None:
+            pos = 0
+        self.pos = pos
+
+        self.scrgbClr = scrgbClr
+        self.srgbClr = srgbClr
+        self.hslClr = hslClr
+        self.sysClr = sysClr
+        self.schemeClr = schemeClr
+        self.prstClr = prstClr
+
+
+class LinearShadeProperties(Serialisable):
+
+    tagname = "lin"
+    namespace = DRAWING_NS
+
+    ang = Integer()
+    scaled = Bool(allow_none=True)
+
+    def __init__(self,
+                 ang=None,
+                 scaled=None,
+                ):
+        self.ang = ang
+        self.scaled = scaled
+
+
+class PathShadeProperties(Serialisable):
+
+    tagname = "path"
+    namespace = DRAWING_NS
+
+    path = Set(values=(['shape', 'circle', 'rect']))
+    fillToRect = Typed(expected_type=RelativeRect, allow_none=True)
+
+    def __init__(self,
+                 path=None,
+                 fillToRect=None,
+                ):
+        self.path = path
+        self.fillToRect = fillToRect
+
+
+class GradientFillProperties(Serialisable):
+
+    tagname = "gradFill"
+    namespace = DRAWING_NS
+
+    flip = NoneSet(values=(['x', 'y', 'xy']))
+    rotWithShape = Bool(allow_none=True)
+
+    gsLst = NestedSequence(expected_type=GradientStop, count=False)
+    stop_list = Alias("gsLst")
+
+    lin = Typed(expected_type=LinearShadeProperties, allow_none=True)
+    linear = Alias("lin")
+    path = Typed(expected_type=PathShadeProperties, allow_none=True)
+
+    tileRect = Typed(expected_type=RelativeRect, allow_none=True)
+
+    __elements__ = ('gsLst', 'lin', 'path', 'tileRect')
+
+    def __init__(self,
+                 flip=None,
+                 rotWithShape=None,
+                 gsLst=(),
+                 lin=None,
+                 path=None,
+                 tileRect=None,
+                ):
+        self.flip = flip
+        self.rotWithShape = rotWithShape
+        self.gsLst = gsLst
+        self.lin = lin
+        self.path = path
+        self.tileRect = tileRect
+
+
+class SolidColorFillProperties(Serialisable):
+
+    tagname = "solidFill"
+
+    # uses element group EG_ColorChoice
+    scrgbClr = Typed(expected_type=RGBPercent, allow_none=True)
+    RGBPercent = Alias('scrgbClr')
+    srgbClr = NestedValue(expected_type=str, allow_none=True) # needs pattern and can have transform
+    RGB = Alias('srgbClr')
+    hslClr = Typed(expected_type=HSLColor, allow_none=True)
+    sysClr = Typed(expected_type=SystemColor, allow_none=True)
+    schemeClr = Typed(expected_type=SchemeColor, allow_none=True)
+    prstClr = NestedNoneSet(values=PRESET_COLORS)
+
+    __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr')
+
+    def __init__(self,
+                 scrgbClr=None,
+                 srgbClr=None,
+                 hslClr=None,
+                 sysClr=None,
+                 schemeClr=None,
+                 prstClr=None,
+                ):
+        self.scrgbClr = scrgbClr
+        self.srgbClr = srgbClr
+        self.hslClr = hslClr
+        self.sysClr = sysClr
+        self.schemeClr = schemeClr
+        self.prstClr = prstClr
+
+
+class Blip(Serialisable):
+
+    tagname = "blip"
+    namespace = DRAWING_NS
+
+    # Using attribute groupAG_Blob
+    cstate = NoneSet(values=(['email', 'screen', 'print', 'hqprint']))
+    embed = Relation() # rId
+    link = Relation() # hyperlink
+    noGrp = Bool(allow_none=True)
+    noSelect = Bool(allow_none=True)
+    noRot = Bool(allow_none=True)
+    noChangeAspect = Bool(allow_none=True)
+    noMove = Bool(allow_none=True)
+    noResize = Bool(allow_none=True)
+    noEditPoints = Bool(allow_none=True)
+    noAdjustHandles = Bool(allow_none=True)
+    noChangeArrowheads = Bool(allow_none=True)
+    noChangeShapeType = Bool(allow_none=True)
+    # some elements are choice
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+    alphaBiLevel = Typed(expected_type=AlphaBiLevelEffect, allow_none=True)
+    alphaCeiling = Typed(expected_type=AlphaCeilingEffect, allow_none=True)
+    alphaFloor = Typed(expected_type=AlphaFloorEffect, allow_none=True)
+    alphaInv = Typed(expected_type=AlphaInverseEffect, allow_none=True)
+    alphaMod = Typed(expected_type=AlphaModulateEffect, allow_none=True)
+    alphaModFix = Typed(expected_type=AlphaModulateFixedEffect, allow_none=True)
+    alphaRepl = Typed(expected_type=AlphaReplaceEffect, allow_none=True)
+    biLevel = Typed(expected_type=BiLevelEffect, allow_none=True)
+    blur = Typed(expected_type=BlurEffect, allow_none=True)
+    clrChange = Typed(expected_type=ColorChangeEffect, allow_none=True)
+    clrRepl = Typed(expected_type=ColorReplaceEffect, allow_none=True)
+    duotone = Typed(expected_type=DuotoneEffect, allow_none=True)
+    fillOverlay = Typed(expected_type=FillOverlayEffect, allow_none=True)
+    grayscl = Typed(expected_type=GrayscaleEffect, allow_none=True)
+    hsl = Typed(expected_type=HSLEffect, allow_none=True)
+    lum = Typed(expected_type=LuminanceEffect, allow_none=True)
+    tint = Typed(expected_type=TintEffect, allow_none=True)
+
+    __elements__ = ('alphaBiLevel', 'alphaCeiling', 'alphaFloor', 'alphaInv',
+                    'alphaMod', 'alphaModFix', 'alphaRepl', 'biLevel', 'blur', 'clrChange',
+                    'clrRepl', 'duotone', 'fillOverlay', 'grayscl', 'hsl', 'lum', 'tint')
+
+    def __init__(self,
+                 cstate=None,
+                 embed=None,
+                 link=None,
+                 noGrp=None,
+                 noSelect=None,
+                 noRot=None,
+                 noChangeAspect=None,
+                 noMove=None,
+                 noResize=None,
+                 noEditPoints=None,
+                 noAdjustHandles=None,
+                 noChangeArrowheads=None,
+                 noChangeShapeType=None,
+                 extLst=None,
+                 alphaBiLevel=None,
+                 alphaCeiling=None,
+                 alphaFloor=None,
+                 alphaInv=None,
+                 alphaMod=None,
+                 alphaModFix=None,
+                 alphaRepl=None,
+                 biLevel=None,
+                 blur=None,
+                 clrChange=None,
+                 clrRepl=None,
+                 duotone=None,
+                 fillOverlay=None,
+                 grayscl=None,
+                 hsl=None,
+                 lum=None,
+                 tint=None,
+                ):
+        self.cstate = cstate
+        self.embed = embed
+        self.link = link
+        self.noGrp = noGrp
+        self.noSelect = noSelect
+        self.noRot = noRot
+        self.noChangeAspect = noChangeAspect
+        self.noMove = noMove
+        self.noResize = noResize
+        self.noEditPoints = noEditPoints
+        self.noAdjustHandles = noAdjustHandles
+        self.noChangeArrowheads = noChangeArrowheads
+        self.noChangeShapeType = noChangeShapeType
+        self.extLst = extLst
+        self.alphaBiLevel = alphaBiLevel
+        self.alphaCeiling = alphaCeiling
+        self.alphaFloor = alphaFloor
+        self.alphaInv = alphaInv
+        self.alphaMod = alphaMod
+        self.alphaModFix = alphaModFix
+        self.alphaRepl = alphaRepl
+        self.biLevel = biLevel
+        self.blur = blur
+        self.clrChange = clrChange
+        self.clrRepl = clrRepl
+        self.duotone = duotone
+        self.fillOverlay = fillOverlay
+        self.grayscl = grayscl
+        self.hsl = hsl
+        self.lum = lum
+        self.tint = tint
+
+
+class TileInfoProperties(Serialisable):
+
+    tx = Integer(allow_none=True)
+    ty = Integer(allow_none=True)
+    sx = Integer(allow_none=True)
+    sy = Integer(allow_none=True)
+    flip = NoneSet(values=(['x', 'y', 'xy']))
+    algn = Set(values=(['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br']))
+
+    def __init__(self,
+                 tx=None,
+                 ty=None,
+                 sx=None,
+                 sy=None,
+                 flip=None,
+                 algn=None,
+                ):
+        self.tx = tx
+        self.ty = ty
+        self.sx = sx
+        self.sy = sy
+        self.flip = flip
+        self.algn = algn
+
+
+class BlipFillProperties(Serialisable):
+
+    tagname = "blipFill"
+
+    dpi = Integer(allow_none=True)
+    rotWithShape = Bool(allow_none=True)
+
+    blip = Typed(expected_type=Blip, allow_none=True)
+    srcRect = Typed(expected_type=RelativeRect, allow_none=True)
+    tile = Typed(expected_type=TileInfoProperties, allow_none=True)
+    stretch = Typed(expected_type=StretchInfoProperties, allow_none=True)
+
+    __elements__ = ("blip", "srcRect", "tile", "stretch")
+
+    def __init__(self,
+                 dpi=None,
+                 rotWithShape=None,
+                 blip=None,
+                 tile=None,
+                 stretch=StretchInfoProperties(),
+                 srcRect=None,
+                ):
+        self.dpi = dpi
+        self.rotWithShape = rotWithShape
+        self.blip = blip
+        self.tile = tile
+        self.stretch = stretch
+        self.srcRect = srcRect
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/geometry.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/geometry.py
new file mode 100644
index 00000000..2cc7ca63
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/geometry.py
@@ -0,0 +1,584 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Float,
+    Integer,
+    Bool,
+    MinMax,
+    Set,
+    NoneSet,
+    String,
+    Alias,
+)
+from openpyxl.descriptors.excel import Coordinate, Percentage
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+from .line import LineProperties
+
+from openpyxl.styles.colors import Color
+from openpyxl.xml.constants import DRAWING_NS
+
+
+class Point2D(Serialisable):
+
+    tagname = "off"
+    namespace = DRAWING_NS
+
+    x = Coordinate()
+    y = Coordinate()
+
+    def __init__(self,
+                 x=None,
+                 y=None,
+                ):
+        self.x = x
+        self.y = y
+
+
+class PositiveSize2D(Serialisable):
+
+    tagname = "ext"
+    namespace = DRAWING_NS
+
+    """
+    Dimensions in EMUs
+    """
+
+    cx = Integer()
+    width = Alias('cx')
+    cy = Integer()
+    height = Alias('cy')
+
+    def __init__(self,
+                 cx=None,
+                 cy=None,
+                ):
+        self.cx = cx
+        self.cy = cy
+
+
+class Transform2D(Serialisable):
+
+    tagname = "xfrm"
+    namespace = DRAWING_NS
+
+    rot = Integer(allow_none=True)
+    flipH = Bool(allow_none=True)
+    flipV = Bool(allow_none=True)
+    off = Typed(expected_type=Point2D, allow_none=True)
+    ext = Typed(expected_type=PositiveSize2D, allow_none=True)
+    chOff = Typed(expected_type=Point2D, allow_none=True)
+    chExt = Typed(expected_type=PositiveSize2D, allow_none=True)
+
+    __elements__ = ('off', 'ext', 'chOff', 'chExt')
+
+    def __init__(self,
+                 rot=None,
+                 flipH=None,
+                 flipV=None,
+                 off=None,
+                 ext=None,
+                 chOff=None,
+                 chExt=None,
+                ):
+        self.rot = rot
+        self.flipH = flipH
+        self.flipV = flipV
+        self.off = off
+        self.ext = ext
+        self.chOff = chOff
+        self.chExt = chExt
+
+
+class GroupTransform2D(Serialisable):
+
+    tagname = "xfrm"
+    namespace = DRAWING_NS
+
+    rot = Integer(allow_none=True)
+    flipH = Bool(allow_none=True)
+    flipV = Bool(allow_none=True)
+    off = Typed(expected_type=Point2D, allow_none=True)
+    ext = Typed(expected_type=PositiveSize2D, allow_none=True)
+    chOff = Typed(expected_type=Point2D, allow_none=True)
+    chExt = Typed(expected_type=PositiveSize2D, allow_none=True)
+
+    __elements__ = ("off", "ext", "chOff", "chExt")
+
+    def __init__(self,
+                 rot=0,
+                 flipH=None,
+                 flipV=None,
+                 off=None,
+                 ext=None,
+                 chOff=None,
+                 chExt=None,
+                ):
+        self.rot = rot
+        self.flipH = flipH
+        self.flipV = flipV
+        self.off = off
+        self.ext = ext
+        self.chOff = chOff
+        self.chExt = chExt
+
+
+class SphereCoords(Serialisable):
+
+    tagname = "sphereCoords" # usually
+
+    lat = Integer()
+    lon = Integer()
+    rev = Integer()
+
+    def __init__(self,
+                 lat=None,
+                 lon=None,
+                 rev=None,
+                ):
+        self.lat = lat
+        self.lon = lon
+        self.rev = rev
+
+
+class Camera(Serialisable):
+
+    tagname = "camera"
+
+    prst = Set(values=[
+        'legacyObliqueTopLeft', 'legacyObliqueTop', 'legacyObliqueTopRight', 'legacyObliqueLeft',
+         'legacyObliqueFront', 'legacyObliqueRight', 'legacyObliqueBottomLeft',
+         'legacyObliqueBottom', 'legacyObliqueBottomRight', 'legacyPerspectiveTopLeft',
+         'legacyPerspectiveTop', 'legacyPerspectiveTopRight', 'legacyPerspectiveLeft',
+         'legacyPerspectiveFront', 'legacyPerspectiveRight', 'legacyPerspectiveBottomLeft',
+         'legacyPerspectiveBottom', 'legacyPerspectiveBottomRight', 'orthographicFront',
+         'isometricTopUp', 'isometricTopDown', 'isometricBottomUp', 'isometricBottomDown',
+         'isometricLeftUp', 'isometricLeftDown', 'isometricRightUp', 'isometricRightDown',
+         'isometricOffAxis1Left', 'isometricOffAxis1Right', 'isometricOffAxis1Top',
+         'isometricOffAxis2Left', 'isometricOffAxis2Right', 'isometricOffAxis2Top',
+         'isometricOffAxis3Left', 'isometricOffAxis3Right', 'isometricOffAxis3Bottom',
+         'isometricOffAxis4Left', 'isometricOffAxis4Right', 'isometricOffAxis4Bottom',
+         'obliqueTopLeft',  'obliqueTop', 'obliqueTopRight', 'obliqueLeft', 'obliqueRight',
+         'obliqueBottomLeft', 'obliqueBottom', 'obliqueBottomRight', 'perspectiveFront',
+         'perspectiveLeft', 'perspectiveRight', 'perspectiveAbove', 'perspectiveBelow',
+         'perspectiveAboveLeftFacing', 'perspectiveAboveRightFacing',
+         'perspectiveContrastingLeftFacing', 'perspectiveContrastingRightFacing',
+         'perspectiveHeroicLeftFacing', 'perspectiveHeroicRightFacing',
+         'perspectiveHeroicExtremeLeftFacing', 'perspectiveHeroicExtremeRightFacing',
+         'perspectiveRelaxed', 'perspectiveRelaxedModerately'])
+    fov = Integer(allow_none=True)
+    zoom = Typed(expected_type=Percentage, allow_none=True)
+    rot = Typed(expected_type=SphereCoords, allow_none=True)
+
+
+    def __init__(self,
+                 prst=None,
+                 fov=None,
+                 zoom=None,
+                 rot=None,
+                ):
+        self.prst = prst
+        self.fov = fov
+        self.zoom = zoom
+        self.rot = rot
+
+
+class LightRig(Serialisable):
+
+    tagname = "lightRig"
+
+    rig = Set(values=['legacyFlat1', 'legacyFlat2', 'legacyFlat3', 'legacyFlat4', 'legacyNormal1',
+         'legacyNormal2', 'legacyNormal3', 'legacyNormal4', 'legacyHarsh1',
+         'legacyHarsh2', 'legacyHarsh3', 'legacyHarsh4', 'threePt', 'balanced',
+         'soft', 'harsh', 'flood', 'contrasting', 'morning', 'sunrise', 'sunset',
+         'chilly', 'freezing', 'flat', 'twoPt', 'glow', 'brightRoom']
+    )
+    dir = Set(values=(['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br']))
+    rot = Typed(expected_type=SphereCoords, allow_none=True)
+
+    def __init__(self,
+                 rig=None,
+                 dir=None,
+                 rot=None,
+                ):
+        self.rig = rig
+        self.dir = dir
+        self.rot = rot
+
+
+class Vector3D(Serialisable):
+
+    tagname = "vector"
+
+    dx = Integer() # can be in or universl measure :-/
+    dy = Integer()
+    dz = Integer()
+
+    def __init__(self,
+                 dx=None,
+                 dy=None,
+                 dz=None,
+                ):
+        self.dx = dx
+        self.dy = dy
+        self.dz = dz
+
+
+class Point3D(Serialisable):
+
+    tagname = "anchor"
+
+    x = Integer()
+    y = Integer()
+    z = Integer()
+
+    def __init__(self,
+                 x=None,
+                 y=None,
+                 z=None,
+                ):
+        self.x = x
+        self.y = y
+        self.z = z
+
+
+class Backdrop(Serialisable):
+
+    anchor = Typed(expected_type=Point3D, )
+    norm = Typed(expected_type=Vector3D, )
+    up = Typed(expected_type=Vector3D, )
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 anchor=None,
+                 norm=None,
+                 up=None,
+                 extLst=None,
+                ):
+        self.anchor = anchor
+        self.norm = norm
+        self.up = up
+        self.extLst = extLst
+
+
+class Scene3D(Serialisable):
+
+    camera = Typed(expected_type=Camera, )
+    lightRig = Typed(expected_type=LightRig, )
+    backdrop = Typed(expected_type=Backdrop, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 camera=None,
+                 lightRig=None,
+                 backdrop=None,
+                 extLst=None,
+                ):
+        self.camera = camera
+        self.lightRig = lightRig
+        self.backdrop = backdrop
+        self.extLst = extLst
+
+
+class Bevel(Serialisable):
+
+    tagname = "bevel"
+
+    w = Integer()
+    h = Integer()
+    prst = NoneSet(values=
+               ['relaxedInset', 'circle', 'slope', 'cross', 'angle',
+                'softRound', 'convex', 'coolSlant', 'divot', 'riblet',
+                 'hardEdge', 'artDeco']
+               )
+
+    def __init__(self,
+                 w=None,
+                 h=None,
+                 prst=None,
+                ):
+        self.w = w
+        self.h = h
+        self.prst = prst
+
+
+class Shape3D(Serialisable):
+
+    namespace = DRAWING_NS
+
+    z = Typed(expected_type=Coordinate, allow_none=True)
+    extrusionH = Integer(allow_none=True)
+    contourW = Integer(allow_none=True)
+    prstMaterial = NoneSet(values=[
+        'legacyMatte','legacyPlastic', 'legacyMetal', 'legacyWireframe', 'matte', 'plastic',
+        'metal', 'warmMatte', 'translucentPowder', 'powder', 'dkEdge',
+        'softEdge', 'clear', 'flat', 'softmetal']
+                       )
+    bevelT = Typed(expected_type=Bevel, allow_none=True)
+    bevelB = Typed(expected_type=Bevel, allow_none=True)
+    extrusionClr = Typed(expected_type=Color, allow_none=True)
+    contourClr = Typed(expected_type=Color, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 z=None,
+                 extrusionH=None,
+                 contourW=None,
+                 prstMaterial=None,
+                 bevelT=None,
+                 bevelB=None,
+                 extrusionClr=None,
+                 contourClr=None,
+                 extLst=None,
+                ):
+        self.z = z
+        self.extrusionH = extrusionH
+        self.contourW = contourW
+        self.prstMaterial = prstMaterial
+        self.bevelT = bevelT
+        self.bevelB = bevelB
+        self.extrusionClr = extrusionClr
+        self.contourClr = contourClr
+        self.extLst = extLst
+
+
+class Path2D(Serialisable):
+
+    w = Float()
+    h = Float()
+    fill = NoneSet(values=(['norm', 'lighten', 'lightenLess', 'darken', 'darkenLess']))
+    stroke = Bool(allow_none=True)
+    extrusionOk = Bool(allow_none=True)
+
+    def __init__(self,
+                 w=None,
+                 h=None,
+                 fill=None,
+                 stroke=None,
+                 extrusionOk=None,
+                ):
+        self.w = w
+        self.h = h
+        self.fill = fill
+        self.stroke = stroke
+        self.extrusionOk = extrusionOk
+
+
+class Path2DList(Serialisable):
+
+    path = Typed(expected_type=Path2D, allow_none=True)
+
+    def __init__(self,
+                 path=None,
+                ):
+        self.path = path
+
+
+class GeomRect(Serialisable):
+
+    l = Coordinate()
+    t = Coordinate()
+    r = Coordinate()
+    b = Coordinate()
+
+    def __init__(self,
+                 l=None,
+                 t=None,
+                 r=None,
+                 b=None,
+                ):
+        self.l = l
+        self.t = t
+        self.r = r
+        self.b = b
+
+
+class AdjPoint2D(Serialisable):
+
+    x = Coordinate()
+    y = Coordinate()
+
+    def __init__(self,
+                 x=None,
+                 y=None,
+                ):
+        self.x = x
+        self.y = y
+
+
+class ConnectionSite(Serialisable):
+
+    ang = MinMax(min=0, max=360) # guess work, can also be a name
+    pos = Typed(expected_type=AdjPoint2D, )
+
+    def __init__(self,
+                 ang=None,
+                 pos=None,
+                ):
+        self.ang = ang
+        self.pos = pos
+
+
+class ConnectionSiteList(Serialisable):
+
+    cxn = Typed(expected_type=ConnectionSite, allow_none=True)
+
+    def __init__(self,
+                 cxn=None,
+                ):
+        self.cxn = cxn
+
+
+class AdjustHandleList(Serialisable):
+
+    pass
+
+class GeomGuide(Serialisable):
+
+    name = String()
+    fmla = String()
+
+    def __init__(self,
+                 name=None,
+                 fmla=None,
+                ):
+        self.name = name
+        self.fmla = fmla
+
+
+class GeomGuideList(Serialisable):
+
+    gd = Typed(expected_type=GeomGuide, allow_none=True)
+
+    def __init__(self,
+                 gd=None,
+                ):
+        self.gd = gd
+
+
+class CustomGeometry2D(Serialisable):
+
+    avLst = Typed(expected_type=GeomGuideList, allow_none=True)
+    gdLst = Typed(expected_type=GeomGuideList, allow_none=True)
+    ahLst = Typed(expected_type=AdjustHandleList, allow_none=True)
+    cxnLst = Typed(expected_type=ConnectionSiteList, allow_none=True)
+    #rect = Typed(expected_type=GeomRect, allow_none=True)
+    pathLst = Typed(expected_type=Path2DList, )
+
+    def __init__(self,
+                 avLst=None,
+                 gdLst=None,
+                 ahLst=None,
+                 cxnLst=None,
+                 rect=None,
+                 pathLst=None,
+                ):
+        self.avLst = avLst
+        self.gdLst = gdLst
+        self.ahLst = ahLst
+        self.cxnLst = cxnLst
+        self.rect = None
+        self.pathLst = pathLst
+
+
+class PresetGeometry2D(Serialisable):
+
+    namespace = DRAWING_NS
+
+    prst = Set(values=(
+        ['line', 'lineInv', 'triangle', 'rtTriangle', 'rect',
+         'diamond', 'parallelogram', 'trapezoid', 'nonIsoscelesTrapezoid',
+         'pentagon', 'hexagon', 'heptagon', 'octagon', 'decagon', 'dodecagon',
+         'star4', 'star5', 'star6', 'star7', 'star8', 'star10', 'star12',
+         'star16', 'star24', 'star32', 'roundRect', 'round1Rect',
+         'round2SameRect', 'round2DiagRect', 'snipRoundRect', 'snip1Rect',
+         'snip2SameRect', 'snip2DiagRect', 'plaque', 'ellipse', 'teardrop',
+         'homePlate', 'chevron', 'pieWedge', 'pie', 'blockArc', 'donut',
+         'noSmoking', 'rightArrow', 'leftArrow', 'upArrow', 'downArrow',
+         'stripedRightArrow', 'notchedRightArrow', 'bentUpArrow',
+         'leftRightArrow', 'upDownArrow', 'leftUpArrow', 'leftRightUpArrow',
+         'quadArrow', 'leftArrowCallout', 'rightArrowCallout', 'upArrowCallout',
+         'downArrowCallout', 'leftRightArrowCallout', 'upDownArrowCallout',
+         'quadArrowCallout', 'bentArrow', 'uturnArrow', 'circularArrow',
+         'leftCircularArrow', 'leftRightCircularArrow', 'curvedRightArrow',
+         'curvedLeftArrow', 'curvedUpArrow', 'curvedDownArrow', 'swooshArrow',
+         'cube', 'can', 'lightningBolt', 'heart', 'sun', 'moon', 'smileyFace',
+         'irregularSeal1', 'irregularSeal2', 'foldedCorner', 'bevel', 'frame',
+         'halfFrame', 'corner', 'diagStripe', 'chord', 'arc', 'leftBracket',
+         'rightBracket', 'leftBrace', 'rightBrace', 'bracketPair', 'bracePair',
+         'straightConnector1', 'bentConnector2', 'bentConnector3',
+         'bentConnector4', 'bentConnector5', 'curvedConnector2',
+         'curvedConnector3', 'curvedConnector4', 'curvedConnector5', 'callout1',
+         'callout2', 'callout3', 'accentCallout1', 'accentCallout2',
+         'accentCallout3', 'borderCallout1', 'borderCallout2', 'borderCallout3',
+         'accentBorderCallout1', 'accentBorderCallout2', 'accentBorderCallout3',
+         'wedgeRectCallout', 'wedgeRoundRectCallout', 'wedgeEllipseCallout',
+         'cloudCallout', 'cloud', 'ribbon', 'ribbon2', 'ellipseRibbon',
+         'ellipseRibbon2', 'leftRightRibbon', 'verticalScroll',
+         'horizontalScroll', 'wave', 'doubleWave', 'plus', 'flowChartProcess',
+         'flowChartDecision', 'flowChartInputOutput',
+         'flowChartPredefinedProcess', 'flowChartInternalStorage',
+         'flowChartDocument', 'flowChartMultidocument', 'flowChartTerminator',
+         'flowChartPreparation', 'flowChartManualInput',
+         'flowChartManualOperation', 'flowChartConnector', 'flowChartPunchedCard',
+         'flowChartPunchedTape', 'flowChartSummingJunction', 'flowChartOr',
+         'flowChartCollate', 'flowChartSort', 'flowChartExtract',
+         'flowChartMerge', 'flowChartOfflineStorage', 'flowChartOnlineStorage',
+         'flowChartMagneticTape', 'flowChartMagneticDisk',
+         'flowChartMagneticDrum', 'flowChartDisplay', 'flowChartDelay',
+         'flowChartAlternateProcess', 'flowChartOffpageConnector',
+         'actionButtonBlank', 'actionButtonHome', 'actionButtonHelp',
+         'actionButtonInformation', 'actionButtonForwardNext',
+         'actionButtonBackPrevious', 'actionButtonEnd', 'actionButtonBeginning',
+         'actionButtonReturn', 'actionButtonDocument', 'actionButtonSound',
+         'actionButtonMovie', 'gear6', 'gear9', 'funnel', 'mathPlus', 'mathMinus',
+         'mathMultiply', 'mathDivide', 'mathEqual', 'mathNotEqual', 'cornerTabs',
+         'squareTabs', 'plaqueTabs', 'chartX', 'chartStar', 'chartPlus']))
+    avLst = Typed(expected_type=GeomGuideList, allow_none=True)
+
+    def __init__(self,
+                 prst=None,
+                 avLst=None,
+                ):
+        self.prst = prst
+        self.avLst = avLst
+
+
+class FontReference(Serialisable):
+
+    idx = NoneSet(values=(['major', 'minor']))
+
+    def __init__(self,
+                 idx=None,
+                ):
+        self.idx = idx
+
+
+class StyleMatrixReference(Serialisable):
+
+    idx = Integer()
+
+    def __init__(self,
+                 idx=None,
+                ):
+        self.idx = idx
+
+
+class ShapeStyle(Serialisable):
+
+    lnRef = Typed(expected_type=StyleMatrixReference, )
+    fillRef = Typed(expected_type=StyleMatrixReference, )
+    effectRef = Typed(expected_type=StyleMatrixReference, )
+    fontRef = Typed(expected_type=FontReference, )
+
+    def __init__(self,
+                 lnRef=None,
+                 fillRef=None,
+                 effectRef=None,
+                 fontRef=None,
+                ):
+        self.lnRef = lnRef
+        self.fillRef = fillRef
+        self.effectRef = effectRef
+        self.fontRef = fontRef
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/graphic.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/graphic.py
new file mode 100644
index 00000000..2c340870
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/graphic.py
@@ -0,0 +1,177 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.xml.constants import CHART_NS, DRAWING_NS
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Bool,
+    String,
+    Alias,
+)
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+
+from .effect import (
+    EffectList,
+    EffectContainer,
+)
+from .fill import (
+    Blip,
+    GradientFillProperties,
+    BlipFillProperties,
+)
+from .picture import PictureFrame
+from .properties import (
+    NonVisualDrawingProps,
+    NonVisualGroupShape,
+    GroupShapeProperties,
+)
+from .relation import ChartRelation
+from .xdr import XDRTransform2D
+
+
+class GraphicFrameLocking(Serialisable):
+
+    noGrp = Bool(allow_none=True)
+    noDrilldown = Bool(allow_none=True)
+    noSelect = Bool(allow_none=True)
+    noChangeAspect = Bool(allow_none=True)
+    noMove = Bool(allow_none=True)
+    noResize = Bool(allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 noGrp=None,
+                 noDrilldown=None,
+                 noSelect=None,
+                 noChangeAspect=None,
+                 noMove=None,
+                 noResize=None,
+                 extLst=None,
+                ):
+        self.noGrp = noGrp
+        self.noDrilldown = noDrilldown
+        self.noSelect = noSelect
+        self.noChangeAspect = noChangeAspect
+        self.noMove = noMove
+        self.noResize = noResize
+        self.extLst = extLst
+
+
+class NonVisualGraphicFrameProperties(Serialisable):
+
+    tagname = "cNvGraphicFramePr"
+
+    graphicFrameLocks = Typed(expected_type=GraphicFrameLocking, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 graphicFrameLocks=None,
+                 extLst=None,
+                ):
+        self.graphicFrameLocks = graphicFrameLocks
+        self.extLst = extLst
+
+
+class NonVisualGraphicFrame(Serialisable):
+
+    tagname = "nvGraphicFramePr"
+
+    cNvPr = Typed(expected_type=NonVisualDrawingProps)
+    cNvGraphicFramePr = Typed(expected_type=NonVisualGraphicFrameProperties)
+
+    __elements__ = ('cNvPr', 'cNvGraphicFramePr')
+
+    def __init__(self,
+                 cNvPr=None,
+                 cNvGraphicFramePr=None,
+                ):
+        if cNvPr is None:
+            cNvPr = NonVisualDrawingProps(id=0, name="Chart 0")
+        self.cNvPr = cNvPr
+        if cNvGraphicFramePr is None:
+            cNvGraphicFramePr = NonVisualGraphicFrameProperties()
+        self.cNvGraphicFramePr = cNvGraphicFramePr
+
+
+class GraphicData(Serialisable):
+
+    tagname = "graphicData"
+    namespace = DRAWING_NS
+
+    uri = String()
+    chart = Typed(expected_type=ChartRelation, allow_none=True)
+
+
+    def __init__(self,
+                 uri=CHART_NS,
+                 chart=None,
+                ):
+        self.uri = uri
+        self.chart = chart
+
+
+class GraphicObject(Serialisable):
+
+    tagname = "graphic"
+    namespace = DRAWING_NS
+
+    graphicData = Typed(expected_type=GraphicData)
+
+    def __init__(self,
+                 graphicData=None,
+                ):
+        if graphicData is None:
+            graphicData = GraphicData()
+        self.graphicData = graphicData
+
+
+class GraphicFrame(Serialisable):
+
+    tagname = "graphicFrame"
+
+    nvGraphicFramePr = Typed(expected_type=NonVisualGraphicFrame)
+    xfrm = Typed(expected_type=XDRTransform2D)
+    graphic = Typed(expected_type=GraphicObject)
+    macro = String(allow_none=True)
+    fPublished = Bool(allow_none=True)
+
+    __elements__ = ('nvGraphicFramePr', 'xfrm', 'graphic', 'macro', 'fPublished')
+
+    def __init__(self,
+                 nvGraphicFramePr=None,
+                 xfrm=None,
+                 graphic=None,
+                 macro=None,
+                 fPublished=None,
+                 ):
+        if nvGraphicFramePr is None:
+            nvGraphicFramePr = NonVisualGraphicFrame()
+        self.nvGraphicFramePr = nvGraphicFramePr
+        if xfrm is None:
+            xfrm = XDRTransform2D()
+        self.xfrm = xfrm
+        if graphic is None:
+            graphic = GraphicObject()
+        self.graphic = graphic
+        self.macro = macro
+        self.fPublished = fPublished
+
+
+class GroupShape(Serialisable):
+
+    nvGrpSpPr = Typed(expected_type=NonVisualGroupShape)
+    nonVisualProperties = Alias("nvGrpSpPr")
+    grpSpPr = Typed(expected_type=GroupShapeProperties)
+    visualProperties = Alias("grpSpPr")
+    pic = Typed(expected_type=PictureFrame, allow_none=True)
+
+    __elements__ = ["nvGrpSpPr", "grpSpPr", "pic"]
+
+    def __init__(self,
+                 nvGrpSpPr=None,
+                 grpSpPr=None,
+                 pic=None,
+                ):
+        self.nvGrpSpPr = nvGrpSpPr
+        self.grpSpPr = grpSpPr
+        self.pic = pic
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/image.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/image.py
new file mode 100644
index 00000000..9d0446fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/image.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from io import BytesIO
+
+try:
+    from PIL import Image as PILImage
+except ImportError:
+    PILImage = False
+
+
+def _import_image(img):
+    if not PILImage:
+        raise ImportError('You must install Pillow to fetch image objects')
+
+    if not isinstance(img, PILImage.Image):
+        img = PILImage.open(img)
+
+    return img
+
+
+class Image:
+    """Image in a spreadsheet"""
+
+    _id = 1
+    _path = "/xl/media/image{0}.{1}"
+    anchor = "A1"
+
+    def __init__(self, img):
+
+        self.ref = img
+        mark_to_close = isinstance(img, str)
+        image = _import_image(img)
+        self.width, self.height = image.size
+
+        try:
+            self.format = image.format.lower()
+        except AttributeError:
+            self.format = "png"
+        if mark_to_close:
+            # PIL instances created for metadata should be closed.
+            image.close()
+
+
+    def _data(self):
+        """
+        Return image data, convert to supported types if necessary
+        """
+        img = _import_image(self.ref)
+        # don't convert these file formats
+        if self.format in ['gif', 'jpeg', 'png']:
+            img.fp.seek(0)
+            fp = img.fp
+        else:
+            fp = BytesIO()
+            img.save(fp, format="png")
+            fp.seek(0)
+
+        data = fp.read()
+        fp.close()
+        return data
+
+
+    @property
+    def path(self):
+        return self._path.format(self._id, self.format)
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/line.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/line.py
new file mode 100644
index 00000000..43388e63
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/line.py
@@ -0,0 +1,144 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Integer,
+    MinMax,
+    NoneSet,
+    Alias,
+    Sequence
+)
+
+from openpyxl.descriptors.nested import (
+    NestedInteger,
+    NestedNoneSet,
+    EmptyTag,
+)
+from openpyxl.xml.constants import DRAWING_NS
+
+from .colors import ColorChoiceDescriptor
+from .fill import GradientFillProperties, PatternFillProperties
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+
+"""
+Line elements from drawing main schema
+"""
+
+
+class LineEndProperties(Serialisable):
+
+    tagname = "end"
+    namespace = DRAWING_NS
+
+    type = NoneSet(values=(['none', 'triangle', 'stealth', 'diamond', 'oval', 'arrow']))
+    w = NoneSet(values=(['sm', 'med', 'lg']))
+    len = NoneSet(values=(['sm', 'med', 'lg']))
+
+    def __init__(self,
+                 type=None,
+                 w=None,
+                 len=None,
+                ):
+        self.type = type
+        self.w = w
+        self.len = len
+
+
+class DashStop(Serialisable):
+
+    tagname = "ds"
+    namespace = DRAWING_NS
+
+    d = Integer()
+    length = Alias('d')
+    sp = Integer()
+    space = Alias('sp')
+
+    def __init__(self,
+                 d=0,
+                 sp=0,
+                ):
+        self.d = d
+        self.sp = sp
+
+
+class DashStopList(Serialisable):
+
+    ds = Sequence(expected_type=DashStop, allow_none=True)
+
+    def __init__(self,
+                 ds=None,
+                ):
+        self.ds = ds
+
+
+class LineProperties(Serialisable):
+
+    tagname = "ln"
+    namespace = DRAWING_NS
+
+    w = MinMax(min=0, max=20116800, allow_none=True) # EMU
+    width = Alias('w')
+    cap = NoneSet(values=(['rnd', 'sq', 'flat']))
+    cmpd = NoneSet(values=(['sng', 'dbl', 'thickThin', 'thinThick', 'tri']))
+    algn = NoneSet(values=(['ctr', 'in']))
+
+    noFill = EmptyTag()
+    solidFill = ColorChoiceDescriptor()
+    gradFill = Typed(expected_type=GradientFillProperties, allow_none=True)
+    pattFill = Typed(expected_type=PatternFillProperties, allow_none=True)
+
+    prstDash = NestedNoneSet(values=(['solid', 'dot', 'dash', 'lgDash', 'dashDot',
+                       'lgDashDot', 'lgDashDotDot', 'sysDash', 'sysDot', 'sysDashDot',
+                       'sysDashDotDot']), namespace=namespace)
+    dashStyle = Alias('prstDash')
+
+    custDash = Typed(expected_type=DashStop, allow_none=True)
+
+    round = EmptyTag()
+    bevel = EmptyTag()
+    miter = NestedInteger(allow_none=True, attribute="lim")
+
+    headEnd = Typed(expected_type=LineEndProperties, allow_none=True)
+    tailEnd = Typed(expected_type=LineEndProperties, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ('noFill', 'solidFill', 'gradFill', 'pattFill',
+                    'prstDash', 'custDash', 'round', 'bevel', 'miter', 'headEnd', 'tailEnd')
+
+    def __init__(self,
+                 w=None,
+                 cap=None,
+                 cmpd=None,
+                 algn=None,
+                 noFill=None,
+                 solidFill=None,
+                 gradFill=None,
+                 pattFill=None,
+                 prstDash=None,
+                 custDash=None,
+                 round=None,
+                 bevel=None,
+                 miter=None,
+                 headEnd=None,
+                 tailEnd=None,
+                 extLst=None,
+                ):
+        self.w = w
+        self.cap = cap
+        self.cmpd = cmpd
+        self.algn = algn
+        self.noFill = noFill
+        self.solidFill = solidFill
+        self.gradFill = gradFill
+        self.pattFill = pattFill
+        if prstDash is None:
+            prstDash = "solid"
+        self.prstDash = prstDash
+        self.custDash = custDash
+        self.round = round
+        self.bevel = bevel
+        self.miter = miter
+        self.headEnd = headEnd
+        self.tailEnd = tailEnd
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/picture.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/picture.py
new file mode 100644
index 00000000..9a83facf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/picture.py
@@ -0,0 +1,144 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.xml.constants import DRAWING_NS
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Bool,
+    String,
+    Alias,
+)
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+
+from openpyxl.chart.shapes import GraphicalProperties
+
+from .fill import BlipFillProperties
+from .properties import NonVisualDrawingProps
+from .geometry import ShapeStyle
+
+
+class PictureLocking(Serialisable):
+
+    tagname = "picLocks"
+    namespace = DRAWING_NS
+
+    # Using attribute group AG_Locking
+    noCrop = Bool(allow_none=True)
+    noGrp = Bool(allow_none=True)
+    noSelect = Bool(allow_none=True)
+    noRot = Bool(allow_none=True)
+    noChangeAspect = Bool(allow_none=True)
+    noMove = Bool(allow_none=True)
+    noResize = Bool(allow_none=True)
+    noEditPoints = Bool(allow_none=True)
+    noAdjustHandles = Bool(allow_none=True)
+    noChangeArrowheads = Bool(allow_none=True)
+    noChangeShapeType = Bool(allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ()
+
+    def __init__(self,
+                 noCrop=None,
+                 noGrp=None,
+                 noSelect=None,
+                 noRot=None,
+                 noChangeAspect=None,
+                 noMove=None,
+                 noResize=None,
+                 noEditPoints=None,
+                 noAdjustHandles=None,
+                 noChangeArrowheads=None,
+                 noChangeShapeType=None,
+                 extLst=None,
+                ):
+        self.noCrop = noCrop
+        self.noGrp = noGrp
+        self.noSelect = noSelect
+        self.noRot = noRot
+        self.noChangeAspect = noChangeAspect
+        self.noMove = noMove
+        self.noResize = noResize
+        self.noEditPoints = noEditPoints
+        self.noAdjustHandles = noAdjustHandles
+        self.noChangeArrowheads = noChangeArrowheads
+        self.noChangeShapeType = noChangeShapeType
+
+
+class NonVisualPictureProperties(Serialisable):
+
+    tagname = "cNvPicPr"
+
+    preferRelativeResize = Bool(allow_none=True)
+    picLocks = Typed(expected_type=PictureLocking, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ("picLocks",)
+
+    def __init__(self,
+                 preferRelativeResize=None,
+                 picLocks=None,
+                 extLst=None,
+                ):
+        self.preferRelativeResize = preferRelativeResize
+        self.picLocks = picLocks
+
+
+class PictureNonVisual(Serialisable):
+
+    tagname = "nvPicPr"
+
+    cNvPr = Typed(expected_type=NonVisualDrawingProps, )
+    cNvPicPr = Typed(expected_type=NonVisualPictureProperties, )
+
+    __elements__ = ("cNvPr", "cNvPicPr")
+
+    def __init__(self,
+                 cNvPr=None,
+                 cNvPicPr=None,
+                ):
+        if cNvPr is None:
+            cNvPr = NonVisualDrawingProps(id=0, name="Image 1", descr="Name of file")
+        self.cNvPr = cNvPr
+        if cNvPicPr is None:
+            cNvPicPr = NonVisualPictureProperties()
+        self.cNvPicPr = cNvPicPr
+
+
+
+
+class PictureFrame(Serialisable):
+
+    tagname = "pic"
+
+    macro = String(allow_none=True)
+    fPublished = Bool(allow_none=True)
+    nvPicPr = Typed(expected_type=PictureNonVisual, )
+    blipFill = Typed(expected_type=BlipFillProperties, )
+    spPr = Typed(expected_type=GraphicalProperties, )
+    graphicalProperties = Alias('spPr')
+    style = Typed(expected_type=ShapeStyle, allow_none=True)
+
+    __elements__ = ("nvPicPr", "blipFill", "spPr", "style")
+
+    def __init__(self,
+                 macro=None,
+                 fPublished=None,
+                 nvPicPr=None,
+                 blipFill=None,
+                 spPr=None,
+                 style=None,
+                ):
+        self.macro = macro
+        self.fPublished = fPublished
+        if nvPicPr is None:
+            nvPicPr = PictureNonVisual()
+        self.nvPicPr = nvPicPr
+        if blipFill is None:
+            blipFill = BlipFillProperties()
+        self.blipFill = blipFill
+        if spPr is None:
+            spPr = GraphicalProperties()
+        self.spPr = spPr
+        self.style = style
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/properties.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/properties.py
new file mode 100644
index 00000000..77b00728
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/properties.py
@@ -0,0 +1,174 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.xml.constants import DRAWING_NS
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Bool,
+    Integer,
+    Set,
+    String,
+    Alias,
+    NoneSet,
+)
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+
+from .geometry import GroupTransform2D, Scene3D
+from .text import Hyperlink
+
+
+class GroupShapeProperties(Serialisable):
+
+    tagname = "grpSpPr"
+
+    bwMode = NoneSet(values=(['clr', 'auto', 'gray', 'ltGray', 'invGray',
+                          'grayWhite', 'blackGray', 'blackWhite', 'black', 'white', 'hidden']))
+    xfrm = Typed(expected_type=GroupTransform2D, allow_none=True)
+    scene3d = Typed(expected_type=Scene3D, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    def __init__(self,
+                 bwMode=None,
+                 xfrm=None,
+                 scene3d=None,
+                 extLst=None,
+                ):
+        self.bwMode = bwMode
+        self.xfrm = xfrm
+        self.scene3d = scene3d
+        self.extLst = extLst
+
+
+class GroupLocking(Serialisable):
+
+    tagname = "grpSpLocks"
+    namespace = DRAWING_NS
+
+    noGrp = Bool(allow_none=True)
+    noUngrp = Bool(allow_none=True)
+    noSelect = Bool(allow_none=True)
+    noRot = Bool(allow_none=True)
+    noChangeAspect = Bool(allow_none=True)
+    noMove = Bool(allow_none=True)
+    noResize = Bool(allow_none=True)
+    noChangeArrowheads = Bool(allow_none=True)
+    noEditPoints = Bool(allow_none=True)
+    noAdjustHandles = Bool(allow_none=True)
+    noChangeArrowheads = Bool(allow_none=True)
+    noChangeShapeType = Bool(allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ()
+
+    def __init__(self,
+                 noGrp=None,
+                 noUngrp=None,
+                 noSelect=None,
+                 noRot=None,
+                 noChangeAspect=None,
+                 noChangeArrowheads=None,
+                 noMove=None,
+                 noResize=None,
+                 noEditPoints=None,
+                 noAdjustHandles=None,
+                 noChangeShapeType=None,
+                 extLst=None,
+                ):
+        self.noGrp = noGrp
+        self.noUngrp = noUngrp
+        self.noSelect = noSelect
+        self.noRot = noRot
+        self.noChangeAspect = noChangeAspect
+        self.noChangeArrowheads = noChangeArrowheads
+        self.noMove = noMove
+        self.noResize = noResize
+        self.noEditPoints = noEditPoints
+        self.noAdjustHandles = noAdjustHandles
+        self.noChangeShapeType = noChangeShapeType
+
+
+class NonVisualGroupDrawingShapeProps(Serialisable):
+
+    tagname = "cNvGrpSpPr"
+
+    grpSpLocks = Typed(expected_type=GroupLocking, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ("grpSpLocks",)
+
+    def __init__(self,
+                 grpSpLocks=None,
+                 extLst=None,
+                ):
+        self.grpSpLocks = grpSpLocks
+
+
+class NonVisualDrawingShapeProps(Serialisable):
+
+    tagname = "cNvSpPr"
+
+    spLocks = Typed(expected_type=GroupLocking, allow_none=True)
+    txBax = Bool(allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ("spLocks", "txBax")
+
+    def __init__(self,
+                 spLocks=None,
+                 txBox=None,
+                 extLst=None,
+                ):
+        self.spLocks = spLocks
+        self.txBox = txBox
+
+
+class NonVisualDrawingProps(Serialisable):
+
+    tagname = "cNvPr"
+
+    id = Integer()
+    name = String()
+    descr = String(allow_none=True)
+    hidden = Bool(allow_none=True)
+    title = String(allow_none=True)
+    hlinkClick = Typed(expected_type=Hyperlink, allow_none=True)
+    hlinkHover = Typed(expected_type=Hyperlink, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ["hlinkClick", "hlinkHover"]
+
+    def __init__(self,
+                 id=None,
+                 name=None,
+                 descr=None,
+                 hidden=None,
+                 title=None,
+                 hlinkClick=None,
+                 hlinkHover=None,
+                 extLst=None,
+                ):
+        self.id = id
+        self.name = name
+        self.descr = descr
+        self.hidden = hidden
+        self.title = title
+        self.hlinkClick = hlinkClick
+        self.hlinkHover = hlinkHover
+        self.extLst = extLst
+
+class NonVisualGroupShape(Serialisable):
+
+    tagname = "nvGrpSpPr"
+
+    cNvPr = Typed(expected_type=NonVisualDrawingProps)
+    cNvGrpSpPr = Typed(expected_type=NonVisualGroupDrawingShapeProps)
+
+    __elements__ = ("cNvPr", "cNvGrpSpPr")
+
+    def __init__(self,
+                 cNvPr=None,
+                 cNvGrpSpPr=None,
+                ):
+        self.cNvPr = cNvPr
+        self.cNvGrpSpPr = cNvGrpSpPr
+
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/relation.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/relation.py
new file mode 100644
index 00000000..01632934
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/relation.py
@@ -0,0 +1,17 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.xml.constants import CHART_NS
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors.excel import Relation
+
+
+class ChartRelation(Serialisable):
+
+    tagname = "chart"
+    namespace = CHART_NS
+
+    id = Relation()
+
+    def __init__(self, id):
+        self.id = id
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/spreadsheet_drawing.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/spreadsheet_drawing.py
new file mode 100644
index 00000000..4f378ca2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/spreadsheet_drawing.py
@@ -0,0 +1,382 @@
+# Copyright (c) 2010-2024 openpyxl
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Typed,
+    Bool,
+    NoneSet,
+    Integer,
+    Sequence,
+    Alias,
+)
+from openpyxl.descriptors.nested import (
+    NestedText,
+    NestedNoneSet,
+)
+from openpyxl.descriptors.excel import Relation
+
+from openpyxl.packaging.relationship import (
+    Relationship,
+    RelationshipList,
+)
+from openpyxl.utils import coordinate_to_tuple
+from openpyxl.utils.units import (
+    cm_to_EMU,
+    pixels_to_EMU,
+)
+from openpyxl.drawing.image import Image
+
+from openpyxl.xml.constants import SHEET_DRAWING_NS
+
+from openpyxl.chart._chart import ChartBase
+from .xdr import (
+    XDRPoint2D,
+    XDRPositiveSize2D,
+)
+from .fill import Blip
+from .connector import Shape
+from .graphic import (
+    GroupShape,
+    GraphicFrame,
+    )
+from .geometry import PresetGeometry2D
+from .picture import PictureFrame
+from .relation import ChartRelation
+
+
+class AnchorClientData(Serialisable):
+
+    fLocksWithSheet = Bool(allow_none=True)
+    fPrintsWithSheet = Bool(allow_none=True)
+
+    def __init__(self,
+                 fLocksWithSheet=None,
+                 fPrintsWithSheet=None,
+                 ):
+        self.fLocksWithSheet = fLocksWithSheet
+        self.fPrintsWithSheet = fPrintsWithSheet
+
+
+class AnchorMarker(Serialisable):
+
+    tagname = "marker"
+
+    col = NestedText(expected_type=int)
+    colOff = NestedText(expected_type=int)
+    row = NestedText(expected_type=int)
+    rowOff = NestedText(expected_type=int)
+
+    def __init__(self,
+                 col=0,
+                 colOff=0,
+                 row=0,
+                 rowOff=0,
+                 ):
+        self.col = col
+        self.colOff = colOff
+        self.row = row
+        self.rowOff = rowOff
+
+
+class _AnchorBase(Serialisable):
+
+    #one of
+    sp = Typed(expected_type=Shape, allow_none=True)
+    shape = Alias("sp")
+    grpSp = Typed(expected_type=GroupShape, allow_none=True)
+    groupShape = Alias("grpSp")
+    graphicFrame = Typed(expected_type=GraphicFrame, allow_none=True)
+    cxnSp = Typed(expected_type=Shape, allow_none=True)
+    connectionShape = Alias("cxnSp")
+    pic = Typed(expected_type=PictureFrame, allow_none=True)
+    contentPart = Relation()
+
+    clientData = Typed(expected_type=AnchorClientData)
+
+    __elements__ = ('sp', 'grpSp', 'graphicFrame',
+                    'cxnSp', 'pic', 'contentPart', 'clientData')
+
+    def __init__(self,
+                 clientData=None,
+                 sp=None,
+                 grpSp=None,
+                 graphicFrame=None,
+                 cxnSp=None,
+                 pic=None,
+                 contentPart=None
+                 ):
+        if clientData is None:
+            clientData = AnchorClientData()
+        self.clientData = clientData
+        self.sp = sp
+        self.grpSp = grpSp
+        self.graphicFrame = graphicFrame
+        self.cxnSp = cxnSp
+        self.pic = pic
+        self.contentPart = contentPart
+
+
+class AbsoluteAnchor(_AnchorBase):
+
+    tagname = "absoluteAnchor"
+
+    pos = Typed(expected_type=XDRPoint2D)
+    ext = Typed(expected_type=XDRPositiveSize2D)
+
+    sp = _AnchorBase.sp
+    grpSp = _AnchorBase.grpSp
+    graphicFrame = _AnchorBase.graphicFrame
+    cxnSp = _AnchorBase.cxnSp
+    pic = _AnchorBase.pic
+    contentPart = _AnchorBase.contentPart
+    clientData = _AnchorBase.clientData
+
+    __elements__ = ('pos', 'ext') + _AnchorBase.__elements__
+
+    def __init__(self,
+                 pos=None,
+                 ext=None,
+                 **kw
+                ):
+        if pos is None:
+            pos = XDRPoint2D(0, 0)
+        self.pos = pos
+        if ext is None:
+            ext = XDRPositiveSize2D(0, 0)
+        self.ext = ext
+        super().__init__(**kw)
+
+
+class OneCellAnchor(_AnchorBase):
+
+    tagname = "oneCellAnchor"
+
+    _from = Typed(expected_type=AnchorMarker)
+    ext = Typed(expected_type=XDRPositiveSize2D)
+
+    sp = _AnchorBase.sp
+    grpSp = _AnchorBase.grpSp
+    graphicFrame = _AnchorBase.graphicFrame
+    cxnSp = _AnchorBase.cxnSp
+    pic = _AnchorBase.pic
+    contentPart = _AnchorBase.contentPart
+    clientData = _AnchorBase.clientData
+
+    __elements__ = ('_from', 'ext') + _AnchorBase.__elements__
+
+
+    def __init__(self,
+                 _from=None,
+                 ext=None,
+                 **kw
+                ):
+        if _from is None:
+            _from = AnchorMarker()
+        self._from = _from
+        if ext is None:
+            ext = XDRPositiveSize2D(0, 0)
+        self.ext = ext
+        super().__init__(**kw)
+
+
+class TwoCellAnchor(_AnchorBase):
+
+    tagname = "twoCellAnchor"
+
+    editAs = NoneSet(values=(['twoCell', 'oneCell', 'absolute']))
+    _from = Typed(expected_type=AnchorMarker)
+    to = Typed(expected_type=AnchorMarker)
+
+    sp = _AnchorBase.sp
+    grpSp = _AnchorBase.grpSp
+    graphicFrame = _AnchorBase.graphicFrame
+    cxnSp = _AnchorBase.cxnSp
+    pic = _AnchorBase.pic
+    contentPart = _AnchorBase.contentPart
+    clientData = _AnchorBase.clientData
+
+    __elements__ = ('_from', 'to') + _AnchorBase.__elements__
+
+    def __init__(self,
+                 editAs=None,
+                 _from=None,
+                 to=None,
+                 **kw
+                 ):
+        self.editAs = editAs
+        if _from is None:
+            _from = AnchorMarker()
+        self._from = _from
+        if to is None:
+            to = AnchorMarker()
+        self.to = to
+        super().__init__(**kw)
+
+
+def _check_anchor(obj):
+    """
+    Check whether an object has an existing Anchor object
+    If not create a OneCellAnchor using the provided coordinate
+    """
+    anchor = obj.anchor
+    if not isinstance(anchor, _AnchorBase):
+        row, col = coordinate_to_tuple(anchor.upper())
+        anchor = OneCellAnchor()
+        anchor._from.row = row -1
+        anchor._from.col = col -1
+        if isinstance(obj, ChartBase):
+            anchor.ext.width = cm_to_EMU(obj.width)
+            anchor.ext.height = cm_to_EMU(obj.height)
+        elif isinstance(obj, Image):
+            anchor.ext.width = pixels_to_EMU(obj.width)
+            anchor.ext.height = pixels_to_EMU(obj.height)
+    return anchor
+
+
+class SpreadsheetDrawing(Serialisable):
+
+    tagname = "wsDr"
+    mime_type = "application/vnd.openxmlformats-officedocument.drawing+xml"
+    _rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
+    _path = PartName="/xl/drawings/drawing{0}.xml"
+    _id = None
+
+    twoCellAnchor = Sequence(expected_type=TwoCellAnchor, allow_none=True)
+    oneCellAnchor = Sequence(expected_type=OneCellAnchor, allow_none=True)
+    absoluteAnchor = Sequence(expected_type=AbsoluteAnchor, allow_none=True)
+
+    __elements__ = ("twoCellAnchor", "oneCellAnchor", "absoluteAnchor")
+
+    def __init__(self,
+                 twoCellAnchor=(),
+                 oneCellAnchor=(),
+                 absoluteAnchor=(),
+                 ):
+        self.twoCellAnchor = twoCellAnchor
+        self.oneCellAnchor = oneCellAnchor
+        self.absoluteAnchor = absoluteAnchor
+        self.charts = []
+        self.images = []
+        self._rels = []
+
+
+    def __hash__(self):
+        """
+        Just need to check for identity
+        """
+        return id(self)
+
+
+    def __bool__(self):
+        return bool(self.charts) or bool(self.images)
+
+
+
+    def _write(self):
+        """
+        create required structure and the serialise
+        """
+        anchors = []
+        for idx, obj in enumerate(self.charts + self.images, 1):
+            anchor = _check_anchor(obj)
+            if isinstance(obj, ChartBase):
+                rel = Relationship(type="chart", Target=obj.path)
+                anchor.graphicFrame = self._chart_frame(idx)
+            elif isinstance(obj, Image):
+                rel = Relationship(type="image", Target=obj.path)
+                child = anchor.pic or anchor.groupShape and anchor.groupShape.pic
+                if not child:
+                    anchor.pic = self._picture_frame(idx)
+                else:
+                    child.blipFill.blip.embed = "rId{0}".format(idx)
+
+            anchors.append(anchor)
+            self._rels.append(rel)
+
+        for a in anchors:
+            if isinstance(a, OneCellAnchor):
+                self.oneCellAnchor.append(a)
+            elif isinstance(a, TwoCellAnchor):
+                self.twoCellAnchor.append(a)
+            else:
+                self.absoluteAnchor.append(a)
+
+        tree = self.to_tree()
+        tree.set('xmlns', SHEET_DRAWING_NS)
+        return tree
+
+
+    def _chart_frame(self, idx):
+        chart_rel = ChartRelation(f"rId{idx}")
+        frame = GraphicFrame()
+        nv = frame.nvGraphicFramePr.cNvPr
+        nv.id = idx
+        nv.name = "Chart {0}".format(idx)
+        frame.graphic.graphicData.chart = chart_rel
+        return frame
+
+
+    def _picture_frame(self, idx):
+        pic = PictureFrame()
+        pic.nvPicPr.cNvPr.descr = "Picture"
+        pic.nvPicPr.cNvPr.id = idx
+        pic.nvPicPr.cNvPr.name = "Image {0}".format(idx)
+
+        pic.blipFill.blip = Blip()
+        pic.blipFill.blip.embed = "rId{0}".format(idx)
+        pic.blipFill.blip.cstate = "print"
+
+        pic.spPr.prstGeom = PresetGeometry2D(prst="rect")
+        pic.spPr.ln = None
+        return pic
+
+
+    def _write_rels(self):
+        rels = RelationshipList()
+        for r in self._rels:
+            rels.append(r)
+        return rels.to_tree()
+
+
+    @property
+    def path(self):
+        return self._path.format(self._id)
+
+
+    @property
+    def _chart_rels(self):
+        """
+        Get relationship information for each chart and bind anchor to it
+        """
+        rels = []
+        anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor
+        for anchor in anchors:
+            if anchor.graphicFrame is not None:
+                graphic = anchor.graphicFrame.graphic
+                rel = graphic.graphicData.chart
+                if rel is not None:
+                    rel.anchor = anchor
+                    rel.anchor.graphicFrame = None
+                    rels.append(rel)
+        return rels
+
+
+    @property
+    def _blip_rels(self):
+        """
+        Get relationship information for each blip and bind anchor to it
+
+        Images that are not part of the XLSX package will be ignored.
+        """
+        rels = []
+        anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor
+
+        for anchor in anchors:
+            child = anchor.pic or anchor.groupShape and anchor.groupShape.pic
+            if child and child.blipFill:
+                rel = child.blipFill.blip
+                if rel is not None and rel.embed:
+                    rel.anchor = anchor
+                    rels.append(rel)
+
+        return rels
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/text.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/text.py
new file mode 100644
index 00000000..5bdc771f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/text.py
@@ -0,0 +1,717 @@
+# Copyright (c) 2010-2024 openpyxl
+
+
+from openpyxl.descriptors.serialisable import Serialisable
+from openpyxl.descriptors import (
+    Alias,
+    Typed,
+    Set,
+    NoneSet,
+    Sequence,
+    String,
+    Bool,
+    MinMax,
+    Integer
+)
+from openpyxl.descriptors.excel import (
+    HexBinary,
+    Coordinate,
+    Relation,
+)
+from openpyxl.descriptors.nested import (
+    NestedInteger,
+    NestedText,
+    NestedValue,
+    EmptyTag
+)
+from openpyxl.xml.constants import DRAWING_NS
+
+
+from .colors import ColorChoiceDescriptor
+from .effect import (
+    EffectList,
+    EffectContainer,
+)
+from .fill import(
+    GradientFillProperties,
+    BlipFillProperties,
+    PatternFillProperties,
+    Blip
+)
+from .geometry import (
+    LineProperties,
+    Color,
+    Scene3D
+)
+
+from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
+from openpyxl.descriptors.nested import NestedBool
+
+
+class EmbeddedWAVAudioFile(Serialisable):
+
+    name = String(allow_none=True)
+
+    def __init__(self,
+                 name=None,
+                ):
+        self.name = name
+
+
+class Hyperlink(Serialisable):
+
+    tagname = "hlinkClick"
+    namespace = DRAWING_NS
+
+    invalidUrl = String(allow_none=True)
+    action = String(allow_none=True)
+    tgtFrame = String(allow_none=True)
+    tooltip = String(allow_none=True)
+    history = Bool(allow_none=True)
+    highlightClick = Bool(allow_none=True)
+    endSnd = Bool(allow_none=True)
+    snd = Typed(expected_type=EmbeddedWAVAudioFile, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+    id = Relation(allow_none=True)
+
+    __elements__ = ('snd',)
+
+    def __init__(self,
+                 invalidUrl=None,
+                 action=None,
+                 tgtFrame=None,
+                 tooltip=None,
+                 history=None,
+                 highlightClick=None,
+                 endSnd=None,
+                 snd=None,
+                 extLst=None,
+                 id=None,
+                ):
+        self.invalidUrl = invalidUrl
+        self.action = action
+        self.tgtFrame = tgtFrame
+        self.tooltip = tooltip
+        self.history = history
+        self.highlightClick = highlightClick
+        self.endSnd = endSnd
+        self.snd = snd
+        self.id = id
+
+
+class Font(Serialisable):
+
+    tagname = "latin"
+    namespace = DRAWING_NS
+
+    typeface = String()
+    panose = HexBinary(allow_none=True)
+    pitchFamily = MinMax(min=0, max=52, allow_none=True)
+    charset = Integer(allow_none=True)
+
+    def __init__(self,
+                 typeface=None,
+                 panose=None,
+                 pitchFamily=None,
+                 charset=None,
+                ):
+        self.typeface = typeface
+        self.panose = panose
+        self.pitchFamily = pitchFamily
+        self.charset = charset
+
+
+class CharacterProperties(Serialisable):
+
+    tagname = "defRPr"
+    namespace = DRAWING_NS
+
+    kumimoji = Bool(allow_none=True)
+    lang = String(allow_none=True)
+    altLang = String(allow_none=True)
+    sz = MinMax(allow_none=True, min=100, max=400000) # 100ths of a point
+    b = Bool(allow_none=True)
+    i = Bool(allow_none=True)
+    u = NoneSet(values=(['words', 'sng', 'dbl', 'heavy', 'dotted',
+                         'dottedHeavy', 'dash', 'dashHeavy', 'dashLong', 'dashLongHeavy',
+                         'dotDash', 'dotDashHeavy', 'dotDotDash', 'dotDotDashHeavy', 'wavy',
+                         'wavyHeavy', 'wavyDbl']))
+    strike = NoneSet(values=(['noStrike', 'sngStrike', 'dblStrike']))
+    kern = Integer(allow_none=True)
+    cap = NoneSet(values=(['small', 'all']))
+    spc = Integer(allow_none=True)
+    normalizeH = Bool(allow_none=True)
+    baseline = Integer(allow_none=True)
+    noProof = Bool(allow_none=True)
+    dirty = Bool(allow_none=True)
+    err = Bool(allow_none=True)
+    smtClean = Bool(allow_none=True)
+    smtId = Integer(allow_none=True)
+    bmk = String(allow_none=True)
+    ln = Typed(expected_type=LineProperties, allow_none=True)
+    highlight = Typed(expected_type=Color, allow_none=True)
+    latin = Typed(expected_type=Font, allow_none=True)
+    ea = Typed(expected_type=Font, allow_none=True)
+    cs = Typed(expected_type=Font, allow_none=True)
+    sym = Typed(expected_type=Font, allow_none=True)
+    hlinkClick = Typed(expected_type=Hyperlink, allow_none=True)
+    hlinkMouseOver = Typed(expected_type=Hyperlink, allow_none=True)
+    rtl = NestedBool(allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+    # uses element group EG_FillProperties
+    noFill = EmptyTag(namespace=DRAWING_NS)
+    solidFill = ColorChoiceDescriptor()
+    gradFill = Typed(expected_type=GradientFillProperties, allow_none=True)
+    blipFill = Typed(expected_type=BlipFillProperties, allow_none=True)
+    pattFill = Typed(expected_type=PatternFillProperties, allow_none=True)
+    grpFill = EmptyTag(namespace=DRAWING_NS)
+    # uses element group EG_EffectProperties
+    effectLst = Typed(expected_type=EffectList, allow_none=True)
+    effectDag = Typed(expected_type=EffectContainer, allow_none=True)
+    # uses element group EG_TextUnderlineLine
+    uLnTx = EmptyTag()
+    uLn = Typed(expected_type=LineProperties, allow_none=True)
+    # uses element group EG_TextUnderlineFill
+    uFillTx = EmptyTag()
+    uFill = EmptyTag()
+
+    __elements__ = ('ln', 'noFill', 'solidFill', 'gradFill', 'blipFill',
+                    'pattFill', 'grpFill', 'effectLst', 'effectDag', 'highlight','uLnTx',
+                    'uLn', 'uFillTx', 'uFill', 'latin', 'ea', 'cs', 'sym', 'hlinkClick',
+                    'hlinkMouseOver', 'rtl', )
+
+    def __init__(self,
+                 kumimoji=None,
+                 lang=None,
+                 altLang=None,
+                 sz=None,
+                 b=None,
+                 i=None,
+                 u=None,
+                 strike=None,
+                 kern=None,
+                 cap=None,
+                 spc=None,
+                 normalizeH=None,
+                 baseline=None,
+                 noProof=None,
+                 dirty=None,
+                 err=None,
+                 smtClean=None,
+                 smtId=None,
+                 bmk=None,
+                 ln=None,
+                 highlight=None,
+                 latin=None,
+                 ea=None,
+                 cs=None,
+                 sym=None,
+                 hlinkClick=None,
+                 hlinkMouseOver=None,
+                 rtl=None,
+                 extLst=None,
+                 noFill=None,
+                 solidFill=None,
+                 gradFill=None,
+                 blipFill=None,
+                 pattFill=None,
+                 grpFill=None,
+                 effectLst=None,
+                 effectDag=None,
+                 uLnTx=None,
+                 uLn=None,
+                 uFillTx=None,
+                 uFill=None,
+                ):
+        self.kumimoji = kumimoji
+        self.lang = lang
+        self.altLang = altLang
+        self.sz = sz
+        self.b = b
+        self.i = i
+        self.u = u
+        self.strike = strike
+        self.kern = kern
+        self.cap = cap
+        self.spc = spc
+        self.normalizeH = normalizeH
+        self.baseline = baseline
+        self.noProof = noProof
+        self.dirty = dirty
+        self.err = err
+        self.smtClean = smtClean
+        self.smtId = smtId
+        self.bmk = bmk
+        self.ln = ln
+        self.highlight = highlight
+        self.latin = latin
+        self.ea = ea
+        self.cs = cs
+        self.sym = sym
+        self.hlinkClick = hlinkClick
+        self.hlinkMouseOver = hlinkMouseOver
+        self.rtl = rtl
+        self.noFill = noFill
+        self.solidFill = solidFill
+        self.gradFill = gradFill
+        self.blipFill = blipFill
+        self.pattFill = pattFill
+        self.grpFill = grpFill
+        self.effectLst = effectLst
+        self.effectDag = effectDag
+        self.uLnTx = uLnTx
+        self.uLn = uLn
+        self.uFillTx = uFillTx
+        self.uFill = uFill
+
+
+class TabStop(Serialisable):
+
+    pos = Typed(expected_type=Coordinate, allow_none=True)
+    algn = Typed(expected_type=Set(values=(['l', 'ctr', 'r', 'dec'])))
+
+    def __init__(self,
+                 pos=None,
+                 algn=None,
+                ):
+        self.pos = pos
+        self.algn = algn
+
+
+class TabStopList(Serialisable):
+
+    tab = Typed(expected_type=TabStop, allow_none=True)
+
+    def __init__(self,
+                 tab=None,
+                ):
+        self.tab = tab
+
+
+class Spacing(Serialisable):
+
+    spcPct = NestedInteger(allow_none=True)
+    spcPts = NestedInteger(allow_none=True)
+
+    __elements__ = ('spcPct', 'spcPts')
+
+    def __init__(self,
+                 spcPct=None,
+                 spcPts=None,
+                 ):
+        self.spcPct = spcPct
+        self.spcPts = spcPts
+
+
+class AutonumberBullet(Serialisable):
+
+    type = Set(values=(['alphaLcParenBoth', 'alphaUcParenBoth',
+                        'alphaLcParenR', 'alphaUcParenR', 'alphaLcPeriod', 'alphaUcPeriod',
+                        'arabicParenBoth', 'arabicParenR', 'arabicPeriod', 'arabicPlain',
+                        'romanLcParenBoth', 'romanUcParenBoth', 'romanLcParenR', 'romanUcParenR',
+                        'romanLcPeriod', 'romanUcPeriod', 'circleNumDbPlain',
+                        'circleNumWdBlackPlain', 'circleNumWdWhitePlain', 'arabicDbPeriod',
+                        'arabicDbPlain', 'ea1ChsPeriod', 'ea1ChsPlain', 'ea1ChtPeriod',
+                        'ea1ChtPlain', 'ea1JpnChsDbPeriod', 'ea1JpnKorPlain', 'ea1JpnKorPeriod',
+                        'arabic1Minus', 'arabic2Minus', 'hebrew2Minus', 'thaiAlphaPeriod',
+                        'thaiAlphaParenR', 'thaiAlphaParenBoth', 'thaiNumPeriod',
+                        'thaiNumParenR', 'thaiNumParenBoth', 'hindiAlphaPeriod',
+                        'hindiNumPeriod', 'hindiNumParenR', 'hindiAlpha1Period']))
+    startAt = Integer()
+
+    def __init__(self,
+                 type=None,
+                 startAt=None,
+                ):
+        self.type = type
+        self.startAt = startAt
+
+
+class ParagraphProperties(Serialisable):
+
+    tagname = "pPr"
+    namespace = DRAWING_NS
+
+    marL = Integer(allow_none=True)
+    marR = Integer(allow_none=True)
+    lvl = Integer(allow_none=True)
+    indent = Integer(allow_none=True)
+    algn = NoneSet(values=(['l', 'ctr', 'r', 'just', 'justLow', 'dist', 'thaiDist']))
+    defTabSz = Integer(allow_none=True)
+    rtl = Bool(allow_none=True)
+    eaLnBrk = Bool(allow_none=True)
+    fontAlgn = NoneSet(values=(['auto', 't', 'ctr', 'base', 'b']))
+    latinLnBrk = Bool(allow_none=True)
+    hangingPunct = Bool(allow_none=True)
+
+    # uses element group EG_TextBulletColor
+    # uses element group EG_TextBulletSize
+    # uses element group EG_TextBulletTypeface
+    # uses element group EG_TextBullet
+    lnSpc = Typed(expected_type=Spacing, allow_none=True)
+    spcBef = Typed(expected_type=Spacing, allow_none=True)
+    spcAft = Typed(expected_type=Spacing, allow_none=True)
+    tabLst = Typed(expected_type=TabStopList, allow_none=True)
+    defRPr = Typed(expected_type=CharacterProperties, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+    buClrTx = EmptyTag()
+    buClr = Typed(expected_type=Color, allow_none=True)
+    buSzTx = EmptyTag()
+    buSzPct = NestedInteger(allow_none=True)
+    buSzPts = NestedInteger(allow_none=True)
+    buFontTx = EmptyTag()
+    buFont = Typed(expected_type=Font, allow_none=True)
+    buNone = EmptyTag()
+    buAutoNum = EmptyTag()
+    buChar = NestedValue(expected_type=str, attribute="char", allow_none=True)
+    buBlip = NestedValue(expected_type=Blip, attribute="blip", allow_none=True)
+
+    __elements__ = ('lnSpc', 'spcBef', 'spcAft', 'tabLst', 'defRPr',
+                    'buClrTx', 'buClr', 'buSzTx', 'buSzPct', 'buSzPts', 'buFontTx', 'buFont',
+                    'buNone', 'buAutoNum', 'buChar', 'buBlip')
+
+    def __init__(self,
+                 marL=None,
+                 marR=None,
+                 lvl=None,
+                 indent=None,
+                 algn=None,
+                 defTabSz=None,
+                 rtl=None,
+                 eaLnBrk=None,
+                 fontAlgn=None,
+                 latinLnBrk=None,
+                 hangingPunct=None,
+                 lnSpc=None,
+                 spcBef=None,
+                 spcAft=None,
+                 tabLst=None,
+                 defRPr=None,
+                 extLst=None,
+                 buClrTx=None,
+                 buClr=None,
+                 buSzTx=None,
+                 buSzPct=None,
+                 buSzPts=None,
+                 buFontTx=None,
+                 buFont=None,
+                 buNone=None,
+                 buAutoNum=None,
+                 buChar=None,
+                 buBlip=None,
+                 ):
+        self.marL = marL
+        self.marR = marR
+        self.lvl = lvl
+        self.indent = indent
+        self.algn = algn
+        self.defTabSz = defTabSz
+        self.rtl = rtl
+        self.eaLnBrk = eaLnBrk
+        self.fontAlgn = fontAlgn
+        self.latinLnBrk = latinLnBrk
+        self.hangingPunct = hangingPunct
+        self.lnSpc = lnSpc
+        self.spcBef = spcBef
+        self.spcAft = spcAft
+        self.tabLst = tabLst
+        self.defRPr = defRPr
+        self.buClrTx = buClrTx
+        self.buClr = buClr
+        self.buSzTx = buSzTx
+        self.buSzPct = buSzPct
+        self.buSzPts = buSzPts
+        self.buFontTx = buFontTx
+        self.buFont = buFont
+        self.buNone = buNone
+        self.buAutoNum = buAutoNum
+        self.buChar = buChar
+        self.buBlip = buBlip
+        self.defRPr = defRPr
+
+
+class ListStyle(Serialisable):
+
+    tagname = "lstStyle"
+    namespace = DRAWING_NS
+
+    defPPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl1pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl2pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl3pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl4pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl5pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl6pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl7pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl8pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    lvl9pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+
+    __elements__ = ("defPPr", "lvl1pPr", "lvl2pPr", "lvl3pPr", "lvl4pPr",
+                    "lvl5pPr", "lvl6pPr", "lvl7pPr", "lvl8pPr", "lvl9pPr")
+
+    def __init__(self,
+                 defPPr=None,
+                 lvl1pPr=None,
+                 lvl2pPr=None,
+                 lvl3pPr=None,
+                 lvl4pPr=None,
+                 lvl5pPr=None,
+                 lvl6pPr=None,
+                 lvl7pPr=None,
+                 lvl8pPr=None,
+                 lvl9pPr=None,
+                 extLst=None,
+                ):
+        self.defPPr = defPPr
+        self.lvl1pPr = lvl1pPr
+        self.lvl2pPr = lvl2pPr
+        self.lvl3pPr = lvl3pPr
+        self.lvl4pPr = lvl4pPr
+        self.lvl5pPr = lvl5pPr
+        self.lvl6pPr = lvl6pPr
+        self.lvl7pPr = lvl7pPr
+        self.lvl8pPr = lvl8pPr
+        self.lvl9pPr = lvl9pPr
+
+
+class RegularTextRun(Serialisable):
+
+    tagname = "r"
+    namespace = DRAWING_NS
+
+    rPr = Typed(expected_type=CharacterProperties, allow_none=True)
+    properties = Alias("rPr")
+    t = NestedText(expected_type=str)
+    value = Alias("t")
+
+    __elements__ = ('rPr', 't')
+
+    def __init__(self,
+                 rPr=None,
+                 t="",
+                ):
+        self.rPr = rPr
+        self.t = t
+
+
+class LineBreak(Serialisable):
+
+    tagname = "br"
+    namespace = DRAWING_NS
+
+    rPr = Typed(expected_type=CharacterProperties, allow_none=True)
+
+    __elements__ = ('rPr',)
+
+    def __init__(self,
+                 rPr=None,
+                ):
+        self.rPr = rPr
+
+
+class TextField(Serialisable):
+
+    id = String()
+    type = String(allow_none=True)
+    rPr = Typed(expected_type=CharacterProperties, allow_none=True)
+    pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    t = String(allow_none=True)
+
+    __elements__ = ('rPr', 'pPr')
+
+    def __init__(self,
+                 id=None,
+                 type=None,
+                 rPr=None,
+                 pPr=None,
+                 t=None,
+                ):
+        self.id = id
+        self.type = type
+        self.rPr = rPr
+        self.pPr = pPr
+        self.t = t
+
+
+class Paragraph(Serialisable):
+
+    tagname = "p"
+    namespace = DRAWING_NS
+
+    # uses element group EG_TextRun
+    pPr = Typed(expected_type=ParagraphProperties, allow_none=True)
+    properties = Alias("pPr")
+    endParaRPr = Typed(expected_type=CharacterProperties, allow_none=True)
+    r = Sequence(expected_type=RegularTextRun)
+    text = Alias('r')
+    br = Typed(expected_type=LineBreak, allow_none=True)
+    fld = Typed(expected_type=TextField, allow_none=True)
+
+    __elements__ = ('pPr', 'r', 'br', 'fld', 'endParaRPr')
+
+    def __init__(self,
+                 pPr=None,
+                 endParaRPr=None,
+                 r=None,
+                 br=None,
+                 fld=None,
+                 ):
+        self.pPr = pPr
+        self.endParaRPr = endParaRPr
+        if r is None:
+            r = [RegularTextRun()]
+        self.r = r
+        self.br = br
+        self.fld = fld
+
+
+class GeomGuide(Serialisable):
+
+    name = String(())
+    fmla = String(())
+
+    def __init__(self,
+                 name=None,
+                 fmla=None,
+                ):
+        self.name = name
+        self.fmla = fmla
+
+
+class GeomGuideList(Serialisable):
+
+    gd = Sequence(expected_type=GeomGuide, allow_none=True)
+
+    def __init__(self,
+                 gd=None,
+                ):
+        self.gd = gd
+
+
+class PresetTextShape(Serialisable):
+
+    prst = Typed(expected_type=Set(values=(
+        ['textNoShape', 'textPlain','textStop', 'textTriangle', 'textTriangleInverted', 'textChevron',
+         'textChevronInverted', 'textRingInside', 'textRingOutside', 'textArchUp',
+         'textArchDown', 'textCircle', 'textButton', 'textArchUpPour',
+         'textArchDownPour', 'textCirclePour', 'textButtonPour', 'textCurveUp',
+         'textCurveDown', 'textCanUp', 'textCanDown', 'textWave1', 'textWave2',
+         'textDoubleWave1', 'textWave4', 'textInflate', 'textDeflate',
+         'textInflateBottom', 'textDeflateBottom', 'textInflateTop',
+         'textDeflateTop', 'textDeflateInflate', 'textDeflateInflateDeflate',
+         'textFadeRight', 'textFadeLeft', 'textFadeUp', 'textFadeDown',
+         'textSlantUp', 'textSlantDown', 'textCascadeUp', 'textCascadeDown'
+         ]
+    )))
+    avLst = Typed(expected_type=GeomGuideList, allow_none=True)
+
+    def __init__(self,
+                 prst=None,
+                 avLst=None,
+                ):
+        self.prst = prst
+        self.avLst = avLst
+
+
+class TextNormalAutofit(Serialisable):
+
+    fontScale = Integer()
+    lnSpcReduction = Integer()
+
+    def __init__(self,
+                 fontScale=None,
+                 lnSpcReduction=None,
+                ):
+        self.fontScale = fontScale
+        self.lnSpcReduction = lnSpcReduction
+
+
+class RichTextProperties(Serialisable):
+
+    tagname = "bodyPr"
+    namespace = DRAWING_NS
+
+    rot = Integer(allow_none=True)
+    spcFirstLastPara = Bool(allow_none=True)
+    vertOverflow = NoneSet(values=(['overflow', 'ellipsis', 'clip']))
+    horzOverflow = NoneSet(values=(['overflow', 'clip']))
+    vert = NoneSet(values=(['horz', 'vert', 'vert270', 'wordArtVert',
+                            'eaVert', 'mongolianVert', 'wordArtVertRtl']))
+    wrap = NoneSet(values=(['none', 'square']))
+    lIns = Integer(allow_none=True)
+    tIns = Integer(allow_none=True)
+    rIns = Integer(allow_none=True)
+    bIns = Integer(allow_none=True)
+    numCol = Integer(allow_none=True)
+    spcCol = Integer(allow_none=True)
+    rtlCol = Bool(allow_none=True)
+    fromWordArt = Bool(allow_none=True)
+    anchor = NoneSet(values=(['t', 'ctr', 'b', 'just', 'dist']))
+    anchorCtr = Bool(allow_none=True)
+    forceAA = Bool(allow_none=True)
+    upright = Bool(allow_none=True)
+    compatLnSpc = Bool(allow_none=True)
+    prstTxWarp = Typed(expected_type=PresetTextShape, allow_none=True)
+    scene3d = Typed(expected_type=Scene3D, allow_none=True)
+    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)
+    noAutofit = EmptyTag()
+    normAutofit = EmptyTag()
+    spAutoFit = EmptyTag()
+    flatTx = NestedInteger(attribute="z", allow_none=True)
+
+    __elements__ = ('prstTxWarp', 'scene3d', 'noAutofit', 'normAutofit', 'spAutoFit')
+
+    def __init__(self,
+                 rot=None,
+                 spcFirstLastPara=None,
+                 vertOverflow=None,
+                 horzOverflow=None,
+                 vert=None,
+                 wrap=None,
+                 lIns=None,
+                 tIns=None,
+                 rIns=None,
+                 bIns=None,
+                 numCol=None,
+                 spcCol=None,
+                 rtlCol=None,
+                 fromWordArt=None,
+                 anchor=None,
+                 anchorCtr=None,
+                 forceAA=None,
+                 upright=None,
+                 compatLnSpc=None,
+                 prstTxWarp=None,
+                 scene3d=None,
+                 extLst=None,
+                 noAutofit=None,
+                 normAutofit=None,
+                 spAutoFit=None,
+                 flatTx=None,
+                ):
+        self.rot = rot
+        self.spcFirstLastPara = spcFirstLastPara
+        self.vertOverflow = vertOverflow
+        self.horzOverflow = horzOverflow
+        self.vert = vert
+        self.wrap = wrap
+        self.lIns = lIns
+        self.tIns = tIns
+        self.rIns = rIns
+        self.bIns = bIns
+        self.numCol = numCol
+        self.spcCol = spcCol
+        self.rtlCol = rtlCol
+        self.fromWordArt = fromWordArt
+        self.anchor = anchor
+        self.anchorCtr = anchorCtr
+        self.forceAA = forceAA
+        self.upright = upright
+        self.compatLnSpc = compatLnSpc
+        self.prstTxWarp = prstTxWarp
+        self.scene3d = scene3d
+        self.noAutofit = noAutofit
+        self.normAutofit = normAutofit
+        self.spAutoFit = spAutoFit
+        self.flatTx = flatTx
diff --git a/.venv/lib/python3.12/site-packages/openpyxl/drawing/xdr.py b/.venv/lib/python3.12/site-packages/openpyxl/drawing/xdr.py
new file mode 100644
index 00000000..335480ce
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/openpyxl/drawing/xdr.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2010-2024 openpyxl
+
+"""
+Spreadsheet Drawing has some copies of Drawing ML elements
+"""
+
+from .geometry import Point2D, PositiveSize2D, Transform2D
+
+
+class XDRPoint2D(Point2D):
+
+    namespace = None
+    x = Point2D.x
+    y = Point2D.y
+
+
+class XDRPositiveSize2D(PositiveSize2D):
+
+    namespace = None
+    cx = PositiveSize2D.cx
+    cy = PositiveSize2D.cy
+
+
+class XDRTransform2D(Transform2D):
+
+    namespace = None
+    rot = Transform2D.rot
+    flipH = Transform2D.flipH
+    flipV = Transform2D.flipV
+    off = Transform2D.off
+    ext = Transform2D.ext
+    chOff = Transform2D.chOff
+    chExt = Transform2D.chExt