diff options
author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
---|---|---|
committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/xlsxwriter/shape.py | |
parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
download | gn-ai-master.tar.gz |
Diffstat (limited to '.venv/lib/python3.12/site-packages/xlsxwriter/shape.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/xlsxwriter/shape.py | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/xlsxwriter/shape.py b/.venv/lib/python3.12/site-packages/xlsxwriter/shape.py new file mode 100644 index 00000000..8ad3676f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/xlsxwriter/shape.py @@ -0,0 +1,416 @@ +############################################################################### +# +# Shape - A class for to represent Excel XLSX shape objects. +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org +# +import copy +from warnings import warn + + +class Shape: + """ + A class for to represent Excel XLSX shape objects. + + + """ + + ########################################################################### + # + # Public API. + # + ########################################################################### + + def __init__(self, shape_type, name, options): + """ + Constructor. + + """ + super().__init__() + self.name = name + self.shape_type = shape_type + self.connect = 0 + self.drawing = 0 + self.edit_as = "" + self.id = 0 + self.text = "" + self.textlink = "" + self.stencil = 1 + self.element = -1 + self.start = None + self.start_index = None + self.end = None + self.end_index = None + self.adjustments = [] + self.start_side = "" + self.end_side = "" + self.flip_h = 0 + self.flip_v = 0 + self.rotation = 0 + self.text_rotation = 0 + self.textbox = False + + self.align = None + self.fill = None + self.font = None + self.format = None + self.line = None + self.url_rel_index = None + self.tip = None + + self._set_options(options) + + ########################################################################### + # + # Private API. + # + ########################################################################### + + def _set_options(self, options): + self.align = self._get_align_properties(options.get("align")) + self.fill = self._get_fill_properties(options.get("fill")) + self.font = self._get_font_properties(options.get("font")) + self.gradient = self._get_gradient_properties(options.get("gradient")) + self.line = self._get_line_properties(options.get("line")) + + self.text_rotation = options.get("text_rotation", 0) + + self.textlink = options.get("textlink", "") + if self.textlink.startswith("="): + self.textlink = self.textlink.lstrip("=") + + if options.get("border"): + self.line = self._get_line_properties(options["border"]) + + # Gradient fill overrides solid fill. + if self.gradient: + self.fill = None + + ########################################################################### + # + # Static methods for processing chart/shape style properties. + # + ########################################################################### + + @staticmethod + def _get_line_properties(line): + # Convert user line properties to the structure required internally. + + if not line: + return {"defined": False} + + # Copy the user defined properties since they will be modified. + line = copy.deepcopy(line) + + dash_types = { + "solid": "solid", + "round_dot": "sysDot", + "square_dot": "sysDash", + "dash": "dash", + "dash_dot": "dashDot", + "long_dash": "lgDash", + "long_dash_dot": "lgDashDot", + "long_dash_dot_dot": "lgDashDotDot", + "dot": "dot", + "system_dash_dot": "sysDashDot", + "system_dash_dot_dot": "sysDashDotDot", + } + + # Check the dash type. + dash_type = line.get("dash_type") + + if dash_type is not None: + if dash_type in dash_types: + line["dash_type"] = dash_types[dash_type] + else: + warn(f"Unknown dash type '{dash_type}'") + return {} + + line["defined"] = True + + return line + + @staticmethod + def _get_fill_properties(fill): + # Convert user fill properties to the structure required internally. + + if not fill: + return {"defined": False} + + # Copy the user defined properties since they will be modified. + fill = copy.deepcopy(fill) + + fill["defined"] = True + + return fill + + @staticmethod + def _get_pattern_properties(pattern): + # Convert user defined pattern to the structure required internally. + + if not pattern: + return {} + + # Copy the user defined properties since they will be modified. + pattern = copy.deepcopy(pattern) + + if not pattern.get("pattern"): + warn("Pattern must include 'pattern'") + return {} + + if not pattern.get("fg_color"): + warn("Pattern must include 'fg_color'") + return {} + + types = { + "percent_5": "pct5", + "percent_10": "pct10", + "percent_20": "pct20", + "percent_25": "pct25", + "percent_30": "pct30", + "percent_40": "pct40", + "percent_50": "pct50", + "percent_60": "pct60", + "percent_70": "pct70", + "percent_75": "pct75", + "percent_80": "pct80", + "percent_90": "pct90", + "light_downward_diagonal": "ltDnDiag", + "light_upward_diagonal": "ltUpDiag", + "dark_downward_diagonal": "dkDnDiag", + "dark_upward_diagonal": "dkUpDiag", + "wide_downward_diagonal": "wdDnDiag", + "wide_upward_diagonal": "wdUpDiag", + "light_vertical": "ltVert", + "light_horizontal": "ltHorz", + "narrow_vertical": "narVert", + "narrow_horizontal": "narHorz", + "dark_vertical": "dkVert", + "dark_horizontal": "dkHorz", + "dashed_downward_diagonal": "dashDnDiag", + "dashed_upward_diagonal": "dashUpDiag", + "dashed_horizontal": "dashHorz", + "dashed_vertical": "dashVert", + "small_confetti": "smConfetti", + "large_confetti": "lgConfetti", + "zigzag": "zigZag", + "wave": "wave", + "diagonal_brick": "diagBrick", + "horizontal_brick": "horzBrick", + "weave": "weave", + "plaid": "plaid", + "divot": "divot", + "dotted_grid": "dotGrid", + "dotted_diamond": "dotDmnd", + "shingle": "shingle", + "trellis": "trellis", + "sphere": "sphere", + "small_grid": "smGrid", + "large_grid": "lgGrid", + "small_check": "smCheck", + "large_check": "lgCheck", + "outlined_diamond": "openDmnd", + "solid_diamond": "solidDmnd", + } + + # Check for valid types. + if pattern["pattern"] not in types: + warn(f"unknown pattern type '{pattern['pattern']}'") + return {} + + pattern["pattern"] = types[pattern["pattern"]] + + # Specify a default background color. + pattern["bg_color"] = pattern.get("bg_color", "#FFFFFF") + + return pattern + + @staticmethod + def _get_gradient_properties(gradient): + # pylint: disable=too-many-return-statements + # Convert user defined gradient to the structure required internally. + + if not gradient: + return {} + + # Copy the user defined properties since they will be modified. + gradient = copy.deepcopy(gradient) + + types = { + "linear": "linear", + "radial": "circle", + "rectangular": "rect", + "path": "shape", + } + + # Check the colors array exists and is valid. + if "colors" not in gradient or not isinstance(gradient["colors"], list): + warn("Gradient must include colors list") + return {} + + # Check the colors array has the required number of entries. + if not 2 <= len(gradient["colors"]) <= 10: + warn("Gradient colors list must at least 2 values and not more than 10") + return {} + + if "positions" in gradient: + # Check the positions array has the right number of entries. + if len(gradient["positions"]) != len(gradient["colors"]): + warn("Gradient positions not equal to number of colors") + return {} + + # Check the positions are in the correct range. + for pos in gradient["positions"]: + if not 0 <= pos <= 100: + warn("Gradient position must be in the range 0 <= position <= 100") + return {} + else: + # Use the default gradient positions. + if len(gradient["colors"]) == 2: + gradient["positions"] = [0, 100] + + elif len(gradient["colors"]) == 3: + gradient["positions"] = [0, 50, 100] + + elif len(gradient["colors"]) == 4: + gradient["positions"] = [0, 33, 66, 100] + + else: + warn("Must specify gradient positions") + return {} + + angle = gradient.get("angle") + if angle: + if not 0 <= angle < 360: + warn("Gradient angle must be in the range 0 <= angle < 360") + return {} + else: + gradient["angle"] = 90 + + # Check for valid types. + gradient_type = gradient.get("type") + + if gradient_type is not None: + if gradient_type in types: + gradient["type"] = types[gradient_type] + else: + warn(f"Unknown gradient type '{gradient_type}") + return {} + else: + gradient["type"] = "linear" + + return gradient + + @staticmethod + def _get_font_properties(options): + # Convert user defined font values into private dict values. + if options is None: + options = {} + + font = { + "name": options.get("name"), + "color": options.get("color"), + "size": options.get("size", 11), + "bold": options.get("bold"), + "italic": options.get("italic"), + "underline": options.get("underline"), + "pitch_family": options.get("pitch_family"), + "charset": options.get("charset"), + "baseline": options.get("baseline", -1), + "lang": options.get("lang", "en-US"), + } + + # Convert font size units. + if font["size"]: + font["size"] = int(font["size"] * 100) + + return font + + @staticmethod + def _get_font_style_attributes(font): + # _get_font_style_attributes. + attributes = [] + + if not font: + return attributes + + if font.get("size"): + attributes.append(("sz", font["size"])) + + if font.get("bold") is not None: + attributes.append(("b", 0 + font["bold"])) + + if font.get("italic") is not None: + attributes.append(("i", 0 + font["italic"])) + + if font.get("underline") is not None: + attributes.append(("u", "sng")) + + if font.get("baseline") != -1: + attributes.append(("baseline", font["baseline"])) + + return attributes + + @staticmethod + def _get_font_latin_attributes(font): + # _get_font_latin_attributes. + attributes = [] + + if not font: + return attributes + + if font["name"] is not None: + attributes.append(("typeface", font["name"])) + + if font["pitch_family"] is not None: + attributes.append(("pitchFamily", font["pitch_family"])) + + if font["charset"] is not None: + attributes.append(("charset", font["charset"])) + + return attributes + + @staticmethod + def _get_align_properties(align): + # Convert user defined align to the structure required internally. + if not align: + return {"defined": False} + + # Copy the user defined properties since they will be modified. + align = copy.deepcopy(align) + + if "vertical" in align: + align_type = align["vertical"] + + align_types = { + "top": "top", + "middle": "middle", + "bottom": "bottom", + } + + if align_type in align_types: + align["vertical"] = align_types[align_type] + else: + warn(f"Unknown alignment type '{align_type}'") + return {"defined": False} + + if "horizontal" in align: + align_type = align["horizontal"] + + align_types = { + "left": "left", + "center": "center", + "right": "right", + } + + if align_type in align_types: + align["horizontal"] = align_types[align_type] + else: + warn(f"Unknown alignment type '{align_type}'") + return {"defined": False} + + align["defined"] = True + + return align |