From fc125db0e7fdace3978adf7c8ce052a0576a5aa4 Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Mon, 22 Nov 2021 17:12:51 +0200 Subject: [PATCH] Updated .venv --- .venv/bin/__pycache__/runxlrd.cpython-39.pyc | Bin 0 -> 11670 bytes .venv/bin/runxlrd.py | 410 +++ .../et_xmlfile-1.1.0.dist-info/AUTHORS.txt | 4 + .../et_xmlfile-1.1.0.dist-info/INSTALLER | 1 + .../et_xmlfile-1.1.0.dist-info/LICENCE.rst | 34 + .../et_xmlfile-1.1.0.dist-info/METADATA | 37 + .../et_xmlfile-1.1.0.dist-info/RECORD | 11 + .../et_xmlfile-1.1.0.dist-info/WHEEL | 5 + .../et_xmlfile-1.1.0.dist-info/top_level.txt | 1 + .../site-packages/et_xmlfile/__init__.py | 11 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 469 bytes .../__pycache__/xmlfile.cpython-39.pyc | Bin 0 -> 3238 bytes .../site-packages/et_xmlfile/xmlfile.py | 104 + .../openpyxl-3.0.9.dist-info/INSTALLER | 1 + .../openpyxl-3.0.9.dist-info/LICENCE.rst | 23 + .../openpyxl-3.0.9.dist-info/METADATA | 86 + .../openpyxl-3.0.9.dist-info/RECORD | 377 +++ .../openpyxl-3.0.9.dist-info/REQUESTED | 0 .../openpyxl-3.0.9.dist-info/WHEEL | 6 + .../openpyxl-3.0.9.dist-info/top_level.txt | 1 + .../site-packages/openpyxl/__init__.py | 18 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 613 bytes .../__pycache__/_constants.cpython-39.pyc | Bin 0 -> 480 bytes .../site-packages/openpyxl/_constants.py | 13 + .../site-packages/openpyxl/cell/__init__.py | 4 + .../cell/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 305 bytes .../cell/__pycache__/_writer.cpython-39.pyc | Bin 0 -> 2546 bytes .../cell/__pycache__/cell.cpython-39.pyc | Bin 0 -> 8387 bytes .../cell/__pycache__/read_only.cpython-39.pyc | Bin 0 -> 4400 bytes .../cell/__pycache__/text.cpython-39.pyc | Bin 0 -> 3981 bytes .../site-packages/openpyxl/cell/_writer.py | 108 + .../site-packages/openpyxl/cell/cell.py | 329 ++ .../site-packages/openpyxl/cell/read_only.py | 136 + .../site-packages/openpyxl/cell/text.py | 184 ++ .../site-packages/openpyxl/chart/_3d.py | 105 + .../site-packages/openpyxl/chart/__init__.py | 19 + .../chart/__pycache__/_3d.cpython-39.pyc | Bin 0 -> 2623 bytes .../chart/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 860 bytes .../chart/__pycache__/_chart.cpython-39.pyc | Bin 0 -> 5979 bytes .../__pycache__/area_chart.cpython-39.pyc | Bin 0 -> 2857 bytes .../chart/__pycache__/axis.cpython-39.pyc | Bin 0 -> 8695 bytes .../__pycache__/bar_chart.cpython-39.pyc | Bin 0 -> 3574 bytes .../__pycache__/bubble_chart.cpython-39.pyc | Bin 0 -> 1880 bytes .../__pycache__/chartspace.cpython-39.pyc | Bin 0 -> 4447 bytes .../__pycache__/data_source.cpython-39.pyc | Bin 0 -> 6253 bytes .../__pycache__/descriptors.cpython-39.pyc | Bin 0 -> 1221 bytes .../__pycache__/error_bar.cpython-39.pyc | Bin 0 -> 1687 bytes .../chart/__pycache__/label.cpython-39.pyc | Bin 0 -> 3006 bytes .../chart/__pycache__/layout.cpython-39.pyc | Bin 0 -> 1826 bytes .../chart/__pycache__/legend.cpython-39.pyc | Bin 0 -> 1956 bytes .../__pycache__/line_chart.cpython-39.pyc | Bin 0 -> 3214 bytes .../chart/__pycache__/marker.cpython-39.pyc | Bin 0 -> 2182 bytes .../chart/__pycache__/picture.cpython-39.pyc | Bin 0 -> 1059 bytes .../__pycache__/pie_chart.cpython-39.pyc | Bin 0 -> 4621 bytes .../chart/__pycache__/pivot.cpython-39.pyc | Bin 0 -> 1853 bytes .../chart/__pycache__/plotarea.cpython-39.pyc | Bin 0 -> 4626 bytes .../__pycache__/print_settings.cpython-39.pyc | Bin 0 -> 1730 bytes .../__pycache__/radar_chart.cpython-39.pyc | Bin 0 -> 1644 bytes .../chart/__pycache__/reader.cpython-39.pyc | Bin 0 -> 885 bytes .../__pycache__/reference.cpython-39.pyc | Bin 0 -> 3274 bytes .../__pycache__/scatter_chart.cpython-39.pyc | Bin 0 -> 1629 bytes .../chart/__pycache__/series.cpython-39.pyc | Bin 0 -> 4319 bytes .../__pycache__/series_factory.cpython-39.pyc | Bin 0 -> 1174 bytes .../chart/__pycache__/shapes.cpython-39.pyc | Bin 0 -> 2293 bytes .../__pycache__/stock_chart.cpython-39.pyc | Bin 0 -> 1568 bytes .../__pycache__/surface_chart.cpython-39.pyc | Bin 0 -> 3354 bytes .../chart/__pycache__/text.cpython-39.pyc | Bin 0 -> 2032 bytes .../chart/__pycache__/title.cpython-39.pyc | Bin 0 -> 2324 bytes .../__pycache__/trendline.cpython-39.pyc | Bin 0 -> 2352 bytes .../__pycache__/updown_bars.cpython-39.pyc | Bin 0 -> 1056 bytes .../site-packages/openpyxl/chart/_chart.py | 196 ++ .../openpyxl/chart/area_chart.py | 106 + .../site-packages/openpyxl/chart/axis.py | 401 +++ .../site-packages/openpyxl/chart/bar_chart.py | 144 + .../openpyxl/chart/bubble_chart.py | 67 + .../openpyxl/chart/chartspace.py | 195 ++ .../openpyxl/chart/data_source.py | 246 ++ .../openpyxl/chart/descriptors.py | 43 + .../site-packages/openpyxl/chart/error_bar.py | 62 + .../site-packages/openpyxl/chart/label.py | 127 + .../site-packages/openpyxl/chart/layout.py | 74 + .../site-packages/openpyxl/chart/legend.py | 75 + .../openpyxl/chart/line_chart.py | 129 + .../site-packages/openpyxl/chart/marker.py | 90 + .../site-packages/openpyxl/chart/picture.py | 35 + .../site-packages/openpyxl/chart/pie_chart.py | 177 + .../site-packages/openpyxl/chart/pivot.py | 65 + .../site-packages/openpyxl/chart/plotarea.py | 162 + .../openpyxl/chart/print_settings.py | 57 + .../openpyxl/chart/radar_chart.py | 55 + .../site-packages/openpyxl/chart/reader.py | 29 + .../site-packages/openpyxl/chart/reference.py | 124 + .../openpyxl/chart/scatter_chart.py | 53 + .../site-packages/openpyxl/chart/series.py | 197 ++ .../openpyxl/chart/series_factory.py | 41 + .../site-packages/openpyxl/chart/shapes.py | 89 + .../openpyxl/chart/stock_chart.py | 54 + .../openpyxl/chart/surface_chart.py | 119 + .../site-packages/openpyxl/chart/text.py | 78 + .../site-packages/openpyxl/chart/title.py | 76 + .../site-packages/openpyxl/chart/trendline.py | 98 + .../openpyxl/chart/updown_bars.py | 31 + .../openpyxl/chartsheet/__init__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 232 bytes .../__pycache__/chartsheet.cpython-39.pyc | Bin 0 -> 3319 bytes .../__pycache__/custom.cpython-39.pyc | Bin 0 -> 1751 bytes .../__pycache__/properties.cpython-39.pyc | Bin 0 -> 924 bytes .../__pycache__/protection.cpython-39.pyc | Bin 0 -> 1234 bytes .../__pycache__/publish.cpython-39.pyc | Bin 0 -> 1572 bytes .../__pycache__/relation.cpython-39.pyc | Bin 0 -> 2400 bytes .../__pycache__/views.cpython-39.pyc | Bin 0 -> 1465 bytes .../openpyxl/chartsheet/chartsheet.py | 109 + .../openpyxl/chartsheet/custom.py | 61 + .../openpyxl/chartsheet/properties.py | 28 + .../openpyxl/chartsheet/protection.py | 41 + .../openpyxl/chartsheet/publish.py | 58 + .../openpyxl/chartsheet/relation.py | 97 + .../openpyxl/chartsheet/views.py | 51 + .../openpyxl/comments/__init__.py | 4 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 225 bytes .../__pycache__/author.cpython-39.pyc | Bin 0 -> 726 bytes .../__pycache__/comment_sheet.cpython-39.pyc | Bin 0 -> 5247 bytes .../__pycache__/comments.cpython-39.pyc | Bin 0 -> 2104 bytes .../__pycache__/shape_writer.cpython-39.pyc | Bin 0 -> 3320 bytes .../site-packages/openpyxl/comments/author.py | 21 + .../openpyxl/comments/comment_sheet.py | 214 ++ .../openpyxl/comments/comments.py | 62 + .../openpyxl/comments/shape_writer.py | 116 + .../site-packages/openpyxl/compat/__init__.py | 54 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1536 bytes .../compat/__pycache__/abc.cpython-39.pyc | Bin 0 -> 309 bytes .../compat/__pycache__/numbers.cpython-39.pyc | Bin 0 -> 742 bytes .../compat/__pycache__/product.cpython-39.pyc | Bin 0 -> 498 bytes .../__pycache__/singleton.cpython-39.pyc | Bin 0 -> 1622 bytes .../compat/__pycache__/strings.cpython-39.pyc | Bin 0 -> 686 bytes .../site-packages/openpyxl/compat/abc.py | 8 + .../site-packages/openpyxl/compat/numbers.py | 43 + .../site-packages/openpyxl/compat/product.py | 17 + .../openpyxl/compat/singleton.py | 40 + .../site-packages/openpyxl/compat/strings.py | 25 + .../openpyxl/descriptors/__init__.py | 57 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1616 bytes .../__pycache__/base.cpython-39.pyc | Bin 0 -> 9651 bytes .../__pycache__/excel.cpython-39.pyc | Bin 0 -> 3654 bytes .../__pycache__/namespace.cpython-39.pyc | Bin 0 -> 437 bytes .../__pycache__/nested.cpython-39.pyc | Bin 0 -> 3857 bytes .../__pycache__/sequence.cpython-39.pyc | Bin 0 -> 4548 bytes .../__pycache__/serialisable.cpython-39.pyc | Bin 0 -> 5308 bytes .../__pycache__/slots.cpython-39.pyc | Bin 0 -> 1043 bytes .../openpyxl/descriptors/base.py | 268 ++ .../openpyxl/descriptors/excel.py | 112 + .../openpyxl/descriptors/namespace.py | 12 + .../openpyxl/descriptors/nested.py | 131 + .../openpyxl/descriptors/sequence.py | 127 + .../openpyxl/descriptors/serialisable.py | 240 ++ .../openpyxl/descriptors/slots.py | 18 + .../openpyxl/drawing/__init__.py | 4 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 223 bytes .../drawing/__pycache__/colors.cpython-39.pyc | Bin 0 -> 9967 bytes .../__pycache__/connector.cpython-39.pyc | Bin 0 -> 3891 bytes .../__pycache__/drawing.cpython-39.pyc | Bin 0 -> 2902 bytes .../drawing/__pycache__/effect.cpython-39.pyc | Bin 0 -> 9972 bytes .../drawing/__pycache__/fill.cpython-39.pyc | Bin 0 -> 9282 bytes .../__pycache__/geometry.cpython-39.pyc | Bin 0 -> 16621 bytes .../__pycache__/graphic.cpython-39.pyc | Bin 0 -> 4872 bytes .../drawing/__pycache__/image.cpython-39.pyc | Bin 0 -> 1643 bytes .../drawing/__pycache__/line.cpython-39.pyc | Bin 0 -> 3722 bytes .../__pycache__/picture.cpython-39.pyc | Bin 0 -> 3464 bytes .../__pycache__/properties.cpython-39.pyc | Bin 0 -> 4185 bytes .../__pycache__/relation.cpython-39.pyc | Bin 0 -> 727 bytes .../spreadsheet_drawing.cpython-39.pyc | Bin 0 -> 9236 bytes .../drawing/__pycache__/text.cpython-39.pyc | Bin 0 -> 15500 bytes .../drawing/__pycache__/xdr.cpython-39.pyc | Bin 0 -> 982 bytes .../site-packages/openpyxl/drawing/colors.py | 435 +++ .../openpyxl/drawing/connector.py | 144 + .../site-packages/openpyxl/drawing/drawing.py | 102 + .../site-packages/openpyxl/drawing/effect.py | 409 +++ .../site-packages/openpyxl/drawing/fill.py | 410 +++ .../openpyxl/drawing/geometry.py | 595 ++++ .../site-packages/openpyxl/drawing/graphic.py | 186 ++ .../site-packages/openpyxl/drawing/image.py | 63 + .../site-packages/openpyxl/drawing/line.py | 151 + .../site-packages/openpyxl/drawing/picture.py | 148 + .../openpyxl/drawing/properties.py | 174 + .../openpyxl/drawing/relation.py | 17 + .../openpyxl/drawing/spreadsheet_drawing.py | 381 +++ .../site-packages/openpyxl/drawing/text.py | 712 ++++ .../site-packages/openpyxl/drawing/xdr.py | 33 + .../openpyxl/formatting/__init__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 220 bytes .../__pycache__/formatting.cpython-39.pyc | Bin 0 -> 4029 bytes .../__pycache__/rule.cpython-39.pyc | Bin 0 -> 7236 bytes .../openpyxl/formatting/formatting.py | 118 + .../site-packages/openpyxl/formatting/rule.py | 291 ++ .../openpyxl/formula/__init__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 227 bytes .../__pycache__/tokenizer.cpython-39.pyc | Bin 0 -> 12263 bytes .../__pycache__/translate.cpython-39.pyc | Bin 0 -> 5262 bytes .../openpyxl/formula/tokenizer.py | 446 +++ .../openpyxl/formula/translate.py | 166 + .../openpyxl/packaging/__init__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 281 bytes .../packaging/__pycache__/core.cpython-39.pyc | Bin 0 -> 3079 bytes .../__pycache__/extended.cpython-39.pyc | Bin 0 -> 3131 bytes .../__pycache__/interface.cpython-39.pyc | Bin 0 -> 1615 bytes .../__pycache__/manifest.cpython-39.pyc | Bin 0 -> 6420 bytes .../__pycache__/relationship.cpython-39.pyc | Bin 0 -> 4729 bytes .../__pycache__/workbook.cpython-39.pyc | Bin 0 -> 5617 bytes .../site-packages/openpyxl/packaging/core.py | 112 + .../openpyxl/packaging/extended.py | 141 + .../openpyxl/packaging/interface.py | 56 + .../openpyxl/packaging/manifest.py | 210 ++ .../openpyxl/packaging/relationship.py | 176 + .../openpyxl/packaging/workbook.py | 204 ++ .../site-packages/openpyxl/pivot/__init__.py | 1 + .../pivot/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 182 bytes .../pivot/__pycache__/cache.cpython-39.pyc | Bin 0 -> 23788 bytes .../pivot/__pycache__/fields.cpython-39.pyc | Bin 0 -> 5758 bytes .../pivot/__pycache__/record.cpython-39.pyc | Bin 0 -> 3123 bytes .../pivot/__pycache__/table.cpython-39.pyc | Bin 0 -> 24026 bytes .../site-packages/openpyxl/pivot/cache.py | 1119 +++++++ .../site-packages/openpyxl/pivot/fields.py | 322 ++ .../site-packages/openpyxl/pivot/record.py | 111 + .../site-packages/openpyxl/pivot/table.py | 1178 +++++++ .../site-packages/openpyxl/reader/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 183 bytes .../__pycache__/drawings.cpython-39.pyc | Bin 0 -> 1950 bytes .../reader/__pycache__/excel.cpython-39.pyc | Bin 0 -> 9401 bytes .../reader/__pycache__/strings.cpython-39.pyc | Bin 0 -> 719 bytes .../__pycache__/workbook.cpython-39.pyc | Bin 0 -> 3678 bytes .../site-packages/openpyxl/reader/drawings.py | 63 + .../site-packages/openpyxl/reader/excel.py | 318 ++ .../site-packages/openpyxl/reader/strings.py | 23 + .../site-packages/openpyxl/reader/workbook.py | 126 + .../site-packages/openpyxl/styles/__init__.py | 11 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 643 bytes .../__pycache__/alignment.cpython-39.pyc | Bin 0 -> 1958 bytes .../styles/__pycache__/borders.cpython-39.pyc | Bin 0 -> 2929 bytes .../__pycache__/builtins.cpython-39.pyc | Bin 0 -> 29440 bytes .../__pycache__/cell_style.cpython-39.pyc | Bin 0 -> 5348 bytes .../styles/__pycache__/colors.cpython-39.pyc | Bin 0 -> 4939 bytes .../__pycache__/differential.cpython-39.pyc | Bin 0 -> 2711 bytes .../styles/__pycache__/fills.cpython-39.pyc | Bin 0 -> 7017 bytes .../styles/__pycache__/fonts.cpython-39.pyc | Bin 0 -> 3061 bytes .../__pycache__/named_styles.cpython-39.pyc | Bin 0 -> 7553 bytes .../styles/__pycache__/numbers.cpython-39.pyc | Bin 0 -> 6021 bytes .../__pycache__/protection.cpython-39.pyc | Bin 0 -> 740 bytes .../styles/__pycache__/proxy.cpython-39.pyc | Bin 0 -> 2282 bytes .../__pycache__/styleable.cpython-39.pyc | Bin 0 -> 4802 bytes .../__pycache__/stylesheet.cpython-39.pyc | Bin 0 -> 6823 bytes .../styles/__pycache__/table.cpython-39.pyc | Bin 0 -> 2709 bytes .../openpyxl/styles/alignment.py | 72 + .../site-packages/openpyxl/styles/borders.py | 113 + .../site-packages/openpyxl/styles/builtins.py | 1397 ++++++++ .../openpyxl/styles/cell_style.py | 202 ++ .../site-packages/openpyxl/styles/colors.py | 172 + .../openpyxl/styles/differential.py | 95 + .../site-packages/openpyxl/styles/fills.py | 224 ++ .../site-packages/openpyxl/styles/fonts.py | 113 + .../openpyxl/styles/named_styles.py | 291 ++ .../site-packages/openpyxl/styles/numbers.py | 200 ++ .../openpyxl/styles/protection.py | 17 + .../site-packages/openpyxl/styles/proxy.py | 62 + .../openpyxl/styles/styleable.py | 152 + .../openpyxl/styles/stylesheet.py | 263 ++ .../site-packages/openpyxl/styles/table.py | 94 + .../site-packages/openpyxl/utils/__init__.py | 17 + .../utils/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 524 bytes .../bound_dictionary.cpython-39.pyc | Bin 0 -> 1160 bytes .../utils/__pycache__/cell.cpython-39.pyc | Bin 0 -> 6326 bytes .../__pycache__/dataframe.cpython-39.pyc | Bin 0 -> 2867 bytes .../utils/__pycache__/datetime.cpython-39.pyc | Bin 0 -> 3802 bytes .../utils/__pycache__/escape.cpython-39.pyc | Bin 0 -> 1149 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 1720 bytes .../utils/__pycache__/formulas.cpython-39.pyc | Bin 0 -> 3177 bytes .../__pycache__/indexed_list.cpython-39.pyc | Bin 0 -> 1680 bytes .../__pycache__/inference.cpython-39.pyc | Bin 0 -> 1602 bytes .../__pycache__/protection.cpython-39.pyc | Bin 0 -> 970 bytes .../utils/__pycache__/units.cpython-39.pyc | Bin 0 -> 2575 bytes .../openpyxl/utils/bound_dictionary.py | 26 + .../site-packages/openpyxl/utils/cell.py | 227 ++ .../site-packages/openpyxl/utils/dataframe.py | 92 + .../site-packages/openpyxl/utils/datetime.py | 140 + .../site-packages/openpyxl/utils/escape.py | 43 + .../openpyxl/utils/exceptions.py | 34 + .../site-packages/openpyxl/utils/formulas.py | 9 + .../openpyxl/utils/indexed_list.py | 49 + .../site-packages/openpyxl/utils/inference.py | 60 + .../openpyxl/utils/protection.py | 22 + .../site-packages/openpyxl/utils/units.py | 108 + .../openpyxl/workbook/__init__.py | 4 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 226 bytes .../__pycache__/_writer.cpython-39.pyc | Bin 0 -> 6183 bytes .../workbook/__pycache__/child.cpython-39.pyc | Bin 0 -> 4793 bytes .../__pycache__/defined_name.cpython-39.pyc | Bin 0 -> 7397 bytes .../external_reference.cpython-39.pyc | Bin 0 -> 734 bytes .../__pycache__/function_group.cpython-39.pyc | Bin 0 -> 1186 bytes .../__pycache__/properties.cpython-39.pyc | Bin 0 -> 3311 bytes .../__pycache__/protection.cpython-39.pyc | Bin 0 -> 4288 bytes .../__pycache__/smart_tags.cpython-39.pyc | Bin 0 -> 1616 bytes .../workbook/__pycache__/views.cpython-39.pyc | Bin 0 -> 3110 bytes .../workbook/__pycache__/web.cpython-39.pyc | Bin 0 -> 2448 bytes .../__pycache__/workbook.cpython-39.pyc | Bin 0 -> 14717 bytes .../openpyxl/workbook/_writer.py | 191 ++ .../site-packages/openpyxl/workbook/child.py | 166 + .../openpyxl/workbook/defined_name.py | 266 ++ .../workbook/external_link/__init__.py | 3 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 244 bytes .../__pycache__/external.cpython-39.pyc | Bin 0 -> 5173 bytes .../workbook/external_link/external.py | 190 ++ .../openpyxl/workbook/external_reference.py | 18 + .../openpyxl/workbook/function_group.py | 36 + .../openpyxl/workbook/properties.py | 151 + .../openpyxl/workbook/protection.py | 163 + .../openpyxl/workbook/smart_tags.py | 56 + .../site-packages/openpyxl/workbook/views.py | 155 + .../site-packages/openpyxl/workbook/web.py | 98 + .../openpyxl/workbook/workbook.py | 459 +++ .../openpyxl/worksheet/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 186 bytes .../__pycache__/_read_only.cpython-39.pyc | Bin 0 -> 5019 bytes .../__pycache__/_reader.cpython-39.pyc | Bin 0 -> 13611 bytes .../__pycache__/_write_only.cpython-39.pyc | Bin 0 -> 4138 bytes .../__pycache__/_writer.cpython-39.pyc | Bin 0 -> 11759 bytes .../__pycache__/cell_range.cpython-39.pyc | Bin 0 -> 16100 bytes .../__pycache__/cell_watch.cpython-39.pyc | Bin 0 -> 1045 bytes .../__pycache__/controls.cpython-39.pyc | Bin 0 -> 2356 bytes .../__pycache__/copier.cpython-39.pyc | Bin 0 -> 2311 bytes .../__pycache__/custom.cpython-39.pyc | Bin 0 -> 1079 bytes .../__pycache__/datavalidation.cpython-39.pyc | Bin 0 -> 5817 bytes .../__pycache__/dimensions.cpython-39.pyc | Bin 0 -> 7759 bytes .../__pycache__/drawing.cpython-39.pyc | Bin 0 -> 646 bytes .../__pycache__/errors.cpython-39.pyc | Bin 0 -> 2365 bytes .../__pycache__/filters.cpython-39.pyc | Bin 0 -> 9069 bytes .../__pycache__/header_footer.cpython-39.pyc | Bin 0 -> 7527 bytes .../__pycache__/hyperlink.cpython-39.pyc | Bin 0 -> 1849 bytes .../__pycache__/merge.cpython-39.pyc | Bin 0 -> 4394 bytes .../worksheet/__pycache__/ole.cpython-39.pyc | Bin 0 -> 3035 bytes .../worksheet/__pycache__/page.cpython-39.pyc | Bin 0 -> 4378 bytes .../__pycache__/pagebreak.cpython-39.pyc | Bin 0 -> 2371 bytes .../__pycache__/picture.cpython-39.pyc | Bin 0 -> 463 bytes .../__pycache__/properties.cpython-39.pyc | Bin 0 -> 2378 bytes .../__pycache__/protection.cpython-39.pyc | Bin 0 -> 3441 bytes .../__pycache__/related.cpython-39.pyc | Bin 0 -> 816 bytes .../__pycache__/scenario.cpython-39.pyc | Bin 0 -> 2564 bytes .../__pycache__/smart_tag.cpython-39.pyc | Bin 0 -> 2032 bytes .../__pycache__/table.cpython-39.pyc | Bin 0 -> 10511 bytes .../__pycache__/views.cpython-39.pyc | Bin 0 -> 3530 bytes .../__pycache__/worksheet.cpython-39.pyc | Bin 0 -> 25879 bytes .../openpyxl/worksheet/_read_only.py | 186 ++ .../openpyxl/worksheet/_reader.py | 455 +++ .../openpyxl/worksheet/_write_only.py | 160 + .../openpyxl/worksheet/_writer.py | 390 +++ .../openpyxl/worksheet/cell_range.py | 501 +++ .../openpyxl/worksheet/cell_watch.py | 34 + .../openpyxl/worksheet/controls.py | 107 + .../openpyxl/worksheet/copier.py | 70 + .../openpyxl/worksheet/custom.py | 35 + .../openpyxl/worksheet/datavalidation.py | 203 ++ .../openpyxl/worksheet/dimensions.py | 296 ++ .../openpyxl/worksheet/drawing.py | 14 + .../openpyxl/worksheet/errors.py | 93 + .../openpyxl/worksheet/filters.py | 363 +++ .../openpyxl/worksheet/header_footer.py | 270 ++ .../openpyxl/worksheet/hyperlink.py | 61 + .../site-packages/openpyxl/worksheet/merge.py | 141 + .../site-packages/openpyxl/worksheet/ole.py | 133 + .../site-packages/openpyxl/worksheet/page.py | 174 + .../openpyxl/worksheet/pagebreak.py | 94 + .../openpyxl/worksheet/picture.py | 8 + .../openpyxl/worksheet/properties.py | 97 + .../openpyxl/worksheet/protection.py | 120 + .../openpyxl/worksheet/related.py | 17 + .../openpyxl/worksheet/scenario.py | 105 + .../openpyxl/worksheet/smart_tag.py | 78 + .../site-packages/openpyxl/worksheet/table.py | 385 +++ .../site-packages/openpyxl/worksheet/views.py | 149 + .../openpyxl/worksheet/worksheet.py | 901 ++++++ .../site-packages/openpyxl/writer/__init__.py | 1 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 183 bytes .../writer/__pycache__/excel.cpython-39.pyc | Bin 0 -> 8396 bytes .../writer/__pycache__/theme.cpython-39.pyc | Bin 0 -> 10531 bytes .../site-packages/openpyxl/writer/excel.py | 310 ++ .../site-packages/openpyxl/writer/theme.py | 291 ++ .../site-packages/openpyxl/xml/__init__.py | 42 + .../xml/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 1197 bytes .../xml/__pycache__/constants.cpython-39.pyc | Bin 0 -> 4195 bytes .../xml/__pycache__/functions.cpython-39.pyc | Bin 0 -> 1818 bytes .../site-packages/openpyxl/xml/constants.py | 122 + .../site-packages/openpyxl/xml/functions.py | 84 + .../xlrd-2.0.1.dist-info/INSTALLER | 1 + .../xlrd-2.0.1.dist-info/LICENSE | 76 + .../xlrd-2.0.1.dist-info/METADATA | 93 + .../site-packages/xlrd-2.0.1.dist-info/RECORD | 29 + .../xlrd-2.0.1.dist-info/REQUESTED | 0 .../site-packages/xlrd-2.0.1.dist-info/WHEEL | 6 + .../xlrd-2.0.1.dist-info/top_level.txt | 1 + .../python3.9/site-packages/xlrd/__init__.py | 213 ++ .../xlrd/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 6558 bytes .../xlrd/__pycache__/biffh.cpython-39.pyc | Bin 0 -> 12963 bytes .../xlrd/__pycache__/book.cpython-39.pyc | Bin 0 -> 32519 bytes .../xlrd/__pycache__/compdoc.cpython-39.pyc | Bin 0 -> 12345 bytes .../__pycache__/formatting.cpython-39.pyc | Bin 0 -> 23492 bytes .../xlrd/__pycache__/formula.cpython-39.pyc | Bin 0 -> 56656 bytes .../xlrd/__pycache__/info.cpython-39.pyc | Bin 0 -> 206 bytes .../xlrd/__pycache__/sheet.cpython-39.pyc | Bin 0 -> 49682 bytes .../__pycache__/timemachine.cpython-39.pyc | Bin 0 -> 1774 bytes .../xlrd/__pycache__/xldate.cpython-39.pyc | Bin 0 -> 7225 bytes .../lib/python3.9/site-packages/xlrd/biffh.py | 643 ++++ .../lib/python3.9/site-packages/xlrd/book.py | 1474 +++++++++ .../python3.9/site-packages/xlrd/compdoc.py | 485 +++ .../site-packages/xlrd/formatting.py | 1324 ++++++++ .../python3.9/site-packages/xlrd/formula.py | 2190 +++++++++++++ .../lib/python3.9/site-packages/xlrd/info.py | 1 + .../lib/python3.9/site-packages/xlrd/sheet.py | 2490 ++++++++++++++ .../site-packages/xlrd/timemachine.py | 53 + .../python3.9/site-packages/xlrd/xldate.py | 248 ++ .../xlwt-1.3.0.dist-info/DESCRIPTION.rst | 85 + .../xlwt-1.3.0.dist-info/INSTALLER | 1 + .../xlwt-1.3.0.dist-info/METADATA | 113 + .../site-packages/xlwt-1.3.0.dist-info/RECORD | 47 + .../xlwt-1.3.0.dist-info/REQUESTED | 0 .../site-packages/xlwt-1.3.0.dist-info/WHEEL | 6 + .../xlwt-1.3.0.dist-info/metadata.json | 1 + .../xlwt-1.3.0.dist-info/top_level.txt | 1 + .../site-packages/xlwt/BIFFRecords.py | 2457 ++++++++++++++ .../python3.9/site-packages/xlwt/Bitmap.py | 275 ++ .../lib/python3.9/site-packages/xlwt/Cell.py | 244 ++ .../python3.9/site-packages/xlwt/Column.py | 49 + .../site-packages/xlwt/CompoundDoc.py | 283 ++ .../site-packages/xlwt/ExcelFormula.py | 43 + .../site-packages/xlwt/ExcelFormulaLexer.py | 127 + .../site-packages/xlwt/ExcelFormulaParser.py | 669 ++++ .../site-packages/xlwt/ExcelMagic.py | 862 +++++ .../site-packages/xlwt/Formatting.py | 265 ++ .venv/lib/python3.9/site-packages/xlwt/Row.py | 293 ++ .../lib/python3.9/site-packages/xlwt/Style.py | 741 +++++ .../site-packages/xlwt/UnicodeUtils.py | 122 + .../lib/python3.9/site-packages/xlwt/Utils.py | 167 + .../python3.9/site-packages/xlwt/Workbook.py | 712 ++++ .../python3.9/site-packages/xlwt/Worksheet.py | 1420 ++++++++ .../python3.9/site-packages/xlwt/__init__.py | 9 + .../__pycache__/BIFFRecords.cpython-39.pyc | Bin 0 -> 101444 bytes .../xlwt/__pycache__/Bitmap.cpython-39.pyc | Bin 0 -> 7148 bytes .../xlwt/__pycache__/Cell.cpython-39.pyc | Bin 0 -> 6145 bytes .../xlwt/__pycache__/Column.cpython-39.pyc | Bin 0 -> 1954 bytes .../__pycache__/CompoundDoc.cpython-39.pyc | Bin 0 -> 4513 bytes .../__pycache__/ExcelFormula.cpython-39.pyc | Bin 0 -> 1905 bytes .../ExcelFormulaLexer.cpython-39.pyc | Bin 0 -> 3823 bytes .../ExcelFormulaParser.cpython-39.pyc | Bin 0 -> 11494 bytes .../__pycache__/ExcelMagic.cpython-39.pyc | Bin 0 -> 21432 bytes .../__pycache__/Formatting.cpython-39.pyc | Bin 0 -> 7541 bytes .../xlwt/__pycache__/Row.cpython-39.pyc | Bin 0 -> 9436 bytes .../xlwt/__pycache__/Style.cpython-39.pyc | Bin 0 -> 16125 bytes .../__pycache__/UnicodeUtils.cpython-39.pyc | Bin 0 -> 3785 bytes .../xlwt/__pycache__/Utils.cpython-39.pyc | Bin 0 -> 4180 bytes .../xlwt/__pycache__/Workbook.cpython-39.pyc | Bin 0 -> 22785 bytes .../xlwt/__pycache__/Worksheet.cpython-39.pyc | Bin 0 -> 41855 bytes .../xlwt/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 563 bytes .../xlwt/__pycache__/antlr.cpython-39.pyc | Bin 0 -> 75711 bytes .../xlwt/__pycache__/compat.cpython-39.pyc | Bin 0 -> 842 bytes .../lib/python3.9/site-packages/xlwt/antlr.py | 2864 +++++++++++++++++ .../python3.9/site-packages/xlwt/compat.py | 28 + .../site-packages/xlwt/excel-formula.g | 374 +++ 464 files changed, 51032 insertions(+) create mode 100644 .venv/bin/__pycache__/runxlrd.cpython-39.pyc create mode 100644 .venv/bin/runxlrd.py create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile/__pycache__/xmlfile.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/et_xmlfile/xmlfile.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/LICENCE.rst create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/__pycache__/_constants.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/_constants.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/_writer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/cell.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/read_only.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/text.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/_writer.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/cell.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/read_only.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/cell/text.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/_3d.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/_3d.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/area_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/axis.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/bar_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/bubble_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/chartspace.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/data_source.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/descriptors.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/error_bar.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/label.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/layout.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/legend.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/line_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/marker.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/picture.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/pie_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/pivot.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/plotarea.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/print_settings.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/radar_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/reader.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/reference.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/scatter_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/series.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/series_factory.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/shapes.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/stock_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/surface_chart.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/text.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/title.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/trendline.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/updown_bars.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/area_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/axis.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/bar_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/bubble_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/chartspace.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/data_source.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/descriptors.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/error_bar.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/label.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/layout.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/legend.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/line_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/marker.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/picture.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/pie_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/pivot.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/plotarea.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/print_settings.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/radar_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/reader.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/reference.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/scatter_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/series.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/series_factory.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/shapes.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/stock_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/surface_chart.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/text.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/title.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/trendline.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chart/updown_bars.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/chartsheet.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/custom.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/properties.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/protection.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/publish.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/relation.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/views.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/chartsheet.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/custom.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/properties.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/protection.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/publish.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/relation.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/chartsheet/views.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/author.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/comment_sheet.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/comments.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/shape_writer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/author.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/comment_sheet.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/comments.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/comments/shape_writer.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/abc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/numbers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/product.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/singleton.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/strings.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/abc.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/numbers.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/product.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/singleton.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/compat/strings.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/base.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/excel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/namespace.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/nested.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/sequence.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/serialisable.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/slots.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/base.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/excel.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/namespace.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/nested.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/sequence.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/serialisable.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/descriptors/slots.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/colors.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/connector.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/drawing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/effect.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/fill.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/geometry.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/graphic.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/image.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/line.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/picture.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/properties.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/relation.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/spreadsheet_drawing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/text.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/xdr.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/colors.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/connector.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/drawing.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/effect.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/fill.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/geometry.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/graphic.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/image.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/line.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/picture.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/properties.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/relation.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/spreadsheet_drawing.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/text.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/drawing/xdr.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formatting/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formatting/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formatting/__pycache__/formatting.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formatting/__pycache__/rule.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formatting/formatting.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formatting/rule.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formula/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formula/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formula/__pycache__/tokenizer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formula/__pycache__/translate.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formula/tokenizer.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/formula/translate.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/core.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/extended.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/interface.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/manifest.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/relationship.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/workbook.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/core.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/extended.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/interface.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/manifest.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/relationship.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/packaging/workbook.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/cache.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/fields.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/record.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/table.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/cache.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/fields.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/record.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/pivot/table.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/drawings.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/excel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/strings.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/workbook.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/drawings.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/excel.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/strings.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/reader/workbook.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/alignment.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/borders.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/builtins.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/cell_style.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/colors.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/differential.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/fills.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/fonts.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/named_styles.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/numbers.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/protection.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/proxy.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/styleable.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/stylesheet.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/table.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/alignment.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/borders.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/builtins.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/cell_style.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/colors.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/differential.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/fills.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/fonts.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/named_styles.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/numbers.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/protection.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/proxy.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/styleable.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/stylesheet.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/styles/table.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/bound_dictionary.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/cell.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/dataframe.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/datetime.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/escape.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/exceptions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/formulas.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/indexed_list.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/inference.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/protection.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/units.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/bound_dictionary.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/cell.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/dataframe.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/datetime.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/escape.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/exceptions.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/formulas.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/indexed_list.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/inference.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/protection.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/utils/units.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/_writer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/child.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/defined_name.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/external_reference.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/function_group.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/properties.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/protection.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/smart_tags.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/views.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/web.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/workbook.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/_writer.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/child.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/defined_name.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__pycache__/external.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/external.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/external_reference.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/function_group.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/properties.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/protection.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/smart_tags.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/views.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/web.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/workbook/workbook.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_read_only.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_reader.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_write_only.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_writer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/cell_range.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/cell_watch.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/controls.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/copier.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/custom.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/datavalidation.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/dimensions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/drawing.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/errors.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/filters.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/header_footer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/hyperlink.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/merge.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/ole.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/page.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/pagebreak.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/picture.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/properties.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/protection.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/related.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/scenario.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/smart_tag.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/table.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/views.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/worksheet.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/_read_only.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/_reader.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/_write_only.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/_writer.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_range.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_watch.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/controls.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/copier.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/custom.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/datavalidation.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/dimensions.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/drawing.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/errors.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/filters.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/header_footer.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/hyperlink.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/merge.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/ole.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/page.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/pagebreak.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/picture.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/properties.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/protection.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/related.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/scenario.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/smart_tag.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/table.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/views.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/worksheet/worksheet.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/writer/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/excel.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/theme.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/writer/excel.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/writer/theme.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/xml/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/constants.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/functions.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/xml/constants.py create mode 100644 .venv/lib/python3.9/site-packages/openpyxl/xml/functions.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/LICENSE create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/biffh.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/book.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/compdoc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/formatting.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/formula.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/info.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/sheet.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/timemachine.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/__pycache__/xldate.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlrd/biffh.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/book.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/compdoc.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/formatting.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/formula.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/info.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/sheet.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/timemachine.py create mode 100644 .venv/lib/python3.9/site-packages/xlrd/xldate.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json create mode 100644 .venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Bitmap.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Cell.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Column.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/CompoundDoc.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/ExcelFormula.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/ExcelFormulaLexer.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Formatting.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Row.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Style.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Utils.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Workbook.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/Worksheet.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__init__.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/BIFFRecords.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Bitmap.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Cell.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Column.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/CompoundDoc.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormula.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelMagic.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Formatting.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Row.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Style.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/UnicodeUtils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Utils.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Workbook.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/Worksheet.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/__init__.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/antlr.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/__pycache__/compat.cpython-39.pyc create mode 100644 .venv/lib/python3.9/site-packages/xlwt/antlr.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/compat.py create mode 100644 .venv/lib/python3.9/site-packages/xlwt/excel-formula.g diff --git a/.venv/bin/__pycache__/runxlrd.cpython-39.pyc b/.venv/bin/__pycache__/runxlrd.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac96fd0253506a95dc19e01625ec38c244310311 GIT binary patch literal 11670 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 I(@E|B0p$h2qW}N^ literal 0 HcmV?d00001 diff --git a/.venv/bin/runxlrd.py b/.venv/bin/runxlrd.py new file mode 100644 index 00000000..f20f2a7d --- /dev/null +++ b/.venv/bin/runxlrd.py @@ -0,0 +1,410 @@ +#!/media/HardDrive/Pyhton/School/.venv/bin/python +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This script is part of the xlrd package, which is released under a +# BSD-style licence. + +from __future__ import print_function + +cmd_doc = """ +Commands: + +2rows Print the contents of first and last row in each sheet +3rows Print the contents of first, second and last row in each sheet +bench Same as "show", but doesn't print -- for profiling +biff_count[1] Print a count of each type of BIFF record in the file +biff_dump[1] Print a dump (char and hex) of the BIFF records in the file +fonts hdr + print a dump of all font objects +hdr Mini-overview of file (no per-sheet information) +hotshot Do a hotshot profile run e.g. ... -f1 hotshot bench bigfile*.xls +labels Dump of sheet.col_label_ranges and ...row... for each sheet +name_dump Dump of each object in book.name_obj_list +names Print brief information for each NAME record +ov Overview of file +profile Like "hotshot", but uses cProfile +show Print the contents of all rows in each sheet +version[0] Print versions of xlrd and Python and exit +xfc Print "XF counts" and cell-type counts -- see code for details + +[0] means no file arg +[1] means only one file arg i.e. no glob.glob pattern +""" + +options = None +if __name__ == "__main__": + import xlrd + import sys + import time + import glob + import traceback + import gc + + from xlrd.timemachine import xrange, REPR + + + class LogHandler(object): + + def __init__(self, logfileobj): + self.logfileobj = logfileobj + self.fileheading = None + self.shown = 0 + + def setfileheading(self, fileheading): + self.fileheading = fileheading + self.shown = 0 + + def write(self, text): + if self.fileheading and not self.shown: + self.logfileobj.write(self.fileheading) + self.shown = 1 + self.logfileobj.write(text) + + null_cell = xlrd.empty_cell + + def show_row(bk, sh, rowx, colrange, printit): + if bk.ragged_rows: + colrange = range(sh.row_len(rowx)) + if not colrange: return + if printit: print() + if bk.formatting_info: + for colx, ty, val, cxfx in get_row_data(bk, sh, rowx, colrange): + if printit: + print("cell %s%d: type=%d, data: %r, xfx: %s" + % (xlrd.colname(colx), rowx+1, ty, val, cxfx)) + else: + for colx, ty, val, _unused in get_row_data(bk, sh, rowx, colrange): + if printit: + print("cell %s%d: type=%d, data: %r" % (xlrd.colname(colx), rowx+1, ty, val)) + + def get_row_data(bk, sh, rowx, colrange): + result = [] + dmode = bk.datemode + ctys = sh.row_types(rowx) + cvals = sh.row_values(rowx) + for colx in colrange: + cty = ctys[colx] + cval = cvals[colx] + if bk.formatting_info: + cxfx = str(sh.cell_xf_index(rowx, colx)) + else: + cxfx = '' + if cty == xlrd.XL_CELL_DATE: + try: + showval = xlrd.xldate_as_tuple(cval, dmode) + except xlrd.XLDateError as e: + showval = "%s:%s" % (type(e).__name__, e) + cty = xlrd.XL_CELL_ERROR + elif cty == xlrd.XL_CELL_ERROR: + showval = xlrd.error_text_from_code.get(cval, '' % cval) + else: + showval = cval + result.append((colx, cty, showval, cxfx)) + return result + + def bk_header(bk): + print() + print("BIFF version: %s; datemode: %s" + % (xlrd.biff_text_from_num[bk.biff_version], bk.datemode)) + print("codepage: %r (encoding: %s); countries: %r" + % (bk.codepage, bk.encoding, bk.countries)) + print("Last saved by: %r" % bk.user_name) + print("Number of data sheets: %d" % bk.nsheets) + print("Use mmap: %d; Formatting: %d; On demand: %d" + % (bk.use_mmap, bk.formatting_info, bk.on_demand)) + print("Ragged rows: %d" % bk.ragged_rows) + if bk.formatting_info: + print("FORMATs: %d, FONTs: %d, XFs: %d" + % (len(bk.format_list), len(bk.font_list), len(bk.xf_list))) + if not options.suppress_timing: + print("Load time: %.2f seconds (stage 1) %.2f seconds (stage 2)" + % (bk.load_time_stage_1, bk.load_time_stage_2)) + print() + + def show_fonts(bk): + print("Fonts:") + for x in xrange(len(bk.font_list)): + font = bk.font_list[x] + font.dump(header='== Index %d ==' % x, indent=4) + + def show_names(bk, dump=0): + bk_header(bk) + if bk.biff_version < 50: + print("Names not extracted in this BIFF version") + return + nlist = bk.name_obj_list + print("Name list: %d entries" % len(nlist)) + for nobj in nlist: + if dump: + nobj.dump(sys.stdout, + header="\n=== Dump of name_obj_list[%d] ===" % nobj.name_index) + else: + print("[%d]\tName:%r macro:%r scope:%d\n\tresult:%r\n" + % (nobj.name_index, nobj.name, nobj.macro, nobj.scope, nobj.result)) + + def print_labels(sh, labs, title): + if not labs:return + for rlo, rhi, clo, chi in labs: + print("%s label range %s:%s contains:" + % (title, xlrd.cellname(rlo, clo), xlrd.cellname(rhi-1, chi-1))) + for rx in xrange(rlo, rhi): + for cx in xrange(clo, chi): + print(" %s: %r" % (xlrd.cellname(rx, cx), sh.cell_value(rx, cx))) + + def show_labels(bk): + # bk_header(bk) + hdr = 0 + for shx in range(bk.nsheets): + sh = bk.sheet_by_index(shx) + clabs = sh.col_label_ranges + rlabs = sh.row_label_ranges + if clabs or rlabs: + if not hdr: + bk_header(bk) + hdr = 1 + print("sheet %d: name = %r; nrows = %d; ncols = %d" % + (shx, sh.name, sh.nrows, sh.ncols)) + print_labels(sh, clabs, 'Col') + print_labels(sh, rlabs, 'Row') + if bk.on_demand: bk.unload_sheet(shx) + + def show(bk, nshow=65535, printit=1): + bk_header(bk) + if 0: + rclist = xlrd.sheet.rc_stats.items() + rclist = sorted(rclist) + print("rc stats") + for k, v in rclist: + print("0x%04x %7d" % (k, v)) + if options.onesheet: + try: + shx = int(options.onesheet) + except ValueError: + shx = bk.sheet_by_name(options.onesheet).number + shxrange = [shx] + else: + shxrange = range(bk.nsheets) + # print("shxrange", list(shxrange)) + for shx in shxrange: + sh = bk.sheet_by_index(shx) + nrows, ncols = sh.nrows, sh.ncols + colrange = range(ncols) + anshow = min(nshow, nrows) + print("sheet %d: name = %s; nrows = %d; ncols = %d" % + (shx, REPR(sh.name), sh.nrows, sh.ncols)) + if nrows and ncols: + # Beat the bounds + for rowx in xrange(nrows): + nc = sh.row_len(rowx) + if nc: + sh.row_types(rowx)[nc-1] + sh.row_values(rowx)[nc-1] + sh.cell(rowx, nc-1) + for rowx in xrange(anshow-1): + if not printit and rowx % 10000 == 1 and rowx > 1: + print("done %d rows" % (rowx-1,)) + show_row(bk, sh, rowx, colrange, printit) + if anshow and nrows: + show_row(bk, sh, nrows-1, colrange, printit) + print() + if bk.on_demand: bk.unload_sheet(shx) + + def count_xfs(bk): + bk_header(bk) + for shx in range(bk.nsheets): + sh = bk.sheet_by_index(shx) + nrows = sh.nrows + print("sheet %d: name = %r; nrows = %d; ncols = %d" % + (shx, sh.name, sh.nrows, sh.ncols)) + # Access all xfindexes to force gathering stats + type_stats = [0, 0, 0, 0, 0, 0, 0] + for rowx in xrange(nrows): + for colx in xrange(sh.row_len(rowx)): + xfx = sh.cell_xf_index(rowx, colx) + assert xfx >= 0 + cty = sh.cell_type(rowx, colx) + type_stats[cty] += 1 + print("XF stats", sh._xf_index_stats) + print("type stats", type_stats) + print() + if bk.on_demand: bk.unload_sheet(shx) + + def main(cmd_args): + import optparse + global options + usage = "\n%prog [options] command [input-file-patterns]\n" + cmd_doc + oparser = optparse.OptionParser(usage) + oparser.add_option( + "-l", "--logfilename", + default="", + help="contains error messages") + oparser.add_option( + "-v", "--verbosity", + type="int", default=0, + help="level of information and diagnostics provided") + oparser.add_option( + "-m", "--mmap", + type="int", default=-1, + help="1: use mmap; 0: don't use mmap; -1: accept heuristic") + oparser.add_option( + "-e", "--encoding", + default="", + help="encoding override") + oparser.add_option( + "-f", "--formatting", + type="int", default=0, + help="0 (default): no fmt info\n" + "1: fmt info (all cells)\n", + ) + oparser.add_option( + "-g", "--gc", + type="int", default=0, + help="0: auto gc enabled; 1: auto gc disabled, manual collect after each file; 2: no gc") + oparser.add_option( + "-s", "--onesheet", + default="", + help="restrict output to this sheet (name or index)") + oparser.add_option( + "-u", "--unnumbered", + action="store_true", default=0, + help="omit line numbers or offsets in biff_dump") + oparser.add_option( + "-d", "--on-demand", + action="store_true", default=0, + help="load sheets on demand instead of all at once") + oparser.add_option( + "-t", "--suppress-timing", + action="store_true", default=0, + help="don't print timings (diffs are less messy)") + oparser.add_option( + "-r", "--ragged-rows", + action="store_true", default=0, + help="open_workbook(..., ragged_rows=True)") + options, args = oparser.parse_args(cmd_args) + if len(args) == 1 and args[0] in ("version", ): + pass + elif len(args) < 2: + oparser.error("Expected at least 2 args, found %d" % len(args)) + cmd = args[0] + xlrd_version = getattr(xlrd, "__VERSION__", "unknown; before 0.5") + if cmd == 'biff_dump': + xlrd.dump(args[1], unnumbered=options.unnumbered) + sys.exit(0) + if cmd == 'biff_count': + xlrd.count_records(args[1]) + sys.exit(0) + if cmd == 'version': + print("xlrd: %s, from %s" % (xlrd_version, xlrd.__file__)) + print("Python:", sys.version) + sys.exit(0) + if options.logfilename: + logfile = LogHandler(open(options.logfilename, 'w')) + else: + logfile = sys.stdout + mmap_opt = options.mmap + mmap_arg = xlrd.USE_MMAP + if mmap_opt in (1, 0): + mmap_arg = mmap_opt + elif mmap_opt != -1: + print('Unexpected value (%r) for mmap option -- assuming default' % mmap_opt) + fmt_opt = options.formatting | (cmd in ('xfc', )) + gc_mode = options.gc + if gc_mode: + gc.disable() + for pattern in args[1:]: + for fname in glob.glob(pattern): + print("\n=== File: %s ===" % fname) + if logfile != sys.stdout: + logfile.setfileheading("\n=== File: %s ===\n" % fname) + if gc_mode == 1: + n_unreachable = gc.collect() + if n_unreachable: + print("GC before open:", n_unreachable, "unreachable objects") + try: + t0 = time.time() + bk = xlrd.open_workbook( + fname, + verbosity=options.verbosity, logfile=logfile, + use_mmap=mmap_arg, + encoding_override=options.encoding, + formatting_info=fmt_opt, + on_demand=options.on_demand, + ragged_rows=options.ragged_rows, + ) + t1 = time.time() + if not options.suppress_timing: + print("Open took %.2f seconds" % (t1-t0,)) + except xlrd.XLRDError as e: + print("*** Open failed: %s: %s" % (type(e).__name__, e)) + continue + except KeyboardInterrupt: + print("*** KeyboardInterrupt ***") + traceback.print_exc(file=sys.stdout) + sys.exit(1) + except BaseException as e: + print("*** Open failed: %s: %s" % (type(e).__name__, e)) + traceback.print_exc(file=sys.stdout) + continue + t0 = time.time() + if cmd == 'hdr': + bk_header(bk) + elif cmd == 'ov': # OverView + show(bk, 0) + elif cmd == 'show': # all rows + show(bk) + elif cmd == '2rows': # first row and last row + show(bk, 2) + elif cmd == '3rows': # first row, 2nd row and last row + show(bk, 3) + elif cmd == 'bench': + show(bk, printit=0) + elif cmd == 'fonts': + bk_header(bk) + show_fonts(bk) + elif cmd == 'names': # named reference list + show_names(bk) + elif cmd == 'name_dump': # named reference list + show_names(bk, dump=1) + elif cmd == 'labels': + show_labels(bk) + elif cmd == 'xfc': + count_xfs(bk) + else: + print("*** Unknown command <%s>" % cmd) + sys.exit(1) + del bk + if gc_mode == 1: + n_unreachable = gc.collect() + if n_unreachable: + print("GC post cmd:", fname, "->", n_unreachable, "unreachable objects") + if not options.suppress_timing: + t1 = time.time() + print("\ncommand took %.2f seconds\n" % (t1-t0,)) + + return None + + av = sys.argv[1:] + if not av: + main(av) + firstarg = av[0].lower() + if firstarg == "hotshot": + import hotshot + import hotshot.stats + av = av[1:] + prof_log_name = "XXXX.prof" + prof = hotshot.Profile(prof_log_name) + # benchtime, result = prof.runcall(main, *av) + result = prof.runcall(main, *(av, )) + print("result", repr(result)) + prof.close() + stats = hotshot.stats.load(prof_log_name) + stats.strip_dirs() + stats.sort_stats('time', 'calls') + stats.print_stats(20) + elif firstarg == "profile": + import cProfile + av = av[1:] + cProfile.run('main(av)', 'YYYY.prof') + import pstats + p = pstats.Stats('YYYY.prof') + p.strip_dirs().sort_stats('cumulative').print_stats(30) + else: + main(av) diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt new file mode 100644 index 00000000..f75816c8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt @@ -0,0 +1,4 @@ +The authors in alphabetical order + +* Charlie Clark +* Elias Rabel \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst new file mode 100644 index 00000000..d7cb633e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst @@ -0,0 +1,34 @@ +This software is under the MIT Licence +====================================== + +Copyright (c) 2010 openpyxl + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Odict implementation in openpyxl/writer/odict.py uses the following licence: + +Copyright (c) 2001-2011 Python Software Foundation + 2011 Raymond Hettinger +License: PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 + See http://www.opensource.org/licenses/Python-2.0 for full terms +Note: backport changes by Raymond were originally distributed under MIT + license, but since the original license for Python is more + restrictive than MIT, code cannot be released under its terms and + still adheres to the limitations of Python license. diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/METADATA new file mode 100644 index 00000000..dbbcb92a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/METADATA @@ -0,0 +1,37 @@ +Metadata-Version: 2.1 +Name: et-xmlfile +Version: 1.1.0 +Summary: An implementation of lxml.xmlfile for the standard library +Home-page: https://foss.heptapod.net/openpyxl/et_xmlfile +Author: See ATUHORS.txt +Author-email: charlie.clark@clark-consulting.eu +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.6 + +et_xmfile +========= + +et_xmlfile is a low memory library for creating large XML files. + +It is based upon the `xmlfile module from lxml `_ with the aim of allowing code to be developed that will work with both libraries. It was developed initially for the openpyxl project but is now a standalone module. + +The code was written by Elias Rabel as part of the `Python Düsseldorf `_ openpyxl sprint in September 2014. + + +Note on performance +------------------- + +The code was not developed with performance in mind but turned out to be faster than the existing SAX-based implementation but is significantly slower than lxml's xmlfile. There is one area where an optimisation for lxml will negatively affect the performance of et_xmfile and that is when using the `.element()` method on an xmlfile context manager. It is, therefore, recommended not to use this, though the method is provided for code compatibility. + + diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/RECORD new file mode 100644 index 00000000..acfd0471 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/RECORD @@ -0,0 +1,11 @@ +et_xmlfile-1.1.0.dist-info/AUTHORS.txt,sha256=Y6mQLe0ywXMVP7WVFrZgEW3CqhIv-plM1CaOtdtBuXs,64 +et_xmlfile-1.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +et_xmlfile-1.1.0.dist-info/LICENCE.rst,sha256=r-YrNgzcqB-43m7kt2ENodhsORd3qAx6y20RRVxTxCk,1694 +et_xmlfile-1.1.0.dist-info/METADATA,sha256=B5hV5UW4GqmWGgwAW44C2ofZZEmV0MRnTEU98kzcUGw,1775 +et_xmlfile-1.1.0.dist-info/RECORD,, +et_xmlfile-1.1.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +et_xmlfile-1.1.0.dist-info/top_level.txt,sha256=34-74d5NNARgTsPxCMta5o28XpBNmSN0iCZhtmx2Fk8,11 +et_xmlfile/__init__.py,sha256=-oTKwE6upIG2gOmnOc4KLgV-pKbBwy-zhQfizbmCruQ,269 +et_xmlfile/__pycache__/__init__.cpython-39.pyc,, +et_xmlfile/__pycache__/xmlfile.cpython-39.pyc,, +et_xmlfile/xmlfile.py,sha256=_h20RRb3ptDZ6xXoxMU_Wrx8rG6UZsg0TS7HEOrotzg,3204 diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL new file mode 100644 index 00000000..385faab0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt new file mode 100644 index 00000000..f573c275 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +et_xmlfile diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile/__init__.py b/.venv/lib/python3.9/site-packages/et_xmlfile/__init__.py new file mode 100644 index 00000000..11483588 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile/__init__.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +from .xmlfile import xmlfile + +# constants +__version__ = '1.1.0' + +__author__ = 'See ATUHORS.txt' +__license__ = 'MIT' +__author_email__ = 'charlie.clark@clark-consulting.eu' +__url__ = 'https://foss.heptapod.net/openpyxl/et_xmlfile' diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/et_xmlfile/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c637dc23d2f218fc2d9f1e0ff65358a6564e89f5 GIT binary patch literal 469 zcmYjNyH3L}6m`;+meLA70mcrsi!xS1P?Jk=;EgKJ1)J9wX8MI=)x2okrNZ51_@gbgW zc5{IJi-*VA?R}2@%D0{CtB1`&SuraG;8HUCa{hypQY+U;FVr)JW;3b0H|~Uxg?0{C zU_3LLV+EdQ1Inydl7OebayR3CJD_yY_{IXIHonqk0ool(mtdXHiqhD^8eeG}cqm9w zf^wn1-TNy*&4dh@9;J;9scck9Y)Me?%JS`xejwIou^ ziCG63b%c+J6CNgpl`rfW+`lUmN`(@hQf$_3|FqV;k#H8j(M7FcI_zX=mPG#s7Ov`>+a6~!1PBCJ)Ley;u#o^ERtQDPW>_R-MLPk6L29+T$}^p`yPfLt zTr5XgG4sqH;AJB1OP=`!{Ed3uC;q~|?0%=Z?YU$bYNn2>s_S;n`ObGLxpu9?Pv3~E8CPXK*D}tgvr=gcZKh?R<)NM? zMY1bZ&oQmLxtz*En^2dPR#~x|((?0#qjMXzf?iCr5f^Oa2#%v1bN%bXX})t*=;ZLO zQl%R9TocA|kxXSAn@${0OEJ%>-Hqc%^CYjoJY|Z^WyOEK9ZaRjlEL2-CH|(eeL1*y zw5Q8ru#@hUWj^Td%VK|!XXC-_Nbi-!NBz$RRi@`+|)P9I2rHbPJJ1;6HQ4VeT zov1F~RI-X1Z{1f?M!Jl~^DGw;W<^;MP35#yNBxx4T@`9my@Oi42C>VQEC%Qx`I1Az zlaQ`p)D&oi5`tT7Q+8Wx7qEStgbg&1Z@fGffa=9<_^(I+`fr;*SR$##{7{fmSEG>nEn9dTNjk7${ zM^RbiM^SJQd z!m{dh2-XgG$US}&GUS^)a67aX^%j4zlmW2R%pAd&)qLD&&>t#dW_1&y*D;+8z(T+lsWdL; zTNhc*Gj1*m^%Bi}nFLv}cIB(I)M@;ZsNKtU_pJv%!K{i-VQvT34V+EqhItp`dfi3u{v#cW#Z1j{PnJ6DK)O8Z?KwyACq#_98g7+@n zSdm6wqgIwKfQ?1(irZa8XiDZ>6>3wpkf6X~nT9(>PYd?}{93on*ac zy`p+F)+cdhG~1&5peRw3G8GAgwi5;p@4gzo6ndd+zoz(bq0k|>QLFbME~IXxVLY4o z$g7`ABfowoA$_}G=MB?pUI~kj@6_+16$^u26CI^V5ve*!*C|+PS9ERCb(f%#*t1F39UA9ge81RK;dnloNF{{{WdU|aMlrsfcFwletmKU!P%E-DYZwxa-dZWMO0ds> zfBkBpl~4)Md5kxo4FLd;{8h*0d(^Uyw89=sz9xUIBH+2R=A+y@7IL`PauAZ}9clelyPa>N9+Ap#_Yb1FALcDM1~X8nUJ(`gbu-mx}3%!{x|zed*G`n^T3x;niQ yrcC?pVJc@jD~p=OgL3>prn)vgopVm-wdqaxdHv-6g80L_y&H6$EqBY?`s#m?1LV&D literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/et_xmlfile/xmlfile.py b/.venv/lib/python3.9/site-packages/et_xmlfile/xmlfile.py new file mode 100644 index 00000000..09ec5555 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/et_xmlfile/xmlfile.py @@ -0,0 +1,104 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + +"""Implements the lxml.etree.xmlfile API using the standard library xml.etree""" + + +from contextlib import contextmanager + +from xml.etree.ElementTree import Element, tostring + + +class LxmlSyntaxError(Exception): + pass + + +class _FakeIncrementalFileWriter(object): + """Replacement for _IncrementalFileWriter of lxml. + Uses ElementTree to build xml in memory.""" + def __init__(self, output_file): + self._element_stack = [] + self._top_element = None + self._file = output_file + self._have_root = False + + @contextmanager + def element(self, tag, attrib=None, nsmap=None, **_extra): + """Create a new xml element using a context manager. + The elements are written when the top level context is left. + + This is for code compatibility only as it is quite slow. + """ + + # __enter__ part + self._have_root = True + if attrib is None: + attrib = {} + self._top_element = Element(tag, attrib=attrib, **_extra) + self._top_element.text = '' + self._top_element.tail = '' + self._element_stack.append(self._top_element) + yield + + # __exit__ part + el = self._element_stack.pop() + if self._element_stack: + parent = self._element_stack[-1] + parent.append(self._top_element) + self._top_element = parent + else: + self._write_element(el) + self._top_element = None + + def write(self, arg): + """Write a string or subelement.""" + + if isinstance(arg, str): + # it is not allowed to write a string outside of an element + if self._top_element is None: + raise LxmlSyntaxError() + + if len(self._top_element) == 0: + # element has no children: add string to text + self._top_element.text += arg + else: + # element has children: add string to tail of last child + self._top_element[-1].tail += arg + + else: + if self._top_element is not None: + self._top_element.append(arg) + elif not self._have_root: + self._write_element(arg) + else: + raise LxmlSyntaxError() + + def _write_element(self, element): + xml = tostring(element) + self._file.write(xml) + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + # without root the xml document is incomplete + if not self._have_root: + raise LxmlSyntaxError() + + +class xmlfile(object): + """Context manager that can replace lxml.etree.xmlfile.""" + def __init__(self, output_file, buffered=False, encoding=None, close=False): + if isinstance(output_file, str): + self._file = open(output_file, 'wb') + self._close = True + else: + self._file = output_file + self._close = close + + def __enter__(self): + return _FakeIncrementalFileWriter(self._file) + + def __exit__(self, type, value, traceback): + if self._close == True: + self._file.close() diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/LICENCE.rst b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/LICENCE.rst new file mode 100644 index 00000000..82213c59 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/LICENCE.rst @@ -0,0 +1,23 @@ +This software is under the MIT Licence +====================================== + +Copyright (c) 2010 openpyxl + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/METADATA b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/METADATA new file mode 100644 index 00000000..5e1e3d47 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/METADATA @@ -0,0 +1,86 @@ +Metadata-Version: 2.1 +Name: openpyxl +Version: 3.0.9 +Summary: A Python library to read/write Excel 2010 xlsx/xlsm files +Home-page: https://openpyxl.readthedocs.io +Author: See AUTHORS +Author-email: charlie.clark@clark-consulting.eu +License: MIT +Project-URL: Documentation, https://openpyxl.readthedocs.io/en/stable/ +Project-URL: Source, https://foss.heptapod.net/openpyxl/openpyxl +Project-URL: Tracker, https://foss.heptapod.net/openpyxl/openpyxl/-/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.6 +Requires-Dist: et-xmlfile + +.. image:: https://coveralls.io/repos/bitbucket/openpyxl/openpyxl/badge.svg?branch=default + :target: https://coveralls.io/bitbucket/openpyxl/openpyxl?branch=default + :alt: coverage status + +Introduction +------------ + +openpyxl is a Python library to read/write Excel 2010 xlsx/xlsm/xltx/xltm files. + +It was born from lack of existing library to read/write natively from Python +the Office Open XML format. + +All kudos to the PHPExcel team as openpyxl was initially based on PHPExcel. + + +Security +-------- + +By default openpyxl does not guard against quadratic blowup or billion laughs +xml attacks. To guard against these attacks install defusedxml. + +Mailing List +------------ + +The user list can be found on http://groups.google.com/group/openpyxl-users + + +Sample code:: + + from openpyxl import Workbook + wb = Workbook() + + # grab the active worksheet + ws = wb.active + + # Data can be assigned directly to cells + ws['A1'] = 42 + + # Rows can also be appended + ws.append([1, 2, 3]) + + # Python types will automatically be converted + import datetime + ws['A2'] = datetime.datetime.now() + + # Save the file + wb.save("sample.xlsx") + + +Documentation +------------- + +The documentation is at: https://openpyxl.readthedocs.io + +* installation methods +* code examples +* instructions for contributing + +Release notes: https://openpyxl.readthedocs.io/en/stable/changes.html + + diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/RECORD b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/RECORD new file mode 100644 index 00000000..a03b3077 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/RECORD @@ -0,0 +1,377 @@ +openpyxl-3.0.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +openpyxl-3.0.9.dist-info/LICENCE.rst,sha256=DIS7QvXTZ-Xr-fwt3jWxYUHfXuD9wYklCFi8bFVg9p4,1131 +openpyxl-3.0.9.dist-info/METADATA,sha256=nyOB7PVYprTd7J8RFY9j81D4RTbwAKyaee9puN1aoLc,2402 +openpyxl-3.0.9.dist-info/RECORD,, +openpyxl-3.0.9.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openpyxl-3.0.9.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +openpyxl-3.0.9.dist-info/top_level.txt,sha256=mKJO5QFAsUEDtJ_c97F-IbmVtHYEDymqD7d5X0ULkVs,9 +openpyxl/__init__.py,sha256=LwUYr610sjX-FNO8AT02VSLu9_Yqfm2VSth0jxY0guY,589 +openpyxl/__pycache__/__init__.cpython-39.pyc,, +openpyxl/__pycache__/_constants.cpython-39.pyc,, +openpyxl/_constants.py,sha256=DkkixDltIGNgNXc3iMlgfAgipwp1qdCuX_AugMD2ELM,306 +openpyxl/cell/__init__.py,sha256=fAUl7XybyQyh23ueFBrqscifJ7cEYFCRrisYOwIuacU,122 +openpyxl/cell/__pycache__/__init__.cpython-39.pyc,, +openpyxl/cell/__pycache__/_writer.cpython-39.pyc,, +openpyxl/cell/__pycache__/cell.cpython-39.pyc,, +openpyxl/cell/__pycache__/read_only.cpython-39.pyc,, +openpyxl/cell/__pycache__/text.cpython-39.pyc,, +openpyxl/cell/_writer.py,sha256=wCS-bH6If6BVYjgNs3K75TEq44nREOWGbmtducFBcHE,3186 +openpyxl/cell/cell.py,sha256=5LwtMVPSpKf3GzlgK0Wewn-_9e7lljlxlvogn8GSTts,8670 +openpyxl/cell/read_only.py,sha256=LLrQscGUa7PVBhH_YEmrSLbr3P3Ipz4euYs_1sQqMLM,3113 +openpyxl/cell/text.py,sha256=KaOdI6yy3sPD9Y_HEDgATmGobOnWCgyq8wrPXosJl_A,4367 +openpyxl/chart/_3d.py,sha256=HvOtimbdR_-v_INDkUmrmzvHPndBTVToWdt-pe3rJHM,3104 +openpyxl/chart/__init__.py,sha256=av7vi6nQcBqnGi58WkSIvTFNpvsmAfwu559ihLqwqoo,564 +openpyxl/chart/__pycache__/_3d.cpython-39.pyc,, +openpyxl/chart/__pycache__/__init__.cpython-39.pyc,, +openpyxl/chart/__pycache__/_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/area_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/axis.cpython-39.pyc,, +openpyxl/chart/__pycache__/bar_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/bubble_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/chartspace.cpython-39.pyc,, +openpyxl/chart/__pycache__/data_source.cpython-39.pyc,, +openpyxl/chart/__pycache__/descriptors.cpython-39.pyc,, +openpyxl/chart/__pycache__/error_bar.cpython-39.pyc,, +openpyxl/chart/__pycache__/label.cpython-39.pyc,, +openpyxl/chart/__pycache__/layout.cpython-39.pyc,, +openpyxl/chart/__pycache__/legend.cpython-39.pyc,, +openpyxl/chart/__pycache__/line_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/marker.cpython-39.pyc,, +openpyxl/chart/__pycache__/picture.cpython-39.pyc,, +openpyxl/chart/__pycache__/pie_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/pivot.cpython-39.pyc,, +openpyxl/chart/__pycache__/plotarea.cpython-39.pyc,, +openpyxl/chart/__pycache__/print_settings.cpython-39.pyc,, +openpyxl/chart/__pycache__/radar_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/reader.cpython-39.pyc,, +openpyxl/chart/__pycache__/reference.cpython-39.pyc,, +openpyxl/chart/__pycache__/scatter_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/series.cpython-39.pyc,, +openpyxl/chart/__pycache__/series_factory.cpython-39.pyc,, +openpyxl/chart/__pycache__/shapes.cpython-39.pyc,, +openpyxl/chart/__pycache__/stock_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/surface_chart.cpython-39.pyc,, +openpyxl/chart/__pycache__/text.cpython-39.pyc,, +openpyxl/chart/__pycache__/title.cpython-39.pyc,, +openpyxl/chart/__pycache__/trendline.cpython-39.pyc,, +openpyxl/chart/__pycache__/updown_bars.cpython-39.pyc,, +openpyxl/chart/_chart.py,sha256=zWv9EVLTCb1PnQGGigmAIYce3fLi0mlC30j9lapcaWQ,5583 +openpyxl/chart/area_chart.py,sha256=e9dGvxHLSCCKINixj4fSATVd3Ua_h-F1URARDgPUCr0,2925 +openpyxl/chart/axis.py,sha256=27sfB6ocrOL-ea0fK_K5eSlKxPSIl79CR6GfbZ0Yh-8,12657 +openpyxl/chart/bar_chart.py,sha256=aINFtJNQKz3GS8xRRTtOvs0L7YDYfaNgm8X86v_DU7Q,4175 +openpyxl/chart/bubble_chart.py,sha256=oQFIoXmFbck665Xrha8tTJkgaWYzXpnSaIWRaN20_FU,2021 +openpyxl/chart/chartspace.py,sha256=uWSLy5bHdEYFkknUauCNi4OZ4VcbJVWdixH-rVuRAxg,6122 +openpyxl/chart/data_source.py,sha256=ZGFU04_tol5aj99Cy_z90A0TOySo-zsdgLXXLm98ZnU,5809 +openpyxl/chart/descriptors.py,sha256=U_9bYcapV0h-JB09bEPxjGqy2UIiYFfK6wuxmIKQ05U,764 +openpyxl/chart/error_bar.py,sha256=z9wX9DL7DIiASfKZHkxylQgtRsZGUbXX1nduhEFbGM0,1832 +openpyxl/chart/label.py,sha256=vBKKrK3FNASphKfbj8thZgYbqB3q5MOcBydKn9VmA10,4167 +openpyxl/chart/layout.py,sha256=Cdn9GpkzBqlNeHo6Bo3t1L0zT3JD0zdW6XyQR0G2L-Y,2040 +openpyxl/chart/legend.py,sha256=guKi88vLYDmUV9mvz80kGTkVVisHT06y-nSVDqnrcgY,2040 +openpyxl/chart/line_chart.py,sha256=CiVznoifZD8fBclgJXqXl5GgoC3B-GXF8FyQA48GMQI,3986 +openpyxl/chart/marker.py,sha256=MB7NmYfAPizDueNn-_0n0OoQTU6tbBMulyxjHrKNpKw,2600 +openpyxl/chart/picture.py,sha256=xNjAGqYIGUHUAboSVnmAKD9yDNXiXG4nK7mxo8V935A,1156 +openpyxl/chart/pie_chart.py,sha256=gyX0Wx4qJs4J7UFfyG1aPd1Dmp-lGyssK--TwsZNuZ8,4868 +openpyxl/chart/pivot.py,sha256=8zTdCLHxfZqPBj0Ffz9p0P2SioOsiTsTeEZS-AgMqOI,1779 +openpyxl/chart/plotarea.py,sha256=at7r2AP67z5seangCuvQNPpt7W_f3DUC8-PsA4rQfnU,5832 +openpyxl/chart/print_settings.py,sha256=oPGqTrBebiRyrNka5NCbwT0ARBn7nh4Ykm51YHPRdJ4,1454 +openpyxl/chart/radar_chart.py,sha256=VjHjqPKGuLSK9kkXmLM6K_J38Ed4QBr_u5oMI8wBk4M,1537 +openpyxl/chart/reader.py,sha256=ffUGLiMWCc9dgjlz6LdU8Q_aMhU4TZcP6VqvmzIDezg,719 +openpyxl/chart/reference.py,sha256=sAhYJdEEVe4GkvbmnSDF_2kjZ_pnEeocSqhEkqRlfQY,3098 +openpyxl/chart/scatter_chart.py,sha256=1Uwjv5t7EK_Z74hOg4I0DDBtHQ4S8oIMrZoIt9hGpnA,1559 +openpyxl/chart/series.py,sha256=_Fo54wbhKseHZ8QyYKZ_p01odJVTDU-_i2EPRhCtlAE,5908 +openpyxl/chart/series_factory.py,sha256=PTWxPZYdkZxDZZCkblxxUoKVumbDFtj3r3ZqEercdlc,1368 +openpyxl/chart/shapes.py,sha256=rgTSDxEzGjZqCPNLWSLBSoSSC2N7llwJSEVKwlYvX0s,2815 +openpyxl/chart/stock_chart.py,sha256=qHNjyvI36ZVyZ81Vu6FfBR7EvCBWbYsEFeDZopROm9s,1620 +openpyxl/chart/surface_chart.py,sha256=jxYrjOaJ11joRSUFeH0NO4uy5OyPZI78KeBAECgnZKQ,2955 +openpyxl/chart/text.py,sha256=fba-LTfVB6AtIgpFKliWgIk6tQ7HNYL4JOLNJeTg3x8,1857 +openpyxl/chart/title.py,sha256=QX_OPzjF8JLyou9WY7V7jtBZImGpCfbf4nssM0nifRQ,1973 +openpyxl/chart/trendline.py,sha256=LEZS8QABT9tFv9-kZS0mAoop6_yRVo2nNXks_WTMGGM,3053 +openpyxl/chart/updown_bars.py,sha256=IKN86TcUsVkzNaavvlSftHUg1-j458qqBmEzjQ0FAfs,897 +openpyxl/chartsheet/__init__.py,sha256=myQp-ZuyMgRDVI9Qk69Wm9DV94MRtnraap70zf4FJUo,71 +openpyxl/chartsheet/__pycache__/__init__.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/chartsheet.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/custom.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/properties.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/protection.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/publish.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/relation.cpython-39.pyc,, +openpyxl/chartsheet/__pycache__/views.cpython-39.pyc,, +openpyxl/chartsheet/chartsheet.py,sha256=2c6SlJXQwPaJ-wkfbe1P8_J9bZz1l8VoleEHosiAatw,4042 +openpyxl/chartsheet/custom.py,sha256=SPLIS-hgBwvPCnkL5ShRmObvCLECgs_CJ9eNB2aKnFY,1691 +openpyxl/chartsheet/properties.py,sha256=g8TGLCIj90WVPWVzHI_whqm7Mrb3B-avBUx5I6KCjRA,679 +openpyxl/chartsheet/protection.py,sha256=eJixEBmdoTDO2_0h6g51sdSdfSdCaP8UUNsbEqHds6U,1265 +openpyxl/chartsheet/publish.py,sha256=a7elqmA6cqRTKvQSbIIwbDS4xKU6bVjsFsiGSK-Dbg0,1587 +openpyxl/chartsheet/relation.py,sha256=qTKpAWyYIH_UMRYiHKd_xlZ4nD2F3a6PmyduFgj1_1M,2731 +openpyxl/chartsheet/views.py,sha256=cyLt6jsuSO7Yix_hFz-ZmZdSReVqCIIhgv-Xd6-oen4,1341 +openpyxl/comments/__init__.py,sha256=QcxwWmrCTsmEL7cL9FCtKO9SQhbaoCsyVs_9zpZU8us,67 +openpyxl/comments/__pycache__/__init__.cpython-39.pyc,, +openpyxl/comments/__pycache__/author.cpython-39.pyc,, +openpyxl/comments/__pycache__/comment_sheet.cpython-39.pyc,, +openpyxl/comments/__pycache__/comments.cpython-39.pyc,, +openpyxl/comments/__pycache__/shape_writer.cpython-39.pyc,, +openpyxl/comments/author.py,sha256=5DjOW6-SJsuQoRQ8y7IB3r1NVmD6rGzvuH7e0f1IbyI,388 +openpyxl/comments/comment_sheet.py,sha256=kP94CQHhB78R_tvfv2kYdaMXA_r5vtyPFhRD8xDvq2U,5874 +openpyxl/comments/comments.py,sha256=qwBDd1wv0PEKuQ6XO1ysMk57xUZweXy_93BxyYV7Hak,1474 +openpyxl/comments/shape_writer.py,sha256=PZ9mp2Qc-p0c4Xa6q_S2drMsfHOOX39FzlupX5FbTk0,3868 +openpyxl/compat/__init__.py,sha256=kA10fld-rDc2G78_o3DfL8hJ2g87kVKCc_9quMmasIo,1592 +openpyxl/compat/__pycache__/__init__.cpython-39.pyc,, +openpyxl/compat/__pycache__/abc.cpython-39.pyc,, +openpyxl/compat/__pycache__/numbers.cpython-39.pyc,, +openpyxl/compat/__pycache__/product.cpython-39.pyc,, +openpyxl/compat/__pycache__/singleton.cpython-39.pyc,, +openpyxl/compat/__pycache__/strings.cpython-39.pyc,, +openpyxl/compat/abc.py,sha256=oZ1qLqqHcdL6RB4YfD1mny3MYNxwIgf9edtGniukZP8,155 +openpyxl/compat/numbers.py,sha256=Rqr8gY47XjdwiDRlSl8QJ5nwMr7RTJolXKy1oB_vTts,1617 +openpyxl/compat/product.py,sha256=gyp89bpR4jeJjr4uNvQMcHfx-1VohyUhiF_r4Sm4msY,264 +openpyxl/compat/singleton.py,sha256=faYZiBq_k8gDeKbOtvHm-SVb0-4uYTUwtQI7G2XJIrc,1083 +openpyxl/compat/strings.py,sha256=Bty1sXQiiW2etuQXRfy-pqqXQ5GgADJx1ewZID7s_e0,604 +openpyxl/descriptors/__init__.py,sha256=ySMQq3DVAZjV3IMwyAs3rYsYQOn2qqy5AltpjNVvXGs,1816 +openpyxl/descriptors/__pycache__/__init__.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/base.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/excel.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/namespace.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/nested.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/sequence.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/serialisable.cpython-39.pyc,, +openpyxl/descriptors/__pycache__/slots.cpython-39.pyc,, +openpyxl/descriptors/base.py,sha256=0Q4psdmTQs3oYxY7VikwItnfM_0srgitB7eCv0wm040,7110 +openpyxl/descriptors/excel.py,sha256=TPYzzf0x5uyCyXC3USjO8AZhzD83J0T08OaUOfqCyhs,2438 +openpyxl/descriptors/namespace.py,sha256=ZvQcYT_aLJIsdc5LjlmRPZQ7UZp-1OdWkPrD7hG2Jmc,309 +openpyxl/descriptors/nested.py,sha256=lkmVD1zG_VPAbukFpk8jnj5MjPkDaVRzVKKJfBRCWqc,2651 +openpyxl/descriptors/sequence.py,sha256=DJjJ8RyFaiuPSfNvEVevMii5uzUoZe2hjOESaS4kHig,3324 +openpyxl/descriptors/serialisable.py,sha256=cfRFFSAMbTM0GCPY5Lqy_UrmJABbKf-Klesw8pitTRo,7343 +openpyxl/descriptors/slots.py,sha256=xNj5vLWWoounpYqbP2JDnnhlTiTLRn-uTfQxncpFfn0,824 +openpyxl/drawing/__init__.py,sha256=oP2zHU2hnNw8Iq5oAlUejZ3zxyGjAd7fpbtHR2mhVcc,66 +openpyxl/drawing/__pycache__/__init__.cpython-39.pyc,, +openpyxl/drawing/__pycache__/colors.cpython-39.pyc,, +openpyxl/drawing/__pycache__/connector.cpython-39.pyc,, +openpyxl/drawing/__pycache__/drawing.cpython-39.pyc,, +openpyxl/drawing/__pycache__/effect.cpython-39.pyc,, +openpyxl/drawing/__pycache__/fill.cpython-39.pyc,, +openpyxl/drawing/__pycache__/geometry.cpython-39.pyc,, +openpyxl/drawing/__pycache__/graphic.cpython-39.pyc,, +openpyxl/drawing/__pycache__/image.cpython-39.pyc,, +openpyxl/drawing/__pycache__/line.cpython-39.pyc,, +openpyxl/drawing/__pycache__/picture.cpython-39.pyc,, +openpyxl/drawing/__pycache__/properties.cpython-39.pyc,, +openpyxl/drawing/__pycache__/relation.cpython-39.pyc,, +openpyxl/drawing/__pycache__/spreadsheet_drawing.cpython-39.pyc,, +openpyxl/drawing/__pycache__/text.cpython-39.pyc,, +openpyxl/drawing/__pycache__/xdr.cpython-39.pyc,, +openpyxl/drawing/colors.py,sha256=qiCOIvoETNtYJ1Sdzi6bD-mTidUqeLvYk3txBKjpM1w,15278 +openpyxl/drawing/connector.py,sha256=K3_RiZI0g9q6CfGGWm-R5y1lip56qnzdX5u8hHeI2g0,3863 +openpyxl/drawing/drawing.py,sha256=FqPCzLScbKSgHzT4GKiEcgKrleGIkYvTRbLPrbzNz5Y,2785 +openpyxl/drawing/effect.py,sha256=-eVp0TnnTE9qSWndoaer__sWMgC3BYDvm1k15W3eoRQ,9529 +openpyxl/drawing/fill.py,sha256=Yg3kTFWtsmIDTfNxNshOMT7QwkkplDpwt0umoZ7s9VU,12701 +openpyxl/drawing/geometry.py,sha256=l9VQKibbVOKIXEaZANMA1d46HcMPspsc42LLIjegz4A,17733 +openpyxl/drawing/graphic.py,sha256=QlfuEt8OiL6d24jXx3FF7TabOBxB8K8Ou_9n-hNAzGg,5127 +openpyxl/drawing/image.py,sha256=Homv8y1OgvKwruDQ5t59U-46cMdh3uen8PyundZuqrQ,1424 +openpyxl/drawing/line.py,sha256=Ml4QPvOT4hHUyRYss3TNUeX77UXWTpe-G9j2irdFiuM,4105 +openpyxl/drawing/picture.py,sha256=Byum7rekO_gFujozHcQ3v1pYCarEhpFpuJjPwutq_aQ,4287 +openpyxl/drawing/properties.py,sha256=whzCZhq7EU6wpyj4j-ZYYrb1M9x-KfXHIbmgV7jFKTU,4948 +openpyxl/drawing/relation.py,sha256=1U3pSBA6NAuJdD0YCSFGTUJ_pMeexWgbX29BZaKtxnc,344 +openpyxl/drawing/spreadsheet_drawing.py,sha256=euiEVY2pslbznlMkRl5wlF99WHRrDZfJ4ke0fMRPTbg,10762 +openpyxl/drawing/text.py,sha256=i5mTpE7UcEZ1LfuAJ3_vjeDMTrbYx6UJD4_Qc7LJsNU,22345 +openpyxl/drawing/xdr.py,sha256=2LXQIZUST4MNRZ1zSe-TKjSSioVBju2kbiL5riMvSQ4,626 +openpyxl/formatting/__init__.py,sha256=g7jBykulaodAZuQl8W_qcI7aBR3sqtuAzEv9XnF6ZNM,59 +openpyxl/formatting/__pycache__/__init__.cpython-39.pyc,, +openpyxl/formatting/__pycache__/formatting.cpython-39.pyc,, +openpyxl/formatting/__pycache__/rule.cpython-39.pyc,, +openpyxl/formatting/formatting.py,sha256=dlb9q-z-7yk1FUaVZwN7LDwGBwTCo_3SuA8X8yvwqiE,2805 +openpyxl/formatting/rule.py,sha256=Ajl4HITXbeAnJv2gqlxIe6dnAS7NFn2UzVASo7Xg9c4,9308 +openpyxl/formula/__init__.py,sha256=QC48KwwIjQQVRxkMQnaDxjtZxZfiF5XAX7K7qgTXnfk,69 +openpyxl/formula/__pycache__/__init__.cpython-39.pyc,, +openpyxl/formula/__pycache__/tokenizer.cpython-39.pyc,, +openpyxl/formula/__pycache__/translate.cpython-39.pyc,, +openpyxl/formula/tokenizer.py,sha256=LYD7rjTds1kbKo_EeL22H4QtPgvzD04uAAh_XX_XQV0,15104 +openpyxl/formula/translate.py,sha256=3yduwyIg71VUHquJFbFKYorfpfwSdC4QNXdM3HyqGug,6661 +openpyxl/packaging/__init__.py,sha256=KcNtO2zoYizOgG-iZzayZffSL1WeZR98i1Q8QYTRhfI,90 +openpyxl/packaging/__pycache__/__init__.cpython-39.pyc,, +openpyxl/packaging/__pycache__/core.cpython-39.pyc,, +openpyxl/packaging/__pycache__/extended.cpython-39.pyc,, +openpyxl/packaging/__pycache__/interface.cpython-39.pyc,, +openpyxl/packaging/__pycache__/manifest.cpython-39.pyc,, +openpyxl/packaging/__pycache__/relationship.cpython-39.pyc,, +openpyxl/packaging/__pycache__/workbook.cpython-39.pyc,, +openpyxl/packaging/core.py,sha256=OQmPubwaPH5g-fkOthCN8q_Z9n14Nf4q9iJMvpJPj84,4011 +openpyxl/packaging/extended.py,sha256=Da8Xr9kn4gfZWSA5Qf3xXBCkT11uNUovv4uAVUG2l64,4755 +openpyxl/packaging/interface.py,sha256=1QT67yOeMnthkLOdgS_7m7FO-WoSGfIsDPo6q61Xa-8,920 +openpyxl/packaging/manifest.py,sha256=UYYsrPqMfGHCzkkV-G7HsSxVXTTFwha3cQkR_yxmR_k,5643 +openpyxl/packaging/relationship.py,sha256=Pr3xtHVlvC6VenHqTBNVyeef0aocVbCHRw98iVynW4A,4356 +openpyxl/packaging/workbook.py,sha256=MN_oSLd0kCLWKbgUNMfzGD57V427vW_NxMA8dUi-qYw,7024 +openpyxl/pivot/__init__.py,sha256=C431i_Ek0iWxSP_jixBAabUFuCJ-3T3ESB-MEY21epw,35 +openpyxl/pivot/__pycache__/__init__.cpython-39.pyc,, +openpyxl/pivot/__pycache__/cache.cpython-39.pyc,, +openpyxl/pivot/__pycache__/fields.cpython-39.pyc,, +openpyxl/pivot/__pycache__/record.cpython-39.pyc,, +openpyxl/pivot/__pycache__/table.cpython-39.pyc,, +openpyxl/pivot/cache.py,sha256=-QlTsZz0vEiOfgOdUwg7l0oUa-3bnQN9ilBqnwuSXls,30587 +openpyxl/pivot/fields.py,sha256=_IiGKyBNyidZjKS7wtnRvSFG3nDBLo6hBYXffO9_KYw,6984 +openpyxl/pivot/record.py,sha256=guO-qFdU2gPvAbunXIfL23LsSNUlJph7y4ABdz2ZwJg,2687 +openpyxl/pivot/table.py,sha256=NmB0Ln37myAs3oRIZJWmPxusz7FAAxJPIMgUyblo4n4,37779 +openpyxl/reader/__init__.py,sha256=C431i_Ek0iWxSP_jixBAabUFuCJ-3T3ESB-MEY21epw,35 +openpyxl/reader/__pycache__/__init__.cpython-39.pyc,, +openpyxl/reader/__pycache__/drawings.cpython-39.pyc,, +openpyxl/reader/__pycache__/excel.cpython-39.pyc,, +openpyxl/reader/__pycache__/strings.cpython-39.pyc,, +openpyxl/reader/__pycache__/workbook.cpython-39.pyc,, +openpyxl/reader/drawings.py,sha256=8CbA_PRujCea9qBMHcTIlsyveG9LH5Zs6U1YZb2GmUc,2090 +openpyxl/reader/excel.py,sha256=odJKl2KciJTobeSe0v9b5pzUgocs_DwBfGh2M1EJ0l0,10978 +openpyxl/reader/strings.py,sha256=t6rTWWNkCIjj4lvnVXRi0wX1cyvl0MjVeBMLdyWTEzA,565 +openpyxl/reader/workbook.py,sha256=_vPDSwNOlKVV3AM0NUx3jCNc8ihOfWtFdsNcqEv1CEU,3921 +openpyxl/styles/__init__.py,sha256=drQyG97CRWfunkBvFNct9YGO_asNPwrmK8zya5RlYAY,363 +openpyxl/styles/__pycache__/__init__.cpython-39.pyc,, +openpyxl/styles/__pycache__/alignment.cpython-39.pyc,, +openpyxl/styles/__pycache__/borders.cpython-39.pyc,, +openpyxl/styles/__pycache__/builtins.cpython-39.pyc,, +openpyxl/styles/__pycache__/cell_style.cpython-39.pyc,, +openpyxl/styles/__pycache__/colors.cpython-39.pyc,, +openpyxl/styles/__pycache__/differential.cpython-39.pyc,, +openpyxl/styles/__pycache__/fills.cpython-39.pyc,, +openpyxl/styles/__pycache__/fonts.cpython-39.pyc,, +openpyxl/styles/__pycache__/named_styles.cpython-39.pyc,, +openpyxl/styles/__pycache__/numbers.cpython-39.pyc,, +openpyxl/styles/__pycache__/protection.cpython-39.pyc,, +openpyxl/styles/__pycache__/proxy.cpython-39.pyc,, +openpyxl/styles/__pycache__/styleable.cpython-39.pyc,, +openpyxl/styles/__pycache__/stylesheet.cpython-39.pyc,, +openpyxl/styles/__pycache__/table.cpython-39.pyc,, +openpyxl/styles/alignment.py,sha256=ZuBWNhLBW4g_Q4TU0KBJ5my22FFqhdKB37TXwkRYrNQ,2512 +openpyxl/styles/borders.py,sha256=dnt-7DHqO4QLjV2Sr-U4zQnF6ixzzaIsqRw4LMvIZ68,3594 +openpyxl/styles/builtins.py,sha256=YGYBPgTNLbaAZHNUGTEr91FqaRCaL_4rY0dsKAVHpVY,31182 +openpyxl/styles/cell_style.py,sha256=w5EqHsP2Y-tV2jLGdruxDnJpDVuW8vc5VJXW1t3wP_k,5304 +openpyxl/styles/colors.py,sha256=kUlwq2k4xq6PJgAEqeDGbYMLaoXDPPy7YeFKVKMaols,4653 +openpyxl/styles/differential.py,sha256=XG-vfPkpLuRXVkd3S5wbSJuPPV0u_o7cbEFqCtK5uzk,2267 +openpyxl/styles/fills.py,sha256=_TJZBNSOKXDgX0t_4Z8om5Jn09BvNrTm0N3EVpsvucs,6443 +openpyxl/styles/fonts.py,sha256=cowp6CGjDVB_OBvSHFhrlb1EQyKo_jA98s-9FkiKspU,3525 +openpyxl/styles/named_styles.py,sha256=VJen65pr6LDo1GFeRMUQv8sYFIuaOVASZZhLyjV3a5M,7424 +openpyxl/styles/numbers.py,sha256=frWSLxHhpD36wyOuKGeocURPC6IuhKwLf5Kykex2A8Y,5120 +openpyxl/styles/protection.py,sha256=gXijc5Ns9CMOg6wMkCuxvfofAUWLmJmTkyaspuriDzA,394 +openpyxl/styles/proxy.py,sha256=J2Wxdfme7B7WP8F31F_YhWChe8SWsW3zA5wtTHsSp5w,1456 +openpyxl/styles/styleable.py,sha256=89EE07Dpzsc2LNZ9K5S39WYxY3Qh25am7Maqcf23VmM,4565 +openpyxl/styles/stylesheet.py,sha256=xnKGb-nqzFeckXiUNuCa16KPW7kVrHwfPOvzjVh3jKw,8535 +openpyxl/styles/table.py,sha256=pEcpnQqGSGHL3c7yX6diCoXgo0jXZmHE8sEgVk80Y70,2801 +openpyxl/utils/__init__.py,sha256=9di7orVwjmoDs91vIIFG2905cRz6GSqcZGULCSVDdws,324 +openpyxl/utils/__pycache__/__init__.cpython-39.pyc,, +openpyxl/utils/__pycache__/bound_dictionary.cpython-39.pyc,, +openpyxl/utils/__pycache__/cell.cpython-39.pyc,, +openpyxl/utils/__pycache__/dataframe.cpython-39.pyc,, +openpyxl/utils/__pycache__/datetime.cpython-39.pyc,, +openpyxl/utils/__pycache__/escape.cpython-39.pyc,, +openpyxl/utils/__pycache__/exceptions.cpython-39.pyc,, +openpyxl/utils/__pycache__/formulas.cpython-39.pyc,, +openpyxl/utils/__pycache__/indexed_list.cpython-39.pyc,, +openpyxl/utils/__pycache__/inference.cpython-39.pyc,, +openpyxl/utils/__pycache__/protection.cpython-39.pyc,, +openpyxl/utils/__pycache__/units.cpython-39.pyc,, +openpyxl/utils/bound_dictionary.py,sha256=M9CR0VGuA7ez3Gygakxi_LDM4eTHnaAfQZjqzwzMRg8,759 +openpyxl/utils/cell.py,sha256=YyVEya6Sg7n1TruyH6KoCV-vf5HXn9dvrUhIO-42yMM,6535 +openpyxl/utils/dataframe.py,sha256=9V6xPyszo2D1jTRzboOPCpt4h5MQKXd_336ayZT6mcw,2596 +openpyxl/utils/datetime.py,sha256=KmETLpuUlIKa-PmATdqLF1dZb1gccCBxZyczHhf6h_8,4529 +openpyxl/utils/escape.py,sha256=jGDj670JLnb2m6aaNNqZ_-FqfAj30gldsa_KkwNEivs,790 +openpyxl/utils/exceptions.py,sha256=JOsAobMRZyID8Rdml9ZMy1rvi1zybK2jjm11qFNhqZg,889 +openpyxl/utils/formulas.py,sha256=Punoz4y-nP5ZGS-sO6iOZ7YzLirB_oY-E7kvspLKIaE,3733 +openpyxl/utils/indexed_list.py,sha256=wnQVqowfbz-3MiZA2nHT4OZOmxvtH_1cGQ4FTJwDfMw,1257 +openpyxl/utils/inference.py,sha256=DKezUUNPWcHTwjsH34sGMPe70BPYKDyuc_tgO0zhVaw,1582 +openpyxl/utils/protection.py,sha256=ICR82mHKJJIBCLX0h-MqyBpwdETL7hA4u4TvbDNyBlM,830 +openpyxl/utils/units.py,sha256=EDSpkdD3O5aQcvhtHI4ocBA2Ur2RoYXdvjbnHNtSq0I,2674 +openpyxl/workbook/__init__.py,sha256=gAI3oh1brO22ZBCWiiBm9tYm7fdPlT2toDExpCuTb5w,68 +openpyxl/workbook/__pycache__/__init__.cpython-39.pyc,, +openpyxl/workbook/__pycache__/_writer.cpython-39.pyc,, +openpyxl/workbook/__pycache__/child.cpython-39.pyc,, +openpyxl/workbook/__pycache__/defined_name.cpython-39.pyc,, +openpyxl/workbook/__pycache__/external_reference.cpython-39.pyc,, +openpyxl/workbook/__pycache__/function_group.cpython-39.pyc,, +openpyxl/workbook/__pycache__/properties.cpython-39.pyc,, +openpyxl/workbook/__pycache__/protection.cpython-39.pyc,, +openpyxl/workbook/__pycache__/smart_tags.cpython-39.pyc,, +openpyxl/workbook/__pycache__/views.cpython-39.pyc,, +openpyxl/workbook/__pycache__/web.cpython-39.pyc,, +openpyxl/workbook/__pycache__/workbook.cpython-39.pyc,, +openpyxl/workbook/_writer.py,sha256=rddZuAmxptpRh4uhjQl-1jeJF07wO91oCt3pyvYoJZU,6537 +openpyxl/workbook/child.py,sha256=HfXNppjnjsdlECfcNaiTyHrthjvHxVR9T1Aiv0ICx6A,4060 +openpyxl/workbook/defined_name.py,sha256=rubtJsPddvQ0In8P0APjidpKqUfl2F60NOj8wnMIPi8,7444 +openpyxl/workbook/external_link/__init__.py,sha256=EXiVYDdHsQc36QgNBUyMfoLEXzhc-uYNtU7M8I2rQOg,71 +openpyxl/workbook/external_link/__pycache__/__init__.cpython-39.pyc,, +openpyxl/workbook/external_link/__pycache__/external.cpython-39.pyc,, +openpyxl/workbook/external_link/external.py,sha256=GOlUBsxdeXlzxS5ifcOijDqwFnxZhflwIsXEUWfSD1Y,4555 +openpyxl/workbook/external_reference.py,sha256=ECt6-vqhJRCMpWiDkrjpPoLbrnTxTbDwXgaP2YasaDI,348 +openpyxl/workbook/function_group.py,sha256=W_jJtIljy6u5Tdh6qnrQ-aXPpPfk_4X0fz34XOWZu0g,803 +openpyxl/workbook/properties.py,sha256=j56RvVtYZr-jxs4VyBHtCIDTTQN5znjk9UhEYM2ObgQ,5261 +openpyxl/workbook/protection.py,sha256=tr-9WBLU4XMw2Fbjnt2H2wSoZXRsWty9LUm-zrIP1rY,6031 +openpyxl/workbook/smart_tags.py,sha256=280qvvXX5gaxpxNFIGPu2-v9GMWXZwmw7yqlbBfib0o,1181 +openpyxl/workbook/views.py,sha256=n7L8Ca-ePuPmqUbqSG4LbiMz3lLKiOijngYA4Krwv74,5214 +openpyxl/workbook/web.py,sha256=9aBRZJbzblD5X1ccys23n92zX2AnhA3S5SdUa6-0E7w,2642 +openpyxl/workbook/workbook.py,sha256=7XRflZNTCU-oQ6YigaZYVts9jep0GUK4INf5GjXv-gQ,13948 +openpyxl/worksheet/__init__.py,sha256=C431i_Ek0iWxSP_jixBAabUFuCJ-3T3ESB-MEY21epw,35 +openpyxl/worksheet/__pycache__/__init__.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/_read_only.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/_reader.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/_write_only.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/_writer.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/cell_range.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/cell_watch.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/controls.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/copier.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/custom.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/datavalidation.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/dimensions.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/drawing.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/errors.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/filters.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/header_footer.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/hyperlink.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/merge.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/ole.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/page.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/pagebreak.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/picture.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/properties.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/protection.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/related.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/scenario.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/smart_tag.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/table.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/views.cpython-39.pyc,, +openpyxl/worksheet/__pycache__/worksheet.cpython-39.pyc,, +openpyxl/worksheet/_read_only.py,sha256=zT2SdyLb2ZQO5Mh5EWUsUCpuIkCGn786EmdbJ-ojyms,5439 +openpyxl/worksheet/_reader.py,sha256=KADKaQLN1fle3chk8LKzYzlzLItIQnHEM29MHOtpQuI,15856 +openpyxl/worksheet/_write_only.py,sha256=Fydke8dkCnGq3KJDxWHC3ZrbCOcV_WAkgTeN4R1saaw,4256 +openpyxl/worksheet/_writer.py,sha256=3qxPExZWpdSzs5t-KICBNMxqfdV8usKrsp3sv-85K-o,10318 +openpyxl/worksheet/cell_range.py,sha256=SkSDqISNDMpVTJWHifJZCeNxGcdzjpEcq_3AUayVe8c,14642 +openpyxl/worksheet/cell_watch.py,sha256=LdxGcTmXbZ4sxm6inasFgZPld1ijdL5_ODSUvvz13DU,608 +openpyxl/worksheet/controls.py,sha256=xw1ctipJCdjPDLhELRpJjh0aX5Q5KMMELKqLBABC0Gk,2735 +openpyxl/worksheet/copier.py,sha256=b04yP2jHolt4qtVMwmCLEbJ1dq7X9bGb2UsJOMb6Y8s,2327 +openpyxl/worksheet/custom.py,sha256=CRlQ98GwqqKmEDkv8gPUCa0ApNM2Vz-BLs_-RMu3jLA,639 +openpyxl/worksheet/datavalidation.py,sha256=8h-Bg8Xbd_L2S-I8w5nBUU137S8tsjDTXuKywQ4Jo2I,6136 +openpyxl/worksheet/dimensions.py,sha256=EZkNxkAwhDlcyJ2UL8My2Sarm54QPNHkUT2hfUYFDSE,8877 +openpyxl/worksheet/drawing.py,sha256=wqz6I8g7DyZgd-K8JjuqUJYeRzVJCqcrROnFkNXW4K8,275 +openpyxl/worksheet/errors.py,sha256=KkFC4bnckvCp74XsVXA7JUCi4MIimEFu3uAddcQpjo0,2435 +openpyxl/worksheet/filters.py,sha256=zRo479MYRiSuTLYotZuAQKBUX7UL3SGlMYxZY_LUk10,10814 +openpyxl/worksheet/header_footer.py,sha256=e8baJAetr9EDwc22iZ6Yw82BD0rQv61K4ldE_BEMRng,7886 +openpyxl/worksheet/hyperlink.py,sha256=j4D98tw6TDEB-KsYRxlnCevaQg-zcNYDsCL8eRX11KY,1391 +openpyxl/worksheet/merge.py,sha256=MuolhcXe5n3_ZoWa6lmDnhVJV-bSyI4QCJ7Q_pu220M,4140 +openpyxl/worksheet/ole.py,sha256=7kJAf077bd_bAErjMxI_8SKBAESD9vgxOKojfEPBAaw,3530 +openpyxl/worksheet/page.py,sha256=YEQSycU12rLi6r0B49NkzqOwrr8gmTd6lQ1EvnMslMc,4920 +openpyxl/worksheet/pagebreak.py,sha256=Qc83q67o3mwkr3jIvPO_RjsVMkThXvf0ghNqa5QEYO8,1811 +openpyxl/worksheet/picture.py,sha256=72TctCxzk2JU8uFfjiEbTBufEe5eQxIieSPBRhU6m1Q,185 +openpyxl/worksheet/properties.py,sha256=KAL6M0PGdnYS1A7H3X-uuOV70tVwIYAjnOdH8HLJ00E,3087 +openpyxl/worksheet/protection.py,sha256=hQGagSq3SmXEsSyMSuVCMl5jNmyJvxHXoU5rYjVZRX4,3787 +openpyxl/worksheet/related.py,sha256=VMAfTmQ-Rv0S_eAFaG02hofoLrecuF9Wk4JSEPFmPsA,348 +openpyxl/worksheet/scenario.py,sha256=bYh2uOE5yzTeTymNQNT8Box_PdMuHRccFOpAs6VyBdw,2401 +openpyxl/worksheet/smart_tag.py,sha256=nLbt04IqeJllk7TmNS1eTNdb7On5jMf3llfyy3otDSk,1608 +openpyxl/worksheet/table.py,sha256=z88TiQybtbjiKvNx4NL8Rm40c63g2zxjlwt4v8c6m78,11716 +openpyxl/worksheet/views.py,sha256=TfgCdEMCNr8-qOTpJ-iRmt4MBWgni_OagqfzRUC-Stc,4632 +openpyxl/worksheet/worksheet.py,sha256=J4YwSgqO-OQmik5f7nfQxezPIdW39kotT1NTqPy6VUU,27473 +openpyxl/writer/__init__.py,sha256=C431i_Ek0iWxSP_jixBAabUFuCJ-3T3ESB-MEY21epw,35 +openpyxl/writer/__pycache__/__init__.cpython-39.pyc,, +openpyxl/writer/__pycache__/excel.cpython-39.pyc,, +openpyxl/writer/__pycache__/theme.cpython-39.pyc,, +openpyxl/writer/excel.py,sha256=ZRrHP_eMSZ4PdRYW_nwruF-HH3heVe3uohQWMx4O-_E,9854 +openpyxl/writer/theme.py,sha256=cPZepfx2NiU3ONndCaVwpXDSGWl-EKhgdLQtVq8VHvY,10320 +openpyxl/xml/__init__.py,sha256=YGR_-Pq3vdU9VmItEzkOLfjCPO1BMivPWvPH0_uwuqw,1016 +openpyxl/xml/__pycache__/__init__.cpython-39.pyc,, +openpyxl/xml/__pycache__/constants.cpython-39.pyc,, +openpyxl/xml/__pycache__/functions.cpython-39.pyc,, +openpyxl/xml/constants.py,sha256=lgyd3712Dsl5KutfUZywwl9bj1z2V3x917umj9N7cFw,4546 +openpyxl/xml/functions.py,sha256=f3knJ6x0TRsF8YU6iVjnCLu9OdZLk7atyOSMT7dQIok,1929 diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/WHEEL new file mode 100644 index 00000000..01b8fc7d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/top_level.txt new file mode 100644 index 00000000..794cc3d3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl-3.0.9.dist-info/top_level.txt @@ -0,0 +1 @@ +openpyxl diff --git a/.venv/lib/python3.9/site-packages/openpyxl/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/__init__.py new file mode 100644 index 00000000..da757c49 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2010-2021 openpyxl + + +from openpyxl.compat.numbers import NUMPY +from openpyxl.xml import DEFUSEDXML, LXML +from openpyxl.workbook import Workbook +from openpyxl.reader.excel import load_workbook as open +from openpyxl.reader.excel import load_workbook +import openpyxl._constants as constants + +# Expose constants especially the version number + +__author__ = constants.__author__ +__author_email__ = constants.__author_email__ +__license__ = constants.__license__ +__maintainer_email__ = constants.__maintainer_email__ +__url__ = constants.__url__ +__version__ = constants.__version__ diff --git a/.venv/lib/python3.9/site-packages/openpyxl/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..788a59c1c53f405c7f5d900e41641143ef483cbb GIT binary patch literal 613 zcmYk2%Z}496o&0K$#go&w4LGd3|$Z}SRoiEIa{g z-X>dCyaFqZ%cQC;fBOBMqa-IH%Z3Ek;@1x`?-BAlT+SCd@*Yp)5hW?9BARkaL++`b z?(@Emcyzq?Rjd=99CM^D^nhQU<-sw>YN)SxN{LLrWjsS(^K0aYkC0J(l&lr&L6)_d+l&`_hGrXn|+?&x6wTki-S+rZC2K9FlWk&623o)hv_YU zX=`cf!(Qcu)wS@s+3J;ajSJ)P^i}HxC1-OyvRc`p^#EYFloVd>8&!^8%65F-`yPD52f{2SAsxBLDyZ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/__pycache__/_constants.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/__pycache__/_constants.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce1fa163d8ebf871b71ff5d4dac0c34b8687bddd GIT binary patch literal 480 zcmYjOO-lnY5Z$_4TPuo)cNM&NXd)H_MWljc8;hw0=&Q$IHyiBbkV`wO)jgzP*UURusL@H~#}`(Gb(`A;^eDvc+0*i7m;P#WG=u zY_qmpX3Mg}I~KEoaW3jmSciP$mqRI(#>y(^T4i87aL%?k$Fd2S z;P{UOooe^3o#5S+HK;|~YKpeVUnAGWkuBikftDbZpd;brg;Nut<7pABp=nlFt0|d) ynNY1B>DTcAIi_9(=vQBZ^uG_Jd_JcjOz@Vmc>YK02uYH({?;e0c9n|& literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/_constants.py b/.venv/lib/python3.9/site-packages/openpyxl/_constants.py new file mode 100644 index 00000000..7f958413 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/_constants.py @@ -0,0 +1,13 @@ +# Copyright (c) 2010-2021 openpyxl + +""" +Package metadata +""" + +__author__ = "See AUTHORS" +__author_email__ = "charlie.clark@clark-consulting.eu" +__license__ = "MIT" +__maintainer_email__ = "openpyxl-users@googlegroups.com" +__url__ = "https://openpyxl.readthedocs.io" +__version__ = "3.0.9" +__python__ = "3.6" diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/cell/__init__.py new file mode 100644 index 00000000..6a09ab09 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/cell/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2010-2021 openpyxl + +from .cell import Cell, WriteOnlyCell, MergedCell +from .read_only import ReadOnlyCell diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6aceb37da98abea995a11167d82bca02e3d47ee5 GIT binary patch literal 305 zcmYjLyGq4C5Y6V{)r;x;1e+9Q3M)ZG(89(CH-)fG*qa@07WWZ0F}VJUUtsOuq_y%F zteh1D2j<S)2#p>8+sg3jIQIUX%N>Q00H z&~jzLtnaS;!Ae$-dhqQ&fnx+4XNy5<+XvU87@m>P@wl!f&#O#}IIDQfGo9)EsjzxO zrJ*Vb^Qy=zG14&7hHe?kjjs>#YOCHj*uIAejd?9pT-7>Ov+1;!I>wY9)D=kc3cU&x zs6qV6M8wOvsL(JvQ4Y}}t~a;ESjXi|>3CDb3YMtrcmq5Q46XWWCVIN!Bv5+45NXa$ zhzn0~<;G8UvtlMpYm%u@^n9sXE2z%0#)!E2Xn)EMyzZ zC02?IaiS-t^>~tm6PPa{4?9UaJ>`}0v*}b+9DwOU9tz{zC2@Jd&c*o3cy>YG5*dT= zf1eKlYNBa|9SHsbGQrE?Buq&$FVUceRr~7(RSFaD=8Y)2dYk=;Z z?4eM@8X`{j_lhA#9Hx&XXystKZ&p*tSBgRN%^BfjB{cyw3 zu_1C5w)QIpB3FPImMhsshVJ&;HY*bQfbo{8zKHz#RxQg}k)27maUKe7PhMiujH|_- zme@rkwv~Rzu)Qr4KC0!8ng}qxgsK;iTmfRDrzd9^bg8bI08)8+94Tp?Fe}T7JZ_5e zk1#puAQ;kdU1`9!McR_Qh@N{8Oo$~?QZ&RV>N*g@x`2cppu)A^4&bC~dBllNBR2v` z#N;ioy?+iISRVLVaQqXpcOD#&e7eKbWqSxjK7iCy&KmCtd%RA`rvMBWehX&_ky}Ll z=_aA}_c_p~)`#@*t1xEJToFP)0)(6+goI}yj7qCporuy;B}o|WgXgqj@Jc>IKJWHSH1!EeY#=)_xl%;)stR27}w=A({`oF zGpACBIoO(2W1ZKPT5bqSds)zGP(o literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/cell.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/cell.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74f3a27862d2710d0f7aab9deb5f1f6021c51994 GIT binary patch literal 8387 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 F{eMv0wT}P* literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/read_only.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/cell/__pycache__/read_only.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4da57b0a5e777a557d6d66f5f7815ec36295d43 GIT binary patch literal 4400 zcmb_f-H+SG5$BQ=MN!gy`Od!Bu5)%il_pZ$07Z)$hF#~J8n7=x&IJ+{!Gfn<`y}&2 zUh{88h#`4 ze2*Dl7{U?t7lx?I=CS3s(04@x{f6$hW&7ChmxQ-#bejK$Jfp*M`;H7l{H{Kh8Sf|I zK@{^)W|>qN^d6}s;_`>Sq$Nv(l#3vf{86H!AnTa<@*n?v@BYJkJN)jKUq870a5vwa z8{iLaf5Uh0`P+RGSAHyXZlR_B0+EK#gyEZl`IhJiThv}K-!3-e*NRQ}j_^eDh2htw zD_Wxc!Z>Dr1N>5nUluER+{5^)SkrtH{5i3%`4;#MabELn@E63U=9j>~B`#`y8T=)2 zS@SF4e!QB2YFlLCuK;YE5dO_QVFQO&m~17`w(Rw$rJr2EJ49QbUo`swR;( zNM6%$)DrkkJ-1UCKJshnzLZ&MPp384JY6lJhH=O0_c13a0r!IotFho?@03OuG{df{h^?!pDZzOH(W4aPvla2V2iS zy{eA7Í?AWyGNmsD4k|V|WHH=T;&Re!v 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/_writer.py b/.venv/lib/python3.9/site-packages/openpyxl/cell/_writer.py new file mode 100644 index 00000000..2daa0137 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/cell/_writer.py @@ -0,0 +1,108 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element, SubElement, whitespace, XML_NS, REL_NS +from openpyxl import LXML +from openpyxl.utils.datetime import to_excel, to_ISO8601 +from datetime import timedelta + + +def _set_attributes(cell, styled=None): + """ + Set coordinate and datatype + """ + coordinate = cell.coordinate + attrs = {'r': coordinate} + if styled: + attrs['s'] = f"{cell.style_id}" + + if cell.data_type == "s": + attrs['t'] = "inlineStr" + elif cell.data_type != 'f': + attrs['t'] = cell.data_type + + value = cell._value + + if cell.data_type == "d": + if hasattr(value, "tzinfo") and value.tzinfo is not None: + raise TypeError("Excel does not support timezones in datetimes. " + "The tzinfo in the datetime/time object must be set to None.") + + if cell.parent.parent.iso_dates and not isinstance(value, timedelta): + value = to_ISO8601(value) + else: + attrs['t'] = "n" + value = to_excel(value, cell.parent.parent.epoch) + + if cell.hyperlink: + cell.parent._hyperlinks.append(cell.hyperlink) + + return value, attrs + + +def etree_write_cell(xf, worksheet, cell, styled=None): + + value, attributes = _set_attributes(cell, styled) + + el = Element("c", attributes) + if value is None or value == "": + xf.write(el) + return + + if cell.data_type == 'f': + shared_formula = worksheet.formula_attributes.get(cell.coordinate, {}) + formula = SubElement(el, 'f', shared_formula) + if value is not None: + formula.text = value[1:] + value = None + + if cell.data_type == 's': + inline_string = SubElement(el, 'is') + text = SubElement(inline_string, 't') + text.text = value + whitespace(text) + + + else: + cell_content = SubElement(el, 'v') + if value is not None: + cell_content.text = safe_string(value) + + xf.write(el) + + +def lxml_write_cell(xf, worksheet, cell, styled=False): + value, attributes = _set_attributes(cell, styled) + + if value == '' or value is None: + with xf.element("c", attributes): + return + + with xf.element('c', attributes): + if cell.data_type == 'f': + shared_formula = worksheet.formula_attributes.get(cell.coordinate, {}) + with xf.element('f', shared_formula): + if value is not None: + xf.write(value[1:]) + value = None + + if cell.data_type == 's': + with xf.element("is"): + attrs = {} + if value != value.strip(): + attrs["{%s}space" % XML_NS] = "preserve" + el = Element("t", attrs) # lxml can't handle xml-ns + el.text = value + xf.write(el) + #with xf.element("t", attrs): + #xf.write(value) + else: + with xf.element("v"): + if value is not None: + xf.write(safe_string(value)) + + +if LXML: + write_cell = lxml_write_cell +else: + write_cell = etree_write_cell diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/cell.py b/.venv/lib/python3.9/site-packages/openpyxl/cell/cell.py new file mode 100644 index 00000000..0ecfc030 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/cell/cell.py @@ -0,0 +1,329 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Manage individual cells in a spreadsheet. + +The Cell class is required to know its value and type, display options, +and any other features of an Excel cell. Utilities for referencing +cells using Excel's 'A1' column/row nomenclature are also provided. + +""" + +__docformat__ = "restructuredtext en" + +# Python stdlib imports +from copy import copy +import datetime +import re + + +from openpyxl.compat import ( + NUMERIC_TYPES, + deprecated, +) + +from openpyxl.utils.exceptions import IllegalCharacterError + +from openpyxl.utils import get_column_letter +from openpyxl.styles import numbers, is_date_format +from openpyxl.styles.styleable import StyleableObject +from openpyxl.worksheet.hyperlink import Hyperlink + +# constants + +TIME_TYPES = (datetime.datetime, datetime.date, datetime.time, datetime.timedelta) +TIME_FORMATS = { + datetime.datetime:numbers.FORMAT_DATE_DATETIME, + datetime.date:numbers.FORMAT_DATE_YYYYMMDD2, + datetime.time:numbers.FORMAT_DATE_TIME6, + datetime.timedelta:numbers.FORMAT_DATE_TIMEDELTA, + } + +STRING_TYPES = (str, bytes) +KNOWN_TYPES = NUMERIC_TYPES + TIME_TYPES + STRING_TYPES + (bool, type(None)) + +ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]') +ERROR_CODES = ('#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', + '#N/A') + +TYPE_STRING = 's' +TYPE_FORMULA = 'f' +TYPE_NUMERIC = 'n' +TYPE_BOOL = 'b' +TYPE_NULL = 'n' +TYPE_INLINE = 'inlineStr' +TYPE_ERROR = 'e' +TYPE_FORMULA_CACHE_STRING = 'str' + +VALID_TYPES = (TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL, + TYPE_NULL, TYPE_INLINE, TYPE_ERROR, TYPE_FORMULA_CACHE_STRING) + + +_TYPES = {int:'n', float:'n', str:'s', bool:'b'} + + +def get_type(t, value): + if isinstance(value, NUMERIC_TYPES): + dt = 'n' + elif isinstance(value, STRING_TYPES): + dt = 's' + elif isinstance(value, TIME_TYPES): + dt = 'd' + else: + return + _TYPES[t] = dt + return dt + + +def get_time_format(t): + value = TIME_FORMATS.get(t) + if value: + return value + for base in t.mro()[1:]: + value = TIME_FORMATS.get(base) + if value: + TIME_FORMATS[t] = value + return value + raise ValueError("Could not get time format for {0!r}".format(value)) + + +class Cell(StyleableObject): + """Describes cell associated properties. + + Properties of interest include style, type, value, and address. + + """ + __slots__ = ( + 'row', + 'column', + '_value', + 'data_type', + 'parent', + '_hyperlink', + '_comment', + ) + + def __init__(self, worksheet, row=None, column=None, value=None, style_array=None): + super(Cell, self).__init__(worksheet, style_array) + self.row = row + """Row number of this cell (1-based)""" + self.column = column + """Column number of this cell (1-based)""" + # _value is the stored value, while value is the displayed value + self._value = None + self._hyperlink = None + self.data_type = 'n' + if value is not None: + self.value = value + self._comment = None + + + @property + def coordinate(self): + """This cell's coordinate (ex. 'A5')""" + col = get_column_letter(self.column) + return f"{col}{self.row}" + + + @property + def col_idx(self): + """The numerical index of the column""" + return self.column + + + @property + def column_letter(self): + return get_column_letter(self.column) + + + @property + def encoding(self): + return self.parent.encoding + + @property + def base_date(self): + return self.parent.parent.epoch + + + def __repr__(self): + return "".format(self.parent.title, self.coordinate) + + def check_string(self, value): + """Check string coding, length, and line break character""" + if value is None: + return + # convert to str string + if not isinstance(value, str): + value = str(value, self.encoding) + value = str(value) + # string must never be longer than 32,767 characters + # truncate if necessary + value = value[:32767] + if next(ILLEGAL_CHARACTERS_RE.finditer(value), None): + raise IllegalCharacterError + return value + + def check_error(self, value): + """Tries to convert Error" else N/A""" + try: + return str(value) + except UnicodeDecodeError: + return u'#N/A' + + + def _bind_value(self, value): + """Given a value, infer the correct data type""" + + self.data_type = "n" + t = type(value) + try: + dt = _TYPES[t] + except KeyError: + dt = get_type(t, value) + + if dt is None and value is not None: + raise ValueError("Cannot convert {0!r} to Excel".format(value)) + + if dt: + self.data_type = dt + + if dt == 'd': + if not is_date_format(self.number_format): + self.number_format = get_time_format(t) + + elif dt == "s": + value = self.check_string(value) + if len(value) > 1 and value.startswith("="): + self.data_type = 'f' + elif value in ERROR_CODES: + self.data_type = 'e' + + self._value = value + + + @property + def value(self): + """Get or set the value held in the cell. + + :type: depends on the value (string, float, int or + :class:`datetime.datetime`) + """ + return self._value + + @value.setter + def value(self, value): + """Set the value and infer type and display options.""" + self._bind_value(value) + + @property + def internal_value(self): + """Always returns the value for excel.""" + return self._value + + @property + def hyperlink(self): + """Return the hyperlink target or an empty string""" + return self._hyperlink + + + @hyperlink.setter + def hyperlink(self, val): + """Set value and display for hyperlinks in a cell. + Automatically sets the `value` of the cell with link text, + but you can modify it afterwards by setting the `value` + property, and the hyperlink will remain. + Hyperlink is removed if set to ``None``.""" + if val is None: + self._hyperlink = None + else: + if not isinstance(val, Hyperlink): + val = Hyperlink(ref="", target=val) + val.ref = self.coordinate + self._hyperlink = val + if self._value is None: + self.value = val.target or val.location + + + @property + def is_date(self): + """True if the value is formatted as a date + + :type: bool + """ + return self.data_type == 'd' or ( + self.data_type == 'n' and is_date_format(self.number_format) + ) + + + def offset(self, row=0, column=0): + """Returns a cell location relative to this cell. + + :param row: number of rows to offset + :type row: int + + :param column: number of columns to offset + :type column: int + + :rtype: :class:`openpyxl.cell.Cell` + """ + offset_column = self.col_idx + column + offset_row = self.row + row + return self.parent.cell(column=offset_column, row=offset_row) + + + @property + def comment(self): + """ Returns the comment associated with this cell + + :type: :class:`openpyxl.comments.Comment` + """ + return self._comment + + + @comment.setter + def comment(self, value): + """ + Assign a comment to a cell + """ + + if value is not None: + if value.parent: + value = copy(value) + value.bind(self) + elif value is None and self._comment: + self._comment.unbind() + self._comment = value + + +class MergedCell(StyleableObject): + + """ + Describes the properties of a cell in a merged cell and helps to + display the borders of the merged cell. + + The value of a MergedCell is always None. + """ + + __slots__ = ('row', 'column') + + _value = None + data_type = "n" + comment = None + hyperlink = None + + + def __init__(self, worksheet, row=None, column=None): + super(MergedCell, self).__init__(worksheet) + self.row = row + self.column = column + + + def __repr__(self): + return "".format(self.parent.title, self.coordinate) + + coordinate = Cell.coordinate + _comment = comment + value = _value + + +def WriteOnlyCell(ws=None, value=None): + return Cell(worksheet=ws, column=1, row=1, value=value) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/read_only.py b/.venv/lib/python3.9/site-packages/openpyxl/cell/read_only.py new file mode 100644 index 00000000..e22543b6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/cell/read_only.py @@ -0,0 +1,136 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.cell import Cell +from openpyxl.utils import get_column_letter +from openpyxl.utils.datetime import from_excel +from openpyxl.styles import is_date_format +from openpyxl.styles.numbers import BUILTIN_FORMATS, BUILTIN_FORMATS_MAX_SIZE + + +class ReadOnlyCell(object): + + __slots__ = ('parent', 'row', 'column', '_value', 'data_type', '_style_id') + + def __init__(self, sheet, row, column, value, data_type='n', style_id=0): + self.parent = sheet + self._value = None + self.row = row + self.column = column + self.data_type = data_type + self.value = value + self._style_id = style_id + + + def __eq__(self, other): + for a in self.__slots__: + if getattr(self, a) != getattr(other, a): + return + return True + + def __ne__(self, other): + return not self.__eq__(other) + + + def __repr__(self): + return "".format(self.parent.title, self.coordinate) + + + @property + def coordinate(self): + column = get_column_letter(self.column) + return "{1}{0}".format(self.row, column) + + + @property + def coordinate(self): + return Cell.coordinate.__get__(self) + + + @property + def column_letter(self): + return Cell.column_letter.__get__(self) + + + @property + def style_array(self): + return self.parent.parent._cell_styles[self._style_id] + + + @property + def has_style(self): + return self._style_id != 0 + + + @property + def number_format(self): + _id = self.style_array.numFmtId + if _id < BUILTIN_FORMATS_MAX_SIZE: + return BUILTIN_FORMATS.get(_id, "General") + else: + return self.parent.parent._number_formats[ + _id - BUILTIN_FORMATS_MAX_SIZE] + + @property + def font(self): + _id = self.style_array.fontId + return self.parent.parent._fonts[_id] + + @property + def fill(self): + _id = self.style_array.fillId + return self.parent.parent._fills[_id] + + @property + def border(self): + _id = self.style_array.borderId + return self.parent.parent._borders[_id] + + @property + def alignment(self): + _id = self.style_array.alignmentId + return self.parent.parent._alignments[_id] + + @property + def protection(self): + _id = self.style_array.protectionId + return self.parent.parent._protections[_id] + + + @property + def is_date(self): + return Cell.is_date.__get__(self) + + + @property + def internal_value(self): + return self._value + + @property + def value(self): + return self._value + + @value.setter + def value(self, value): + if self._value is not None: + raise AttributeError("Cell is read only") + self._value = value + + +class EmptyCell(object): + + __slots__ = () + + value = None + is_date = False + font = None + border = None + fill = None + number_format = None + alignment = None + data_type = 'n' + + + def __repr__(self): + return "" + +EMPTY_CELL = EmptyCell() diff --git a/.venv/lib/python3.9/site-packages/openpyxl/cell/text.py b/.venv/lib/python3.9/site-packages/openpyxl/cell/text.py new file mode 100644 index 00000000..6f50f99d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/cell/text.py @@ -0,0 +1,184 @@ +# Copyright (c) 2010-2021 openpyxl + +""" +Richtext definition +""" + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Integer, + Set, + NoneSet, + Bool, + String, + Sequence, +) +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedString, + NestedText, +) +from openpyxl.styles.fonts import Font + + +class PhoneticProperties(Serialisable): + + tagname = "phoneticPr" + + fontId = Integer() + type = NoneSet(values=(['halfwidthKatakana', 'fullwidthKatakana', + 'Hiragana', 'noConversion'])) + alignment = NoneSet(values=(['noControl', 'left', 'center', 'distributed'])) + + def __init__(self, + fontId=None, + type=None, + alignment=None, + ): + self.fontId = fontId + self.type = type + self.alignment = alignment + + +class PhoneticText(Serialisable): + + tagname = "rPh" + + sb = Integer() + eb = Integer() + t = NestedText(expected_type=str) + text = Alias('t') + + def __init__(self, + sb=None, + eb=None, + t=None, + ): + self.sb = sb + self.eb = eb + self.t = t + + +class InlineFont(Font): + + """ + Font for inline text because, yes what you need are different objects with the same elements but different constraints. + """ + + tagname = "RPrElt" + + rFont = NestedString(allow_none=True) + charset = Font.charset + family = Font.family + b =Font.b + i = Font.i + strike = Font.strike + outline = Font.outline + shadow = Font.shadow + condense = Font.condense + extend = Font.extend + color = Font.color + sz = Font.sz + u = Font.u + vertAlign = Font.vertAlign + scheme = Font.scheme + + __elements__ = ('rFont', 'charset', 'family', 'b', 'i', 'strike', + 'outline', 'shadow', 'condense', 'extend', 'color', 'sz', 'u', + 'vertAlign', 'scheme') + + def __init__(self, + rFont=None, + charset=None, + family=None, + b=None, + i=None, + strike=None, + outline=None, + shadow=None, + condense=None, + extend=None, + color=None, + sz=None, + u=None, + vertAlign=None, + scheme=None, + ): + self.rFont = rFont + self.charset = charset + self.family = family + self.b = b + self.i = i + self.strike = strike + self.outline = outline + self.shadow = shadow + self.condense = condense + self.extend = extend + self.color = color + self.sz = sz + self.u = u + self.vertAlign = vertAlign + self.scheme = scheme + + +class RichText(Serialisable): + + tagname = "RElt" + + rPr = Typed(expected_type=InlineFont, allow_none=True) + font = Alias("rPr") + t = NestedText(expected_type=str, allow_none=True) + text = Alias("t") + + __elements__ = ('rPr', 't') + + def __init__(self, + rPr=None, + t=None, + ): + self.rPr = rPr + self.t = t + + +class Text(Serialisable): + + tagname = "text" + + t = NestedText(allow_none=True, expected_type=str) + plain = Alias("t") + r = Sequence(expected_type=RichText, allow_none=True) + formatted = Alias("r") + rPh = Sequence(expected_type=PhoneticText, allow_none=True) + phonetic = Alias("rPh") + phoneticPr = Typed(expected_type=PhoneticProperties, allow_none=True) + PhoneticProperties = Alias("phoneticPr") + + __elements__ = ('t', 'r', 'rPh', 'phoneticPr') + + def __init__(self, + t=None, + r=(), + rPh=(), + phoneticPr=None, + ): + self.t = t + self.r = r + self.rPh = rPh + self.phoneticPr = phoneticPr + + + @property + def content(self): + """ + Text stripped of all formatting + """ + snippets = [] + if self.plain is not None: + snippets.append(self.plain) + for block in self.formatted: + if block.t is not None: + snippets.append(block.t) + return u"".join(snippets) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/_3d.py b/.venv/lib/python3.9/site-packages/openpyxl/chart/_3d.py new file mode 100644 index 00000000..7c659c4b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chart/_3d.py @@ -0,0 +1,105 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import Typed, Alias +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedMinMax, +) +from openpyxl.descriptors.excel import ExtensionList +from .marker import PictureOptions +from .shapes import GraphicalProperties + + +class View3D(Serialisable): + + tagname = "view3D" + + rotX = NestedMinMax(min=-90, max=90, allow_none=True) + x_rotation = Alias('rotX') + hPercent = NestedMinMax(min=5, max=500, allow_none=True) + height_percent = Alias('hPercent') + rotY = NestedInteger(min=-90, max=90, allow_none=True) + y_rotation = Alias('rotY') + depthPercent = NestedInteger(allow_none=True) + rAngAx = NestedBool(allow_none=True) + right_angle_axes = Alias('rAngAx') + perspective = NestedInteger(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('rotX', 'hPercent', 'rotY', 'depthPercent', 'rAngAx', + 'perspective',) + + def __init__(self, + rotX=15, + hPercent=None, + rotY=20, + depthPercent=None, + rAngAx=True, + perspective=None, + extLst=None, + ): + self.rotX = rotX + self.hPercent = hPercent + self.rotY = rotY + self.depthPercent = depthPercent + self.rAngAx = rAngAx + self.perspective = perspective + + +class Surface(Serialisable): + + tagname = "surface" + + thickness = NestedInteger(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + pictureOptions = Typed(expected_type=PictureOptions, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('thickness', 'spPr', 'pictureOptions',) + + def __init__(self, + thickness=None, + spPr=None, + pictureOptions=None, + extLst=None, + ): + self.thickness = thickness + self.spPr = spPr + self.pictureOptions = pictureOptions + + +class _3DBase(Serialisable): + + """ + Base class for 3D charts + """ + + tagname = "ChartBase" + + view3D = Typed(expected_type=View3D, allow_none=True) + floor = Typed(expected_type=Surface, allow_none=True) + sideWall = Typed(expected_type=Surface, allow_none=True) + backWall = Typed(expected_type=Surface, allow_none=True) + + def __init__(self, + view3D=None, + floor=None, + sideWall=None, + backWall=None, + ): + if view3D is None: + view3D = View3D() + self.view3D = view3D + if floor is None: + floor = Surface() + self.floor = floor + if sideWall is None: + sideWall = Surface() + self.sideWall = sideWall + if backWall is None: + backWall = Surface() + self.backWall = backWall + super(_3DBase, self).__init__() diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/chart/__init__.py new file mode 100644 index 00000000..5e7c2858 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chart/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2010-2021 openpyxl + +from .area_chart import AreaChart, AreaChart3D +from .bar_chart import BarChart, BarChart3D +from .bubble_chart import BubbleChart +from .line_chart import LineChart, LineChart3D +from .pie_chart import ( + PieChart, + PieChart3D, + DoughnutChart, + ProjectedPieChart +) +from .radar_chart import RadarChart +from .scatter_chart import ScatterChart +from .stock_chart import StockChart +from .surface_chart import SurfaceChart, SurfaceChart3D + +from .series_factory import SeriesFactory as Series +from .reference import Reference diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/_3d.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/_3d.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9b5cd3db83c5cc3a1f738aeb9b8068ebe9ed18b GIT binary patch literal 2623 zcmZ`*OK%%h6rLH+%i~9!M_bhLa34GjGzNl@kPx&bs8F>gQlb{_W;F4=iAS9on|mjz z6PZ;il}$Hn*}w)VYyQn_S!K@#gjBwBZ6|IL#=0lxb?2UYzH`nbs?`Dm&-x#~M9r#U z{7J&>V?(%wrG5h81~(HUHJO=O%rZ%~l2U3jTl1yFNnPfq9`jP4`C4Zu+*&?i$?Z<);QO3;UqN<{$^W0=c5)2IMNQL9S_ep4Zon*4z=IF{2hk8) zYhMj>k*T$%;F|137cEI#BJ_8FEK~B+&wUv2d#%RjI}L>KuFbHJpnhpt?JlUyQlf!wQH4D#ulvq$ZHV)dDu<`kE8b2k>slq@wM0XdU-!$adRF zyxAV?<-LBkvixaV#ksgSh`NuXZK2wznQX8(OxoREB=dH-!j}hog&&4-7Uy9|rzmuy zVU%5S`rDryqqRKsYI*WCxOC*}L^nE})+KsH7NsH#iy#csKHo`*uZH0dJ5e&RczLu< ztfT@R-@F8l1;Y^FMpWY(8dmR#c)OQ}K!OmKF8LE)EZ947)w!aA&})@xo6w8GL%-SR7+;jRgMJF2t4wrN@U;nF=PsMWn4-ajuHzm$ zs#19CJ8~=P3g)RXvmr0yH2De$N^P(v4@*Tk$LM&R38mzFq^}QNnlGI$WpnxvPs!6Y zPVYKf<5bb|4VdKnAT38WiN8sN-Y=JlP})zYm-7AvmLg;gmnv<)F}cA>nb{pYt$Fz- zeB@h1-X=o*HZBQ$k_Na!I(??DsKxEGV^Y$$W*pLSImj02{NZ!}$jV<*&y&xFE{n5pQ|@S~O^^{j z8#{)q=f;8Yqp86+N0!!I%Pr_EJ?=`QlGc5qb&&MHv$gJ?*4cU#I=P#BBjdpO)&C8t zJ1(78SvQTW;Ygjz!r4mteOdS_=Hd~)0ENGa@ufLO?O-OzHu(~2(~{GhKOpUgL_PvJ zG>dW=>SUnNb&p>s8*O`prDzNruCK@3S-Rhtr1o?<)=!lqFbH|Si!*dx>T0CvadIK~ za$q`wqBe=GE0SaypAPb4+N&d_Ti(M`)O@Hu+qO`7^gF3N^69h|*wS>cFLV4uAmc&a zmufuw2vBzQJo~bm`F};)J}58T2{Bl#F`QR6XJ t^`ZfWvhGx!AnKbjl=|W)gl=E>pN+pFpHuv*0n)(Lmz;3|8aEo3{sVcCMF9W+ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4378693712eff25da666f6576c79ab4f765c451 GIT binary patch literal 860 zcmYk4yKdVs6ow`FrmHV;+_;x?_fT_imjXr6rbf}BD2!-h2Z5HDSP3Z!6m3+`(zQ$H zzKzySeT7ctp`;)Lc=Vs|9E$uYksxplHNxLN$ZN|m{y_40C}zA@v;3(-Vk9OrxQR_} zVatTvVm;o+eLlbgo%h&~kMKzAKC`)l9qwXR=L6<(ANxGO0UzTrpWun+LpJ3zJkxr_ z&iEY9wYJ$gU*Lt-4tv5c@C9GurOsXUlt07Iwf5Kxeu*!Y_CsS8{5924S61zO5R`m8 z5Fu;tL2kCHVqX!_6x>cJZr0w_aldC&m*53o(yU2p??I}@er@m4w%~SZv#o<|e!M+o zN7=l4wiEdsjU`RG9<_ygO_FwVmHA;zq@>MZpc=|NzVG(;!=tz%al6y_ai)(OgjA%o z{6u1zi%KnbuIUXGG>hri)l2LWuc2RH!e@3rxk4*|JMnQy@e z8f~CXk#^7l2tSYTAox)B1h{Kg1wewbaoDo4?kd4$OD0WM<5F9U)HQ%d zaRzYM_^ut%*%foee;Hx{3nBdbhB!@9f<6ClD;ZPd{1s^iSmMGMfIH_eS;{9(kzuxv@WXJe#`UZ$mlz`(ehk{sW~-$ NHJ3wQJ(aS2^FNdl?#%!I literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/_chart.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/_chart.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fc4457ee9d706d656f8184e0cf419b4b15ae7f1 GIT binary patch literal 5979 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 UFNW(7ts(88T^jz4NyjPu5A`j16#xJL literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/area_chart.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/area_chart.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5131a2c38a262258177c16d46145f25ff921e748 GIT binary patch literal 2857 zcmb7GOK;mo5MDk-QIsspvYo_Elk}~&k=nYi6h%=dF46#wP(VH{P!P0sZPOtsWtVc~ zKsN`-Dg6Vzv_&sI_fPaU?6s%-g%&6}vy}Wu9kiveA7{v2a(3pMkBX{QpTN=k^@k{} z5b`@ZXAc{kuVBdE02pCLLQ;bo2HIv~rWUnQo7$;Eoz$gnTB4=Yqn^HRCFRtozP9b8 zk_I$LtF)TdXbrA8%uVLfI<0HFl+33M+R(O_ETm1^Oc&{5xkPY4Tm{m7!L;8$4<{2E^c|0?)%tPXyiUj_daHs2$y#&g&WX&EZ$ zaS=yJETip&w_N3H9S%8DR*&b(zC9Qus&p^Q`7RgA>+y#pp7ptM){{7ru)6y7IOmy+ z2iayU^OmK8E|)oH_v7q-G*;!p!)N)0iBEiP6t6Dd*^5MeJCYopG*{fe8x7af!6?gH zw(_?4IA0&fQdPR66vFIl$H$1dENuOV%{b#anvE!rHluBx=x7Y(7E}AU*RzK+bz}kt zC)8ksn%vrj>i%pn^O-?y?l22}_A`^Zyu>}Q9k9#X=as1)@G5hU3|fP0bJJ^eR-*IF zqYYMjN_Gquua*ZqeG1i$2ftULN%yK<+)6M;VI*qb0EL z66D7{h>{VPTP;($uv^Hn@}ndfJPNZx#*a-^<>Mjm!}TzS3W_VRGv%<&?L;a&f;YAt zu^3vm^vmzfw0buz^=_}ydDci}uc(6**Sz7NL4n2?Z6 zOh98|0h$vV(3&`acJAcvgdCYamVP3@SkfcMMz`gOYp_yWN4SB2Zx*itC`XPUaIu88 z7lv^b=V1txv?Wh=Ub^hqP~|Y}Cy|t4C|-lbe|*eXtJcLb;r zDYlI-DpFQLYY{;w56dB%2s(`(Skz~;E|6jYZ((w25JH)uZ8SUZMLgjtkPefE8&cJ2 z68k(!ris+y3f!uA2LUUy0z<9>cxKHA^v^Q_(>84js$Bg3)HDClb*Q9x$bEoW*D-@y z+};QE1O)vA1O){I4FxPuI}Up?fIh40?woZTc4r-C%`<12K;M;h&y|=5J=f4Zw*Woo ztMW<9(K6Ojci*uga35O&w*(z#N8@{}+sBm74vZKFRzhM5a&knF?YXJXRt_vjj_VW#|#{I!tesL>*4b9fuLQ$YAOWvPkp;?82 zi!}s{T6~01M)(+^jexO;4uXzP!zo*EYoEf9C`BGHWY#9G2nzs zvtW@gZoxBzCJa8UNc=?wYQSCPKGpP~I2C=jThTkHiImalITEO&8Vu5-Pk^U#+kH4Wnvu#sB~S literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/axis.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/axis.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8213fba9757578aef83b4e1e0178a2b9687fdd64 GIT binary patch literal 8695 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 L?=-&Jc>DhWU}XD| literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/bar_chart.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/bar_chart.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..218a6d98aee1e944f338bbd24968bfdb328a2879 GIT binary patch literal 3574 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 Pe@)9PHa}`EHXHu|@%HPb literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/data_source.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/data_source.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e916cec5961465d3310e91acfe5793252e8e3507 GIT binary patch literal 6253 zcmb_g%a0sK8SnS>yvO#gAL}@oJRljsJ3tU2A%t14?Igk(A$H|M9~iZ#YP~J@JW}1W z-d*hlHc}*{+_)hm5T_L58fPvrl_4Lf_JZ#INM^#f_S9LwU-}n8# zs`AU_f`(`3%is8ax~yscAYt~gAheL8H*`%Cnx_d}7@^)XJVU3l8Ja!IvwF5?_Z-jZ z<-AjS4+-I7|N#oS?YLH!#hn$xc z$QK|NL=ke4>n}nsi8ACe%aOvS@->#PLEaG8Azx>C1M*Aa2ILzoUx)m%cm?t+EWae`J6hw_O&Ruf;c#kQGIWq>fL=`#nEQrE7EsEPf9yd1%4Pr{)139 zoW!~{7=($vdo+|HvA4p&j}m7mRzd$^lKZ$H%ZE}W?vDI)B>P>74$5~9W7&@|>b)R} z8)i~!%P5v&{7oU1cI9E5l+*IKmoCj<_Aw!}kfPU+NzK!Nydlj~&9kJ9+!3}gh4q>4 z<%EtkJBiaC_3rlK_T4Uhn>|!EkhYPc8nP4pMC)iL26FVdqn+vPhLIGYj-DTH4}?rg z(O{&y@?Ow?Bn8%BMKat|ReY>o74;6ZbE6z>oEZ*RWajDlF+9Qxfy{zDlx2SeE(9vy~F#-S(uU+IJf+weI^p+36&OPNz2zqmas_PUq8+AEq_R zK^N&dNGrS;wMHdc&rEiS#4<9tqZW~Ox`v}WrlxEE;rCse2^h9qU%g_?KX9~l! zKG*i(rNGMd?9Vj|*a0s)S;s;iNOj*2NAk9ex+)mP19kjPp^oP^bWxA?2cuBbAIN%t z)PrSq{jmOE6xV~O|7KkG!^i$nRDbVglfT$qfSpuE6N|ou?8FfIBSY0_I?-+9(VeKzEsq8QXUW5m0_3-%xKr?W1a%d|8=B@ z%)`|!%QARP3m{L3coPjXkTaJQx^#r`h2iC-3-`=}RxM$}JqyBwMsw_*zl*| zAZvHotWlze$`eP}|6-EI`;G^}#e9rs?O(lyGM5lDNA2V3b$Uqh4cB1JTD!nc(A zlCC{bRJU5ucTqc4w6rMlLB%#y4UhT)+$UX`7ZyqdS(K%5eOcIE1#ycaneJ5u`CBo` z!KlcscFzn}KErUPXMcppnVBjyHv|)o`f*|m<2GxtYoj`QsQkP@DRRDsox;N!yNMTy zOek>>2kybYmcaBA)Tb~x3{!19`6*6TJDpj(QJV*A4x$nb$fJ^zvC4UTMFywxlr@nN zS;|oMWIrY!T?yw~XnPAOq86@E(hK;N42$TIe*ad~Vl}9@QF|U#z(m2S_W4K@6!+kui_AeX-%f7{g zB{1JX?R}((?8!9>VAIkRm?w(r7ho~*vV{~83umxU46w)?d`|aVsNNP1ln;M{ zKe@aS0&{o{EExX`g2EORtVW$?50xj*vQpn*MP?Ubl5q@~zF4O50UFM13b4UC$6`i0 zlSW}q=}9j3AJTSUDWg4|$G(pt_i0#iDA%ySWQBB3Ks5`RB@|mo(Fe%RYgQP{Ua5A8 zy})9!fVVbGy(k=*dlAN7fVRsb2W^*<99Br|<>jDmrZGi`rCJ}G{e)S@oJBLp9x6W@ z2K6qQWoAF=Zl&4(0IQB=PgoLi_p5011M9>as_OLtt(qf7vq43GtYTs}AvKsEr%vpc})6LJUq&v6l6 zzBm`Xk2z+bFjf2rFIIx`YgA4_sREQmE?SPqG;Z)HOhcqS#p69M4ZVjienoT0@Bj(T z(h$|J#^e0nY9{2H9Q1jIHQ8IaJkeUap>yL!%`tk@38H@F-wy@W_i5Ftew*WQA1@0ImjG>Q_jIfN^Pc+8D9~XzQ`67 zUc)r}5x&nLoV-{G;os1D3gNZ+$_)uDEt5#=N)O*}QHfQ@p^LXztTl?^9F7#b8*7bLJcH!~(8PT|z2^KtM+?3t ewycs~cT4(Ou2!y9Yqi>1?IQj!;a9;A|NjFO$!#3~ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/descriptors.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/descriptors.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df37f2b05a12f47d2d37087f83d891851c0c4d95 GIT binary patch literal 1221 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/layout.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/layout.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9d581489ce643f6284db3709a1b94fd023ab91b GIT binary patch literal 1826 zcmZ`(OK%)S5T2g*?#|kdV<%R^E4-J89Rv~*2v86ZIM^ek^@YBSI@^`q5%XBO$M$Zt zx8&+yz%THb3%{kWocsq6;s8|5*v_sKy47D-b@lVBsuulz*TORT?N{+vU|D~VvRYhJ z9$?c`1Zhc@TLt5ck!pT-0=l8#x*HG%LD)%#84;<4^K-VBImvvf|c8FT>AS(~Vd`#!97GmWAHvKyS zSe!|V+mdkyTxm;ZVoUdwaSt_o&4uQ#X#s>Qw$7T?g&w_Hc4YU|;nAAbN3C~d+kj0O zJ!O1rZS)3?_HlF@Zo&@kvTge6;AskO8b+0sF@moB2S^bp;pkLWD}M1L6~2Fng&D6Nro#u9Z0 zcNp}Ht08}^?xLbzA$XPGHGkbydcr@wBS)cz6uuahzw5;@P5}R^=zd&tjd`@Zn61e-;zaaW#W-wwUMfcq&vK zw{#6>3lpYkR%UgYzKaLxZ3HXw*?N7lyXxlQ<%&T!NfNb%D;hv0Nd|kgV<`$qQ`1e; zqLN2B$x)jAf{}7z32HGRQH8Yn8Z{jnd(IE#%5{=_95dcYChKYAM&nN*n@nrv(PaE- zEcG@`!BZiJ0?Jyasd|sbiO?0GXDjkMJzal{5ZbQ2!PaYtddMQ%buIQn{j*vBLXKbk zZJ*+%>p2b{WjSUQ<}?HsawuKi!OZk!fEm`69%fj__-$@+|02TG9`1I=RD@kJb`UQM z$ydQkq_x3I!KmIuRegjoaMW8Q)AEL*>r@a&53y<5%5tA0x{XfmsrS*S1_U1v z+$U(bCo0?Bue_j!H1@yb$7uZ$o8CYm@*^VJL7M+jWJ?B{{N82!4khTZ%4Ya==%HTu zy%pV;@4RfDB4!l1qrD;p55M!WnM0V5A#dV`-+k$Xvia}Zux;YBiO%NHbk`L&n>eHB YGT|3(7JNnb(6k_8dqKo@{H^={0RX_FjsO4v literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/legend.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/legend.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0113ecefca5358b732ed7f6fa4428be24302e1f GIT binary patch literal 1956 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/marker.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/marker.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..324fc873e317e90235a6b80b6fabb0a1da837bd0 GIT binary patch literal 2182 zcmZ`)&5s*36rZomOeT|Ty1Ol?MbPruDQyDufRHLwps20X4J++lu#iUHv73zQnF+SD zBvm+Vue~F2cKR^f}-gC0sEo?pVZ{B-;9^3Ezo`-h3i34r#_h0FCkp4jB zYzsiS3tjyJz#QfZr*etwqU;H;@`+ysB(SBgt$pXd!`du)=CFjjM?TpGy~9$_Q>$NO-90Dk z9mDRN%r)sAmn9XYq6dO!o@wnbrkt79SE8f}^zOHF&1+RQ^`la0)64mMD$1hNdEL}J z3rsTNN^|yg(+JaD$q#GI$6T6prS6pV4xOJ2j$qmz*T?Q^{qPZ8G&2o5>Ft(81azhw2yJ*s#f&Qw=_c2b z^J5Rrbo>BdKl6=Wl{EkaE}=p+KiPP{fP_E-cmWa5HDpR&0e2Iq>8^x1p;}Z2kcU8( zPq};>hDHTO?4+TLi+lrJy$`T-mrm|1JwSKqGiT<=%i23~e)g9>*ub*qyl_WZAm0IR zc@@BH-IsJaDGMrgWdl3VC0BN>tOW~m@%W{$HJgyn^&_QUIPwFS|JV1!ibLdv52$4K zWO>MkyNii#>fv570TK-dhrB)kOK`y}UTc--@*}ik zgwDW+sBQqf1zjP;Uf{)^_fP2e+!PL$dV%k__W9d-{;y;mskV^ z)iLdRRMXw2tTpl3uHMgFOJ9lSS}`axe#z#hU)F~}tcQvZ+&-+3e3w%L-~~TcR+cS^}us)86ex2Ex=$I+B&$YTTln#G6Y+= zi~u9726+uWki~KcFZl@qZcToQ@EO8O3fu-YYs>f0`dW8O7WA}~_;2cw6L=x0O#mlB zF0USBG;n(JG@>tQ9V4x=WpBvqFiU0#SP}AL1mweNKM_jy43@mev)7*n2`z6TbP?>9 zPpK?#q3yK{9>D15&=u|n863I^us5*?97YxgYZkA#>^gr+oHfcWk!vUQK44rGvYhHh zs)0K5-;$VKvVET4sxEQ!m=d)Okh=X=zx8GW MQG$Ah$M0YH7dH<2mH+?% literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/picture.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/picture.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad346eb218341998476ad898ec3b49e69b7c6b64 GIT binary patch literal 1059 zcmZuwOK;RL5VrH&rrD(k5EY!@mIIpu7myIDcoZ&ODy3X}AaY~Vjp{tGy-OGEjb8Z$ z+_?43zwwn5e*rFxlPyXSWBJQWGGl+=IHu!qgut@zU+KAr&<`674c?(WnCcM#LktU4 zVS=&s9pO|iaVw8_l~4RCAOS|N5ObM#j+n>sXP1P`&rlNlgld#v6K7nORFsMy3!Zo; znsKE$d)73dcM=omh0+q(&yBA%Elx?)J+gvAD;yInsaS9&w?M;%Z(`cE zVs+H)%ch2QY@)1WT*31Fx0f|DpiED=N}HC~?dnXV z#hgl=ZX`|Hl^NxE38M0R7j{zJuNwxqAD?a?UO9;AWp3intR)j$Uek)_xry?;YS=

AF;N7y<&SYI@tuux!@JAwaRmO%f7dL7{E;%h5)vY>JGrj z@thdjv4dmBa}nN*-(B7;8IgOJU6{g=7qV=1Bh^$5?vJ!5N$&nPum9$Frs@T~L{{XWd4cPzy literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/pie_chart.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/pie_chart.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5205c9b282e02092eecdd8a21d45cc18562fbd60 GIT binary patch literal 4621 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/print_settings.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/print_settings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28a0177bb756861ceb97e6554860e7b5010ce0bb GIT binary patch literal 1730 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 I2%qcz|Cbz|YXATM literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/series.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/series.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fc3a3cebd9e0aecf001b2d8fb57e6545a7f3fea GIT binary patch literal 4319 zcmbtX%WoUU8Q&L|Z|X%oEXnrzm4rzw#<9~jZJIiXQ^%;o2$fnib6G4oBWdk@FtbaQ z61JDxKu)lM-z+In4hsb667%CX-^_gT`d;HTn>7vJ z-ar26wbwQ6pH!Lss;JyYNxlJajq8CH>P**(HUc9wnHgHl3QMdM+RP5itQ=NYMYWqj zHLS6kqOG7FHdrHUvS!#~t#E-YfM4Qvuoy0}B}JEm<#2_qgl*OiSJ`T~#?}ahW)GzT()HlTqwBO`gds=7vYwTU?=y_vLNZ$+m#Onv5 zW9Oy)lcC_by_ZTqI?T(DqEs9TnOh&magdij2z)QeD|_OLk%$HY0-7I>QxPS896k1v zv}5JXo=8%`rvqz6RSlms_2NkEi8P-y?Tc~xwO%Y|<)QS3NB+PIc4a&iGWA6QnT-#| z54_adi$`)GFvBREsoJRiQXHr?;~#6Pek^4yKlEfm3#6`(y}k(40?oWijNRCe(!90n z57LnopAA!NAj$1dJ^55f60t8u!~;JPoqBHie4LkJ$#D=yI?kJ~SL8mDRr#e?gRg8y0ObRn2=4?ar2gx@s?&Q4t+;zU({ruowB{{DVyb0*o z^HzAilTIbKJSn`~?0b^g#o^=@X$3QfzF;NkakP{ZTx}|d7|8=~AO;YA;j>V?kCMC( z5E|3D#tg1AQ&^(J4Q_sAFk6&GWkOd)ja$!jR_7(w;8kAcmFGHZavQc=%j;ycB2h}M z6lnj2kvDO!VgSo?Qwe-%{y;)^ySpbxX&iO; z21jrl-JN3*9d`r2-yNQ$M{#s_=iP4Nr{ea|8$9(6Mbd?}N5hkG&>b9kGVLa6hdaZQ zyyCikWyP?J zhThQeZ)&>sAJsZQ>xFN1QcHU9gR|RM5Y68ju6={IiobX!U z+2AdJw>aS~iDj`eq1$LLqkR?aYZKl&copzAz`HczZGzsK(A%IdPv|S?TSeci;5ifC zHPF{5^bK(nRP8IB-4d@HYP|keoi|W>bwG?pi7 zpzwInm6_z;adrR`{tPZSa4PYZ{0-IS2*vb}w3)v!YF1u% zVL4j{ZzWp+wzD>Hm3)o-5f;~3d)YBBfUCmf_Xvtb+|^qjE#|Ic@Es@z7p^BDd`U6calHOQTeP?EO*+PbaNucEM^=i#*dbfHkgGe`Ix z|4v?xiV~k8^o(@rWZDxAMSp6@P37$;K0h^5Xv8z)&sL`YMTZR;r(65bc+$hMGw>oO z60i#?`d)%8vDYaTxeOg2GLjJzH~E0*avHlxfJDbE&i6i;>X2U`!*bn0;3ecr&rjhK zbod)eqEdLH0?$eNK5b3*HJ*9i4QOOekqFTu^=!OvCsF*6&O=?rR zVb=7PYN1{=P&Kn=SQg^p_xt^;c1P*c08?K0$j{wJQHz<4o5Fg6=Nk|YI3S*IKs@4r zRaOP!5eKZZIuMUIAf9nRJmi3Q$^jSIA`nkG;4)hV;wi@s*n^o@e)D;ulBf3`AoD@~ z2ZMmOM_T3_#Igtu3F3s8I!E5IaK?@oapz>xr`Kt}q?+;<*oWHXj7Dw~j~)jzK|`sF z(v)-OrgY%k!<~B^b$REYZtye6`#VbF04P71c$VouS)73%p)RR+uXBrCn5?1gR48?z(EP(!8dY-?_ZNIf(QD^{kU ztbh_YW~*mWqU?5Jgo%1QN@ZTkn3idNO8lS@^j>t44pyS-}cht(qiymoVmDu literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/series_factory.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/series_factory.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..857d5df87aaf02550a97217aaff41bc2f7ec9f3a GIT binary patch literal 1174 zcmYjQ&2AGh5MFzKvq_p#Dp5Fv;~vNsaYBL$)T-iuqKZfee2Ls$r&-vaU^}5{H>Yy# z6L4vdoRN429)OdNAR+Y?I5Bqm=haSTKF@e|zR#iUc4)yGeE&+{xt8@u2e+34!3%iB zTQI4WqTDJFK?Nqbu!)T{hI6NIiEDg2_X?l*ML>e0K^jF!LcpEW&6`Dww2C%qLmZ|- z+W3OVO6m=)sCj|l@W@u}{p$T%w#7CFirFrG1? zf_LvPz2@@wHQi@9Y)cO57tp|puT518tFnpTnx#h5W!(t`DO6QJRG z1>k84w&4|j!APsNBs#%jU7{N3I7JJqcKB9po#At9;imY^uB{JF?VjU>EnVr=_6bUD z$g|6}+D@G*;(O8s&()apWdP@RrzrIy_G)A@enKB)`qa<|nLbE1POODrd$m6dWfSVq zRUII$8k@>rP>D_D52(bZ5}PhqXP+R*ZI>;%a(!0k@~eK=Htem|K1dQAJrBFEwySc+ z%1m?J-KB}F_`Ex+csJpU%B(!-CS%HFHwDqU!U)kbbrB*hL-0^Y4tYoZ@#f;;@#6(- zcmRPUeNYn3v>9ozG+IY@RG5jZ6p}(quBA}+q?#xXx)xND# zad|(N6MwV#24w%Ealz7z#;+((cX&2q@!otat1=!WIsgCw literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/shapes.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/shapes.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbe177df011cb7086dab989cdb1bc45eb2616660 GIT binary patch literal 2293 zcmZuyOK%%D5Z;wm+J_!?9(G<%vT2eQDpCot4@HV1jbW#0fz&lB0YQ0L&`U~Mg!?E- z*;b*RoNInTZ@u;0ztCT?*PeRssh4(!l4>@`S|!!gA&%)+MJ=t9y5Wv+Q;&Ft*MvVn`??6y25A_X zE1GGGw9+%ObVS#P}m;>v|lC}Ka_dF zrB1l&)JnHMrUl>G2NLrPNn;>sriQspxA)8Sy@}F!`hw>v*K&%IL&+7_=aLv#x!Ri0 zc?O^Q9e`WJW)`V1n^akaIJ`EpSoIg1Id5%J=PvhHjn&^)h|dG&p4p@UvgSh80^VNm zHNMU-F%L#v=2y-vauswN3*9F0YYToI_>Bd>2|j)Bc?)D)3)yYpcNY9E=mXG)AiKAa zeZU&zK3iig)_!A?2doL1STC=hPG3CR|Lz?Z@41 z_&t8|X{hK_g?gN*&jMpR02*?^Q=aKi6+BLckO{EhA;z3)on#}VMpRIVcJY`>y&TmI z2V-u0#gH6K#G|l~OQuX3*`ttVEIb{`bU93g;492>m~~PVxzwDUdpbJ#CTBc^*=2Gx z(L97j{@-+E^(^Nq`$&g^(_cVRZ%!*XXZexf)FlPqM(aLCOz3PEG8x@PUf zQN^L`G)OfTvUKP~=foLFIwf_XA=*>oCfTteK8jzB6U~W#1TJ11X<(=kmo>x7+6fw! z^>M-&&!FDx$5c$X8o;4x@lyykY&z0#ddh$bk)K3ap7GMbFoCt7(UhwK)>u|VR>A^z zSjU%}P{yUVjBr`c@-Gu1N?+w7VTO9Jzy&KHgea5Oag@8rhP(m2gSZ8_Y{)C{?ZKx8 z05f}LMb@kWXwRyEm6-!roz(!HSsk!8bD1@<+|fIjmTRtmE1zOjc^CSLof^WmL(-`qU8 z^FjBJ86P;jtn*X-Qt4kU`2f0qf8R?vOK9&aD%rkFj(P8JI@WpC>&N3f7rpK=&yIT{ zIqDTtJ+aG)r+Ki_IuFq`R!9_;K@_Dqn+W91DEeVS#X{po5zAu`yPA$r zM%)9au|C|MAuL5!5gG_4i1^YFUoUT5iFc&}c^|DH28)BQqDX#-qcP7b@DRIgfNSunrvP5X37ks1*0eplX*a6@ zBnJK~i#|##j$_#u{{Aw)4mwxJy=5hI*?-TS>Z+9&Q{pirS1+=gr^6)S)(A9aeoA` z_!eIK3P2J`HK{106uGCp%4dERu%HTA=xn}@ssS4S4`it0Dq)G^kxr{28&(<1su3GG z+dyws+ictMSnpK3Y}fHb?^XM3pOPPlOy%$ukwcZ-`0PMtGcq2%gGk7j_SsCCQs~l( zGp)vcAD%8-CHr`$Ud~lrs6KqEOJQN>==(*dYFjq-w6xtjy8b@-?n0REnXveAe5w}R z(?x0f;mN!L$Aw#@ZqKx=75YhkZjYPm`k64!>#^v>RGcY|Ug75m15&oY`0fwT)eZYQ zOo}in3G*anz6w+*y*HFaYM^52%ixX25|zsEhO!~pvW;zYk8c6r-q?45?{4@W@O|g) z0N9~p4`jq1$^m;Mv)AODIyDpa_%#U#J3=k{bk;S+rOT3~_Ku@yYSF5qQ<8U}>*?4t zI}m=K3ay)KUNHEWy~IKN~CF8r&^z0l+)&V!{>MUu|O<-lyAvN zF>uB%@V2jI~KXbzD*KzN9d0QAv<3upj?2cnObZqbK!-a=N}Xp1>7>$2l~ z9QT1$`uu+#_7QZ`)ONjhdHt653Fn0t)^cthfur}ov0@~N=I;#fiPZOKAp*Wa|f3DcFL5(7Wfo z`{rbP$e!FvK9O+5P1$yhu@j8x_6jl{cx4A4+Gm_7PAI3x(T6ro)B<8RCul{e1R4K literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/surface_chart.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/surface_chart.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..828e178e95a22ba7933630744429ee49c8d76dea GIT binary patch literal 3354 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/title.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/title.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c050386ac4e15203e51a74940ee6fee20b97f2d0 GIT binary patch literal 2324 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/updown_bars.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chart/__pycache__/updown_bars.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28946b536c96b4a29f58be8f937fab44bd2b5efe GIT binary patch literal 1056 zcmZuvL2uJA6t#y!Snu4TmX$G5z(EeG3u#?Tz+`z<34AegNQz zV}%M#F!nr9K@pO$;h~C(4(R}oct^!WmvkE*tEA|WUc6y%#Iy99Q z#z|qpIo+8t;|^rmSep}Jo#1;+zb>kI>2jZ|J#KaG!>&LCb3_7;NhqRoL^>iCT^{i8 zdq9#F?*UJ*_xn5oB|YBd$pt1u9)qT|PT%R>>a2Xjj3x10AH(-(6x2hp&?1M5r~`3z z%9N_kXjzrwGOUvmrr%5MW;I0k$U5QI|G4@AMrLys;Ik1SasD6LaUi;B;c=L1T=%$d4A z={k1esYxJgtrQ+NGF=b1nvK77b02^}xKN@HrL&Zp2Y%nTp#|IC>;d>DHhoTlD2VaE z&mamBZsy;{XEGX+CpQfkb76BMwW~n7ZM|*duZ?Wqh&(bq{>yEj5ieFR4ymtb6#g4R^Wa^+^ zxc742@Gk51ViJ^H&ukI;-on440Mp>AAA%E36)EY4Qo^H*B%KEh=Y-zknPARW0@1IM zXcHG-bnS-=ah*_~cx|CXE*UJD<8vo#gmojl(U29w7zJsKx&|e8U>h%t9;PFxq{o3} l#eway0%K9z=A_^^19>jB^m(4z=GGmTh94D9+R{b&K6wBD literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/chartsheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/chartsheet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..352341ca1dcc0baf6d22dab3fea6588878986109 GIT binary patch literal 3319 zcmaJ@Nt4^g6$U_n;7$&QvuL!AG+Jm|q#akKvN)+IvYe4iW=)x@IMo*f!|oX(YAk6q zXA6|esLCh*f?TAd&-oYm34P7URk`GvLn`kzz-3Y@1BJ)08;=j&?|ol)`^{#}fNT8s zzxn@Y8^%Aev3fbs_zgVif5A|Ln%oFYVw#98ZiOXM3T`o7 zV~5wmI;n>Z($MWPZ-y<>3frWu+b-{fYh*3#lCEx7_lk3oK&?dhT-Xu4}TjZ8*xA^Vw4!NUoo9~8q$z6>*{9d?6_B3AO_rnL| zfyQ0_FnmNFna1x7x=wp<4BBJcta@paKHV4_{mmaB2S(piCBcsRCFQ(4PZ(9DF_X%9 z!UI2rcg<&Kl0|6{M=yd@LPulFMBwuv^$$7gTdMWsFpc?4vM14G8Vl7OCxZDjoiZjr z7yfAw9fMc>z&~bR`QkW;Qq?*TK_syUJ~d0=b>nNseF+~)r$M4RtF7X*?mQNZU?1W$ z@kJaI*j92Pp1K=5Ux4ifA;L9_tlfR80Hh#mwJITH+m_MT3CA@wW{M4T`o1BUsy+ArAzM-BbmrT^~(c3O;j zNIPTlWJ(g4&fdRu_o1?~V|D{vR^dVza@HwwH7c&otMfUgyJ2k`X* z-vE5Gz_-|KTA@|=>K*9a)!)C%?$O$%Np@+S?9m3fPdC3cj!fF2Yj3LL0qxTDHwJmA z`+9WaO@%!A*02rI2U>2a+A?3HHd)kxIJ*Dh#_)^}m)+@5SLrVwzaE^0{NhVLNq8{P zY#N?KbO6PR;KflaLSLqj~* za~_|1Q5*qD#0{to2**7M(g4(;%F}>S7LnQs6X*LHL#@jvKA*8vHKwb)tt+=+m#S%i zv`JNaPPu|wsx#3P8Y5A2mwK|Ir7AYGjKnUCM!7WCoT?T8Sneh{tyTNPk z;D3`2nE8d+hNlEi`d2X0kY;9FT5qkanYG?org3a!hF8j<*RwN=8Z$$DB%Mp+wUt@W zSI$ac-OL7C$sDlNtPHl6x!`MxKV&Yn>as!2ON`OXs$g4L4QxBBgY9H(uxr!+Mpz?a zi|6!{epfl^474b&!=O~f^MWXlo~K+dFPyjw{bC!VHc@bh6B zNcK2^)eAU~4w2}|`4w%f$YhY5iy?gIGk8+Wld;w{%Wzk;MKo{K3VgAy1*cOom?wK; z;eU$@F|y<-JSpZ~8ka_9dQj6pS#N!C|0;7gCTl@r? z#L;!s3CYdR(5dQEnpgOb&`gn<1`PXRWi1{oTqd~6&o3O?g2`V(LyynM#|L9Hy^^#g z@DsRQerLSKal7d+GV^VP!q|b$=G$s!;Vb~vs3q&j{>+3Kl}5SZ99Su$wBHa_h)*hB zp*uZ4QZ9%&fOk1-T^V~)FqZ$>J_;hL%7TUQ2~(w%NwEg5{gTdyve8pnjEkRQ03?dQ zl!#w}Veq6{w~&mQ*@CLo;BJ@~E1S`v_*}m|TD`<$jDpJVe}F=Y{E&f=YMvLybjA@k zJnzplpBHaj=^rB`EswGzf5xS!=}fAgU_4(O9WU|alvKk2g5h#0F#lo`&HpKL3;VP@ zuOZSJ#gkhu)H))q)}>H;*r(OHfk;bs2NCL4l+k>O2FtBACU6VQ<&LL50`p;<;i3qx{VNMw5g*fxX>k{bf*1~btK zBD?Q(NLLPY!o{!cE&cM{WObWd5FFKMUTpSd_r>%X-8_w;xMRrtviaXS|F*3bAtQ0-h@E?dhZsmw%4fL(e~sGF3w6WlX*zXmxC>%+tT_UrmHr3PQD|NO literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/custom.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/custom.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96a92857f8b687cf1ec0dda8a8dd140ce8f1f419 GIT binary patch literal 1751 zcmZ`(%Wm8@6s6wMylkg-)24a%0jNMni>z7{MNk)syXe5c7+r7?K@qQKgy=CLHDix4 zyJVX$$gVrD`#0Qn)lXo$3tl#f2EaTtbW^aAQ{-9v9xEMUf zs#*lWgjKT2;+93+F73+UPUUj9^0-&|+^+&2m~T!QRuPX3cgs%INaV-WHuPi@Sf~%q+Ha?*K9Z@=4RQAY)IvKQ>LNgP-db zCLpylhE{t=%NYnAj^X7T>KyPl_vyS4^!pfOk(GtYo|h2Yx_g*S;FnBJidyNxp)Bf_ zT5y#4ET<_qOMt;+tm+s67`FuDwy?MZ?kmPU@DT$DCn(BqmT=x!Jc5pJg@>#QJ?JC% z-`ISBeCr+GM!xfo-w*-+Nc2QUbYEM1SA@9nzTVxRtG223rp5Vx`JV^u?hm9=6iTiJ-#%0aYOE~2yY5Z#rJ z=&b@oU$8Mdw?=WG-3cC-_EetXc0AQ)E$Ha1X{w{+Eq{<%TK7~ zQ-WIrpAo#@^_Cr{6!6fVBAzt1_cHXLqk%)71f^ z%OiQ81|o;ZhXkJ!&>dq0T2J!@h5sx3m-zJ&Rz*uD;f>h;NNOL&JN*8IONS?od|@t+ z;W~ZOV{?H@S}xzc>?T0vvY54vRMI%bSjf1?*o9WdZ(Y}Y2&Xxe#(ez2wH?&P%_}1s v=;md=tL4!}vv#F1Bw5RBXG3sfK{B2Wzh5WA542WAeS7xpfLyojZ{PbD5)7Y4 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/properties.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/properties.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5346c01300b3b2c017e6e8bc2b2af87d26bb0076 GIT binary patch literal 924 zcmZuvy^hmB5Z?8Fd=7`uK}SL9g1DeULMOx_L1mqktF~EfyhCEK{$Y3TCnz1)-V^Wu zh?=)?OT{Z7Au;P*q7*llKhMn0c)s~;F`cFaZT{=K_~8-q+XaUfVDJ`i<3JKg6=^7= zjmNykXFhc~PX$fL!qN9t)Wj^NY1sI2Co$7SunhtGG>J+m;7?p&1k}@9x=}G^NvJj$sFNOpr z=m&QmOJ&H8VQ41odlNgrXLGY2mHljukI_`4_?khLg23N7F3`U;Bk^~!c_F|Ssr zb1}Pu_Nq|zqUhIl)wQo?uM1OKc-f2Uvsi*DaP78V->9P6Yfgb|JC*XIw(H``U`ep#0J)U~&`4d+A3dDF>D<@}8EFPB2ypTt%yovWQ-+f{w% zjr%$wwHrHb&ae{{G|*bZxqj^OPKpCKc43U*+RYP$ga%$pQ<`~!Pbgmh#`7|tu#=qt zGYO{Bb#FUuwme1(ZaRDMP|?gB{IWW9b;tWUW7i7ImbRmEKyPI0I1b61tsbXt(x~i7 In9Xb5}-;*2vsc-uo+bv#KIfP6Prn>^T77B9h4nx z`2xOxHGh*WE0!!_#dT&n9ic9;<&%4zYv-P0->}iBkDyJze`UWsgnn3OyY;|1f}sfj zM;uE;?9-9BaEMd5#4SAHVLRu_Uf~nJvppFUAqhLXC!?ZI`o(|@FnWo&&x6m12O<=M zPaYX^JVkN%6ZS`w*loSzs*-K+x-?=gRO?TT%FB6-+uUt7V( z3PrS@>x;qK8g8r+_s9+pJ9e1t@*c2?+WnJ^DWfwX%sQRCD#`9Y6K{Y{pGjF=(6TB; z9JE1NfdHjxgKG93Na!})FpDhFw@fZX8|gYPPpSn3Mw&^pnjJAYuT*ZbV!{eBNy9Df z)^=c=So((m4Q>!cjRS}q7tm=uK)2}udW{d*YXXiISUqY2@O+Md8lJ>~8o+urw6J4g z*TSBKTNXwDaoBoV$ant{`VFbuu;R}wDFn}1@`@?`LgnWoIbCL^DwAoN!PQ8{=b}7M zWIjvkrOB%D@%U+?b0Z$sEIng$p_8f>Wxc$VNxBg~saKZ))F0Q&Hl#E!b3^G}_@cHc zqsYhCJ;WamZaTT~)w(#CtXjNhH!DGBO5x7YqT&l_?IEQf7EG?+1cuG6si4T#^4QW^ z>DEc+TDMcs9?!L2ZA2+EMrlgb9s9UYkm3)uqv8WpgI?ZVDQU%T!2ZsgMUZ2I#FKRlbco*u+7do^_Ql ozCImgYg5@<+NHTgwBv2rw&Amt%Fi))+Wx>oXCDvo(Ao3<0#;lzq5uE@ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/publish.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/publish.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..baff8e6c95e975f964f9365e47acaa027a9c3f49 GIT binary patch literal 1572 zcmZ`(OOG2x5bmD0C$@J3fdmMUNL=O+k3`^rgwXO3uo5y+yb@9$G8#`;?16cur|ra% zZ<{N>fE%|x^KbgfiGP4NAgbECi#L!S)i+hsGhMH*CLfJrhHLiQFZum|vERwqUmhAq zDE)T`l1W~%nhRb#!l_;1)}HWcU-+E%oGPe85pwnoldkk$FzG>f>WT<$Uj}Fg)*i@k z#**j{Jdq{7@xEx=%7n*_h6N}S%(N<-g>h$~O*DfiD`*NxxEansm3dY6`MiRHKHgsu z8b>JoBLrZAOC}u2g$v#(6FvlpAw&xd^H(n2pSc*=XE6lQlRnzltUbK6M=(C$Igl8# zj9zeYC<6>VHsc>){$Mr7V3x-kYBPR%#ZDsO_e%gR1|24&KUa{8IA7`Z>$1|Aq_6U3 z0m3hqxzZx;%5|$xC@#}VyH;iAG8gC;?_91k;8YKhc^-w)l*D*Nt z8o}!XHwfM!c$45J!7YR{rryTeUs+m%Ec5i+T*oo^?nWkeNnGC0r40 zrv-VuSfpPPnaH7$#HU1NZprKrQ5Cf*agAF+WBj6BHQFAPxQe={r~vlC%O#@PeT>pm z*_jvf=eJ&p_21??(`j-|y^HUu_Xyr6xI;iO?tI|?a#wfjO<=d7f*Km#XW6S8br-+7 zhtgA7s8KPG9go`cx1zmi5{nxbw4LC16{_rXt9oTwVBK5ZSjtcg;_m;wKI!*5jOck&B)Vz8wzT!#n=~45w)( literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/relation.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/__pycache__/relation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..001d131bea361ab6a2d9bb03b1dba64fafef968d GIT binary patch literal 2400 zcmcJR%W~sH6o%E3EXj_YIJR@0OOlykz+9{aD=3NzFoaM=g%oDh8;wTYwpGZIX(^e> zly=FMC*TRDSn@XBvSPstu;M>*B0HX;U;%RJuXDcBI^C^4-Ll_k)HGcEUw`(0f%O|X ziQJDJAA{o#liW`87Q_PHPYFjUD|EKb89J5!R|lbI1v zx|@q^y1&4k(o4!&u6dYqUwg$)Ffu(97`>UwXrLU#UulY({rS`#OA-3+v9H7v6`o7? z^kS4HvD*(uNfNoea~YqzQTWWATx6pp{;>Cvn}(VEV1j}9Lz%kCM8=bg7m*u`e3hj% zXjfgolHTNE>UdrlhneTC(b$SXvzL2so?P#y?Mc_B2V;LMJ#Sj`ym2DVBC_kA_v4u# zT}SN9ACjqP>5GY}I$1Qg1z922f{>At;V_d~dcj^>r#9k;)bJ^ruah*eMy9L?CiI^e zx1=qMOU9j&R6$ITT1i$wDj@ZeG(aqn)sn1%*dWc4tb;fpt&+4ssvw<`Y=G22HcPSv zvI4SQk{ysb$ZkpQfHXkvmgFADD#-nk?18L-?3d&KqzUq%Bo9H>K^~RlEsz$-KTb#@`bgsPvs_^@;MIhw~i;PN^V1~+=1G873$$wQJy zkZwm+$);04{cpW2Z$8?UALC9RL$nQx{qyf-tE`LiTUcb8Us|J#)RT4U77ES|N z*4Zo#SiBX<2foCSH;d4lUyK}uD$OWVEC=rd5+4J#l#UjO zZ!Cu932E(a$L1+%NqJj^!_o2pX`OjzAu>-$tA8sr&qp;eo9|JQV3_56sZM6Ing!+Pe<>$-05--vI|mpa|}%ZG4Dn%7X}(#w$1BO8*~g) zP-R|~IzOnOpg6k&>sjYMK?9iJk_k(4VS^(rX}{yb1#e^d;GHuOYTeKMe8n=hvJm;oMzmHAqAgjgb?tk0`lUo2_a4V`*Zqb2L=Ht7~|Gx=wI|;3h)n!NR=OMvcfr@b_;? z4YJIWpK~Q&sPY7oUzRh|Hc48{uwlvg1e%kiDi4zR(#+cC`|+bhmj)ip^WrE!1f8^V zXy(gBl@wdXk`v5IkLSxS$g;92O_uHBVLCuyzQ^e~yK|M{@#UAXJ6Ur#nZ(y9K$F*y zWnGkIbt{i6vWHpr<~Xl5JHE*e$y7w{x^A1StHmo4yP_Vb+Xxf`iLIeAI?L48@zP9^#L$Y7|^)p+-Hx+am2=i6JU6 zlA-ivfJz+nN{qUjS9zklA)>7sbr8CI!`hImq71j;dJURXIj_3_E9s z{14vC5Pd0Ad$=82eKCEc*jBgD)!RL7I~?`&5N{7~>R#whzyp4E|MQGLcGtqT4%HnB zN}5o22`DQ?7y9GLpR`Ner``YU65XcnaB3=G-K9-`{#CR$i3j5LrBcTdbfL<*X_a1U zrk6{v4@yGZ`_h+VSQJq80utYT@dLf{ccpv`w62EE-=VD+8FAMIkJo~JO2_w?W8WL{ Ip?!V-Kkp+%^8f$< literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/chartsheet.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/chartsheet.py new file mode 100644 index 00000000..2991b2ef --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/chartsheet.py @@ -0,0 +1,109 @@ +# Copyright (c) 2010-2021 openpyxl + +from weakref import ref + +from openpyxl.descriptors import Typed, Set, Alias +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.drawing.spreadsheet_drawing import ( + AbsoluteAnchor, + SpreadsheetDrawing, +) +from openpyxl.worksheet.page import ( + PageMargins, + PrintPageSetup +) +from openpyxl.packaging.relationship import Relationship, RelationshipList +from openpyxl.worksheet.drawing import Drawing +from openpyxl.worksheet.header_footer import HeaderFooter +from openpyxl.workbook.child import _WorkbookChild +from openpyxl.xml.constants import SHEET_MAIN_NS, REL_NS + +from .relation import DrawingHF, SheetBackgroundPicture +from .properties import ChartsheetProperties +from .protection import ChartsheetProtection +from .views import ChartsheetViewList +from .custom import CustomChartsheetViews +from .publish import WebPublishItems + + +class Chartsheet(_WorkbookChild, Serialisable): + + tagname = "chartsheet" + _default_title = "Chart" + _rel_type = "chartsheet" + _path = "/xl/chartsheets/sheet{0}.xml" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml" + + sheetPr = Typed(expected_type=ChartsheetProperties, allow_none=True) + sheetViews = Typed(expected_type=ChartsheetViewList) + sheetProtection = Typed(expected_type=ChartsheetProtection, allow_none=True) + customSheetViews = Typed(expected_type=CustomChartsheetViews, allow_none=True) + pageMargins = Typed(expected_type=PageMargins, allow_none=True) + pageSetup = Typed(expected_type=PrintPageSetup, allow_none=True) + drawing = Typed(expected_type=Drawing, allow_none=True) + drawingHF = Typed(expected_type=DrawingHF, allow_none=True) + picture = Typed(expected_type=SheetBackgroundPicture, allow_none=True) + webPublishItems = Typed(expected_type=WebPublishItems, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + sheet_state = Set(values=('visible', 'hidden', 'veryHidden')) + headerFooter = Typed(expected_type=HeaderFooter) + HeaderFooter = Alias('headerFooter') + + __elements__ = ( + 'sheetPr', 'sheetViews', 'sheetProtection', 'customSheetViews', + 'pageMargins', 'pageSetup', 'headerFooter', 'drawing', 'drawingHF', + 'picture', 'webPublishItems') + + __attrs__ = () + + def __init__(self, + sheetPr=None, + sheetViews=None, + sheetProtection=None, + customSheetViews=None, + pageMargins=None, + pageSetup=None, + headerFooter=None, + drawing=None, + drawingHF=None, + picture=None, + webPublishItems=None, + extLst=None, + parent=None, + title="", + sheet_state='visible', + ): + super(Chartsheet, self).__init__(parent, title) + self._charts = [] + self.sheetPr = sheetPr + if sheetViews is None: + sheetViews = ChartsheetViewList() + self.sheetViews = sheetViews + self.sheetProtection = sheetProtection + self.customSheetViews = customSheetViews + self.pageMargins = pageMargins + self.pageSetup = pageSetup + if headerFooter is not None: + self.headerFooter = headerFooter + self.drawing = Drawing("rId1") + self.drawingHF = drawingHF + self.picture = picture + self.webPublishItems = webPublishItems + self.sheet_state = sheet_state + + + def add_chart(self, chart): + chart.anchor = AbsoluteAnchor() + self._charts.append(chart) + + + def to_tree(self): + self._drawing = SpreadsheetDrawing() + self._drawing.charts = self._charts + tree = super(Chartsheet, self).to_tree() + if not self.headerFooter: + el = tree.find('headerFooter') + tree.remove(el) + tree.set("xmlns", SHEET_MAIN_NS) + return tree diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/custom.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/custom.py new file mode 100644 index 00000000..c22a13fc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/custom.py @@ -0,0 +1,61 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.worksheet.header_footer import HeaderFooter + +from openpyxl.descriptors import ( + Bool, + Integer, + Set, + Typed, + Sequence +) +from openpyxl.descriptors.excel import Guid +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.worksheet.page import ( + PageMargins, + PrintPageSetup +) + + +class CustomChartsheetView(Serialisable): + tagname = "customSheetView" + + guid = Guid() + scale = Integer() + state = Set(values=(['visible', 'hidden', 'veryHidden'])) + zoomToFit = Bool(allow_none=True) + pageMargins = Typed(expected_type=PageMargins, allow_none=True) + pageSetup = Typed(expected_type=PrintPageSetup, allow_none=True) + headerFooter = Typed(expected_type=HeaderFooter, allow_none=True) + + __elements__ = ('pageMargins', 'pageSetup', 'headerFooter') + + def __init__(self, + guid=None, + scale=None, + state='visible', + zoomToFit=None, + pageMargins=None, + pageSetup=None, + headerFooter=None, + ): + self.guid = guid + self.scale = scale + self.state = state + self.zoomToFit = zoomToFit + self.pageMargins = pageMargins + self.pageSetup = pageSetup + self.headerFooter = headerFooter + + +class CustomChartsheetViews(Serialisable): + tagname = "customSheetViews" + + customSheetView = Sequence(expected_type=CustomChartsheetView, allow_none=True) + + __elements__ = ('customSheetView',) + + def __init__(self, + customSheetView=None, + ): + self.customSheetView = customSheetView diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/properties.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/properties.py new file mode 100644 index 00000000..7bece8df --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/properties.py @@ -0,0 +1,28 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Bool, + String, + Typed +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.styles import Color + + +class ChartsheetProperties(Serialisable): + tagname = "sheetPr" + + published = Bool(allow_none=True) + codeName = String(allow_none=True) + tabColor = Typed(expected_type=Color, allow_none=True) + + __elements__ = ('tabColor',) + + def __init__(self, + published=None, + codeName=None, + tabColor=None, + ): + self.published = published + self.codeName = codeName + self.tabColor = tabColor diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/protection.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/protection.py new file mode 100644 index 00000000..f76a306b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/protection.py @@ -0,0 +1,41 @@ +import hashlib + +from openpyxl.descriptors import (Bool, Integer, String) +from openpyxl.descriptors.excel import Base64Binary +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.worksheet.protection import ( + hash_password, + _Protected +) + + +class ChartsheetProtection(Serialisable, _Protected): + tagname = "sheetProtection" + + algorithmName = String(allow_none=True) + hashValue = Base64Binary(allow_none=True) + saltValue = Base64Binary(allow_none=True) + spinCount = Integer(allow_none=True) + content = Bool(allow_none=True) + objects = Bool(allow_none=True) + + __attrs__ = ("content", "objects", "password", "hashValue", "spinCount", "saltValue", "algorithmName") + + def __init__(self, + content=None, + objects=None, + hashValue=None, + spinCount=None, + saltValue=None, + algorithmName=None, + password=None, + ): + self.content = content + self.objects = objects + self.hashValue = hashValue + self.spinCount = spinCount + self.saltValue = saltValue + self.algorithmName = algorithmName + if password is not None: + self.password = password diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/publish.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/publish.py new file mode 100644 index 00000000..0d397525 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/publish.py @@ -0,0 +1,58 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Bool, + Integer, + String, + Set, + Sequence +) +from openpyxl.descriptors.serialisable import Serialisable + + +class WebPublishItem(Serialisable): + tagname = "webPublishItem" + + id = Integer() + divId = String() + sourceType = Set(values=(['sheet', 'printArea', 'autoFilter', 'range', 'chart', 'pivotTable', 'query', 'label'])) + sourceRef = String() + sourceObject = String(allow_none=True) + destinationFile = String() + title = String(allow_none=True) + autoRepublish = Bool(allow_none=True) + + def __init__(self, + id=None, + divId=None, + sourceType=None, + sourceRef=None, + sourceObject=None, + destinationFile=None, + title=None, + autoRepublish=None, + ): + self.id = id + self.divId = divId + self.sourceType = sourceType + self.sourceRef = sourceRef + self.sourceObject = sourceObject + self.destinationFile = destinationFile + self.title = title + self.autoRepublish = autoRepublish + + +class WebPublishItems(Serialisable): + tagname = "WebPublishItems" + + count = Integer(allow_none=True) + webPublishItem = Sequence(expected_type=WebPublishItem, ) + + __elements__ = ('webPublishItem',) + + def __init__(self, + count=None, + webPublishItem=None, + ): + self.count = len(webPublishItem) + self.webPublishItem = webPublishItem diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/relation.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/relation.py new file mode 100644 index 00000000..f42e3b0e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/relation.py @@ -0,0 +1,97 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Integer, + Alias +) +from openpyxl.descriptors.excel import Relation +from openpyxl.descriptors.serialisable import Serialisable + + +class SheetBackgroundPicture(Serialisable): + tagname = "picture" + id = Relation() + + def __init__(self, id): + self.id = id + + +class DrawingHF(Serialisable): + id = Relation() + lho = Integer(allow_none=True) + leftHeaderOddPages = Alias('lho') + lhe = Integer(allow_none=True) + leftHeaderEvenPages = Alias('lhe') + lhf = Integer(allow_none=True) + leftHeaderFirstPage = Alias('lhf') + cho = Integer(allow_none=True) + centerHeaderOddPages = Alias('cho') + che = Integer(allow_none=True) + centerHeaderEvenPages = Alias('che') + chf = Integer(allow_none=True) + centerHeaderFirstPage = Alias('chf') + rho = Integer(allow_none=True) + rightHeaderOddPages = Alias('rho') + rhe = Integer(allow_none=True) + rightHeaderEvenPages = Alias('rhe') + rhf = Integer(allow_none=True) + rightHeaderFirstPage = Alias('rhf') + lfo = Integer(allow_none=True) + leftFooterOddPages = Alias('lfo') + lfe = Integer(allow_none=True) + leftFooterEvenPages = Alias('lfe') + lff = Integer(allow_none=True) + leftFooterFirstPage = Alias('lff') + cfo = Integer(allow_none=True) + centerFooterOddPages = Alias('cfo') + cfe = Integer(allow_none=True) + centerFooterEvenPages = Alias('cfe') + cff = Integer(allow_none=True) + centerFooterFirstPage = Alias('cff') + rfo = Integer(allow_none=True) + rightFooterOddPages = Alias('rfo') + rfe = Integer(allow_none=True) + rightFooterEvenPages = Alias('rfe') + rff = Integer(allow_none=True) + rightFooterFirstPage = Alias('rff') + + def __init__(self, + id=None, + lho=None, + lhe=None, + lhf=None, + cho=None, + che=None, + chf=None, + rho=None, + rhe=None, + rhf=None, + lfo=None, + lfe=None, + lff=None, + cfo=None, + cfe=None, + cff=None, + rfo=None, + rfe=None, + rff=None, + ): + self.id = id + self.lho = lho + self.lhe = lhe + self.lhf = lhf + self.cho = cho + self.che = che + self.chf = chf + self.rho = rho + self.rhe = rhe + self.rhf = rhf + self.lfo = lfo + self.lfe = lfe + self.lff = lff + self.cfo = cfo + self.cfe = cfe + self.cff = cff + self.rfo = rfo + self.rfe = rfe + self.rff = rff diff --git a/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/views.py b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/views.py new file mode 100644 index 00000000..ebaa4e00 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/chartsheet/views.py @@ -0,0 +1,51 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Bool, + Integer, + Typed, + Sequence +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + + +class ChartsheetView(Serialisable): + tagname = "sheetView" + + tabSelected = Bool(allow_none=True) + zoomScale = Integer(allow_none=True) + workbookViewId = Integer() + zoomToFit = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + tabSelected=None, + zoomScale=None, + workbookViewId=0, + zoomToFit=None, + extLst=None, + ): + self.tabSelected = tabSelected + self.zoomScale = zoomScale + self.workbookViewId = workbookViewId + self.zoomToFit = zoomToFit + + +class ChartsheetViewList(Serialisable): + tagname = "sheetViews" + + sheetView = Sequence(expected_type=ChartsheetView, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('sheetView',) + + def __init__(self, + sheetView=None, + extLst=None, + ): + if sheetView is None: + sheetView = [ChartsheetView()] + self.sheetView = sheetView diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/comments/__init__.py new file mode 100644 index 00000000..9f089d91 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/comments/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2010-2021 openpyxl + + +from .comments import Comment diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d34e535d7dfe933f614f35af67a2fc5c0daaed6 GIT binary patch literal 225 zcmYjKy$ZrG7)+{Q5emLW2m1|9f{0KDClR_iSYi`wFn_eMm}l_`oPC?FPR?#lri$Rf z-F^4LaU_a{$RoR5Nb3FGg?|J-EIjLx7m6qrNa8KVhz1#odk^g2F>VK`mXayEj)OKV zzIR=98hBK+@Nw9f0`Se6@+Ic)L literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/author.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/author.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..666a982f878a5cca7bc8e4b3020bff89a4ed8e4f GIT binary patch literal 726 zcmZuv&59H;5Kj7Udd3+L^#wdOXh-lQi0rb09%K>v7KD)Plsm!n&nB5+NA|XJ^$C0e z&%RAxJ^2b=G}Z0s9JZi7zN%C@-tV>Im2aeu!V!l+j(HZ+;nm#G8zO?+@MU^lP@9KdFpxMf zAIHMZLuZ6vA6NrQOZHY;_1fw!u=l&Q?^;&X>#j4b+(Nr$MqjXg7ZkS7%NNXP4^Mkp zf0CErSl2__@2(B2yQYEGJ2t5-``wTWph@+gTGG>^&e zv437<{PDq0sle4%_r9~PbVqN&q7VM)uDMZ<9aZ2tcL^Bsmy@YiIOY(R^nUgSuvxCH literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/comment_sheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/comment_sheet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ae22772a30ec221ac6c313d8b459c74a172376c GIT binary patch literal 5247 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/comments.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/comments.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..490968c80dfbe597d0582a33fa8902f1344d8d29 GIT binary patch literal 2104 zcmZuy!EPHj5GA>*l`PAS6DLMcr0pIG6gpID5hOs1AaLpy?a3`*^rAxvdP#X#UM)9V z%83;DQu_mX?)yaAQ$fONeGEGxr<)h^5;2B8S-BLgPsz>|-pFB|7{ z;H!oVWb>Sy(Lo?va_yW9nzAj|;oFiOxdGobxhe0#w=K8iefX}!nFn&)o$08~XhS}{ zT1g6V?3B$tJszvf?tl0R9Dn#7Lv zB`upg7Y49jQnLoXI53syz_JV~>pjp5WR;iL|?a#IsSiTA0eR&!lPQ!O!op!x47-};Xb{U<)3vLDlPhwPVIR6NY6+86$ zV^qeKppYAkf0>+Q859Ht={fQ*{8Qiw0;IFg_ly!&g(8&7iYQhxjC4K?^)R$?QozF1 zLJv}Ojq@lKMFBz8q%PC6rZ-A;rtUz3*K+;|B0<&&d%S|BGOxU~2fNUd!?Fv?oJu;O z<~d*hvahr`J@(9V$xfN2bM~G=26!<2QR9N0(cb_mmZvLjZ}zkuF2=V>hFBfE&}v`+ z(#E;$W>hU6#%vfv;-Y?6g#%N7 zpM8wA^oC=5lXU+OAbtiXs%rM1P@nG5S!V&Jhvt>!!78qc3mdvzQpaY!nvh>IJbDe( z(C9HPS6DNfaKoK>9ZcK>Jq!)~5r0`|mFZSkcQhEt-j{Vqud?ZqVrZga0Yv6Gybxrv zNYk(?IGbcgOB~RWTqfU9dj{b@s{?E-S;H2+lBS4Yg|$V(UG+foDQu|A<3yyBo8{r0 zy{Mfr*o+q%4-al)XU@b}abC7LA8R>DQSWg6dLq)w;?l=#!2Yt88))>#PF=E?ZM^6a z3hb8VV-#3~&Sx1E%0gOQ%N+b)W3V$Dq#P@?!0yvxK5I7_ZTqdDg|pqlRp3*%Z@pw8b{|_v~tN;K2 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/shape_writer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/comments/__pycache__/shape_writer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2db440a16edb9bc5e089cf531525530938d8a59 GIT binary patch literal 3320 zcmai0&2!tv6~_XL4}ugWTe7UAaV*zyJ)E&bSxGtx)uigkO><}zPt|E=kYq3rb}2#z zu*_m9Q!sgA4n4GgL3^-|J@>!CwWs_E^`uVUTTn9XrtM<(>+RcJyszKeXlcnepd9`E zFVS1ehVc)aEEWoreW=AlXqdrBYGfoJnHiXw6<8S!C_%cJ+L;qLnH#t;w*rsRpuz0G z|Bb;M=6-81mp5lt&|)5Id}{X^IWF)Yd8BnM; zvls=HILu}a^e%Im2R$f?`Or6*iK?oaM}ug{zmSRIvb^PQLOGBeMBV(uXTRO-3%Lm* zp%M$db{ONu7X`;y51%Zkil-hv#Y_@^`mC>=y8&Tf-=R6rJ#*D~3ED zo{!V+#Xa2u#R(s4dA5h=l^23V6BULx@E`ThlX7iA=tW=&0?n~W~fQQ?U>vq-(i)!bU`tjSuMbBVRtazbIh1^X?wqIs^ct9Yi~ zXR%dHTVvPQy54iWK2z&<knXsT4j(4DhYIkQhXlO?-l9HFr-TOb^*}13N`siY)0t>h*I*inyd?UM; z*2}VHP3vyVOJ853qn{61}dXQV12aYTMWP2>g16hiGrBn-;QPix>;% z9usOdY;uHS|YEt5m_6urZPvP?j{g?TII%_aSiuZ&0qP%tVGJdvy^xW9N zGv@`}--fFF>Qfk?{b%GWbLvdpiNnYT#?+a(x_1?sz<5F@_KZ%wiTBiGCbK5sea}n+ z{`l0Ic+UtTCBz#&{EonRas`^QH8*Q1;YpcpbWC*}j=>-N+FDxFxU zpl{xRu?=BbbI=Jb9!`v>&V=Bqawo>A$LtyI`ze^ho}0sX+5kYa(FmtL)aElA&iYEV zCS*oH<<4Q{_90(HXeGS0rurSy3|K^Z!g5x-LK2dO9ov2az9}o-F<~4F#&lx zOO-o_ibyFb5wYYQY}TQv9LY0r#w&{V$_*qncs|Ow1jyFlD&Iloo7ik(gGko#URqZ( zRU{6Y+JGVcNFfm&<=fCy?mRGFx2&x(^Vv8F6BVjFmehw4oGbN*_={Loy?w4d}A)dlV~O}E)K zoljgi)n)6DL#{9IY$zLcy}{npJRb0P>?cT@0S@TdTNg8SL(ku?=b?knJtLZSbAbj9 ztZADUw21=_ee19s4F#mwMC85bxPTXt;(MD}Bu|ojE9HIFd;i1j-S>xTZ%$KU*t@@t z1dR{9C%T&s$9pwy^q$l`Qpzp;w(8yAJ4=e>I7t(A-Ww#0@w{Wc^w^4`OO~z0&;hj3 zIi%4~(iG4~$kM&thr;Js4U!C3;93wF(MX9p{b>g;b;7}Yfuncs&J)xiVt{4#aSD$K zxe6WuZxjGMI6LsfaS@LsXYwl0CHhrGbCE+{!~j;1W{<_VT+a%z#S+X=ps=kdRXx1C zvRHCfE+ZGvl9RlLnk(D5^&rKB_W%-d;D3I63Oj!f!UptEC|+Jcj?W;xzev>JfTw9u zla3%GUy>^OlPE1fKgj$mxb71WtxnOu6OU`z4)jyD<5dnk;Ic@`Ybb_>5{%tCnY#L6 z3AvQwpF%cJ60!hh!3q@xZ{e3Vk?$heMno#)Z5RjcTnRxl)*^-Y$ap;{-vrJhsKq`s z2BD5g%zru!Os0@Nzo$+cP{e^$3s|CCOuIE@1(GnXv`L$+kTv)uo_5%22kRHFzHPwV z?fxj&>Mv^f@SUJBPhy%4+Lyc|m86BlT&%O9hSORvdqArkp{=2@Lf^C?i^d!Ma`1jp U!`H`njm~Nt?uxDDu~vThZ?aHpv;Y7A literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/author.py b/.venv/lib/python3.9/site-packages/openpyxl/comments/author.py new file mode 100644 index 00000000..7cbf3e89 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/comments/author.py @@ -0,0 +1,21 @@ +# Copyright (c) 2010-2021 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Alias +) + + +class AuthorList(Serialisable): + + tagname = "authors" + + author = Sequence(expected_type=str) + authors = Alias("author") + + def __init__(self, + author=(), + ): + self.author = author diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/comment_sheet.py b/.venv/lib/python3.9/site-packages/openpyxl/comments/comment_sheet.py new file mode 100644 index 00000000..86142175 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/comments/comment_sheet.py @@ -0,0 +1,214 @@ +# Copyright (c) 2010-2021 openpyxl + +## Incomplete! +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Set, + String, + Bool, +) +from openpyxl.descriptors.excel import Guid, ExtensionList +from openpyxl.descriptors.sequence import NestedSequence + +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring + +from openpyxl.cell.text import Text +#from openpyxl.worksheet.ole import ObjectAnchor +from .author import AuthorList +from .comments import Comment +from .shape_writer import ShapeWriter + + +class Properties(Serialisable): + + locked = Bool(allow_none=True) + defaultSize = Bool(allow_none=True) + _print = Bool(allow_none=True) + disabled = Bool(allow_none=True) + uiObject = Bool(allow_none=True) + autoFill = Bool(allow_none=True) + autoLine = Bool(allow_none=True) + altText = String(allow_none=True) + textHAlign = Set(values=(['left', 'center', 'right', 'justify', 'distributed'])) + textVAlign = Set(values=(['top', 'center', 'bottom', 'justify', 'distributed'])) + lockText = Bool(allow_none=True) + justLastX = Bool(allow_none=True) + autoScale = Bool(allow_none=True) + rowHidden = Bool(allow_none=True) + colHidden = Bool(allow_none=True) + #anchor = Typed(expected_type=ObjectAnchor, ) + + __elements__ = ('anchor',) + + def __init__(self, + locked=None, + defaultSize=None, + _print=None, + disabled=None, + uiObject=None, + autoFill=None, + autoLine=None, + altText=None, + textHAlign=None, + textVAlign=None, + lockText=None, + justLastX=None, + autoScale=None, + rowHidden=None, + colHidden=None, + anchor=None, + ): + self.locked = locked + self.defaultSize = defaultSize + self._print = _print + self.disabled = disabled + self.uiObject = uiObject + self.autoFill = autoFill + self.autoLine = autoLine + self.altText = altText + self.textHAlign = textHAlign + self.textVAlign = textVAlign + self.lockText = lockText + self.justLastX = justLastX + self.autoScale = autoScale + self.rowHidden = rowHidden + self.colHidden = colHidden + self.anchor = anchor + + +class CommentRecord(Serialisable): + + tagname = "comment" + + ref = String() + authorId = Integer() + guid = Guid(allow_none=True) + shapeId = Integer(allow_none=True) + text = Typed(expected_type=Text) + commentPr = Typed(expected_type=Properties, allow_none=True) + author = String(allow_none=True) + + __elements__ = ('text', 'commentPr') + __attrs__ = ('ref', 'authorId', 'guid', 'shapeId') + + def __init__(self, + ref="", + authorId=0, + guid=None, + shapeId=0, + text=None, + commentPr=None, + author=None, + height=79, + width=144 + ): + self.ref = ref + self.authorId = authorId + self.guid = guid + self.shapeId = shapeId + if text is None: + text = Text() + self.text = text + self.commentPr = commentPr + self.author = author + self.height = height + self.width = width + + + @classmethod + def from_cell(cls, cell): + """ + Class method to convert cell comment + """ + comment = cell._comment + ref = cell.coordinate + self = cls(ref=ref, author=comment.author) + self.text.t = comment.content + self.height = comment.height + self.width = comment.width + return self + + + @property + def content(self): + """ + Remove all inline formatting and stuff + """ + return self.text.content + + +class CommentSheet(Serialisable): + + tagname = "comments" + + authors = Typed(expected_type=AuthorList) + commentList = NestedSequence(expected_type=CommentRecord, count=0) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + _id = None + _path = "/xl/comments/comment{0}.xml" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml" + _rel_type = "comments" + _rel_id = None + + __elements__ = ('authors', 'commentList') + + def __init__(self, + authors=None, + commentList=None, + extLst=None, + ): + self.authors = authors + self.commentList = commentList + + + def to_tree(self): + tree = super(CommentSheet, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def comments(self): + """ + Return a dictionary of comments keyed by coord + """ + authors = self.authors.author + + for c in self.commentList: + yield c.ref, Comment(c.content, authors[c.authorId], c.height, c.width) + + + @classmethod + def from_comments(cls, comments): + """ + Create a comment sheet from a list of comments for a particular worksheet + """ + authors = IndexedList() + + # dedupe authors and get indexes + for comment in comments: + comment.authorId = authors.add(comment.author) + + return cls(authors=AuthorList(authors), commentList=comments) + + + def write_shapes(self, vml=None): + """ + Create the VML for comments + """ + sw = ShapeWriter(self.comments) + return sw.write(vml) + + + @property + def path(self): + """ + Return path within the archive + """ + return self._path.format(self._id) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/comments.py b/.venv/lib/python3.9/site-packages/openpyxl/comments/comments.py new file mode 100644 index 00000000..5ba0b23e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/comments/comments.py @@ -0,0 +1,62 @@ +# Copyright (c) 2010-2021 openpyxl + + +class Comment(object): + + _parent = None + + def __init__(self, text, author, height=79, width=144): + self.content = text + self.author = author + self.height = height + self.width = width + + + @property + def parent(self): + return self._parent + + + def __eq__(self, other): + return ( + self.content == other.content + and self.author == other.author + ) + + def __repr__(self): + return "Comment: {0} by {1}".format(self.content, self.author) + + + def __copy__(self): + """Create a detached copy of this comment.""" + clone = self.__class__(self.content, self.author, self.height, self.width) + return clone + + + def bind(self, cell): + """ + Bind comment to a particular cell + """ + if cell is not None and self._parent is not None and self._parent != cell: + fmt = "Comment already assigned to {0} in worksheet {1}. Cannot assign a comment to more than one cell" + raise AttributeError(fmt.format(cell.coordinate, cell.parent.title)) + self._parent = cell + + + def unbind(self): + """ + Unbind a comment from a cell + """ + self._parent = None + + + @property + def text(self): + """ + Any comment text stripped of all formatting. + """ + return self.content + + @text.setter + def text(self, value): + self.content = value diff --git a/.venv/lib/python3.9/site-packages/openpyxl/comments/shape_writer.py b/.venv/lib/python3.9/site-packages/openpyxl/comments/shape_writer.py new file mode 100644 index 00000000..7a380445 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/comments/shape_writer.py @@ -0,0 +1,116 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.xml.functions import ( + Element, + SubElement, + tostring, + fromstring, +) + +from openpyxl.utils import ( + column_index_from_string, + coordinate_to_tuple, +) + +vmlns = "urn:schemas-microsoft-com:vml" +officens = "urn:schemas-microsoft-com:office:office" +excelns = "urn:schemas-microsoft-com:office:excel" + + +class ShapeWriter(object): + """ + Create VML for comments + """ + + vml = None + vml_path = None + + + def __init__(self, comments): + self.comments = comments + + + def add_comment_shapetype(self, root): + shape_layout = SubElement(root, "{%s}shapelayout" % officens, + {"{%s}ext" % vmlns: "edit"}) + SubElement(shape_layout, + "{%s}idmap" % officens, + {"{%s}ext" % vmlns: "edit", "data": "1"}) + shape_type = SubElement(root, + "{%s}shapetype" % vmlns, + {"id": "_x0000_t202", + "coordsize": "21600,21600", + "{%s}spt" % officens: "202", + "path": "m,l,21600r21600,l21600,xe"}) + SubElement(shape_type, "{%s}stroke" % vmlns, {"joinstyle": "miter"}) + SubElement(shape_type, + "{%s}path" % vmlns, + {"gradientshapeok": "t", + "{%s}connecttype" % officens: "rect"}) + + + def add_comment_shape(self, root, idx, coord, height, width): + row, col = coordinate_to_tuple(coord) + row -= 1 + col -= 1 + shape = _shape_factory(row, col, height, width) + + shape.set('id', "_x0000_s%04d" % idx) + root.append(shape) + + + def write(self, root): + + if not hasattr(root, "findall"): + root = Element("xml") + + # Remove any existing comment shapes + comments = root.findall("{%s}shape[@type='#_x0000_t202']" % vmlns) + for c in comments: + root.remove(c) + + # check whether comments shape type already exists + shape_types = root.find("{%s}shapetype[@id='_x0000_t202']" % vmlns) + if not shape_types: + self.add_comment_shapetype(root) + + for idx, (coord, comment) in enumerate(self.comments, 1026): + self.add_comment_shape(root, idx, coord, comment.height, comment.width) + + return tostring(root) + + +def _shape_factory(row, column, height, width): + style = ("position:absolute; " + "margin-left:59.25pt;" + "margin-top:1.5pt;" + "width:{width}px;" + "height:{height}px;" + "z-index:1;" + "visibility:hidden").format(height=height, + width=width) + attrs = { + "type": "#_x0000_t202", + "style": style, + "fillcolor": "#ffffe1", + "{%s}insetmode" % officens: "auto" + } + shape = Element("{%s}shape" % vmlns, attrs) + + SubElement(shape, "{%s}fill" % vmlns, + {"color2": "#ffffe1"}) + SubElement(shape, "{%s}shadow" % vmlns, + {"color": "black", "obscured": "t"}) + SubElement(shape, "{%s}path" % vmlns, + {"{%s}connecttype" % officens: "none"}) + textbox = SubElement(shape, "{%s}textbox" % vmlns, + {"style": "mso-direction-alt:auto"}) + SubElement(textbox, "div", {"style": "text-align:left"}) + client_data = SubElement(shape, "{%s}ClientData" % excelns, + {"ObjectType": "Note"}) + SubElement(client_data, "{%s}MoveWithCells" % excelns) + SubElement(client_data, "{%s}SizeWithCells" % excelns) + SubElement(client_data, "{%s}AutoFill" % excelns).text = "False" + SubElement(client_data, "{%s}Row" % excelns).text = str(row) + SubElement(client_data, "{%s}Column" % excelns).text = str(column) + return shape diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/compat/__init__.py new file mode 100644 index 00000000..17366919 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/compat/__init__.py @@ -0,0 +1,54 @@ +# Copyright (c) 2010-2021 openpyxl + +from .numbers import NUMERIC_TYPES +from .strings import safe_string + +import warnings +from functools import wraps +import inspect + + +class DummyCode: + + pass + + +# from https://github.com/tantale/deprecated/blob/master/deprecated/__init__.py +# with an enhancement to update docstrings of deprecated functions +string_types = (type(b''), type(u'')) +def deprecated(reason): + + if isinstance(reason, string_types): + + def decorator(func1): + + if inspect.isclass(func1): + fmt1 = "Call to deprecated class {name} ({reason})." + else: + fmt1 = "Call to deprecated function {name} ({reason})." + + @wraps(func1) + def new_func1(*args, **kwargs): + #warnings.simplefilter('default', DeprecationWarning) + warnings.warn( + fmt1.format(name=func1.__name__, reason=reason), + category=DeprecationWarning, + stacklevel=2 + ) + return func1(*args, **kwargs) + + # Enhance docstring with a deprecation note + deprecationNote = "\n\n.. note::\n Deprecated: " + reason + if new_func1.__doc__: + new_func1.__doc__ += deprecationNote + else: + new_func1.__doc__ = deprecationNote + return new_func1 + + return decorator + + elif inspect.isclass(reason) or inspect.isfunction(reason): + raise TypeError("Reason for deprecation must be supplied") + + else: + raise TypeError(repr(type(reason))) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e91afaa008187ab2a1414b44740dc4a3b497ebc1 GIT binary patch literal 1536 zcmZux&2QX96rUM;ZLhOQqf)7AX~E(^F^Dx42TD{`m6C`L!K!LfAuNRDcxDr){$ge} zVYi!0H&V8T@(*w+5|^I&Z|2G=XKqMHyytC_R_d(g?VFi5@BQAJpI^GX>=PJUfBl}` zaR~Vv7q1Ts7WZMQpFwcKX+cUFQ;Iq(SZT!;=oYsNJGSA!Q*=rvc3$ndvHKO_4tI|U zcV{g2M3;Lr3bH$+Ll*no=S#;VUgGu^345m$f`rs84<3EL`S6?dWc$Yln_IB4q|%{C zl$Lq5dkQvW0GpjdnKmlJ+t7?tICz2;&NTE+h)yA6&0bh@@&{FY&}`J? z9o^y9j6S8+s_tk7VpB#Ay$JLpI_u&H~LTx`Ry*5%1P=Y<#8R zwF_||im+q6!&Fv4T4ik1j5DldnQH06HFJ66RjvU0hlL&bPbHW;kHLOd0ZZ*+sc)MO z=673Of_n=_B2`Sc5{F59@*XT(#uLk?E0j_8pVOIqc2Uj#-J;IYLfz|gk<~KQwY=Cm zcQd)+`~7}U)mp5r`2mbGjDoKPp>5o}QjN$oD5`xQiN!Q-lJGi9lF*XZ5#qZb;!9`I zA-@55d`VmUA(Pk8rZrroaSrJ!9H=WG2=l4KZ069*zDFIp3fko6nM1)Zu%F+Z+@;nj?d){sg2AOYTK`vkH|wmJD1Slf>WyAdSCde>}rh5DAf1?#Y9+-g+r2&p78 z$+!zFO5%qn@uKOrAfUgfw$uxN*ev9SC_X@ez7_z^yVU}PcX!d(?zP=w1ZGBKz9N4e|FV{>O~O8CAC;cwZ<`tbvy0jKLc8K@1oTO9o?- z7m>$v&H%aM7(cQaK6uU^Ku1TQ;tZ0>56yvu1%AG|Aes{0qL(hB_FXawTo&b4guR>A z8t1Q^GtP0c!;UGQvGkwdrX;uUyA~~~T8eAw)TOI?6!$~rjTTv18PkZgN8O92E`%L? zW%O}+BD&h+p_S!}e8Nr`i`ouvjVMiPr5AKSYKJgewnn`+cpB5LZu~>U7HpsL4?TZM Az5oCK literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/numbers.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/numbers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c88e5914d3a4f83b542775223f18559c0cfdcc9e GIT binary patch literal 742 zcmbWz%Wl&^6b9fK-;$;Ylv~TC<-W;+45cV4QiYIcP`jWCqzGkLL{2_QXC$`Lr+8b+`fBumd;Y7TktAa2M{)x3>s)0&7IN?k{`; z=~B};6tgh$<;m%b?tRk;>P%ax8as?LGu}CQ^K$U|`1#~?JRFR;8|P6{a2I!n zW3zr7B}!)lt(7)zy2P4$c^w@*WX9dR45myb+$j^@lqz17_*(vVX`H1m=e$w%-0d4L z4Y5ZUFHreZ3f?L=o~_Gg?Q<9NZodk9kE+mrTzA{YE7Zj*qFP0i%acjHDLLzfYTdLv zJyl9hc(Zy5^fwLc7Ym`k5Z%FlsixoWSR{hbXGgvc4t2N^Y*;Ka6|>Q7iSw+t67h=3 zaLSS*TdH`k_k^WkCLSdI?1R4$DN~7vlj5UfGZiI%#_&t0LZ`i?FwKKV1-TSYZS+fz WFtx3gZP6x1o3?C+der%!@$(OYBE)(C literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/product.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/product.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a03a4a46cf89f379a7791600e42332a15b91501 GIT binary patch literal 498 zcmYjOu}&N@5FLAccgqnF74>$}6q!?X-p*x`H9A@MD&(m7?W!^ZeGJV&T zD^Y3Vvuo?J{p`H=OKY;-d~f<6%fgvNYVQ(lhWZ_~-9+&4Ogz0!9^iLOPR?d5tX+3T zdTXmTjGb1U)FHKvcEVd1lDeyAB&01?)<)_-;3k;o&rTKZk^w#jX zTw*Goue4e5s=VUOz;HMJ%G=WGFO85t#f@$`mM~5KtK!nujqp77*GZo@eVDx2ao#OO z0OHmVkqC>S*(6LB3uWbE;bPdu9)}x&#aX%dFsz@}jdlLmIqL?}A0Yl!NcU?jS?O;H Sdc$}aOG!#oM#&cWL|y@Xk9fQQ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/singleton.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/singleton.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56294c50157725b85a61a69dbc88abac74374c0e GIT binary patch literal 1622 zcmb_c&x;c=6i#NQ-EMVt{eeF&5=77i?Vx)SMZ{gvlZpj}f`sfO-DaE4)Ff@~Qcnur zyo(nFFFpI;%+-_sf+ycgYq!dJ5d(QMd71a}-k0wiv9?wxFq&UKidzXG-*B-!z(?-F zbXUPhBB>!Q@_@)l#&3y?6@3%8B3LJ~0_#dx$C5V5M)f;HkVhHX%8DEGm9-HBlr_$Eu07}FX};(feX}yBahW(}+GQf_t}CPNXiCdf z&O_mxPsxuLX-`Ql(ucyzZL5bWeKPIEw?nT=ny{~eSo5Huls0KtIR zi=O%@z!M+91JHyon#3ZU8;PyL!{TuZgfd|a=XM2sN6=tJ_81z3{sc^iQH0j%xj!nL z#ZFfc>0I;_AYJFYSIB|E_oJM@90)VtS>aq3861Uehur1b9PCVm8p6rJghE0Mc#(K~ zsG&xa>d_>Wo`LVTfeqsL7kyN>MAlJq|3jc5F5sH}kj7R=8#yC2(0HVc0nP*QGpmGG zELWoi{~ZdzcT^A9HH43KJLE-)NYFrj*)w4vOpgD9G%i8UhfBl9pOVigj|$3T#(?U< z<%fGHS4K*7t!kI2p2N#CVFqejXQ*>w$K^3TLk8{%5jvD5c%2sz_yvoP&w~C=VZ19O zoVx*L0;=94Lt;K^2HpQ^C9&rrdZ{0z>qcyA=v@U<#>LLw zVVu4Qn=bsnfz$P99qj%=&2vGlW17Plc?LGlVg{t;oRR1Qb)maV{22Z~$Xko{lriDIB;_S literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/strings.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/compat/__pycache__/strings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99bf996f027cbcfa6b61519cd879d1a0d4c8f76a GIT binary patch literal 686 zcmYjP&x;c=6n>dsovp2khdp@eMWMoW5M^;;5pgXjXj!%@ILelElD5&AWK7cHEbXcO z7hd$}+5cv)p7vkxmSeUnpIE^t9vDQ~e_n1G1etd~}pikK-E-_dxhihQ|67(Y$;OcI3F2Hjy*YG|3;db0=xM=H{a~ruF8_bQB zhZhH9I^%iOqmuP9C54MsxTozo5thb{1lA9VX!aIZehx1V#NF{l{Rkxi$ zW2wxzj<9O&H0Z#A>vO#)jN0X@ePcCtDQt561l`5>pZ7_@nV`vYs@XFwRy;Ya7FNk* zoGp~flfjD1Rg#NoQdV}MY?4ptC*k=OD60y7P&ZvX%Q literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/abc.py b/.venv/lib/python3.9/site-packages/openpyxl/compat/abc.py new file mode 100644 index 00000000..9a9e68e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/compat/abc.py @@ -0,0 +1,8 @@ +# Copyright (c) 2010-2021 openpyxl + + +try: + from abc import ABC +except ImportError: + from abc import ABCMeta + ABC = ABCMeta('ABC', (object, ), {}) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/numbers.py b/.venv/lib/python3.9/site-packages/openpyxl/compat/numbers.py new file mode 100644 index 00000000..8bfe74b0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/compat/numbers.py @@ -0,0 +1,43 @@ +# Copyright (c) 2010-2021 openpyxl + +from decimal import Decimal + +NUMERIC_TYPES = (int, float, Decimal) + + +try: + import numpy + NUMPY = True +except ImportError: + NUMPY = False + + +if NUMPY: + NUMERIC_TYPES = NUMERIC_TYPES + (numpy.short, + numpy.ushort, + numpy.intc, + numpy.uintc, + numpy.int_, + numpy.uint, + numpy.longlong, + numpy.ulonglong, + numpy.half, + numpy.float16, + numpy.single, + numpy.double, + numpy.longdouble, + numpy.int8, + numpy.int16, + numpy.int32, + numpy.int64, + numpy.uint8, + numpy.uint16, + numpy.uint32, + numpy.uint64, + numpy.intp, + numpy.uintp, + numpy.float32, + numpy.float64, + numpy.bool_, + numpy.floating, + numpy.integer) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/product.py b/.venv/lib/python3.9/site-packages/openpyxl/compat/product.py new file mode 100644 index 00000000..31004aa3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/compat/product.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2021 openpyxl + +""" +math.prod equivalent for < Python 3.8 +""" + +import functools +import operator + +def product(sequence): + return functools.reduce(operator.mul, sequence) + + +try: + from math import prod +except ImportError: + prod = product diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/singleton.py b/.venv/lib/python3.9/site-packages/openpyxl/compat/singleton.py new file mode 100644 index 00000000..fdbd44c7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/compat/singleton.py @@ -0,0 +1,40 @@ +# Copyright (c) 2010-2021 openpyxl + +import weakref + + +class Singleton(type): + """ + Singleton metaclass + Based on Python Cookbook 3rd Edition Recipe 9.13 + Only one instance of a class can exist. Does not work with __slots__ + """ + + def __init__(self, *args, **kw): + super(Singleton, self).__init__(*args, **kw) + self.__instance = None + + def __call__(self, *args, **kw): + if self.__instance is None: + self.__instance = super(Singleton, self).__call__(*args, **kw) + return self.__instance + + +class Cached(type): + """ + Caching metaclass + Child classes will only create new instances of themselves if + one doesn't already exist. Does not work with __slots__ + """ + + def __init__(self, *args, **kw): + super(Singleton, self).__init__(*args, **kw) + self.__cache = weakref.WeakValueDictionary() + + def __call__(self, *args): + if args in self.__cache: + return self.__cache[args] + + obj = super(Singleton, self).__call__(*args) + self.__cache[args] = obj + return obj diff --git a/.venv/lib/python3.9/site-packages/openpyxl/compat/strings.py b/.venv/lib/python3.9/site-packages/openpyxl/compat/strings.py new file mode 100644 index 00000000..fdc30fc6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/compat/strings.py @@ -0,0 +1,25 @@ +# Copyright (c) 2010-2021 openpyxl + +from datetime import datetime +from math import isnan, isinf +import sys + +VER = sys.version_info + +from .numbers import NUMERIC_TYPES + + +def safe_string(value): + """Safely and consistently format numeric values""" + if isinstance(value, NUMERIC_TYPES): + if isnan(value) or isinf(value): + value = "" + else: + value = "%.16g" % value + elif value is None: + value = "none" + elif isinstance(value, datetime): + value = value.isoformat() + elif not isinstance(value, str): + value = str(value) + return value diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__init__.py new file mode 100644 index 00000000..837b6851 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__init__.py @@ -0,0 +1,57 @@ +# Copyright (c) 2010-2021 openpyxl + +from .base import * +from .sequence import Sequence + + +class MetaStrict(type): + + def __new__(cls, clsname, bases, methods): + for k, v in methods.items(): + if isinstance(v, Descriptor): + v.name = k + return type.__new__(cls, clsname, bases, methods) + + +class MetaSerialisable(type): + + def __new__(cls, clsname, bases, methods): + attrs = [] + nested = [] + elements = [] + namespaced = [] + for k, v in methods.items(): + if isinstance(v, Descriptor): + ns= getattr(v, 'namespace', None) + if ns: + namespaced.append((k, "{%s}%s" % (ns, k))) + if getattr(v, 'nested', False): + nested.append(k) + elements.append(k) + elif isinstance(v, Sequence): + elements.append(k) + elif isinstance(v, Typed): + if hasattr(v.expected_type, 'to_tree'): + elements.append(k) + else: + attrs.append(k) + else: + if not isinstance(v, Alias): + attrs.append(k) + + if methods.get('__attrs__') is None: + methods['__attrs__'] = tuple(attrs) + methods['__namespaced__'] = tuple(namespaced) + if methods.get('__nested__') is None: + methods['__nested__'] = tuple(sorted(nested)) + if methods.get('__elements__') is None: + methods['__elements__'] = tuple(sorted(elements)) + return MetaStrict.__new__(cls, clsname, bases, methods) + + +Strict = MetaStrict('Strict', (object,), {}) + +_Serialiasable = MetaSerialisable('_Serialisable', (object,), {}) + +#del MetaStrict +#del MetaSerialisable diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef673204481abb28ebf0e57bf3b71661bb1cb1fc GIT binary patch literal 1616 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 OXI9NqeHGH*Nb(DXw``37 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/nested.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/nested.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f17a1e85ea9987042f15e39ed098ba13fd9ca58c GIT binary patch literal 3857 zcmb7HTXWmS6~+P}2~yO>vLrjUtfHwMhiyV;n%>eTDW|cMCNpL`m8LUAXNrNbD~S#+ z%edZUbom*#`0!zXRkBkUEfiK<@HwAlpD3AUpit zuGQI@?6kfRnUKTSSLmZ;sG_Hd@Z%&>N~qTN@TJvh6-%Gx*^!WXh?j-?yF5>d)+3=} zIUMO+7L7rkiCv+K`jcVyBswk}5^A3(d8CWRmzfp^LKgL1Er;1b;qHlJ-C>2dE4~?v zEEeBGi3MOuMf)ODW>&{38jll^icA;HB#)yci&9avzC9djp+-?GV*0B3I5R>00s~b%80-h9D=#&?!$1s*)IRMZDTX~Fjj=O{|z z`I@w}J8OigN9eRQ#rmhV>}oc(PuX*4>P)R?_ScTQuc7sn{gX|tm+sVl#h%-`rt4Gd z)PC8RV)iQ=nPowvGbkJ#9Za_Oq644jLit&){WQ|?p&x1Z;^|n6j#o4eBSo@YgXY35 zz47NL8H>kKA_mQUnWv$aLKHPM9tkPeX<{P`mC#|>aSK<8WWVr+nbHxwUesXVSjY`b zJ7YyN3{BL-@XY#e*h>W;M!nxhl0TBeBhmZg$)V1(-fnz|;P<*mB0K6O!>7H`i9XD; z54#`r6aw=;0`qlrAXG0OiEMOoob>o2O;j&4uI-LaiGkOXf45QhO38ZnPe2o3+c)lRWghRoE#RDG&od)Dg!J&dyvpx7u6!R_ zWS6=gIw~HX#~1AI>7}vbrx<;RrYJ8w<~ov)D;78a`)KNIbQdk)4p_*=NkV+Yz)LP{ z@`_ez0kP!diP_qp#7GJSDpY=yo%p5yjfLM1^=_72(mp}Pu7-a3074~-#&9Z|KH9u&HqQ6ky{2^Na3(hyU0-1U>bzV!pI=it z48-c9bU>$5H*~Z+bx#?yrf?P!sCGjq(*Sfcyz{i()BkkzBTm z5}3-``L}f zfcj{rfX}lBd1B9V-<-08Gw1R)e0)|>ZuI^N*dJ&)8=YrMlHW+@a_NQHkv@tV&S}^h~z7Z9DseA^ftP4$I(6V8pie>rz3rW3*4k*c$Ok7qRb!e z`;nn(aXeH+$C560-T%YIlx&<)oCbSkG z^6GN`($xk0TV-0i!nAa?xSX5BqW?fM$GgQ`!=r6nt`_^NvZwsz#U*?aO^)|5(r6_` z+O7+VBy7<6fnnIfeH!6^7P4Ma#3Z>*(m^AMRAqxYkGdvxblH$g=n7BG{_T)d7{TiN zrrM43bQEbx$49WWK#x-t={Sq^FwazZz|dX9oGs?gVvc>o-iA&kF`Xn9GG%EhMp8mc uN;YFc#brKWWN+bpTwb?6C1cbMI@kH2-G=)%oVD%s#@gn3&HOdW-~R!O68@C{ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/sequence.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/sequence.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02eeca10f611d757615f3739f288ab5292f79eea GIT binary patch literal 4548 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/slots.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/__pycache__/slots.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5acd7f7d6bbb1e281370b7445f434c402ff22528 GIT binary patch literal 1043 zcmZ`(L2uJA6t)v5P16>k32D-Vxc5MV#08`=ftUoRX;N>MhULYk&E_V-b~ie?r%u|L z8xoiHxHJF8SAGIFF7TYL2!wdjdwI{!_ul8{m#|(hMIghkU)Zw{p>IC;QwRhvLG=SL zZxBZuUxNy?3`dv;L(~s**!fE!Pb2yvJOqOG&?vY;?=c6&butQX5J4m2v2N$p%v3{J znPXMeLYYG7+}GSn8=MxXCyG0281$TUXlV2Kxm2RyunwL`SThv>vCA{#AYOI86V zHr_>Rj6EW>(HbvW9%~2(>>{xVM>DLriELuxv(_?D+=hm{Kqj%Rv-T3((8l}7q?UM} zWAHmmwCY-9u?=jvCIP}|2wj1*L`SYosfMFyN^=ZUw-SI8feu0S9+)w*=wxSsEnWe3 zjTN!@Q}Dre1UoVohQc$|hAE>j3p42lE*Ly<;klUKBIVoh<6SltykOZ|rud;ME<|=b zpO~u5hWVtbWHz`E{JSu)SD5RyLf|ik(t%7 z5$PF>j%(df>9nsi9ia7_1ZiH5H%rP-{4MsE z+9aa1>AdpoP`hAa(B?W3zK?e>!S`^AyEwt=&mf2)>f)=t|97k(xda%);VE?~rPGSf zr1!g&ewZ=2*=Z`d-W`WrNV( self.max: + raise ValueError('Max value is {0}'.format(self.max)) + super(Max, self).__set__(instance, value) + + +class Min(Convertible): + """Values must be greater than a `min` value""" + + expected_type = float + allow_none = False + + def __init__(self, **kw): + if 'min' not in kw and not hasattr(self, 'min'): + raise TypeError('missing min value') + super(Min, self).__init__(**kw) + + def __set__(self, instance, value): + if ((self.allow_none and value is not None) + or not self.allow_none): + value = _convert(self.expected_type, value) + if value < self.min: + raise ValueError('Min value is {0}'.format(self.min)) + super(Min, self).__set__(instance, value) + + +class MinMax(Min, Max): + """Values must be greater than `min` value and less than a `max` one""" + pass + + +class Set(Descriptor): + """Value can only be from a set of know values""" + + def __init__(self, name=None, **kw): + if not 'values' in kw: + raise TypeError("missing set of values") + kw['values'] = set(kw['values']) + super(Set, self).__init__(name, **kw) + self.__doc__ = "Value must be one of {0}".format(self.values) + + def __set__(self, instance, value): + if value not in self.values: + raise ValueError(self.__doc__) + super(Set, self).__set__(instance, value) + + +class NoneSet(Set): + + """'none' will be treated as None""" + + def __init__(self, name=None, **kw): + super(NoneSet, self).__init__(name, **kw) + self.values.add(None) + + def __set__(self, instance, value): + if value == 'none': + value = None + super(NoneSet, self).__set__(instance, value) + + +class Integer(Convertible): + + expected_type = int + + +class Float(Convertible): + + expected_type = float + + +class Bool(Convertible): + + expected_type = bool + + def __set__(self, instance, value): + if isinstance(value, str): + if value in ('false', 'f', '0'): + value = False + super(Bool, self).__set__(instance, value) + + +class String(Typed): + + expected_type = str + + +class Text(String, Convertible): + + pass + + +class ASCII(Typed): + + expected_type = bytes + + +class Tuple(Typed): + + expected_type = tuple + + +class Length(Descriptor): + + def __init__(self, name=None, **kw): + if "length" not in kw: + raise TypeError("value length must be supplied") + super(Length, self).__init__(**kw) + + + def __set__(self, instance, value): + if len(value) != self.length: + raise ValueError("Value must be length {0}".format(self.length)) + super(Length, self).__set__(instance, value) + + +class Default(Typed): + """ + When called returns an instance of the expected type. + Additional default values can be passed in to the descriptor + """ + + def __init__(self, name=None, **kw): + if "defaults" not in kw: + kw['defaults'] = {} + super(Default, self).__init__(**kw) + + def __call__(self): + return self.expected_type() + + +class Alias(Descriptor): + """ + Aliases can be used when either the desired attribute name is not allowed + or confusing in Python (eg. "type") or a more descriptve name is desired + (eg. "underline" for "u") + """ + + def __init__(self, alias): + self.alias = alias + + def __set__(self, instance, value): + setattr(instance, self.alias, value) + + def __get__(self, instance, cls): + return getattr(instance, self.alias) + + +class MatchPattern(Descriptor): + """Values must match a regex pattern """ + allow_none = False + + def __init__(self, name=None, **kw): + if 'pattern' not in kw and not hasattr(self, 'pattern'): + raise TypeError('missing pattern value') + + super(MatchPattern, self).__init__(name, **kw) + self.test_pattern = re.compile(self.pattern, re.VERBOSE) + + + def __set__(self, instance, value): + + if value is None and not self.allow_none: + raise ValueError("Value must not be none") + + if ((self.allow_none and value is not None) + or not self.allow_none): + if not self.test_pattern.match(value): + raise ValueError('Value does not match pattern {0}'.format(self.pattern)) + + super(MatchPattern, self).__set__(instance, value) + + +class DateTime(Typed): + + expected_type = datetime.datetime + + def __set__(self, instance, value): + if value is not None and isinstance(value, str): + try: + value = from_ISO8601(value) + except ValueError: + raise ValueError("Value must be ISO datetime format") + super(DateTime, self).__set__(instance, value) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/excel.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/excel.py new file mode 100644 index 00000000..319b96e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/excel.py @@ -0,0 +1,112 @@ +#copyright openpyxl 2010-2018 + +""" +Excel specific descriptors +""" + +from openpyxl.xml.constants import REL_NS +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element + +from . import ( + MatchPattern, + MinMax, + Integer, + String, + Sequence, +) +from .serialisable import Serialisable + + +class HexBinary(MatchPattern): + + pattern = "[0-9a-fA-F]+$" + + +class UniversalMeasure(MatchPattern): + + pattern = r"[0-9]+(\.[0-9]+)?(mm|cm|in|pt|pc|pi)" + + +class TextPoint(MinMax): + """ + Size in hundredths of points. + In theory other units of measurement can be used but these are unbounded + """ + expected_type = int + + min = -400000 + max = 400000 + + +Coordinate = Integer + + +class Percentage(MinMax): + + pattern = r"((100)|([0-9][0-9]?))(\.[0-9][0-9]?)?%" # strict + min = -1000000 + max = 1000000 + + def __set__(self, instance, value): + if isinstance(value, str) and "%" in value: + value = value.replace("%", "") + value = int(float(value) * 1000) + super(Percentage, self).__set__(instance, value) + + +class Extension(Serialisable): + + uri = String() + + def __init__(self, + uri=None, + ): + self.uri = uri + + +class ExtensionList(Serialisable): + + ext = Sequence(expected_type=Extension) + + def __init__(self, + ext=(), + ): + self.ext = ext + + +class Relation(String): + + namespace = REL_NS + allow_none = True + + +class Base64Binary(MatchPattern): + # http://www.w3.org/TR/xmlschema11-2/#nt-Base64Binary + pattern = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$" + + +class Guid(MatchPattern): + # https://msdn.microsoft.com/en-us/library/dd946381(v=office.12).aspx + pattern = r"{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}" + + +class CellRange(MatchPattern): + + pattern = r"^[$]?([A-Za-z]{1,3})[$]?(\d+)(:[$]?([A-Za-z]{1,3})[$]?(\d+)?)?$|^[A-Za-z]{1,3}:[A-Za-z]{1,3}$" + allow_none = True + + def __set__(self, instance, value): + + if value is not None: + value = value.upper() + super(CellRange, self).__set__(instance, value) + + +def _explicit_none(tagname, value, namespace=None): + """ + Override serialisation because explicit none required + """ + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return Element(tagname, val=safe_string(value)) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/namespace.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/namespace.py new file mode 100644 index 00000000..d7b35a2c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/namespace.py @@ -0,0 +1,12 @@ +# copyright openpyxl 2010-2015 + + +def namespaced(obj, tagname, namespace=None): + """ + Utility to create a namespaced tag for an object + """ + + namespace = getattr(obj, "namespace", None) or namespace + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return tagname diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/nested.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/nested.py new file mode 100644 index 00000000..cf1ab257 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/nested.py @@ -0,0 +1,131 @@ +#copyright openpyxl 2010-2015 + +""" +Generic serialisable classes +""" +from .base import ( + Convertible, + Bool, + Descriptor, + NoneSet, + MinMax, + Set, + Float, + Integer, + String, + Text, + ) +from .sequence import Sequence +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element, localname, whitespace + + +class Nested(Descriptor): + + nested = True + attribute = "val" + + def __set__(self, instance, value): + if hasattr(value, "tag"): + tag = localname(value) + if tag != self.name: + raise ValueError("Tag does not match attribute") + + value = self.from_tree(value) + super(Nested, self).__set__(instance, value) + + + def from_tree(self, node): + return node.get(self.attribute) + + + def to_tree(self, tagname=None, value=None, namespace=None): + namespace = getattr(self, "namespace", namespace) + if value is not None: + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + value = safe_string(value) + return Element(tagname, {self.attribute:value}) + + +class NestedValue(Nested, Convertible): + """ + Nested tag storing the value on the 'val' attribute + """ + pass + + +class NestedText(NestedValue): + """ + Represents any nested tag with the value as the contents of the tag + """ + + + def from_tree(self, node): + return node.text + + + def to_tree(self, tagname=None, value=None, namespace=None): + namespace = getattr(self, "namespace", namespace) + if value is not None: + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + el = Element(tagname) + el.text = safe_string(value) + whitespace(el) + return el + + +class NestedFloat(NestedValue, Float): + + pass + + +class NestedInteger(NestedValue, Integer): + + pass + + +class NestedString(NestedValue, String): + + pass + + +class NestedBool(NestedValue, Bool): + + + def from_tree(self, node): + return node.get("val", True) + + +class NestedNoneSet(Nested, NoneSet): + + pass + + +class NestedSet(Nested, Set): + + pass + + +class NestedMinMax(Nested, MinMax): + + pass + + +class EmptyTag(Nested, Bool): + + """ + Boolean if a tag exists or not. + """ + + def from_tree(self, node): + return True + + + def to_tree(self, tagname=None, value=None, namespace=None): + if value: + namespace = getattr(self, "namespace", namespace) + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return Element(tagname) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/sequence.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/sequence.py new file mode 100644 index 00000000..e064fa2b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/sequence.py @@ -0,0 +1,127 @@ +# copyright openpyxl 2010-2015 + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element +from openpyxl.utils.indexed_list import IndexedList + +from .base import Descriptor, Alias, _convert +from .namespace import namespaced + + +class Sequence(Descriptor): + """ + A sequence (list or tuple) that may only contain objects of the declared + type + """ + + expected_type = type(None) + seq_types = (list, tuple) + idx_base = 0 + unique = False + + + def __set__(self, instance, seq): + if not isinstance(seq, self.seq_types): + raise TypeError("Value must be a sequence") + seq = [_convert(self.expected_type, value) for value in seq] + if self.unique: + seq = IndexedList(seq) + + super(Sequence, self).__set__(instance, seq) + + + def to_tree(self, tagname, obj, namespace=None): + """ + Convert the sequence represented by the descriptor to an XML element + """ + for idx, v in enumerate(obj, self.idx_base): + if hasattr(v, "to_tree"): + el = v.to_tree(tagname, idx) + else: + tagname = namespaced(obj, tagname, namespace) + el = Element(tagname) + el.text = safe_string(v) + yield el + + +class ValueSequence(Sequence): + """ + A sequence of primitive types that are stored as a single attribute. + "val" is the default attribute + """ + + attribute = "val" + + + def to_tree(self, tagname, obj, namespace=None): + tagname = namespaced(self, tagname, namespace) + for v in obj: + yield Element(tagname, {self.attribute:safe_string(v)}) + + + def from_tree(self, node): + + return node.get(self.attribute) + + +class NestedSequence(Sequence): + """ + Wrap a sequence in an containing object + """ + + count = False + + def to_tree(self, tagname, obj, namespace=None): + tagname = namespaced(self, tagname, namespace) + container = Element(tagname) + if self.count: + container.set('count', str(len(obj))) + for v in obj: + container.append(v.to_tree()) + return container + + + def from_tree(self, node): + return [self.expected_type.from_tree(el) for el in node] + + +class MultiSequence(Sequence): + """ + Sequences can contain objects with different tags + """ + + def __set__(self, instance, seq): + if not isinstance(seq, (tuple, list)): + raise ValueError("Value must be a sequence") + seq = list(seq) + Descriptor.__set__(self, instance, seq) + + + def to_tree(self, tagname, obj, namespace=None): + """ + Convert the sequence represented by the descriptor to an XML element + """ + for v in obj: + el = v.to_tree(namespace=namespace) + yield el + + +class MultiSequencePart(Alias): + """ + Allow a multisequence to be built up from parts + + Excluded from the instance __elements__ or __attrs__ as is effectively an Alias + """ + + def __init__(self, expected_type, store): + self.expected_type = expected_type + self.store = store + + + def __set__(self, instance, value): + value = _convert(self.expected_type, value) + instance.__dict__[self.store].append(value) + + + def __get__(self, instance, cls): + return self diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/serialisable.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/serialisable.py new file mode 100644 index 00000000..9cf0127e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/serialisable.py @@ -0,0 +1,240 @@ +# copyright openpyxl 2010-2015 + +from copy import copy +from keyword import kwlist +KEYWORDS = frozenset(kwlist) + +from . import Descriptor +from . import _Serialiasable +from .sequence import ( + Sequence, + NestedSequence, + MultiSequencePart, +) +from .namespace import namespaced + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import ( + Element, + localname, +) + +seq_types = (list, tuple) + +class Serialisable(_Serialiasable): + """ + Objects can serialise to XML their attributes and child objects. + The following class attributes are created by the metaclass at runtime: + __attrs__ = attributes + __nested__ = single-valued child treated as an attribute + __elements__ = child elements + """ + + __attrs__ = None + __nested__ = None + __elements__ = None + __namespaced__ = None + + idx_base = 0 + + @property + def tagname(self): + raise(NotImplementedError) + + namespace = None + + @classmethod + def from_tree(cls, node): + """ + Create object from XML + """ + # strip known namespaces from attributes + attrib = dict(node.attrib) + for key, ns in cls.__namespaced__: + if ns in attrib: + attrib[key] = attrib[ns] + del attrib[ns] + + # strip attributes with unknown namespaces + for key in list(attrib): + if key.startswith('{'): + del attrib[key] + elif key in KEYWORDS: + attrib["_" + key] = attrib[key] + del attrib[key] + elif "-" in key: + n = key.replace("-", "_") + attrib[n] = attrib[key] + del attrib[key] + + if node.text and "attr_text" in cls.__attrs__: + attrib["attr_text"] = node.text + + for el in node: + tag = localname(el) + if tag in KEYWORDS: + tag = "_" + tag + desc = getattr(cls, tag, None) + if desc is None or isinstance(desc, property): + continue + + if hasattr(desc, 'from_tree'): + #descriptor manages conversion + obj = desc.from_tree(el) + else: + if hasattr(desc.expected_type, "from_tree"): + #complex type + obj = desc.expected_type.from_tree(el) + else: + #primitive + obj = el.text + + if isinstance(desc, NestedSequence): + attrib[tag] = obj + elif isinstance(desc, Sequence): + attrib.setdefault(tag, []) + attrib[tag].append(obj) + elif isinstance(desc, MultiSequencePart): + attrib.setdefault(desc.store, []) + attrib[desc.store].append(obj) + else: + attrib[tag] = obj + + return cls(**attrib) + + + def to_tree(self, tagname=None, idx=None, namespace=None): + + if tagname is None: + tagname = self.tagname + + # keywords have to be masked + if tagname.startswith("_"): + tagname = tagname[1:] + + tagname = namespaced(self, tagname, namespace) + namespace = getattr(self, "namespace", namespace) + + attrs = dict(self) + for key, ns in self.__namespaced__: + if key in attrs: + attrs[ns] = attrs[key] + del attrs[key] + + el = Element(tagname, attrs) + if "attr_text" in self.__attrs__: + el.text = safe_string(getattr(self, "attr_text")) + + for child_tag in self.__elements__: + desc = getattr(self.__class__, child_tag, None) + obj = getattr(self, child_tag) + if hasattr(desc, "namespace") and hasattr(obj, 'namespace'): + obj.namespace = desc.namespace + + if isinstance(obj, seq_types): + if isinstance(desc, NestedSequence): + # wrap sequence in container + if not obj: + continue + nodes = [desc.to_tree(child_tag, obj, namespace)] + elif isinstance(desc, Sequence): + # sequence + desc.idx_base = self.idx_base + nodes = (desc.to_tree(child_tag, obj, namespace)) + else: # property + nodes = (v.to_tree(child_tag, namespace) for v in obj) + for node in nodes: + el.append(node) + else: + if child_tag in self.__nested__: + node = desc.to_tree(child_tag, obj, namespace) + elif obj is None: + continue + else: + node = obj.to_tree(child_tag) + if node is not None: + el.append(node) + return el + + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if attr.startswith("_"): + attr = attr[1:] + elif attr != "attr_text" and "_" in attr: + desc = getattr(self.__class__, attr) + if getattr(desc, "hyphenated", False): + attr = attr.replace("_", "-") + if attr != "attr_text" and value is not None: + yield attr, safe_string(value) + + + def __eq__(self, other): + if not self.__class__ == other.__class__: + return False + elif not dict(self) == dict(other): + return False + for el in self.__elements__: + if getattr(self, el) != getattr(other, el): + return False + return True + + + def __ne__(self, other): + return not self == other + + + def __repr__(self): + s = u"<{0}.{1} object>\nParameters:".format( + self.__module__, + self.__class__.__name__ + ) + args = [] + for k in self.__attrs__ + self.__elements__: + v = getattr(self, k) + if isinstance(v, Descriptor): + v = None + args.append(u"{0}={1}".format(k, repr(v))) + args = u", ".join(args) + + return u"\n".join([s, args]) + + + def __hash__(self): + fields = [] + for attr in self.__attrs__ + self.__elements__: + val = getattr(self, attr) + if isinstance(val, list): + val = tuple(val) + fields.append(val) + + return hash(tuple(fields)) + + + def __add__(self, other): + if type(self) != type(other): + raise TypeError("Cannot combine instances of different types") + vals = {} + for attr in self.__attrs__: + vals[attr] = getattr(self, attr) or getattr(other, attr) + for el in self.__elements__: + a = getattr(self, el) + b = getattr(other, el) + if a and b: + vals[el] = a + b + else: + vals[el] = a or b + return self.__class__(**vals) + + + def __copy__(self): + # serialise to xml and back to avoid shallow copies + xml = self.to_tree(tagname="dummy") + cp = self.__class__.from_tree(xml) + # copy any non-persisted attributed + for k in self.__dict__: + if k not in self.__attrs__ + self.__elements__: + v = copy(getattr(self, k)) + setattr(cp, k, v) + return cp diff --git a/.venv/lib/python3.9/site-packages/openpyxl/descriptors/slots.py b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/slots.py new file mode 100644 index 00000000..cadc1ef3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/descriptors/slots.py @@ -0,0 +1,18 @@ +# Metaclass for mixing slots and descriptors +# From "Programming in Python 3" by Mark Summerfield Ch.8 p. 383 + +class AutoSlotProperties(type): + + def __new__(mcl, classname, bases, dictionary): + slots = list(dictionary.get("__slots__", [])) + for getter_name in [key for key in dictionary if key.startswith("get_")]: + name = getter_name + slots.append("__" + name) + getter = dictionary.pop(getter_name) + setter = dictionary.get(setter_name, None) + if (setter is not None + and isinstance(setter, collections.Callable)): + del dictionary[setter_name] + dictionary[name] = property(getter. setter) + dictionary["__slots__"] = tuple(slots) + return super().__new__(mcl, classname, bases, dictionary) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__init__.py new file mode 100644 index 00000000..977de8f1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2010-2021 openpyxl + + +from .drawing import Drawing diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca77b987410fa6de1b68a3409b5816782e36171c GIT binary patch literal 223 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 cu3D*9vRan0tS`sQ8_Vma8V9?#6{>wVdmeC zc+V`%gVPCTh4FrxMuq)plJg@j3Tu>$FgYsB_oC3x@L21^Y0i@@Op{w-mZPKlvG6Cy zVcDpNd->-QDUkkVuZ)0qCf>RC+1%FV*PnI;JjayUN> z=<&rxx`uWWE$cwc)R{6?W*Q`>9VrjgA(o(ptk6R??dgS)@#s)&V5r!Hc%XmDz?nb0H$K!8+M0rP;n5JHf*dddab4F~F3FauIWJD%Mvs6o0ROK((AE4{p zRZ;3ZJltR^(|*uA6Vx!6DdEupk6m=;t4PJIG`NEdIe4HIEk2z9GS0?1^0RRLC`!K_ zCuzcm0YSF7NUtCy|4Il6JVA&enJi@EE##~wy66xOlc<*|S4c~L8ZDDk>-Vl+bg2$6 zGQt+SG{+SZS4mJF6+53F9)$I=q z%5{!}moc`_=nnM8yx*;2zsAbg->7I*xkQdN0Dha?)l|SML{r(hm{}P^*Gka)R zAL2ZoKd7ErA_&c9n&(uwHRiZ&R&Z5If1nD&1Kcr#;mN*0)Q2Ys@o2Iy{$tsb%n?M0 zo?b%p40Q1A`C zvhFK(NjA`J_xuuTbWI}Iu-CZ&aAD{E5$!p$PznM?NFN%cNk`hGNpC0;rAxGvlzy#b zUJpYE#f)mPqZx!f*ax{_pQ`*JXw;=FSjxXuS#XJe4GHdNJogJLIU$Nkg#QoEurE>C z7dGfh6P=?R)w*S{#ON<6NA+*B@}|aODbrq}>1gGmrdx|l`JRvfMVe;tULBb|4+vVrz<*8|`m` zo*DM8dyg$n(|yLXK!lS#6V)UxRM`o&9qaL!JtKY*Om?M^J%~%L3uCe ztl(`|qk>~!O337(({16rU48((L&#3b^{~; literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/drawing.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/drawing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b51ac431f9091ff22238ca2ddcf6af6d0d493d23 GIT binary patch literal 2902 zcmcH*O>Y}TbZ5W3UVp?*(o)(23lykD8Xqcw5U6UK778t;Qi_mPl~$XbalGz+F|&?i z%Q+?DPjG=7J@W_n9dkpR@)x+kd$YEav^j8Lt$Fj_n;E}(-xIc4t_I)s?>~os0`?aY zr#};jJ21p^092zS(Nf|Qg1DaOz7DgI7^&%-if1NPYWsFt^J}T&JBnu|_0;v zrr#vmR~ofx?MS0F);cu&Hr2Pau5%0$v@VejjrU>^=h+h=T*?a0qOfGNYe;J=PjW7; zdynsKJ(AWB55Io)0NAY}-e-vj$~<`R?f0?;0P%_|8)A_2GKlbKgfdQl1`u~(h&=#I z^9j{JC*m7a_f2Y0^GNe8W>bsWN7^CrYpgbOXl+T^)S-2?QfGBoaTgR_qYc_TBEHKS zv~@^mo35yRP58GCy`b$8okh{mlzKTH#*4Gh|We-SpV57v)(Q#u?*Y zKj)qpgas2GV9_AtrEtCZH)dWa#3*InNWflEuqf`wu$9Is%h0C6i!-k>%;(-Hi%a2! zoGt$V5PQv;x( zph-=@EouR@sSU8E_+VSW>k4-jY)}oOCyfIgK+2D0lPJn3!jv23rG#5=uWQV#O$*5+qD5tr;{5k@bY z!LnkqpY-NovY0oEiFAS>2A2lGWsoHhXu3_#`%OFZh}&EodCiw-Xyr>AkeN0FFT5m> zlKc&z`CAB3r3!d}NlO{;2A~2Rr0LVvLW)|66c< z?LaS$nLZ=yka`dnWQGt*&bG$igCkv&zX=mZP1Uue#q&`Hm4d$wEJ?<|Ku^7LQ~o}z zZom*2Jeuy3CTWxZVu4C5sU)6f1G@^GTlnX5BeWMrsFwaWD=5DxQ(On>OB)RMhd{n+ zfscUqDPHo71<)aj&jJLizj9C?5H$W!1E-)%z(s&T4i9y{15VqS;SBjb1ZU0k7aISj zG=4D`oL^7er)8Jb3ZB*0HMIgK(7h*)wW=sn+MtW2nTC*;X2jy8Ybo#Y4}i}x+I)Lo zdD*uo=XqPU1S^AO1-uO>1Xd38yQ{as^Q*+sSIP9^!ck}TPp?0PM1@M>r3_M%Vriw` zc)&6bYHS=P(5tfD*MbMlr%Z>=C^5RpAyKnFpN;u!9+JKZC5}w5d7PCao_RO+H@wM) zH{S3DT`a*^i9zie{1wM4$1fo`ark+WxVj5kvvhQ#xp)Mp1g5iQc1Y)+>6L}TuWvo0 z59)%!tn~Q_5DEny9|8v?)erR4I4}rm2o3Xv&hO2PLkNu$O8d};yfuflz)@6vPPrN8 zIJ5xR-JcQM4Rix`HiH&mZH29A>_`#PL}{(ysc*s|ouFoY;mj zFYD{33x&N+IuOJ*nFq2yUDlfFPo8vj)fyG(wy{BAGYkUh20@zBQG$3g2!0%e$$X_2 z1T>F;ZADN^Oa3k@;$Q=WhrvYYAZ^Hyl5vh|@Q)E-<=|LSIbQVCiajPkSyOC{dFwB(9% zk}v^_@Z=L5s~E=^<{10_`f}T-j1#e`UO|;>u^93?0BKjRFLjOcw2{simHlpGpA@KD N({S{rzE0L_{{kqGmd5}9 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/effect.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/effect.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57f81b175cd76ebd79a576388d4b3a525ad2713d GIT binary patch literal 9972 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/fill.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/fill.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d887cd65ec701781e8c85b5e743344342865ecde GIT binary patch literal 9282 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 O*00s+cnwleKli_~Z0O7Y literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/geometry.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/geometry.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0800f0b68f6c1784ea462ef0a25c29416d4c91b GIT binary patch literal 16621 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 EA0v5MCjbBd literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/picture.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/picture.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d1c34c3bd3abffe0f32fe74df4fef0d0a7ea0f3 GIT binary patch literal 3464 zcmbVOTW=f372f+UmrIHwb+MJCQJS?~KGv_STt| zEC~!yzaQ#O=2qAOsv! zo204cSV@q!NL$f%vXXX42WW@7$!gjq-Lyw~iuaPWbe*iH8)QTAjbt;uL2l^Uo<@Dz z{6?cqWbSolg9Da?WqiU8SX9dHtt2k) z#>usbFlQY${wx;9VRDCuk6|xdM#z_I@gC2Qi}~1i8Wv1Vz}qQqT%IK?!V6z-0Wdqz z#XkX(wz(jn`WRT?_)41HvPaB#x1lVK7+8Wv~eG|%22{AnoSlD%Do(O2OS6GQO* ztT;POhO~0yAy^TuIw;PhH<`p)Tuvr$!(IZdrnMdY+28r*qALt8zv~C1YpuVDD`sKJ zCKKsTCTUKO6QqO5Y= z*KrUI0X~s3u2Oc!s50rmrvi1%_p6`qJO`YL#b#zs~48 zu=HZ^&jTM)0CVR-38Bv#bJ~XhZPo!A%mZ|*&P8+cTxB}xMWYjt?(>Iy9d_fJ0DYaW z!QUvtgIqSG>$rqEv=EmVVSEdOkPz1j&JG#BN}U)tYQet6e+_f^`v|y$QusCPD#fD4 z518gcc6>+02ZytY-&LIc9?}$1j?f(!L({q2NJ7KEl8}M z?99zoUthXeWvmOGqwj&XZL}ZfxW_2MEiW6=7$~=)i{ApwpoSc4e4~UX0=f}obn|On zWmkP>sIfOnnN>HtpLZjBWP7e)GplxH7o`7H=-KxU%z- zbNqyzQB9xYDcS&)o&73$|HpAGg1*ok;C7%3Jo}=JuOf9$xy*w&wZPgwP)$VzK(!Rr zW-HWwqLU6tR^bZ=%59grq(?EHeejAk>VZK58O#-OmxI=cS;Wyibbk+LfVxtEJE$nO z`0r5SJ(@pT_y@?^0v{Zs$OY!IAzjDz0Wga-SNX=4@(oi}U~^_E*`Tz*Uo2IR?U}7) zw@L?Oz^Ph{)!Dns4h?CiVZ?LkAMPK+xx|!FrCD`Uz5-kG4-wD})OqP{nUtlsOjzaqX$iF_nsiq> pkTQzW+>$yjy$`DA$47We^|lF&K!2?PXms?B9+;hXI=#;3{{d!h|8W2S literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/properties.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/properties.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..319487bbfb0a1b6ccc0e3d32d169a5e9594cbbbd GIT binary patch literal 4185 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 BcMJdk literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/xdr.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/drawing/__pycache__/xdr.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a24390239e882d183414d18a1ca6f85b7674f248 GIT binary patch literal 982 zcmbtS&5qMB5KiL!HEma2i30}?x%NOKEho?l?V*T0ETy6dt$c}0>@-=DIM~^e>=oXE zC*aK6_{xb_;KVqoph(>lj^?w!v7H%zGh{jq3>cg5Ux}~` zt1wFoTP4D%w6Sd(FAa*Q{l%a*vpys20C%Vh++_&33*4hVaG%-0Jv!PL^Wg40*wlg% zDl^8Ka4E>AyxN8tk)h-z3lm=FOolxDv-RpKWCbf()yUwd2?@=S^42`BnzN-kS#y~; z`HpS!I|h9GR**`jT$E?a1kV05^yLdM`3Qg+*o3E9)WnF{Hx70HUF=d5(zH}?y?nC| zvYLC!kK>AzERIzW$0et?gwv_MMMYT zqLzw9-3bR`=$c9wz_)DEHGAKGX>i(WaHuaxA#?=cY4A0SG#~(MC9SfO?(cht)l?ri z0XPLa%uoC7&+i%R-d7{2l!rY)A47B9jPM9BzyaVGkM|Wkf$R^uVKtu|O

9vz3 z4N7GthYbg!O+*Kl*{tsRTupZt`Sg)56gRbrHo9$x!++d_Fn*)`FJnH9hK-yo}!$Lr$ z0yiZ44H>{efq-H_x!@%YE;Z4Y!Xb+5;oc=El4!0k(8j1m_FRSOMma2-U`$U+nO^u70V&SsJPFGghKnc;-{i}90v3{;7);D0jwdD)V~L@}nZ!h5 z6zRjskwjeeETL21kK216pZ_NadSA+{0nz^0%tMpP1EunD5z7yVc<;LyyopKTJS?_%%@<}hBsjN$dmD|q=bj#n@GRm`j2gnk$E)Wmz2NxXGAmY+n~ z8F5^^{tOr6`D5Y@@g`ouye8cIad9I5I%?JX7}UKXP63st71)bu08??n)|(hAwtFA5 zw79OKINx2z%?HoA%SzGhzFVPmP(>{nfu3N76^LlXA;4f2^{6GF zH}xQNo^QLu%0k^0KjtvxC#hfo4yrdFrdvRIj=uF#PK6#nXm zE4yN&f;T@{CZ4M3?Y*!yg(#}I3`lFLDIyg)*FVZ#jbif)3-jNTHI~?m**oDOW!5j< zUN{dg!1bG-h9jMVn>pr3J9+vGSTgj_fG78E&qF9F{cY%<31(8}g9-ZI-3(EV@Ll%l6NNWX>w&Jod6Y;V{zkBPvDv6w> z?5FT);Bn0d*#$g2xP9a1RXjY1Dd%n!ga6`2wvWwWyspm48Ngb}RF!WYvHhH;pOp(! zPcH1ZPu{}!_Rd8-oQu=PTMU#|t(QwCD3=&?>6r`X>lnVp!kap-o@Zn9tXx<1X-i~C z9mA-K4DXQyc)T3r(qR7NR0&KKc?u z{?=j^?%5nxDjzZ61&a(3M6uOM4X5;&>Ey@s0%+T#$ri-R7j|TqD!#^Bg|hvpr$leS zgFA}WhOHkss7o4H8hq@ayaf7*QKt8rHgbTuIk}8p*GoLo_K#eiSgJ5oB0*b|z94wc zN0q%vThrLZvjIHhKQn3h z8cJb#P*y5`yB5>|vkeC1GV1*050Qf7@EHq8Cy~QN%7@G!1auD-KPAw~K#ajrSRQl^ zwf@kspjaMWBBRtG*C*FU*`%`$lu4F=0>2JrukumLvDRH2VT1=O{@GTZE$MT(C`>$H zv6NpgPEMxB*u&`ZhUGWu|Nu24jN z#ArbCl0C<#PKU=Qc_wYuTk)Dbgi=QRpYhDdIt=R=P7J4{6`xFxtH+*SxDnY4w?Em( zb2`VB!2l!*98jvq0mD~zhC*lME>XvUcR&iE?Xbf$aGe9ilkcrG4r9^ESD{=NOu<9= zn~3C(8Ei9n%3#lawot}5KAPXd{={yyR^5Vmdlss4-$CQFc|HB+LE2n2SSa|FTHu2b zD>bsn>GmA7TE~4>&@UqIG59WnaR!R5k&7v|rWu^#VZVX0Xh^-Z)~{N%t)L*b%=QGE zBy4*2%&qloS8be7Qv`I4d%lDmw7wKR;QpS!l}vR?tb}cu$w}nF4pXlt4(XH{2`Clk zs3g#6kS~msPN5&V#Py3_r7mj1?Vf*UK~dt8eiY7Enfhg?lStU3&gl1z?;G#@H!)_1 A3;+NC literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/formula/__pycache__/translate.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/formula/__pycache__/translate.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..206013c3035d8b868887d84b12329f7e4ba10d38 GIT binary patch literal 5262 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 S|A&kOBsJN(h?p(Rock~F5Qgdi literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/formula/tokenizer.py b/.venv/lib/python3.9/site-packages/openpyxl/formula/tokenizer.py new file mode 100644 index 00000000..195cc79a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/formula/tokenizer.py @@ -0,0 +1,446 @@ +""" +This module contains a tokenizer for Excel formulae. + +The tokenizer is based on the Javascript tokenizer found at +http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html written by Eric +Bachtal +""" + +import re + + +class TokenizerError(Exception): + """Base class for all Tokenizer errors.""" + + +class Tokenizer(object): + + """ + A tokenizer for Excel worksheet formulae. + + Converts a str string representing an Excel formula (in A1 notation) + into a sequence of `Token` objects. + + `formula`: The str string to tokenize + + Tokenizer defines a method `._parse()` to parse the formula into tokens, + which can then be accessed through the `.items` attribute. + + """ + + SN_RE = re.compile("^[1-9](\\.[0-9]+)?[Ee]$") # Scientific notation + WSPACE_RE = re.compile(r"[ \n]+") + STRING_REGEXES = { + # Inside a string, all characters are treated as literals, except for + # the quote character used to start the string. That character, when + # doubled is treated as a single character in the string. If an + # unmatched quote appears, the string is terminated. + '"': re.compile('"(?:[^"]*"")*[^"]*"(?!")'), + "'": re.compile("'(?:[^']*'')*[^']*'(?!')"), + } + ERROR_CODES = ("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", + "#NUM!", "#N/A", "#GETTING_DATA") + TOKEN_ENDERS = ',;}) +-*/^&=><%' # Each of these characters, marks the + # end of an operand token + + def __init__(self, formula): + self.formula = formula + self.items = [] + self.token_stack = [] # Used to keep track of arrays, functions, and + # parentheses + self.offset = 0 # How many chars have we read + self.token = [] # Used to build up token values char by char + self._parse() + + def _parse(self): + """Populate self.items with the tokens from the formula.""" + if self.offset: + return # Already parsed! + if not self.formula: + return + elif self.formula[0] == '=': + self.offset += 1 + else: + self.items.append(Token(self.formula, Token.LITERAL)) + return + consumers = ( + ('"\'', self._parse_string), + ('[', self._parse_brackets), + ('#', self._parse_error), + (' ', self._parse_whitespace), + ('\n', self._parse_whitespace), + ('+-*/^&=><%', self._parse_operator), + ('{(', self._parse_opener), + (')}', self._parse_closer), + (';,', self._parse_separator), + ) + dispatcher = {} # maps chars to the specific parsing function + for chars, consumer in consumers: + dispatcher.update(dict.fromkeys(chars, consumer)) + while self.offset < len(self.formula): + if self.check_scientific_notation(): # May consume one character + continue + curr_char = self.formula[self.offset] + if curr_char in self.TOKEN_ENDERS: + self.save_token() + if curr_char in dispatcher: + self.offset += dispatcher[curr_char]() + else: + # TODO: this can probably be sped up using a regex to get to + # the next interesting character + self.token.append(curr_char) + self.offset += 1 + self.save_token() + + def _parse_string(self): + """ + Parse a "-delimited string or '-delimited link. + + The offset must be pointing to either a single quote ("'") or double + quote ('"') character. The strings are parsed according to Excel + rules where to escape the delimiter you just double it up. E.g., + "abc""def" in Excel is parsed as 'abc"def' in Python. + + Returns the number of characters matched. (Does not update + self.offset) + + """ + self.assert_empty_token(can_follow=':') + delim = self.formula[self.offset] + assert delim in ('"', "'") + regex = self.STRING_REGEXES[delim] + match = regex.match(self.formula[self.offset:]) + if match is None: + subtype = "string" if delim == '"' else 'link' + raise TokenizerError(f"Reached end of formula while parsing {subtype} in {self.formula}") + match = match.group(0) + if delim == '"': + self.items.append(Token.make_operand(match)) + else: + self.token.append(match) + return len(match) + + def _parse_brackets(self): + """ + Consume all the text between square brackets []. + + Returns the number of characters matched. (Does not update + self.offset) + + """ + assert self.formula[self.offset] == '[' + lefts = [(t.start(), 1) for t in + re.finditer(r"\[", self.formula[self.offset:])] + rights = [(t.start(), -1) for t in + re.finditer(r"\]", self.formula[self.offset:])] + + open_count = 0 + for idx, open_close in sorted(lefts + rights): + open_count += open_close + if open_count == 0: + outer_right = idx + 1 + self.token.append( + self.formula[self.offset:self.offset + outer_right]) + return outer_right + + raise TokenizerError(f"Encountered unmatched '[' in {self.formula}") + + def _parse_error(self): + """ + Consume the text following a '#' as an error. + + Looks for a match in self.ERROR_CODES and returns the number of + characters matched. (Does not update self.offset) + + """ + self.assert_empty_token(can_follow='!') + assert self.formula[self.offset] == '#' + subformula = self.formula[self.offset:] + for err in self.ERROR_CODES: + if subformula.startswith(err): + self.items.append(Token.make_operand(''.join(self.token) + err)) + del self.token[:] + return len(err) + raise TokenizerError(f"Invalid error code at position {self.offset} in '{self.formula}'") + + def _parse_whitespace(self): + """ + Consume a string of consecutive spaces. + + Returns the number of spaces found. (Does not update self.offset). + + """ + assert self.formula[self.offset] in (' ', '\n') + self.items.append(Token(self.formula[self.offset], Token.WSPACE)) + return self.WSPACE_RE.match(self.formula[self.offset:]).end() + + def _parse_operator(self): + """ + Consume the characters constituting an operator. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + if self.formula[self.offset:self.offset + 2] in ('>=', '<=', '<>'): + self.items.append(Token( + self.formula[self.offset:self.offset + 2], + Token.OP_IN + )) + return 2 + curr_char = self.formula[self.offset] # guaranteed to be 1 char + assert curr_char in '%*/^&=><+-' + if curr_char == '%': + token = Token('%', Token.OP_POST) + elif curr_char in "*/^&=><": + token = Token(curr_char, Token.OP_IN) + # From here on, curr_char is guaranteed to be in '+-' + elif not self.items: + token = Token(curr_char, Token.OP_PRE) + else: + prev = next((i for i in reversed(self.items) + if i.type != Token.WSPACE), None) + is_infix = prev and ( + prev.subtype == Token.CLOSE + or prev.type == Token.OP_POST + or prev.type == Token.OPERAND + ) + if is_infix: + token = Token(curr_char, Token.OP_IN) + else: + token = Token(curr_char, Token.OP_PRE) + self.items.append(token) + return 1 + + def _parse_opener(self): + """ + Consumes a ( or { character. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + assert self.formula[self.offset] in ('(', '{') + if self.formula[self.offset] == '{': + self.assert_empty_token() + token = Token.make_subexp("{") + elif self.token: + token_value = "".join(self.token) + '(' + del self.token[:] + token = Token.make_subexp(token_value) + else: + token = Token.make_subexp("(") + self.items.append(token) + self.token_stack.append(token) + return 1 + + def _parse_closer(self): + """ + Consumes a } or ) character. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + assert self.formula[self.offset] in (')', '}') + token = self.token_stack.pop().get_closer() + if token.value != self.formula[self.offset]: + raise TokenizerError( + "Mismatched ( and { pair in '%s'" % self.formula) + self.items.append(token) + return 1 + + def _parse_separator(self): + """ + Consumes a ; or , character. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + curr_char = self.formula[self.offset] + assert curr_char in (';', ',') + if curr_char == ';': + token = Token.make_separator(";") + else: + try: + top_type = self.token_stack[-1].type + except IndexError: + token = Token(",", Token.OP_IN) # Range Union operator + else: + if top_type == Token.PAREN: + token = Token(",", Token.OP_IN) # Range Union operator + else: + token = Token.make_separator(",") + self.items.append(token) + return 1 + + def check_scientific_notation(self): + """ + Consumes a + or - character if part of a number in sci. notation. + + Returns True if the character was consumed and self.offset was + updated, False otherwise. + + """ + curr_char = self.formula[self.offset] + if (curr_char in '+-' + and len(self.token) >= 1 + and self.SN_RE.match("".join(self.token))): + self.token.append(curr_char) + self.offset += 1 + return True + return False + + def assert_empty_token(self, can_follow=()): + """ + Ensure that there's no token currently being parsed. + + Or if there is a token being parsed, it must end with a character in + can_follow. + + If there are unconsumed token contents, it means we hit an unexpected + token transition. In this case, we raise a TokenizerError + + """ + if self.token and self.token[-1] not in can_follow: + raise TokenizerError(f"Unexpected character at position {self.offset} in '{self.formula}'") + + def save_token(self): + """If there's a token being parsed, add it to the item list.""" + if self.token: + self.items.append(Token.make_operand("".join(self.token))) + del self.token[:] + + def render(self): + """Convert the parsed tokens back to a string.""" + if not self.items: + return "" + elif self.items[0].type == Token.LITERAL: + return self.items[0].value + return "=" + "".join(token.value for token in self.items) + + +class Token(object): + + """ + A token in an Excel formula. + + Tokens have three attributes: + + * `value`: The string value parsed that led to this token + * `type`: A string identifying the type of token + * `subtype`: A string identifying subtype of the token (optional, and + defaults to "") + + """ + + __slots__ = ['value', 'type', 'subtype'] + + LITERAL = "LITERAL" + OPERAND = "OPERAND" + FUNC = "FUNC" + ARRAY = "ARRAY" + PAREN = "PAREN" + SEP = "SEP" + OP_PRE = "OPERATOR-PREFIX" + OP_IN = "OPERATOR-INFIX" + OP_POST = "OPERATOR-POSTFIX" + WSPACE = "WHITE-SPACE" + + def __init__(self, value, type_, subtype=""): + self.value = value + self.type = type_ + self.subtype = subtype + + # Literal operands: + # + # Literal operands are always of type 'OPERAND' and can be of subtype + # 'TEXT' (for text strings), 'NUMBER' (for all numeric types), 'LOGICAL' + # (for TRUE and FALSE), 'ERROR' (for literal error values), or 'RANGE' + # (for all range references). + + TEXT = 'TEXT' + NUMBER = 'NUMBER' + LOGICAL = 'LOGICAL' + ERROR = 'ERROR' + RANGE = 'RANGE' + + def __repr__(self): + return u"{0} {1} {2}:".format(self.type, self.subtype, self.value) + + @classmethod + def make_operand(cls, value): + """Create an operand token.""" + if value.startswith('"'): + subtype = cls.TEXT + elif value.startswith('#'): + subtype = cls.ERROR + elif value in ('TRUE', 'FALSE'): + subtype = cls.LOGICAL + else: + try: + float(value) + subtype = cls.NUMBER + except ValueError: + subtype = cls.RANGE + return cls(value, cls.OPERAND, subtype) + + + # Subexpresssions + # + # There are 3 types of `Subexpressions`: functions, array literals, and + # parentheticals. Subexpressions have 'OPEN' and 'CLOSE' tokens. 'OPEN' + # is used when parsing the initial expression token (i.e., '(' or '{') + # and 'CLOSE' is used when parsing the closing expression token ('}' or + # ')'). + + OPEN = "OPEN" + CLOSE = "CLOSE" + + @classmethod + def make_subexp(cls, value, func=False): + """ + Create a subexpression token. + + `value`: The value of the token + `func`: If True, force the token to be of type FUNC + + """ + assert value[-1] in ('{', '}', '(', ')') + if func: + assert re.match('.+\\(|\\)', value) + type_ = Token.FUNC + elif value in '{}': + type_ = Token.ARRAY + elif value in '()': + type_ = Token.PAREN + else: + type_ = Token.FUNC + subtype = cls.CLOSE if value in ')}' else cls.OPEN + return cls(value, type_, subtype) + + def get_closer(self): + """Return a closing token that matches this token's type.""" + assert self.type in (self.FUNC, self.ARRAY, self.PAREN) + assert self.subtype == self.OPEN + value = "}" if self.type == self.ARRAY else ")" + return self.make_subexp(value, func=self.type == self.FUNC) + + # Separator tokens + # + # Argument separators always have type 'SEP' and can have one of two + # subtypes: 'ARG', 'ROW'. 'ARG' is used for the ',' token, when used to + # delimit either function arguments or array elements. 'ROW' is used for + # the ';' token, which is always used to delimit rows in an array + # literal. + + ARG = "ARG" + ROW = "ROW" + + @classmethod + def make_separator(cls, value): + """Create a separator token""" + assert value in (',', ';') + subtype = cls.ARG if value == ',' else cls.ROW + return cls(value, cls.SEP, subtype) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/formula/translate.py b/.venv/lib/python3.9/site-packages/openpyxl/formula/translate.py new file mode 100644 index 00000000..447a9711 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/formula/translate.py @@ -0,0 +1,166 @@ +""" +This module contains code to translate formulae across cells in a worksheet. + +The idea is that if A1 has formula "=B1+C1", then translating it to cell A2 +results in formula "=B2+C2". The algorithm relies on the formula tokenizer +to identify the parts of the formula that need to change. + +""" + +import re +from .tokenizer import Tokenizer, Token +from openpyxl.utils import ( + coordinate_to_tuple, + column_index_from_string, + get_column_letter +) + +class TranslatorError(Exception): + """ + Raised when a formula can't be translated across cells. + + This error arises when a formula's references would be translated outside + the worksheet's bounds on the top or left. Excel represents these + situations with a #REF! literal error. E.g., if the formula at B2 is + '=A1', attempting to translate the formula to B1 raises TranslatorError, + since there's no cell above A1. Similarly, translating the same formula + from B2 to A2 raises TranslatorError, since there's no cell to the left of + A1. + + """ + + +class Translator(object): + + """ + Modifies a formula so that it can be translated from one cell to another. + + `formula`: The str string to translate. Must include the leading '=' + character. + `origin`: The cell address (in A1 notation) where this formula was + defined (excluding the worksheet name). + + """ + + def __init__(self, formula, origin): + # Excel errors out when a workbook has formulae in R1C1 notation, + # regardless of the calcPr:refMode setting, so I'm assuming the + # formulae stored in the workbook must be in A1 notation. + self.row, self.col = coordinate_to_tuple(origin) + self.tokenizer = Tokenizer(formula) + + def get_tokens(self): + "Returns a list with the tokens comprising the formula." + return self.tokenizer.items + + ROW_RANGE_RE = re.compile(r"(\$?[1-9][0-9]{0,6}):(\$?[1-9][0-9]{0,6})$") + COL_RANGE_RE = re.compile(r"(\$?[A-Za-z]{1,3}):(\$?[A-Za-z]{1,3})$") + CELL_REF_RE = re.compile(r"(\$?[A-Za-z]{1,3})(\$?[1-9][0-9]{0,6})$") + + @staticmethod + def translate_row(row_str, rdelta): + """ + Translate a range row-snippet by the given number of rows. + """ + if row_str.startswith('$'): + return row_str + else: + new_row = int(row_str) + rdelta + if new_row <= 0: + raise TranslatorError("Formula out of range") + return str(new_row) + + @staticmethod + def translate_col(col_str, cdelta): + """ + Translate a range col-snippet by the given number of columns + """ + if col_str.startswith('$'): + return col_str + else: + try: + return get_column_letter( + column_index_from_string(col_str) + cdelta) + except ValueError: + raise TranslatorError("Formula out of range") + + @staticmethod + def strip_ws_name(range_str): + "Splits out the worksheet reference, if any, from a range reference." + # This code assumes that named ranges cannot contain any exclamation + # marks. Excel refuses to create these (even using VBA), and + # complains of a corrupt workbook when there are names with + # exclamation marks. The ECMA spec only states that named ranges will + # be of `ST_Xstring` type, which in theory allows '!' (char code + # 0x21) per http://www.w3.org/TR/xml/#charsets + if '!' in range_str: + sheet, range_str = range_str.rsplit('!', 1) + return sheet + "!", range_str + return "", range_str + + @classmethod + def translate_range(cls, range_str, rdelta, cdelta): + """ + Translate an A1-style range reference to the destination cell. + + `rdelta`: the row offset to add to the range + `cdelta`: the column offset to add to the range + `range_str`: an A1-style reference to a range. Potentially includes + the worksheet reference. Could also be a named range. + + """ + ws_part, range_str = cls.strip_ws_name(range_str) + match = cls.ROW_RANGE_RE.match(range_str) # e.g. `3:4` + if match is not None: + return (ws_part + cls.translate_row(match.group(1), rdelta) + ":" + + cls.translate_row(match.group(2), rdelta)) + match = cls.COL_RANGE_RE.match(range_str) # e.g. `A:BC` + if match is not None: + return (ws_part + cls.translate_col(match.group(1), cdelta) + ':' + + cls.translate_col(match.group(2), cdelta)) + if ':' in range_str: # e.g. `A1:B5` + # The check is necessarily general because range references can + # have one or both endpoints specified by named ranges. I.e., + # `named_range:C2`, `C2:named_range`, and `name1:name2` are all + # valid references. Further, Excel allows chaining multiple + # colons together (with unclear meaning) + return ws_part + ":".join( + cls.translate_range(piece, rdelta, cdelta) + for piece in range_str.split(':')) + match = cls.CELL_REF_RE.match(range_str) + if match is None: # Must be a named range + return range_str + return (ws_part + cls.translate_col(match.group(1), cdelta) + + cls.translate_row(match.group(2), rdelta)) + + def translate_formula(self, dest=None, row_delta=0, col_delta=0): + """ + Convert the formula into A1 notation, or as row and column coordinates + + The formula is converted into A1 assuming it is assigned to the cell + whose address is `dest` (no worksheet name). + + """ + tokens = self.get_tokens() + if not tokens: + return "" + elif tokens[0].type == Token.LITERAL: + return tokens[0].value + out = ['='] + # per the spec: + # A compliant producer or consumer considers a defined name in the + # range A1-XFD1048576 to be an error. All other names outside this + # range can be defined as names and overrides a cell reference if an + # ambiguity exists. (I.18.2.5) + if dest: + row, col = coordinate_to_tuple(dest) + row_delta = row - self.row + col_delta = col - self.col + for token in tokens: + if (token.type == Token.OPERAND + and token.subtype == Token.RANGE): + out.append(self.translate_range(token.value, row_delta, + col_delta)) + else: + out.append(token.value) + return "".join(out) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__init__.py new file mode 100644 index 00000000..c3085ee5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__init__.py @@ -0,0 +1,3 @@ +""" +Stuff related to Office OpenXML packaging: relationships, archive, content types. +""" diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1388078ae4f7cf596e5e360cfc289143e18be7b1 GIT binary patch literal 281 zcmYjMF-`+95cH)XIw^l}8!6ltR3L;XC@6p+U4znCj=dMJaBRzVPMELo0&0GfmM73r z;SdQi(#&e4UCpeV&01E`f4s?6ou3EuA5Asam1$ZHdxfsh;JA+~(rAMBXJsAuYI!%Fu`luDr|WGS4Mo>e`2Svp*3+dnLp> zi<6r8E8haPs7b=DjQS?lQ+O;Udk4KT-dpHKbR$?=LnzsJch;RlqKv1<4w%5#QlY$9 b_-h41kfSU_7s`Hmx%GNy@#^sIS0(!ag literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/core.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/core.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..016a4e72e34f9639912f64d403cb478500f9810b GIT binary patch literal 3079 zcmZ`*OK%*x5pMQtdY*b%9@}g0y1mI_F-SaDK0V zPq%lTez!AGp$1&k|hc^5X#WEi`!xfLoL> zi`v|I?ogMx%x2CjLOrubPj}`kTx?~SxbV;7LAN2l2~GY1Kv@+yuX0YDmrms{Yf41O z?8>Rg*zQ^{i6Rv_Aurv^He(Me`#Gp%?rXxlYgXk(PUVik*YC(9Sun6A4HTGGPI#gT?YX+P&sD6H5CN=1*Hc-JOOeM)y9%uP&eN{s*;v<9 zypPjr?=a4$T&$tA&9irwKx_Q>PlKGZG#)&R1=|woAs_6_4pdnTM#({0W`q7AFAfJ; zx;L22)InL?>wi6vsp5BGncv6zTn@?!FDA32Y%uSEm>49b;Qh%Al$VhboPPwGWCy?^ zEfSa(kacq0J#Xy(sd&#B4u?Ho2T_Dek0RZOqP%3&4Cz)B{bd?wry~J_phL^(Ha`O6 zKy*cFHsRtE&}O!{1RrDHDnJ*SL{A2`Z=0S=L9_6?{1n6%LBj}jnD>JPbR4P! z9>$ENC=_QQZle*xgKkdurizEDEIwDE6k&G;b%@#L&KEawpn&N4srP3&&*aawQ|LWFN`KE8kV-dA6SJF)m8yk8Xj zDBLnaw1XMry@uVMEq(;<(TzCE$`?^l7EnFJPqC{5QKlZ3%pA1e&3cZJG-D>Y!LKMc zr&n|>5j<9<(4IY!RmvI>_Q;e+sGX^k2?&0LjPb z6L)b5K>|5vzZ5gwhUBc^&k>~JDP-$IE+kGI2!5E(sqgvhMJbrnO@gQwO+k__n3;>7Mimb{5B$W?nS2aMVssqB22K1^npkD=mwQ2=0s5*f4ssY%j znt;u!1=xbZWJPUA30BlGMTZP~E8;8ABYuPMHNvkDFo~GWieDo23xol}&k=rx@CCv> zgkK|k3D8>=*BA7BS~G-3yQsj-XtZ3I%;+;(j6QR=4dB_vl8_duilQgLNz4e=nos`! zdwlb9{Pq`f^gg|iYXl}MaRZ@=fW=bO03h>0rNu2?+!lK5;@hSWob@h_St+Ez zvLsvtW0^Z266(vPo1(yaL2rIgt}r)9;}zOHlQ_z={&-p>roNgh(Y!?AHT;4Y99gO2 zLdm(0jaN)fGj_+SU@ipnvWJHJkcD;d&HO_0Til#n2MEB38y*^XgWL+%JFCtA0fIT& A5C8xG literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/extended.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/extended.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c7ef9d8561ecb5968473cf9210d82ad738b27e5 GIT binary patch literal 3131 zcmbVOOK%)S5T55gW_NAJc3yTKn-^r0unP`vqA0`;1S#HFtTAXVqseyL9+;h3y4%K% zCZ_~NLgK`cOCoW}8GcP)5eNPPC%&3pI};KF$*lYHSKT$;)zvjs9X1-Cfv5ND&*4wg zhVeTI#m9kg4NYbMW-v202BvSC#I4vG*uI@}J9Y-H@8;ZzOM|js26tKMf#Fv^HCUNd z9vG~`&HJ`rg;Q-8t&qh;!PkBcabBN^U_ zIb?TrcgPut*X2@iw#s)Dr0Q2IpWj~T1>IhsUfg>u5EpF`O`ZoZ6C1&%?in!Heq?3F zBb(WeO=d9Xd+Vt2*t}8(i%f zJBbw~nP!-E>xd~Pz@G694bfh%p~+bQH+=ZPx0vY|zU@2Af*)+{Ekzr>XybaE-pRMo zed6j$5G3J%!y#S}3{tig6R!usw_9O69)ZMTKH!Ox7^?IK2_ic z3Vgc2X9|3_zz>2S8ryJ~AK^#Gd=7km%#Rgxju+lNQRqJjzPI0IVV+Y3ozv()GoJHo zLH}HVzXrbNlh>K+pJ#PeVbuqw-+E}Qo6KXi2NnOqLj&=D5wYLU(>KOn*NVWWCsf4c z-370Sr^k77VWGX?<%L6RoCRo1nShe#s*P$A=J55n3g`{>$I+VAoHmWb4@uJ-z2%okynZrqBJZ{!!l z)pRLT93z{d;0((8z)egqVj5oD43iDcx_?bBM}3jXbX_%X?DTouKQ;=q9EsBxxQHfy z17v1q1V)BD8JL+3v@!>1XD-mmN(C|fpasvtz){f$_K-k98z8m6J$@J%Zuq?7$&>gU7>8M-7izYr1sz&5#l(6)YB`1 zOQfva`=6v@5Gw7$ztHwpptT{w;ilB3UK}x+p_?lFtFERhj8~%p*Y%qbV>}s;mzS3p z<7gk(D`AAC8mp0txxBrOd6g6=Nn`e@DPKlvvxhX#rD)PnWB)9#hmz~k z&HU8GDHxJYn`;0=N{ zL5JW?0@{aoi(r9(j24#(ngnkXyhA`{iFXNdoA%B5M+&Aw5+#uef-ZFBhXCVv$$Z&g zC4b}Y{PSOHdvE?e(&+v}Ax?K48%@%dl%Wvl_pL|PV}mXLxD=Smd2E^Z01!}CSGzld zIFT(EC4LLvlsJO7bXlc=5}a#WawX`ZfztmTjX2c(1ox64=*OYlFME02`3M?6ppDD( zgjqN5&b`3)@$9Yn?!P?ZG-l~`TbINE5P>V5A!Y!&90V-wqwJPdxIy(;&~{-aisG|@ zpzR11>(!z`J9ltx8N~#-QcxuuMF*AA{}Uoq_g@$%bRiM%6OgxbW9&yHtoWHL#8pzs zy-FuSp)I!o70W3(Rvn&w>7V5|xE#Lx?+@=v9u?hI!#}lmQEMYh^hGqpl|;5>;VLUA z2L0I==?U_sKmVekHWCraMt++9p?wX+X1kvzQla3;JPt=$Aop&56#2=JhYBBHekCH; b>B^PS9quYciKP1yyk^&(x?L$xx%B%RW9+^i literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/interface.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/interface.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c146991f530106417a90deb37b462a0b2e56113b GIT binary patch literal 1615 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 RSuj=eTO&<9sQt$<{|BX2^-ur+ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/relationship.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/relationship.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da1c6fabca6e9be74ccbb90053cc0a4394cc9374 GIT binary patch literal 4729 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 UwWifJH)`#m{i@$=TK?1j1L$mUGynhq literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/workbook.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/packaging/__pycache__/workbook.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b3f639d2f90b08b362cd71932ea98eebd6e49fc GIT binary patch literal 5617 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 Ag8%>k literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/cache.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/cache.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ed9a8ddc3e107d77846e56f6f72fe3aa9fd232b GIT binary patch literal 23788 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 J!{=%L-sejtyY&D7 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/record.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/pivot/__pycache__/record.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55852f2601481f7b06837d400f0db3148f705a32 GIT binary patch literal 3123 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�dK#Qeug{Mk>d6A0j0-*%T=;%$_7AE`lSZ=qKpdc*veZ9}(4_ z3ZU*()akmNN}FU;^7{QAtb(&6f?iBS^%|cL_b>axkK*%f2LxQMQTfNeXnOn?9Ie!* zI9c#P7y`k2gTFx<{`PNf0bl1|GL}f;L;ipH_l4}YMvmrjIAS)D&WbfMTbed@28YLv z2PcU&l|9ZaPQls6ioLxt(kq@42^Rgp?n}vp6gxA$K`lc{^hk~rUH!(EUhf&V?m<|z zyiwGQtvzF5Oo-8-8eoj=J!9CGJ~~;kEg2ibIOwp);Xtz;bXT^RU8`;`EHz{=hAlmc zu_Js^AF_+#@_45=vE5xsH-x=m9Ol}xB(via?>5zKacPbRPPk3Tm)cC&LfA^!M%Yex zgs_7!M%YQ%MR=63o3Mu5%p0ek7Q`f%WlJXHMF$nN6S{|jO-+iw5> literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/pivot/cache.py b/.venv/lib/python3.9/site-packages/openpyxl/pivot/cache.py new file mode 100644 index 00000000..c1bdf0a3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/pivot/cache.py @@ -0,0 +1,1119 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Float, + Set, + NoneSet, + String, + Integer, + DateTime, + Sequence, +) + +from openpyxl.descriptors.excel import ( + HexBinary, + ExtensionList, + Relation, +) +from openpyxl.descriptors.nested import NestedInteger +from openpyxl.descriptors.sequence import ( + NestedSequence, + MultiSequence, + MultiSequencePart, +) +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring +from openpyxl.packaging.relationship import ( + RelationshipList, + Relationship, + get_rels_path +) + +from .table import ( + PivotArea, + Reference, +) +from .fields import ( + Boolean, + Error, + Missing, + Number, + Text, + TupleList, + DateTimeField, +) + +class MeasureDimensionMap(Serialisable): + + tagname = "map" + + measureGroup = Integer(allow_none=True) + dimension = Integer(allow_none=True) + + def __init__(self, + measureGroup=None, + dimension=None, + ): + self.measureGroup = measureGroup + self.dimension = dimension + + +class MeasureGroup(Serialisable): + + tagname = "measureGroup" + + name = String() + caption = String() + + def __init__(self, + name=None, + caption=None, + ): + self.name = name + self.caption = caption + + +class PivotDimension(Serialisable): + + tagname = "dimension" + + measure = Bool() + name = String() + uniqueName = String() + caption = String() + + def __init__(self, + measure=None, + name=None, + uniqueName=None, + caption=None, + ): + self.measure = measure + self.name = name + self.uniqueName = uniqueName + self.caption = caption + + +class CalculatedMember(Serialisable): + + tagname = "calculatedMember" + + name = String() + mdx = String() + memberName = String() + hierarchy = String() + parent = String() + solveOrder = Integer() + set = Bool() + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + name=None, + mdx=None, + memberName=None, + hierarchy=None, + parent=None, + solveOrder=None, + set=None, + extLst=None, + ): + self.name = name + self.mdx = mdx + self.memberName = memberName + self.hierarchy = hierarchy + self.parent = parent + self.solveOrder = solveOrder + self.set = set + #self.extLst = extLst + + +class CalculatedItem(Serialisable): + + tagname = "calculatedItem" + + field = Integer(allow_none=True) + formula = String() + pivotArea = Typed(expected_type=PivotArea, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotArea', 'extLst') + + def __init__(self, + field=None, + formula=None, + pivotArea=None, + extLst=None, + ): + self.field = field + self.formula = formula + self.pivotArea = pivotArea + self.extLst = extLst + + +class ServerFormat(Serialisable): + + tagname = "serverFormat" + + culture = String(allow_none=True) + format = String(allow_none=True) + + def __init__(self, + culture=None, + format=None, + ): + self.culture = culture + self.format = format + + +class ServerFormatList(Serialisable): + + tagname = "serverFormats" + + serverFormat = Sequence(expected_type=ServerFormat, allow_none=True) + + __elements__ = ('serverFormat',) + __attrs__ = ('count',) + + def __init__(self, + count=None, + serverFormat=None, + ): + self.serverFormat = serverFormat + + + @property + def count(self): + return len(self.serverFormat) + + +class Query(Serialisable): + + tagname = "query" + + mdx = String() + tpls = Typed(expected_type=TupleList, allow_none=True) + + __elements__ = ('tpls',) + + def __init__(self, + mdx=None, + tpls=None, + ): + self.mdx = mdx + self.tpls = tpls + + +class QueryCache(Serialisable): + + tagname = "queryCache" + + count = Integer() + query = Typed(expected_type=Query, ) + + __elements__ = ('query',) + + def __init__(self, + count=None, + query=None, + ): + self.count = count + self.query = query + + +class OLAPSet(Serialisable): + + tagname = "set" + + count = Integer() + maxRank = Integer() + setDefinition = String() + sortType = NoneSet(values=(['ascending', 'descending', 'ascendingAlpha', + 'descendingAlpha', 'ascendingNatural', 'descendingNatural'])) + queryFailed = Bool() + tpls = Typed(expected_type=TupleList, allow_none=True) + sortByTuple = Typed(expected_type=TupleList, allow_none=True) + + __elements__ = ('tpls', 'sortByTuple') + + def __init__(self, + count=None, + maxRank=None, + setDefinition=None, + sortType=None, + queryFailed=None, + tpls=None, + sortByTuple=None, + ): + self.count = count + self.maxRank = maxRank + self.setDefinition = setDefinition + self.sortType = sortType + self.queryFailed = queryFailed + self.tpls = tpls + self.sortByTuple = sortByTuple + + +class OLAPSets(Serialisable): + + count = Integer() + set = Typed(expected_type=OLAPSet, ) + + __elements__ = ('set',) + + def __init__(self, + count=None, + set=None, + ): + self.count = count + self.set = set + + +class PCDSDTCEntries(Serialisable): + + tagname = "pCDSDTCEntries" + + count = Integer() + # some elements are choice + m = Typed(expected_type=Missing, ) + n = Typed(expected_type=Number, ) + e = Typed(expected_type=Error, ) + s = Typed(expected_type=Text) + + __elements__ = ('m', 'n', 'e', 's') + + def __init__(self, + count=None, + m=None, + n=None, + e=None, + s=None, + ): + self.count = count + self.m = m + self.n = n + self.e = e + self.s = s + + +class TupleCache(Serialisable): + + tagname = "tupleCache" + + entries = Typed(expected_type=PCDSDTCEntries, allow_none=True) + sets = Typed(expected_type=OLAPSets, allow_none=True) + queryCache = Typed(expected_type=QueryCache, allow_none=True) + serverFormats = Typed(expected_type=ServerFormatList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('entries', 'sets', 'queryCache', 'serverFormats', 'extLst') + + def __init__(self, + entries=None, + sets=None, + queryCache=None, + serverFormats=None, + extLst=None, + ): + self.entries = entries + self.sets = sets + self.queryCache = queryCache + self.serverFormats = serverFormats + self.extLst = extLst + + +class PCDKPI(Serialisable): + + tagname = "pCDKPI" + + uniqueName = String() + caption = String(allow_none=True) + displayFolder = String() + measureGroup = String() + parent = String() + value = String() + goal = String() + status = String() + trend = String() + weight = String() + time = String() + + def __init__(self, + uniqueName=None, + caption=None, + displayFolder=None, + measureGroup=None, + parent=None, + value=None, + goal=None, + status=None, + trend=None, + weight=None, + time=None, + ): + self.uniqueName = uniqueName + self.caption = caption + self.displayFolder = displayFolder + self.measureGroup = measureGroup + self.parent = parent + self.value = value + self.goal = goal + self.status = status + self.trend = trend + self.weight = weight + self.time = time + + +class GroupMember(Serialisable): + + tagname = "groupMember" + + uniqueName = String() + group = Bool() + + def __init__(self, + uniqueName=None, + group=None, + ): + self.uniqueName = uniqueName + self.group = group + + +class GroupMembers(Serialisable): + + count = Integer() + groupMember = Typed(expected_type=GroupMember, ) + + __elements__ = ('groupMember',) + + def __init__(self, + count=None, + groupMember=None, + ): + self.count = count + self.groupMember = groupMember + + +class LevelGroup(Serialisable): + + tagname = "levelGroup" + + name = String() + uniqueName = String() + caption = String() + uniqueParent = String() + id = Integer() + groupMembers = Typed(expected_type=GroupMembers, ) + + __elements__ = ('groupMembers',) + + def __init__(self, + name=None, + uniqueName=None, + caption=None, + uniqueParent=None, + id=None, + groupMembers=None, + ): + self.name = name + self.uniqueName = uniqueName + self.caption = caption + self.uniqueParent = uniqueParent + self.id = id + self.groupMembers = groupMembers + + +class Groups(Serialisable): + + tagname = "groups" + + count = Integer() + group = Typed(expected_type=LevelGroup, ) + + __elements__ = ('group',) + + def __init__(self, + count=None, + group=None, + ): + self.count = count + self.group = group + + +class GroupLevel(Serialisable): + + tagname = "groupLevel" + + uniqueName = String() + caption = String() + user = Bool() + customRollUp = Bool() + groups = Typed(expected_type=Groups, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('groups', 'extLst') + + def __init__(self, + uniqueName=None, + caption=None, + user=None, + customRollUp=None, + groups=None, + extLst=None, + ): + self.uniqueName = uniqueName + self.caption = caption + self.user = user + self.customRollUp = customRollUp + self.groups = groups + self.extLst = extLst + + +class GroupLevels(Serialisable): + + count = Integer() + groupLevel = Typed(expected_type=GroupLevel, ) + + __elements__ = ('groupLevel',) + + def __init__(self, + count=None, + groupLevel=None, + ): + self.count = count + self.groupLevel = groupLevel + + +class FieldUsage(Serialisable): + + tagname = "fieldUsage" + + x = Integer() + + def __init__(self, + x=None, + ): + self.x = x + + +class FieldsUsage(Serialisable): + + count = Integer() + fieldUsage = Typed(expected_type=FieldUsage, allow_none=True) + + __elements__ = ('fieldUsage',) + + def __init__(self, + count=None, + fieldUsage=None, + ): + self.count = count + self.fieldUsage = fieldUsage + + +class CacheHierarchy(Serialisable): + + tagname = "cacheHierarchy" + + uniqueName = String() + caption = String(allow_none=True) + measure = Bool() + set = Bool() + parentSet = Integer(allow_none=True) + iconSet = Integer() + attribute = Bool() + time = Bool() + keyAttribute = Bool() + defaultMemberUniqueName = String(allow_none=True) + allUniqueName = String(allow_none=True) + allCaption = String(allow_none=True) + dimensionUniqueName = String(allow_none=True) + displayFolder = String(allow_none=True) + measureGroup = String(allow_none=True) + measures = Bool() + count = Integer() + oneField = Bool() + memberValueDatatype = Integer(allow_none=True) + unbalanced = Bool(allow_none=True) + unbalancedGroup = Bool(allow_none=True) + hidden = Bool() + fieldsUsage = Typed(expected_type=FieldsUsage, allow_none=True) + groupLevels = Typed(expected_type=GroupLevels, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('fieldsUsage', 'groupLevels') + + def __init__(self, + uniqueName="", + caption=None, + measure=None, + set=None, + parentSet=None, + iconSet=0, + attribute=None, + time=None, + keyAttribute=None, + defaultMemberUniqueName=None, + allUniqueName=None, + allCaption=None, + dimensionUniqueName=None, + displayFolder=None, + measureGroup=None, + measures=None, + count=None, + oneField=None, + memberValueDatatype=None, + unbalanced=None, + unbalancedGroup=None, + hidden=None, + fieldsUsage=None, + groupLevels=None, + extLst=None, + ): + self.uniqueName = uniqueName + self.caption = caption + self.measure = measure + self.set = set + self.parentSet = parentSet + self.iconSet = iconSet + self.attribute = attribute + self.time = time + self.keyAttribute = keyAttribute + self.defaultMemberUniqueName = defaultMemberUniqueName + self.allUniqueName = allUniqueName + self.allCaption = allCaption + self.dimensionUniqueName = dimensionUniqueName + self.displayFolder = displayFolder + self.measureGroup = measureGroup + self.measures = measures + self.count = count + self.oneField = oneField + self.memberValueDatatype = memberValueDatatype + self.unbalanced = unbalanced + self.unbalancedGroup = unbalancedGroup + self.hidden = hidden + self.fieldsUsage = fieldsUsage + self.groupLevels = groupLevels + self.extLst = extLst + + +class GroupItems(Serialisable): + + tagname = "groupItems" + + m = Sequence(expected_type=Missing) + n = Sequence(expected_type=Number) + b = Sequence(expected_type=Boolean) + e = Sequence(expected_type=Error) + s = Sequence(expected_type=Text) + d = Sequence(expected_type=DateTimeField,) + + __elements__ = ('m', 'n', 'b', 'e', 's', 'd') + __attrs__ = ("count", ) + + def __init__(self, + count=None, + m=(), + n=(), + b=(), + e=(), + s=(), + d=(), + ): + self.m = m + self.n = n + self.b = b + self.e = e + self.s = s + self.d = d + + + @property + def count(self): + return len(self.m + self.n + self.b + self.e + self.s + self.d) + + +class DiscretePr(Serialisable): + + tagname = "discretePr" + + count = Integer() + x = NestedInteger(allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + count=None, + x=None, + ): + self.count = count + self.x = x + + +class RangePr(Serialisable): + + tagname = "rangePr" + + autoStart = Bool(allow_none=True) + autoEnd = Bool(allow_none=True) + groupBy = Set(values=(['range', 'seconds', 'minutes', 'hours', 'days', + 'months', 'quarters', 'years'])) + startNum = Float(allow_none=True) + endNum = Float(allow_none=True) + startDate = DateTime(allow_none=True) + endDate = DateTime(allow_none=True) + groupInterval = Float(allow_none=True) + + def __init__(self, + autoStart=True, + autoEnd=True, + groupBy="range", + startNum=None, + endNum=None, + startDate=None, + endDate=None, + groupInterval=1, + ): + self.autoStart = autoStart + self.autoEnd = autoEnd + self.groupBy = groupBy + self.startNum = startNum + self.endNum = endNum + self.startDate = startDate + self.endDate = endDate + self.groupInterval = groupInterval + + +class FieldGroup(Serialisable): + + tagname = "fieldGroup" + + par = Integer(allow_none=True) + base = Integer(allow_none=True) + rangePr = Typed(expected_type=RangePr, allow_none=True) + discretePr = Typed(expected_type=DiscretePr, allow_none=True) + groupItems = Typed(expected_type=GroupItems, allow_none=True) + + __elements__ = ('rangePr', 'discretePr', 'groupItems') + + def __init__(self, + par=None, + base=None, + rangePr=None, + discretePr=None, + groupItems=None, + ): + self.par = par + self.base = base + self.rangePr = rangePr + self.discretePr = discretePr + self.groupItems = groupItems + + +class SharedItems(Serialisable): + + tagname = "sharedItems" + + _fields = MultiSequence() + m = MultiSequencePart(expected_type=Missing, store="_fields") + n = MultiSequencePart(expected_type=Number, store="_fields") + b = MultiSequencePart(expected_type=Boolean, store="_fields") + e = MultiSequencePart(expected_type=Error, store="_fields") + s = MultiSequencePart(expected_type=Text, store="_fields") + d = MultiSequencePart(expected_type=DateTimeField, store="_fields") + # attributes are optional and must be derived from associated cache records + containsSemiMixedTypes = Bool(allow_none=True) + containsNonDate = Bool(allow_none=True) + containsDate = Bool(allow_none=True) + containsString = Bool(allow_none=True) + containsBlank = Bool(allow_none=True) + containsMixedTypes = Bool(allow_none=True) + containsNumber = Bool(allow_none=True) + containsInteger = Bool(allow_none=True) + minValue = Float(allow_none=True) + maxValue = Float(allow_none=True) + minDate = DateTime(allow_none=True) + maxDate = DateTime(allow_none=True) + longText = Bool(allow_none=True) + + __attrs__ = ('count', 'containsBlank', 'containsDate', 'containsInteger', + 'containsMixedTypes', 'containsNonDate', 'containsNumber', + 'containsSemiMixedTypes', 'containsString', 'minValue', 'maxValue', + 'minDate', 'maxDate', 'longText') + + def __init__(self, + _fields=(), + containsSemiMixedTypes=None, + containsNonDate=None, + containsDate=None, + containsString=None, + containsBlank=None, + containsMixedTypes=None, + containsNumber=None, + containsInteger=None, + minValue=None, + maxValue=None, + minDate=None, + maxDate=None, + count=None, + longText=None, + ): + self._fields = _fields + self.containsBlank = containsBlank + self.containsDate = containsDate + self.containsNonDate = containsNonDate + self.containsString = containsString + self.containsMixedTypes = containsMixedTypes + self.containsSemiMixedTypes = containsSemiMixedTypes + self.containsNumber = containsNumber + self.containsInteger = containsInteger + self.minValue = minValue + self.maxValue = maxValue + self.minDate = minDate + self.maxDate = maxDate + self.longText = longText + + + @property + def count(self): + return len(self._fields) + + +class CacheField(Serialisable): + + tagname = "cacheField" + + sharedItems = Typed(expected_type=SharedItems, allow_none=True) + fieldGroup = Typed(expected_type=FieldGroup, allow_none=True) + mpMap = NestedInteger(allow_none=True, attribute="v") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + name = String() + caption = String(allow_none=True) + propertyName = String(allow_none=True) + serverField = Bool(allow_none=True) + uniqueList = Bool(allow_none=True) + numFmtId = Integer(allow_none=True) + formula = String(allow_none=True) + sqlType = Integer(allow_none=True) + hierarchy = Integer(allow_none=True) + level = Integer(allow_none=True) + databaseField = Bool(allow_none=True) + mappingCount = Integer(allow_none=True) + memberPropertyField = Bool(allow_none=True) + + __elements__ = ('sharedItems', 'fieldGroup', 'mpMap') + + def __init__(self, + sharedItems=None, + fieldGroup=None, + mpMap=None, + extLst=None, + name=None, + caption=None, + propertyName=None, + serverField=None, + uniqueList=True, + numFmtId=None, + formula=None, + sqlType=0, + hierarchy=0, + level=0, + databaseField=True, + mappingCount=None, + memberPropertyField=None, + ): + self.sharedItems = sharedItems + self.fieldGroup = fieldGroup + self.mpMap = mpMap + self.extLst = extLst + self.name = name + self.caption = caption + self.propertyName = propertyName + self.serverField = serverField + self.uniqueList = uniqueList + self.numFmtId = numFmtId + self.formula = formula + self.sqlType = sqlType + self.hierarchy = hierarchy + self.level = level + self.databaseField = databaseField + self.mappingCount = mappingCount + self.memberPropertyField = memberPropertyField + + +class RangeSet(Serialisable): + + tagname = "rangeSet" + + i1 = Integer(allow_none=True) + i2 = Integer(allow_none=True) + i3 = Integer(allow_none=True) + i4 = Integer(allow_none=True) + ref = String() + name = String(allow_none=True) + sheet = String(allow_none=True) + + def __init__(self, + i1=None, + i2=None, + i3=None, + i4=None, + ref=None, + name=None, + sheet=None, + ): + self.i1 = i1 + self.i2 = i2 + self.i3 = i3 + self.i4 = i4 + self.ref = ref + self.name = name + self.sheet = sheet + + +class PageItem(Serialisable): + + tagname = "pageItem" + + name = String() + + def __init__(self, + name=None, + ): + self.name = name + + +class Page(Serialisable): + + # PCDSCPage + tagname = "PCDSCPage" + + pageItem = Sequence(expected_type=PageItem) + + __elements__ = ('pageItem',) + + def __init__(self, + count=None, + pageItem=None, + ): + self.pageItem = pageItem + + + @property + def count(self): + return len(self.pageItem) + + +class Consolidation(Serialisable): + + tagname = "consolidation" + + autoPage = Bool(allow_none=True) + pages = NestedSequence(expected_type=Page, count=True) + rangeSets = NestedSequence(expected_type=RangeSet, count=True) + + __elements__ = ('pages', 'rangeSets') + + def __init__(self, + autoPage=None, + pages=(), + rangeSets=(), + ): + self.autoPage = autoPage + self.pages = pages + self.rangeSets = rangeSets + + +class WorksheetSource(Serialisable): + + tagname = "worksheetSource" + + ref = String(allow_none=True) + name = String(allow_none=True) + sheet = String(allow_none=True) + + def __init__(self, + ref=None, + name=None, + sheet=None, + ): + self.ref = ref + self.name = name + self.sheet = sheet + + +class CacheSource(Serialisable): + + tagname = "cacheSource" + + type = Set(values=(['worksheet', 'external', 'consolidation', 'scenario'])) + connectionId = Integer(allow_none=True) + # some elements are choice + worksheetSource = Typed(expected_type=WorksheetSource, allow_none=True) + consolidation = Typed(expected_type=Consolidation, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('worksheetSource', 'consolidation',) + + def __init__(self, + type=None, + connectionId=None, + worksheetSource=None, + consolidation=None, + extLst=None, + ): + self.type = type + self.connectionId = connectionId + self.worksheetSource = worksheetSource + self.consolidation = consolidation + + +class CacheDefinition(Serialisable): + + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml" + rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" + _id = 1 + _path = "/xl/pivotCache/pivotCacheDefinition{0}.xml" + records = None + + tagname = "pivotCacheDefinition" + + invalid = Bool(allow_none=True) + saveData = Bool(allow_none=True) + refreshOnLoad = Bool(allow_none=True) + optimizeMemory = Bool(allow_none=True) + enableRefresh = Bool(allow_none=True) + refreshedBy = String(allow_none=True) + refreshedDate = Float(allow_none=True) + refreshedDateIso = DateTime(allow_none=True) + backgroundQuery = Bool(allow_none=True) + missingItemsLimit = Integer(allow_none=True) + createdVersion = Integer(allow_none=True) + refreshedVersion = Integer(allow_none=True) + minRefreshableVersion = Integer(allow_none=True) + recordCount = Integer(allow_none=True) + upgradeOnRefresh = Bool(allow_none=True) + tupleCache = Bool(allow_none=True) + supportSubquery = Bool(allow_none=True) + supportAdvancedDrill = Bool(allow_none=True) + cacheSource = Typed(expected_type=CacheSource) + cacheFields = NestedSequence(expected_type=CacheField, count=True) + cacheHierarchies = NestedSequence(expected_type=CacheHierarchy, allow_none=True) + kpis = NestedSequence(expected_type=PCDKPI, allow_none=True) + tupleCache = Typed(expected_type=TupleCache, allow_none=True) + calculatedItems = NestedSequence(expected_type=CalculatedItem, count=True) + calculatedMembers = NestedSequence(expected_type=CalculatedMember, count=True) + dimensions = NestedSequence(expected_type=PivotDimension, allow_none=True) + measureGroups = NestedSequence(expected_type=MeasureGroup, count=True) + maps = NestedSequence(expected_type=MeasureDimensionMap, count=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + id = Relation() + + __elements__ = ('cacheSource', 'cacheFields', 'cacheHierarchies', 'kpis', + 'tupleCache', 'calculatedItems', 'calculatedMembers', 'dimensions', + 'measureGroups', 'maps',) + + def __init__(self, + invalid=None, + saveData=None, + refreshOnLoad=None, + optimizeMemory=None, + enableRefresh=None, + refreshedBy=None, + refreshedDate=None, + refreshedDateIso=None, + backgroundQuery=None, + missingItemsLimit=None, + createdVersion=None, + refreshedVersion=None, + minRefreshableVersion=None, + recordCount=None, + upgradeOnRefresh=None, + tupleCache=None, + supportSubquery=None, + supportAdvancedDrill=None, + cacheSource=None, + cacheFields=(), + cacheHierarchies=(), + kpis=(), + calculatedItems=(), + calculatedMembers=(), + dimensions=(), + measureGroups=(), + maps=(), + extLst=None, + id = None, + ): + self.invalid = invalid + self.saveData = saveData + self.refreshOnLoad = refreshOnLoad + self.optimizeMemory = optimizeMemory + self.enableRefresh = enableRefresh + self.refreshedBy = refreshedBy + self.refreshedDate = refreshedDate + self.refreshedDateIso = refreshedDateIso + self.backgroundQuery = backgroundQuery + self.missingItemsLimit = missingItemsLimit + self.createdVersion = createdVersion + self.refreshedVersion = refreshedVersion + self.minRefreshableVersion = minRefreshableVersion + self.recordCount = recordCount + self.upgradeOnRefresh = upgradeOnRefresh + self.tupleCache = tupleCache + self.supportSubquery = supportSubquery + self.supportAdvancedDrill = supportAdvancedDrill + self.cacheSource = cacheSource + self.cacheFields = cacheFields + self.cacheHierarchies = cacheHierarchies + self.kpis = kpis + self.tupleCache = tupleCache + self.calculatedItems = calculatedItems + self.calculatedMembers = calculatedMembers + self.dimensions = dimensions + self.measureGroups = measureGroups + self.maps = maps + self.id = id + + + def to_tree(self): + node = super(CacheDefinition, self).to_tree() + node.set("xmlns", SHEET_MAIN_NS) + return node + + + @property + def path(self): + return self._path.format(self._id) + + + def _write(self, archive, manifest): + """ + Add to zipfile and update manifest + """ + self._write_rels(archive, manifest) + xml = tostring(self.to_tree()) + archive.writestr(self.path[1:], xml) + manifest.append(self) + + + def _write_rels(self, archive, manifest): + """ + Write the relevant child objects and add links + """ + if self.records is None: + return + + rels = RelationshipList() + r = Relationship(Type=self.records.rel_type, Target=self.records.path) + rels.append(r) + self.id = r.id + self.records._id = self._id + self.records._write(archive, manifest) + + path = get_rels_path(self.path) + xml = tostring(rels.to_tree()) + archive.writestr(path[1:], xml) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/pivot/fields.py b/.venv/lib/python3.9/site-packages/openpyxl/pivot/fields.py new file mode 100644 index 00000000..6ce59c39 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/pivot/fields.py @@ -0,0 +1,322 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + DateTime, + Bool, + Float, + String, + Integer, + Sequence, +) +from openpyxl.descriptors.excel import HexBinary + +class Index(Serialisable): + + tagname = "x" + + v = Integer(allow_none=True) + + def __init__(self, + v=0, + ): + self.v = v + + +class Tuple(Serialisable): + + fld = Integer() + hier = Integer() + item = Integer() + + def __init__(self, + fld=None, + hier=None, + item=None, + ): + self.fld = fld + self.hier = hier + self.item = item + + +class TupleList(Serialisable): + + c = Integer(allow_none=True) + tpl = Typed(expected_type=Tuple, ) + + __elements__ = ('tpl',) + + def __init__(self, + c=None, + tpl=None, + ): + self.c = c + self.tpl = tpl + + +class Missing(Serialisable): + + tagname = "m" + + tpls = Sequence(expected_type=TupleList) + x = Sequence(expected_type=Index) + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=(), + x=(), + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class Number(Serialisable): + + tagname = "n" + + tpls = Sequence(expected_type=TupleList) + x = Sequence(expected_type=Index) + v = Float() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=(), + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class Error(Serialisable): + + tagname = "e" + + tpls = Typed(expected_type=TupleList, allow_none=True) + x = Sequence(expected_type=Index) + v = String() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=None, + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class Boolean(Serialisable): + + tagname = "b" + + x = Sequence(expected_type=Index) + v = Bool() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + ): + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + + +class Text(Serialisable): + + tagname = "s" + + tpls = Sequence(expected_type=TupleList) + x = Sequence(expected_type=Index) + v = String() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=(), + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class DateTimeField(Serialisable): + + tagname = "d" + + x = Sequence(expected_type=Index) + v = DateTime() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + ): + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp diff --git a/.venv/lib/python3.9/site-packages/openpyxl/pivot/record.py b/.venv/lib/python3.9/site-packages/openpyxl/pivot/record.py new file mode 100644 index 00000000..4bc2b055 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/pivot/record.py @@ -0,0 +1,111 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + Sequence, +) +from openpyxl.descriptors.sequence import ( + MultiSequence, + MultiSequencePart, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedBool, +) + +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring + +from .fields import ( + Boolean, + Error, + Missing, + Number, + Text, + TupleList, + DateTimeField, + Index, +) + + +class Record(Serialisable): + + tagname = "r" + + _fields = MultiSequence() + m = MultiSequencePart(expected_type=Missing, store="_fields") + n = MultiSequencePart(expected_type=Number, store="_fields") + b = MultiSequencePart(expected_type=Boolean, store="_fields") + e = MultiSequencePart(expected_type=Error, store="_fields") + s = MultiSequencePart(expected_type=Text, store="_fields") + d = MultiSequencePart(expected_type=DateTimeField, store="_fields") + x = MultiSequencePart(expected_type=Index, store="_fields") + + + def __init__(self, + _fields=(), + m=None, + n=None, + b=None, + e=None, + s=None, + d=None, + x=None, + ): + self._fields = _fields + + +class RecordList(Serialisable): + + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml" + rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords" + _id = 1 + _path = "/xl/pivotCache/pivotCacheRecords{0}.xml" + + tagname ="pivotCacheRecords" + + r = Sequence(expected_type=Record, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('r', ) + __attrs__ = ('count', ) + + def __init__(self, + count=None, + r=(), + extLst=None, + ): + self.r = r + self.extLst = extLst + + + @property + def count(self): + return len(self.r) + + + def to_tree(self): + tree = super(RecordList, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def path(self): + return self._path.format(self._id) + + + def _write(self, archive, manifest): + """ + Write to zipfile and update manifest + """ + xml = tostring(self.to_tree()) + archive.writestr(self.path[1:], xml) + manifest.append(self) + + + def _write_rels(self, archive, manifest): + pass diff --git a/.venv/lib/python3.9/site-packages/openpyxl/pivot/table.py b/.venv/lib/python3.9/site-packages/openpyxl/pivot/table.py new file mode 100644 index 00000000..6ab4aec8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/pivot/table.py @@ -0,0 +1,1178 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + NoneSet, + Set, + Float, + Bool, + DateTime, + String, + Alias, + Bool, + Sequence, +) + +from openpyxl.descriptors.excel import ExtensionList, Relation +from openpyxl.descriptors.nested import NestedInteger +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring +from openpyxl.packaging.relationship import ( + RelationshipList, + Relationship, + get_rels_path +) +from .fields import Index + +from openpyxl.worksheet.filters import ( + AutoFilter, + CellRange, + ColorFilter, + CustomFilter, + CustomFilters, + DateGroupItem, + DynamicFilter, + FilterColumn, + Filters, + IconFilter, + SortCondition, + SortState, + Top10, +) + + +class HierarchyUsage(Serialisable): + + tagname = "hierarchyUsage" + + hierarchyUsage = Integer() + + def __init__(self, + hierarchyUsage=None, + ): + self.hierarchyUsage = hierarchyUsage + + +class ColHierarchiesUsage(Serialisable): + + tagname = "colHierarchiesUsage" + + colHierarchyUsage = Sequence(expected_type=HierarchyUsage, ) + + __elements__ = ('colHierarchyUsage',) + __attrs__ = ('count', ) + + def __init__(self, + count=None, + colHierarchyUsage=(), + ): + self.colHierarchyUsage = colHierarchyUsage + + + @property + def count(self): + return len(self.colHierarchyUsage) + + +class RowHierarchiesUsage(Serialisable): + + tagname = "rowHierarchiesUsage" + + rowHierarchyUsage = Sequence(expected_type=HierarchyUsage, ) + + __elements__ = ('rowHierarchyUsage',) + __attrs__ = ('count', ) + + def __init__(self, + count=None, + rowHierarchyUsage=(), + ): + self.rowHierarchyUsage = rowHierarchyUsage + + @property + def count(self): + return len(self.rowHierarchyUsage) + + +class PivotFilter(Serialisable): + + tagname = "filter" + + fld = Integer() + mpFld = Integer(allow_none=True) + type = Set(values=(['unknown', 'count', 'percent', 'sum', 'captionEqual', + 'captionNotEqual', 'captionBeginsWith', 'captionNotBeginsWith', + 'captionEndsWith', 'captionNotEndsWith', 'captionContains', + 'captionNotContains', 'captionGreaterThan', 'captionGreaterThanOrEqual', + 'captionLessThan', 'captionLessThanOrEqual', 'captionBetween', + 'captionNotBetween', 'valueEqual', 'valueNotEqual', 'valueGreaterThan', + 'valueGreaterThanOrEqual', 'valueLessThan', 'valueLessThanOrEqual', + 'valueBetween', 'valueNotBetween', 'dateEqual', 'dateNotEqual', + 'dateOlderThan', 'dateOlderThanOrEqual', 'dateNewerThan', + 'dateNewerThanOrEqual', 'dateBetween', 'dateNotBetween', 'tomorrow', + 'today', 'yesterday', 'nextWeek', 'thisWeek', 'lastWeek', 'nextMonth', + 'thisMonth', 'lastMonth', 'nextQuarter', 'thisQuarter', 'lastQuarter', + 'nextYear', 'thisYear', 'lastYear', 'yearToDate', 'Q1', 'Q2', 'Q3', 'Q4', + 'M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10', 'M11', + 'M12'])) + evalOrder = Integer(allow_none=True) + id = Integer() + iMeasureHier = Integer(allow_none=True) + iMeasureFld = Integer(allow_none=True) + name = String(allow_none=True) + description = String(allow_none=True) + stringValue1 = String(allow_none=True) + stringValue2 = String(allow_none=True) + autoFilter = Typed(expected_type=AutoFilter, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('autoFilter',) + + def __init__(self, + fld=None, + mpFld=None, + type=None, + evalOrder=None, + id=None, + iMeasureHier=None, + iMeasureFld=None, + name=None, + description=None, + stringValue1=None, + stringValue2=None, + autoFilter=None, + extLst=None, + ): + self.fld = fld + self.mpFld = mpFld + self.type = type + self.evalOrder = evalOrder + self.id = id + self.iMeasureHier = iMeasureHier + self.iMeasureFld = iMeasureFld + self.name = name + self.description = description + self.stringValue1 = stringValue1 + self.stringValue2 = stringValue2 + self.autoFilter = autoFilter + + +class PivotFilters(Serialisable): + + count = Integer() + filter = Typed(expected_type=PivotFilter, allow_none=True) + + __elements__ = ('filter',) + + def __init__(self, + count=None, + filter=None, + ): + self.filter = filter + + +class PivotTableStyle(Serialisable): + + tagname = "pivotTableStyleInfo" + + name = String(allow_none=True) + showRowHeaders = Bool() + showColHeaders = Bool() + showRowStripes = Bool() + showColStripes = Bool() + showLastColumn = Bool() + + def __init__(self, + name=None, + showRowHeaders=None, + showColHeaders=None, + showRowStripes=None, + showColStripes=None, + showLastColumn=None, + ): + self.name = name + self.showRowHeaders = showRowHeaders + self.showColHeaders = showColHeaders + self.showRowStripes = showRowStripes + self.showColStripes = showColStripes + self.showLastColumn = showLastColumn + + +class MemberList(Serialisable): + + tagname = "members" + + level = Integer(allow_none=True) + member = NestedSequence(expected_type=String, attribute="name") + + __elements__ = ('member',) + + def __init__(self, + count=None, + level=None, + member=(), + ): + self.level = level + self.member = member + + @property + def count(self): + return len(self.member) + + +class MemberProperty(Serialisable): + + tagname = "mps" + + name = String(allow_none=True) + showCell = Bool(allow_none=True) + showTip = Bool(allow_none=True) + showAsCaption = Bool(allow_none=True) + nameLen = Integer(allow_none=True) + pPos = Integer(allow_none=True) + pLen = Integer(allow_none=True) + level = Integer(allow_none=True) + field = Integer() + + def __init__(self, + name=None, + showCell=None, + showTip=None, + showAsCaption=None, + nameLen=None, + pPos=None, + pLen=None, + level=None, + field=None, + ): + self.name = name + self.showCell = showCell + self.showTip = showTip + self.showAsCaption = showAsCaption + self.nameLen = nameLen + self.pPos = pPos + self.pLen = pLen + self.level = level + self.field = field + + +class PivotHierarchy(Serialisable): + + tagname = "pivotHierarchy" + + outline = Bool() + multipleItemSelectionAllowed = Bool() + subtotalTop = Bool() + showInFieldList = Bool() + dragToRow = Bool() + dragToCol = Bool() + dragToPage = Bool() + dragToData = Bool() + dragOff = Bool() + includeNewItemsInFilter = Bool() + caption = String(allow_none=True) + mps = NestedSequence(expected_type=MemberProperty, count=True) + members = Typed(expected_type=MemberList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('mps', 'members',) + + def __init__(self, + outline=None, + multipleItemSelectionAllowed=None, + subtotalTop=None, + showInFieldList=None, + dragToRow=None, + dragToCol=None, + dragToPage=None, + dragToData=None, + dragOff=None, + includeNewItemsInFilter=None, + caption=None, + mps=(), + members=None, + extLst=None, + ): + self.outline = outline + self.multipleItemSelectionAllowed = multipleItemSelectionAllowed + self.subtotalTop = subtotalTop + self.showInFieldList = showInFieldList + self.dragToRow = dragToRow + self.dragToCol = dragToCol + self.dragToPage = dragToPage + self.dragToData = dragToData + self.dragOff = dragOff + self.includeNewItemsInFilter = includeNewItemsInFilter + self.caption = caption + self.mps = mps + self.members = members + self.extLst = extLst + + +class Reference(Serialisable): + + tagname = "reference" + + field = Integer(allow_none=True) + selected = Bool(allow_none=True) + byPosition = Bool(allow_none=True) + relative = Bool(allow_none=True) + defaultSubtotal = Bool(allow_none=True) + sumSubtotal = Bool(allow_none=True) + countASubtotal = Bool(allow_none=True) + avgSubtotal = Bool(allow_none=True) + maxSubtotal = Bool(allow_none=True) + minSubtotal = Bool(allow_none=True) + productSubtotal = Bool(allow_none=True) + countSubtotal = Bool(allow_none=True) + stdDevSubtotal = Bool(allow_none=True) + stdDevPSubtotal = Bool(allow_none=True) + varSubtotal = Bool(allow_none=True) + varPSubtotal = Bool(allow_none=True) + x = NestedInteger(allow_none=True, attribute="v") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + field=None, + count=None, + selected=None, + byPosition=None, + relative=None, + defaultSubtotal=None, + sumSubtotal=None, + countASubtotal=None, + avgSubtotal=None, + maxSubtotal=None, + minSubtotal=None, + productSubtotal=None, + countSubtotal=None, + stdDevSubtotal=None, + stdDevPSubtotal=None, + varSubtotal=None, + varPSubtotal=None, + x=None, + extLst=None, + ): + self.field = field + self.selected = selected + self.byPosition = byPosition + self.relative = relative + self.defaultSubtotal = defaultSubtotal + self.sumSubtotal = sumSubtotal + self.countASubtotal = countASubtotal + self.avgSubtotal = avgSubtotal + self.maxSubtotal = maxSubtotal + self.minSubtotal = minSubtotal + self.productSubtotal = productSubtotal + self.countSubtotal = countSubtotal + self.stdDevSubtotal = stdDevSubtotal + self.stdDevPSubtotal = stdDevPSubtotal + self.varSubtotal = varSubtotal + self.varPSubtotal = varPSubtotal + self.x = x + + + @property + def count(self): + return len(self.field) + + +class PivotArea(Serialisable): + + tagname = "pivotArea" + + references = NestedSequence(expected_type=Reference, count=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + field = Integer(allow_none=True) + type = NoneSet(values=(['normal', 'data', 'all', 'origin', 'button', + 'topEnd', 'topRight'])) + dataOnly = Bool(allow_none=True) + labelOnly = Bool(allow_none=True) + grandRow = Bool(allow_none=True) + grandCol = Bool(allow_none=True) + cacheIndex = Bool(allow_none=True) + outline = Bool(allow_none=True) + offset = String(allow_none=True) + collapsedLevelsAreSubtotals = Bool(allow_none=True) + axis = NoneSet(values=(['axisRow', 'axisCol', 'axisPage', 'axisValues'])) + fieldPosition = Integer(allow_none=True) + + __elements__ = ('references',) + + def __init__(self, + references=(), + extLst=None, + field=None, + type="normal", + dataOnly=True, + labelOnly=None, + grandRow=None, + grandCol=None, + cacheIndex=None, + outline=True, + offset=None, + collapsedLevelsAreSubtotals=None, + axis=None, + fieldPosition=None, + ): + self.references = references + self.extLst = extLst + self.field = field + self.type = type + self.dataOnly = dataOnly + self.labelOnly = labelOnly + self.grandRow = grandRow + self.grandCol = grandCol + self.cacheIndex = cacheIndex + self.outline = outline + self.offset = offset + self.collapsedLevelsAreSubtotals = collapsedLevelsAreSubtotals + self.axis = axis + self.fieldPosition = fieldPosition + + +class ChartFormat(Serialisable): + + tagname = "chartFormat" + + chart = Integer() + format = Integer() + series = Bool() + pivotArea = Typed(expected_type=PivotArea, ) + + __elements__ = ('pivotArea',) + + def __init__(self, + chart=None, + format=None, + series=None, + pivotArea=None, + ): + self.chart = chart + self.format = format + self.series = series + self.pivotArea = pivotArea + + +class ConditionalFormat(Serialisable): + + tagname = "conditionalFormat" + + scope = Set(values=(['selection', 'data', 'field'])) + type = NoneSet(values=(['all', 'row', 'column'])) + priority = Integer() + pivotAreas = NestedSequence(expected_type=PivotArea) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotAreas',) + + def __init__(self, + scope=None, + type=None, + priority=None, + pivotAreas=(), + extLst=None, + ): + self.scope = scope + self.type = type + self.priority = priority + self.pivotAreas = pivotAreas + self.extLst = extLst + + +class Format(Serialisable): + + tagname = "format" + + action = NoneSet(values=(['blank', 'formatting', 'drill', 'formula'])) + dxfId = Integer(allow_none=True) + pivotArea = Typed(expected_type=PivotArea, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotArea',) + + def __init__(self, + action="formatting", + dxfId=None, + pivotArea=None, + extLst=None, + ): + self.action = action + self.dxfId = dxfId + self.pivotArea = pivotArea + self.extLst = extLst + + +class DataField(Serialisable): + + tagname = "dataField" + + name = String(allow_none=True) + fld = Integer() + subtotal = Set(values=(['average', 'count', 'countNums', 'max', 'min', + 'product', 'stdDev', 'stdDevp', 'sum', 'var', 'varp'])) + showDataAs = Set(values=(['normal', 'difference', 'percent', + 'percentDiff', 'runTotal', 'percentOfRow', 'percentOfCol', + 'percentOfTotal', 'index'])) + baseField = Integer() + baseItem = Integer() + numFmtId = Integer(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + + def __init__(self, + name=None, + fld=None, + subtotal="sum", + showDataAs="normal", + baseField=-1, + baseItem=1048832, + numFmtId=None, + extLst=None, + ): + self.name = name + self.fld = fld + self.subtotal = subtotal + self.showDataAs = showDataAs + self.baseField = baseField + self.baseItem = baseItem + self.numFmtId = numFmtId + self.extLst = extLst + + +class PageField(Serialisable): + + tagname = "pageField" + + fld = Integer() + item = Integer(allow_none=True) + hier = Integer(allow_none=True) + name = String(allow_none=True) + cap = String(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + fld=None, + item=None, + hier=None, + name=None, + cap=None, + extLst=None, + ): + self.fld = fld + self.item = item + self.hier = hier + self.name = name + self.cap = cap + self.extLst = extLst + + +class RowColItem(Serialisable): + + tagname = "i" + + t = Set(values=(['data', 'default', 'sum', 'countA', 'avg', 'max', 'min', + 'product', 'count', 'stdDev', 'stdDevP', 'var', 'varP', 'grand', + 'blank'])) + r = Integer() + i = Integer() + x = Sequence(expected_type=Index, attribute="v") + + __elements__ = ('x',) + + def __init__(self, + t="data", + r=0, + i=0, + x=(), + ): + self.t = t + self.r = r + self.i = i + self.x = x + + +class RowColField(Serialisable): + + tagname = "field" + + x = Integer() + + def __init__(self, + x=None, + ): + self.x = x + + +class AutoSortScope(Serialisable): + + pivotArea = Typed(expected_type=PivotArea, ) + + __elements__ = ('pivotArea',) + + def __init__(self, + pivotArea=None, + ): + self.pivotArea = pivotArea + + +class FieldItem(Serialisable): + + tagname = "item" + + n = String(allow_none=True) + t = Set(values=(['data', 'default', 'sum', 'countA', 'avg', 'max', 'min', + 'product', 'count', 'stdDev', 'stdDevP', 'var', 'varP', 'grand', + 'blank'])) + h = Bool(allow_none=True) + s = Bool(allow_none=True) + sd = Bool(allow_none=True) + f = Bool(allow_none=True) + m = Bool(allow_none=True) + c = Bool(allow_none=True) + x = Integer(allow_none=True) + d = Bool(allow_none=True) + e = Bool(allow_none=True) + + def __init__(self, + n=None, + t="data", + h=None, + s=None, + sd=True, + f=None, + m=None, + c=None, + x=None, + d=None, + e=None, + ): + self.n = n + self.t = t + self.h = h + self.s = s + self.sd = sd + self.f = f + self.m = m + self.c = c + self.x = x + self.d = d + self.e = e + + +class PivotField(Serialisable): + + tagname = "pivotField" + + items = NestedSequence(expected_type=FieldItem, count=True) + autoSortScope = Typed(expected_type=AutoSortScope, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + name = String(allow_none=True) + axis = NoneSet(values=(['axisRow', 'axisCol', 'axisPage', 'axisValues'])) + dataField = Bool(allow_none=True) + subtotalCaption = String(allow_none=True) + showDropDowns = Bool(allow_none=True) + hiddenLevel = Bool(allow_none=True) + uniqueMemberProperty = String(allow_none=True) + compact = Bool(allow_none=True) + allDrilled = Bool(allow_none=True) + numFmtId = Integer(allow_none=True) + outline = Bool(allow_none=True) + subtotalTop = Bool(allow_none=True) + dragToRow = Bool(allow_none=True) + dragToCol = Bool(allow_none=True) + multipleItemSelectionAllowed = Bool(allow_none=True) + dragToPage = Bool(allow_none=True) + dragToData = Bool(allow_none=True) + dragOff = Bool(allow_none=True) + showAll = Bool(allow_none=True) + insertBlankRow = Bool(allow_none=True) + serverField = Bool(allow_none=True) + insertPageBreak = Bool(allow_none=True) + autoShow = Bool(allow_none=True) + topAutoShow = Bool(allow_none=True) + hideNewItems = Bool(allow_none=True) + measureFilter = Bool(allow_none=True) + includeNewItemsInFilter = Bool(allow_none=True) + itemPageCount = Integer(allow_none=True) + sortType = Set(values=(['manual', 'ascending', 'descending'])) + dataSourceSort = Bool(allow_none=True) + nonAutoSortDefault = Bool(allow_none=True) + rankBy = Integer(allow_none=True) + defaultSubtotal = Bool(allow_none=True) + sumSubtotal = Bool(allow_none=True) + countASubtotal = Bool(allow_none=True) + avgSubtotal = Bool(allow_none=True) + maxSubtotal = Bool(allow_none=True) + minSubtotal = Bool(allow_none=True) + productSubtotal = Bool(allow_none=True) + countSubtotal = Bool(allow_none=True) + stdDevSubtotal = Bool(allow_none=True) + stdDevPSubtotal = Bool(allow_none=True) + varSubtotal = Bool(allow_none=True) + varPSubtotal = Bool(allow_none=True) + showPropCell = Bool(allow_none=True) + showPropTip = Bool(allow_none=True) + showPropAsCaption = Bool(allow_none=True) + defaultAttributeDrillState = Bool(allow_none=True) + + __elements__ = ('items', 'autoSortScope',) + + def __init__(self, + items=(), + autoSortScope=None, + name=None, + axis=None, + dataField=None, + subtotalCaption=None, + showDropDowns=True, + hiddenLevel=None, + uniqueMemberProperty=None, + compact=True, + allDrilled=None, + numFmtId=None, + outline=True, + subtotalTop=True, + dragToRow=True, + dragToCol=True, + multipleItemSelectionAllowed=None, + dragToPage=True, + dragToData=True, + dragOff=True, + showAll=True, + insertBlankRow=None, + serverField=None, + insertPageBreak=None, + autoShow=None, + topAutoShow=True, + hideNewItems=None, + measureFilter=None, + includeNewItemsInFilter=None, + itemPageCount=10, + sortType="manual", + dataSourceSort=None, + nonAutoSortDefault=None, + rankBy=None, + defaultSubtotal=True, + sumSubtotal=None, + countASubtotal=None, + avgSubtotal=None, + maxSubtotal=None, + minSubtotal=None, + productSubtotal=None, + countSubtotal=None, + stdDevSubtotal=None, + stdDevPSubtotal=None, + varSubtotal=None, + varPSubtotal=None, + showPropCell=None, + showPropTip=None, + showPropAsCaption=None, + defaultAttributeDrillState=None, + extLst=None, + ): + self.items = items + self.autoSortScope = autoSortScope + self.name = name + self.axis = axis + self.dataField = dataField + self.subtotalCaption = subtotalCaption + self.showDropDowns = showDropDowns + self.hiddenLevel = hiddenLevel + self.uniqueMemberProperty = uniqueMemberProperty + self.compact = compact + self.allDrilled = allDrilled + self.numFmtId = numFmtId + self.outline = outline + self.subtotalTop = subtotalTop + self.dragToRow = dragToRow + self.dragToCol = dragToCol + self.multipleItemSelectionAllowed = multipleItemSelectionAllowed + self.dragToPage = dragToPage + self.dragToData = dragToData + self.dragOff = dragOff + self.showAll = showAll + self.insertBlankRow = insertBlankRow + self.serverField = serverField + self.insertPageBreak = insertPageBreak + self.autoShow = autoShow + self.topAutoShow = topAutoShow + self.hideNewItems = hideNewItems + self.measureFilter = measureFilter + self.includeNewItemsInFilter = includeNewItemsInFilter + self.itemPageCount = itemPageCount + self.sortType = sortType + self.dataSourceSort = dataSourceSort + self.nonAutoSortDefault = nonAutoSortDefault + self.rankBy = rankBy + self.defaultSubtotal = defaultSubtotal + self.sumSubtotal = sumSubtotal + self.countASubtotal = countASubtotal + self.avgSubtotal = avgSubtotal + self.maxSubtotal = maxSubtotal + self.minSubtotal = minSubtotal + self.productSubtotal = productSubtotal + self.countSubtotal = countSubtotal + self.stdDevSubtotal = stdDevSubtotal + self.stdDevPSubtotal = stdDevPSubtotal + self.varSubtotal = varSubtotal + self.varPSubtotal = varPSubtotal + self.showPropCell = showPropCell + self.showPropTip = showPropTip + self.showPropAsCaption = showPropAsCaption + self.defaultAttributeDrillState = defaultAttributeDrillState + + +class Location(Serialisable): + + tagname = "location" + + ref = String() + firstHeaderRow = Integer() + firstDataRow = Integer() + firstDataCol = Integer() + rowPageCount = Integer(allow_none=True) + colPageCount = Integer(allow_none=True) + + def __init__(self, + ref=None, + firstHeaderRow=None, + firstDataRow=None, + firstDataCol=None, + rowPageCount=None, + colPageCount=None, + ): + self.ref = ref + self.firstHeaderRow = firstHeaderRow + self.firstDataRow = firstDataRow + self.firstDataCol = firstDataCol + self.rowPageCount = rowPageCount + self.colPageCount = colPageCount + + +class TableDefinition(Serialisable): + + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml" + rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" + _id = 1 + _path = "/xl/pivotTables/pivotTable{0}.xml" + + tagname = "pivotTableDefinition" + cache = None + + name = String() + cacheId = Integer() + dataOnRows = Bool() + dataPosition = Integer(allow_none=True) + dataCaption = String() + grandTotalCaption = String(allow_none=True) + errorCaption = String(allow_none=True) + showError = Bool() + missingCaption = String(allow_none=True) + showMissing = Bool() + pageStyle = String(allow_none=True) + pivotTableStyle = String(allow_none=True) + vacatedStyle = String(allow_none=True) + tag = String(allow_none=True) + updatedVersion = Integer() + minRefreshableVersion = Integer() + asteriskTotals = Bool() + showItems = Bool() + editData = Bool() + disableFieldList = Bool() + showCalcMbrs = Bool() + visualTotals = Bool() + showMultipleLabel = Bool() + showDataDropDown = Bool() + showDrill = Bool() + printDrill = Bool() + showMemberPropertyTips = Bool() + showDataTips = Bool() + enableWizard = Bool() + enableDrill = Bool() + enableFieldProperties = Bool() + preserveFormatting = Bool() + useAutoFormatting = Bool() + pageWrap = Integer() + pageOverThenDown = Bool() + subtotalHiddenItems = Bool() + rowGrandTotals = Bool() + colGrandTotals = Bool() + fieldPrintTitles = Bool() + itemPrintTitles = Bool() + mergeItem = Bool() + showDropZones = Bool() + createdVersion = Integer() + indent = Integer() + showEmptyRow = Bool() + showEmptyCol = Bool() + showHeaders = Bool() + compact = Bool() + outline = Bool() + outlineData = Bool() + compactData = Bool() + published = Bool() + gridDropZones = Bool() + immersive = Bool() + multipleFieldFilters = Bool() + chartFormat = Integer() + rowHeaderCaption = String(allow_none=True) + colHeaderCaption = String(allow_none=True) + fieldListSortAscending = Bool() + mdxSubqueries = Bool() + customListSort = Bool(allow_none=True) + autoFormatId = Integer(allow_none=True) + applyNumberFormats = Bool() + applyBorderFormats = Bool() + applyFontFormats = Bool() + applyPatternFormats = Bool() + applyAlignmentFormats = Bool() + applyWidthHeightFormats = Bool() + location = Typed(expected_type=Location, ) + pivotFields = NestedSequence(expected_type=PivotField, count=True) + rowFields = NestedSequence(expected_type=RowColField, count=True) + rowItems = NestedSequence(expected_type=RowColItem, count=True) + colFields = NestedSequence(expected_type=RowColField, count=True) + colItems = NestedSequence(expected_type=RowColItem, count=True) + pageFields = NestedSequence(expected_type=PageField, count=True) + dataFields = NestedSequence(expected_type=DataField, count=True) + formats = NestedSequence(expected_type=Format, count=True) + conditionalFormats = NestedSequence(expected_type=ConditionalFormat, count=True) + chartFormats = NestedSequence(expected_type=ChartFormat, count=True) + pivotHierarchies = NestedSequence(expected_type=PivotHierarchy, count=True) + pivotTableStyleInfo = Typed(expected_type=PivotTableStyle, allow_none=True) + filters = NestedSequence(expected_type=PivotFilter, count=True) + rowHierarchiesUsage = Typed(expected_type=RowHierarchiesUsage, allow_none=True) + colHierarchiesUsage = Typed(expected_type=ColHierarchiesUsage, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + id = Relation() + + __elements__ = ('location', 'pivotFields', 'rowFields', 'rowItems', + 'colFields', 'colItems', 'pageFields', 'dataFields', 'formats', + 'conditionalFormats', 'chartFormats', 'pivotHierarchies', + 'pivotTableStyleInfo', 'filters', 'rowHierarchiesUsage', + 'colHierarchiesUsage',) + + def __init__(self, + name=None, + cacheId=None, + dataOnRows=False, + dataPosition=None, + dataCaption=None, + grandTotalCaption=None, + errorCaption=None, + showError=False, + missingCaption=None, + showMissing=True, + pageStyle=None, + pivotTableStyle=None, + vacatedStyle=None, + tag=None, + updatedVersion=0, + minRefreshableVersion=0, + asteriskTotals=False, + showItems=True, + editData=False, + disableFieldList=False, + showCalcMbrs=True, + visualTotals=True, + showMultipleLabel=True, + showDataDropDown=True, + showDrill=True, + printDrill=False, + showMemberPropertyTips=True, + showDataTips=True, + enableWizard=True, + enableDrill=True, + enableFieldProperties=True, + preserveFormatting=True, + useAutoFormatting=False, + pageWrap=0, + pageOverThenDown=False, + subtotalHiddenItems=False, + rowGrandTotals=True, + colGrandTotals=True, + fieldPrintTitles=False, + itemPrintTitles=False, + mergeItem=False, + showDropZones=True, + createdVersion=0, + indent=1, + showEmptyRow=False, + showEmptyCol=False, + showHeaders=True, + compact=True, + outline=False, + outlineData=False, + compactData=True, + published=False, + gridDropZones=False, + immersive=True, + multipleFieldFilters=None, + chartFormat=0, + rowHeaderCaption=None, + colHeaderCaption=None, + fieldListSortAscending=None, + mdxSubqueries=None, + customListSort=None, + autoFormatId=None, + applyNumberFormats=False, + applyBorderFormats=False, + applyFontFormats=False, + applyPatternFormats=False, + applyAlignmentFormats=False, + applyWidthHeightFormats=False, + location=None, + pivotFields=(), + rowFields=(), + rowItems=(), + colFields=(), + colItems=(), + pageFields=(), + dataFields=(), + formats=(), + conditionalFormats=(), + chartFormats=(), + pivotHierarchies=(), + pivotTableStyleInfo=None, + filters=(), + rowHierarchiesUsage=None, + colHierarchiesUsage=None, + extLst=None, + id=None, + ): + self.name = name + self.cacheId = cacheId + self.dataOnRows = dataOnRows + self.dataPosition = dataPosition + self.dataCaption = dataCaption + self.grandTotalCaption = grandTotalCaption + self.errorCaption = errorCaption + self.showError = showError + self.missingCaption = missingCaption + self.showMissing = showMissing + self.pageStyle = pageStyle + self.pivotTableStyle = pivotTableStyle + self.vacatedStyle = vacatedStyle + self.tag = tag + self.updatedVersion = updatedVersion + self.minRefreshableVersion = minRefreshableVersion + self.asteriskTotals = asteriskTotals + self.showItems = showItems + self.editData = editData + self.disableFieldList = disableFieldList + self.showCalcMbrs = showCalcMbrs + self.visualTotals = visualTotals + self.showMultipleLabel = showMultipleLabel + self.showDataDropDown = showDataDropDown + self.showDrill = showDrill + self.printDrill = printDrill + self.showMemberPropertyTips = showMemberPropertyTips + self.showDataTips = showDataTips + self.enableWizard = enableWizard + self.enableDrill = enableDrill + self.enableFieldProperties = enableFieldProperties + self.preserveFormatting = preserveFormatting + self.useAutoFormatting = useAutoFormatting + self.pageWrap = pageWrap + self.pageOverThenDown = pageOverThenDown + self.subtotalHiddenItems = subtotalHiddenItems + self.rowGrandTotals = rowGrandTotals + self.colGrandTotals = colGrandTotals + self.fieldPrintTitles = fieldPrintTitles + self.itemPrintTitles = itemPrintTitles + self.mergeItem = mergeItem + self.showDropZones = showDropZones + self.createdVersion = createdVersion + self.indent = indent + self.showEmptyRow = showEmptyRow + self.showEmptyCol = showEmptyCol + self.showHeaders = showHeaders + self.compact = compact + self.outline = outline + self.outlineData = outlineData + self.compactData = compactData + self.published = published + self.gridDropZones = gridDropZones + self.immersive = immersive + self.multipleFieldFilters = multipleFieldFilters + self.chartFormat = chartFormat + self.rowHeaderCaption = rowHeaderCaption + self.colHeaderCaption = colHeaderCaption + self.fieldListSortAscending = fieldListSortAscending + self.mdxSubqueries = mdxSubqueries + self.customListSort = customListSort + self.autoFormatId = autoFormatId + self.applyNumberFormats = applyNumberFormats + self.applyBorderFormats = applyBorderFormats + self.applyFontFormats = applyFontFormats + self.applyPatternFormats = applyPatternFormats + self.applyAlignmentFormats = applyAlignmentFormats + self.applyWidthHeightFormats = applyWidthHeightFormats + self.location = location + self.pivotFields = pivotFields + self.rowFields = rowFields + self.rowItems = rowItems + self.colFields = colFields + self.colItems = colItems + self.pageFields = pageFields + self.dataFields = dataFields + self.formats = formats + self.conditionalFormats = conditionalFormats + self.chartFormats = chartFormats + self.pivotHierarchies = pivotHierarchies + self.pivotTableStyleInfo = pivotTableStyleInfo + self.filters = filters + self.rowHierarchiesUsage = rowHierarchiesUsage + self.colHierarchiesUsage = colHierarchiesUsage + self.extLst = extLst + self.id = id + + + def to_tree(self): + tree = super(TableDefinition, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def path(self): + return self._path.format(self._id) + + + def _write(self, archive, manifest): + """ + Add to zipfile and update manifest + """ + self._write_rels(archive, manifest) + xml = tostring(self.to_tree()) + archive.writestr(self.path[1:], xml) + manifest.append(self) + + + def _write_rels(self, archive, manifest): + """ + Write the relevant child objects and add links + """ + if self.cache is None: + return + + rels = RelationshipList() + r = Relationship(Type=self.cache.rel_type, Target=self.cache.path) + rels.append(r) + self.id = r.id + if self.cache.path[1:] not in archive.namelist(): + self.cache._write(archive, manifest) + + path = get_rels_path(self.path) + xml = tostring(rels.to_tree()) + archive.writestr(path[1:], xml) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/reader/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/reader/__init__.py new file mode 100644 index 00000000..843d4997 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/reader/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2010-2021 openpyxl diff --git a/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..495b24669c8e7d40cf3d6f9033ac319545f78c68 GIT binary patch literal 183 zcmYe~<>g`kg5b@w6P1DVV-N=!FakLaKwQiMBvKfH88jLFRx%WUgb~EA82#MTl*~kZ zkHn%Bm!izFRQ-U;jFS93{ov$`{QMk!y|UE2GX0#)B>jTQl8pR3V?9g#;>?m%-GapA z?8NlcV*UJr)VzYqiX8o-)Wnq3BK`RI%)HE!_;|g7%3B;Zx%nxjIjMFaTR#Ia0|4WW BFU0@= literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/drawings.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/drawings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31880ac24e699bd5bf0d457ade1dd8ead87eda09 GIT binary patch literal 1950 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 H{Ph0=zVQGo literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/strings.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/strings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..702bac0c71f80a34d7bd18178b7dc41fec4fd276 GIT binary patch literal 719 zcmY*XO>fgc5ZzsWB~c>Us_HoxgmS>HgnB@#5EU(L4@9VrI2e$%^==cl>$Tb4L`ktv z@GrO|N6!2kUpes?I5AEFDr3#;%o}^>&68=b*Fj+6k8kvdAoQ~-9vWciCAdC>z!1X% zWjMmP$%!DD6xwut5i?A_kY}3qD03k3W#gs_fFfB_+8dYTsB( zs8;yashrYTx{NSuMIg7A-U?w<;$^FJRzUyLeu;sgegEW&Q`S#qp9;~}6RJ4tYok&* z)(vVVyl?2W;P>w5$GXyKprA5Id8@N$to{3oJQw2?LJ2&ctqpQo|c|1BfuW?-4GH1ND4M_MM zsYfv6_xm8@ETzE(RqRxyGakHMOiV6=Fq!1J2>cnBvp}TRL9sBCTt4%k2O6}0TF~T< zj=2tUAW$skB2chDSHX6+Uo7g~28uU8yk5QyGxZ(>WZAeyc5nxG;c1aBQM-T+2VS)I z_eMYALii2ajUWgA8nCN>GnI)+bE((5MgKQJFKwtadZTP}^?Kh2VF$3fb)4mFD)@^= Md)@pjT>^xxKjDGCi~s-t literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/workbook.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/reader/__pycache__/workbook.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2382196c70dd1774b181b81c16d5cc433009ac82 GIT binary patch literal 3678 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/borders.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/borders.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0df577c663a3f01d39d217210454370a235b389 GIT binary patch literal 2929 zcmai0TaVjB6!txir3EQ9G#( zed2@ckUFi9IqH&mS|tnABa5^~mZ(pbX`QUlIkHOU$qBkZ*61QRNtehex=c=gtI-v@ z`dp){%sVj28PHGAHPF`z{VY4n&Kzj3cY5-LP0mrLqcu+b1s-S(UDZU?XQ7ZhPInrP zsBVLxX6S!PbEF`>ZE}^Dhl$7n_vb!vJ(4K3O1n;7H3IJCK44`XRmSuXnXSqf( z(}Tey4I_LVy7&#?^~9zov8jb=&Dj+H*Oc?$tK(}G2I(rH=3``bEU{8JyhTFG#SZULS@~lMegHkSw+=SvV zatqMRZ9prp0NS|==;RJyC9eXyxd&LyYk*$v1J?37ppS*c-v+Z#>i_@B{4%TrLOwj4 z2P5TAg5SkRgd#jN}K0`#(v#i~XfD5q$*ZgqitPVx(|gTIgP0m6p>sv@Hu zg%yhY}x^u6B8hNDOp$4ypdqjdJOAtuyXcwP8V z@GJhiab$LK{QUpuH3ICAHZ+000m~FuLrJg R_@)J)&^KJ>!rH>se*t{jx%dD8 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/builtins.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/builtins.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f406bc669945adf1e37d95d68435810af58a3ea GIT binary patch literal 29440 zcmeHQTW{OQ6_#xICOe69zbA}rCvg&6ciHONxhc^m?be$%X}6utR;4wx#hM}oiq6J< z?awF*bRXNN0!4p7fj%$VrviR!i=tR;Z|FsG$sn9Z zNQ4fNAv#Qk=?EF2qhyqhkue%2Q94e>=>(aeN5~O+lpLkU$T2!eCh2iyvLA!q1Wa+aPW=jeHIo?ajq=tXjoULu$1WpbHbAy?>Ca+L~1px4MX8Y3|} zMW*O9nWi&jhF&Mv=?!v&-Xu5aESaUZ$Sr!C+@^QP9Xdzm=scOHaT2EqlAuYFq$!f3 zX_BTHlA&3Wm2xyEg@3dl4N8&khd&Ch4bTO|Lx>jcM)Ged=K$?#P<)3y8mt_!{Eph+~Lf zAf7_}4aC!kzlnGT@q37`BmNfR8;HM+_$Fe4coy;dh;Jc&iTF0+4-nr${2jz|h)am) z5q}qP9I=Qvfp`^h5^))E3Nb~TMl2!DAYMa!5%D_Wtc}+pAF@a*&qK5FCt>glv*DfS ziddECb7M=9K7lLNr8Qlv)(u@&*To+~vZx>|sVtSSx+yD$tUjg7u|!NTZ!E`FG`%V+ zvAd8I5{rtwu2v=0h~tG-O{bD>1Guk9YtAh|y1ZWDprO@J4DPLJhM`p(n9Ab1rizM_ zah%V2X-!rX_K=!r7?Q3Q;TlWbNU*e~sRm1HehJ}~s4T|{@Yz>&Io5dOnpl;UE&FbY zyI$55O&5%cWPbl7ccZ>-Cr

  • t!>ceWNO?n(hcJ#T%bwsitd&R5oNy6)G~Nl4`wT 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/differential.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/differential.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97909efd1b7e39e1529eede470ad3f51f4fcc12e GIT binary patch literal 2711 zcmb7GNsk*v6z=NH?Y2EzCWI`I4hKjhVoM+)kWk1*;1H}x4oI|wIz3(GnI^qBRpp6o z`NZU!U%(|C=ghw;j-2un;KX~?9>hUJ^2S(U>1t%JzrJP685u?f*u%}~Irc61v z^DGe|QSAZaS(0ZW*~?kzDEod@O2917m3yb+lyTLB$@^u&;Di5=SCXYNtIAgd_l6z% z<6@8T`xP$|nc{(~tpS5u(8Umd84;mIWKt4Y)QoKAFqc}?eqlyn2J;p&n85sn+yc41 zkUJo6EaXj)w-)j?n2uzSXoosrc2jNN%Z5Y7L1VCOAg4J~7M%=3SJ^|f&n?Hr{Q~S9 zqMc2!e_@}ySDWmQmUcc0P1RwO5m?P=EP+VnCwX2yi_5BH{S;ijY$%)1??M+{fH|2P zu`xFR$=m`o=Qf}5?r5sKIL^vU#&H1Wi46dw>5|vK z*`+gn>7FbJ8~wV4=Ww4gDOemUKaPuvj&qdTar|_g|;4%(70O$r00C=VizcyL^EQ754{?xP2 zB!dMjZ$TH20oEtO3j(R&QTvEQP3lBG-PL(bq89aO>$wrNX`6Q7*`XVB6P_D%i*Cbn z6VhRao_lV<%5BKY(_R(eVhgK1d-YlB{Gr7*;%3b!t#Mx~msN zHlz^L41ON8=jdI?ekZNQrPN&YQ?&1-p}b55q_<8540C<~hOm#`!r~WUq-FjdO0T)s zw4*<6LKnE1L1yo+wP_hfsh);Yp`nb`_bznt8Gr;WBsnt0B?(1HS|F1Pz}U|Owfo6F zH28ID4h)oEI&<9_UtEUc!vV>FO_&Zqt zpYd2vJ;Uc4N`MN$=f#zrKmx+rsmjXeKdh*6Z9NSuv;Qw0R?f;g zEkA;DPU)dj^~dG;=IrwN^L2FqdyvU~xWnJUt=>hr3ZNQsOsf>ic7U%i)O6I>$D)ht zfM~{XA|=QAuFJCyl@Iar4TK(m^5DiEF)pX4tB7`@E+yAs-p9}d-ijWvZQJw-JT2e$ zTAt_Go^2A2v-;syw0m;fbtw}m&qlK1qP}0WqNrYUZQZ1>eMRXz yNnb+xPQtKM4ftyUf2Rc3R~KG7TB8;Imj)^Ci~4fBi3;g7-*wvLN~3LUUHu!(i(`WT literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/fills.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/fills.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3affab390483ad9da80cff120815134119bd4fc6 GIT binary patch literal 7017 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 Ov-PT3uQ})ImHz{3?2HEh literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/fonts.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/fonts.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a371f8683a6f8db0a03ba7b5f8e4686dc0f8b8c GIT binary patch literal 3061 zcmZ`*Nps`I6-HwrxRa77S|e#}jN`G*u|qm>mU1$2S);|C@(jlnYEqTHAP~AKA;v;? zgR-PRr=~RylY7pe~(2Oia-p;XamNW%nF-Pi?*URZAVLVNzE&T%h3v5QL-I&qE)&Yb!k`WBtw9m^Mlt+398O*dF&r1iR&@cmlP z$gQm~@MK;d@o%O)_IYkUj#GZfMUUk55f_0M2GTnSdC$&kLoQR!{^W&Io>vRyQIdpt zqmYM5%tt)U>xKF>h@W~Vb8VE0AU>QAoxNmkaxYB0^pX^x)Y?wMMC@?si(rx_BDWNg zTaTV>ZGQ>hRg>PByHXkK>AAfd@`%T2UL8#jW-^RwjK0|2-FKgEJs!Hl(a<;Wk7ozU zCS2Qa$u)qCh)$|yT1qnVf-rKSk06xbD18W~jAJkC>0%iMiX{N>o1J=xu@~{&f@k24 z3Mz8q`TToL+<`Ga{CN;@7I=d%Ji&HEaLfl^ogSr0JQ(>$;Iu*in8(M1FgO@YPSc|# z{<#0iKn5xQIT-ocJLGbZOn5vwJqZUgJqpRP<_*3)o3#c^3z$0#W_=-TETqkabeF%0DGb)% zf|<)O(*b5hG57e}Gpj!XuA{hj*eboxy7XPP$=28%D2(^```>9}!q(Zw%QDRGeFtHu z4edA5n@B$G5qV(CmJXHF11g~R)Ki6v9W{k&b=w|qaD#wq4;Ndn@N;(qmH zAM&N@g<o4-p4AZULfHAU$VjgCVb2Br6F|(FphFR;pl$C(7vZiAGk~Pom%m${El>zOn z4Oq@Bz)H3RSk0CJYuO55J?j8AvI-!~hb$q(UQ6`h8^uQm>L;&Uguko&Bz}d`Zx99u zzd-mU!p8`|M))m2Z#6d|F5|-F4Qp!V4U;%Yy&C2PXFN9#l91)4Acd~%=Zzv&(j$>f z50CQ76hb2~T*ar{_7&KfRC^NZAmwG(4dNhm-CfWmez9hiNQso8oBV&o5m)C~152ns zx8!4N=f}Ly;$40QkdargXr`pAy(So8rbDzp%Jf${GhP$W;CDeYn8D1~I?+-yg;>4N zUzIZQnm|OXv!_*OcEDQWFh2ICVd|)=aK?#n7G7|IvEv^FA#-@KKsbSPCW1>?D;74+ ztiRot=hLCUv{WSr3!OO*Af>T!=EfmZhk+$xsag}8$X3Q8iB#2MRr{fYdP>0ge}ISI z6X?&bA9_-{?v<|v<~xT=mH{-YNxGy?&YH@3SLQmyTaMlb7%8k+*UhW08zpQSqFi^~ zZ>C;2dt$pTOMKU*?dQXt-9J8gJlu76o;`o`WS4H-WZbRo?Pt%2`;Uj8UmrdK{inkV z=Gt(9JES6mYbdF_#eGpxBXyKiYGWdZPY`SbT%3yj@;j7NlleVL*lEN=gxfL}y`>t9 za&D2?*iMU@Qw7omm4Yi~VMe!q3RGTGL5Mi4u1vWY9Y{)Hfd+RHR!X&Hc1NQa4a=}I|6#|wn2S)ZBHjG9Jg`n zxLED=LC<$j)3(>rXK8jR5=z&ge6GpJG*;Z&!6nOd(VdZMVZ}fZV!t(`4qh&F-baB WU4N(2(d(p5>PFRSe^n>d|NalYb@BWF literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/named_styles.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/named_styles.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54c6fcd75e5cf6a6e6a271ba87703dcf3d89556d GIT binary patch literal 7553 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/numbers.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/numbers.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19f8aeec13ef4de790baa84a3ab48066d6636d79 GIT binary patch literal 6021 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/protection.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/protection.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a9b2133922a14aa670253bd684ede070e1faffd GIT binary patch literal 740 zcmZuvy>1jS5VrR}r;9*>2UrS^M0qzV%}7ow(?_q{=Jg z38;CSZK-$#D#ja*+k%ni^Za^#YDjM_Ae2D05xPuIi*34 zOtg%7Ovxt#F(kJH5*6RXJb`phX4x;yAT!!0@2xd>PUp&LX|$K;M%BSNrWlnYWd8y| z5l(^d2q=#s;)zOc2+s!0p@0;!TgnT_vCKg~Id!&Eb*F7}{d_N%whbmmvVMohtfL=a)kT=**`|u(k&5W8zEv#SdgB~qCPXWzEaR<3k{%}S==XH znG2Uh zg_G6hz~oza@(0ifB4|uT#3vMOE2bmMwaRBeP#2s-L#_gBHA^r|r^IqBP>Pak* zRr+EwJ!~>)#%3p}8UJmA;YSe)QlrIX9}q^wsbUJ-?#{_}HZBt;Z- zo&-ufR?(U498U&1O*-9hkfw2`eI}E$P8{_**+dW0KSmVqxF z^9JL&a*0jO=X*K7zjBXzYY|v(2^YGBtpsgM7aa3Xpev}LLtE`?NR=7=jcTf`g3PQ} zHjr<ZFv$vLOoJ_NU;-dM};l}c4v%g32i3a?p$uXz<0&ON*0WDgf=XF)ucpveu+!x#{N z851YuN!^9+eR%RZbcDLpwWjOgS)O`Zjd*nWl}R2s0u^D`G>Grsmi&b=W6{8Rjuh6)C_dqN4n_rrzQNM5N!QwOsBwA z3VbLQCAkigup&=YZXr=&I_gEq(wpUJ3L|az4ME8VP81f zF08^XoS99@>3#tT2f=xA8~)f+IXO?2U{EM?kZc@d8O=ay5EVU8>9{}eoNH>!8dR@Q zUN+{^@@iEMTf_6x3bT?9OL}%AR_5z3&yHO2%YY&AXTgXhgHy_)dG!~npq*d1uK!9KYA+`u6nyzQIlWiiJr;tf9)34Jo<7Q9C2G#%l;QdF%b0t literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/styleable.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/styleable.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29cc835e54a86098adef9af7af43f65bc8978f34 GIT binary patch literal 4802 zcmb_gTaOz_74ELSxZU=6Jhxof>|(-lu@Ld1T|kRgD3?vLLF{aZmk2dNtsYk=<4L>Q zsVdLT49*J^B!qZc9(hS5Uh=jQzlE26UW|=);5yRNwZsrW!$Q^i*H}E50 z&v{v85JZ6<`&o4mMxh>8vf7{?)%7^Y)&`BJVOw`CUghC)i-+m@sS`Cp*LWRtUDF$& z*LVYTL(`j}*Lf3kQ`1|ZH~1##O-*m}t*+JH{u*3cZM$@P#qgMZzeq&>wM~;wxqkE4 z_kQ{Lz1{fEXAeHT@p-qrv@{WadgC{7_g-|nytusb;P%7Y54yK;WY|^5S$ba-NAv+h z-4G(t)NV3Jx!!;^U(!z5Bc2u?2I4xJd>376MK-r0#%<1?TaiP$#jndf?&H_v704Bo z>-x}-ahm84RUvu~-km-)rbBO`$tJq7J+@+N%+TZTuJy{^ZL>1$6>UxD4XBpZ8T9Z{cH- zOwO`>`{dG+jMt{y&In7}eKalXM;IuGdw{>Ob-+&9GlytypE7w>Ij0!MuJW+M#kxOs zUR1{HrG4ta8c8v1xAbGpBa~cuoT@}AQToF~q`5MRsp1=A2VGf_W?k#5bP~?nt{{Dv zK|hx&$$M$(9VXc*B?HVdtxVHx>i6RqC&ckZ5Hdh#*-bmN1N-FS>!RzOXC(LV0$uh0 zB01@XB=?{hS3&-gZ@*l6?d$aD$4T>w!G;oP$81j{$iGf!+jx{se&+c1MG~RNN`VAW?H5MnOIr z+!?5QJX()2#FqGx-XmYTJThHk>v}1PJdgkU07^_Rhs=SM!XJ&m< z`P!^2D!}DdaJkj7J2hx?t>N;uR`5Y_nL1L9n3=S=L?l6f@s`C~G)uQEzDeC&*ILbV z2|qxS)mj3SSKISUCHHaEOvFuPYY~}N65QB>QB)oIw4&&(CX06$@w~&0H7kS(&68x?p^9MSK2GT+2Fr}&EG}S z&Lm*2?XsXQX!ieN^4s%pwqWq?(IXi9nno-)zVi*H{&z5Q&b>MZ4sO=fKHG!&`=?BN zGM?Pwp)J0Id1DVew)@hyt^5}VXwE@^l|N&56<|40RRb2fb>E$NR3T?CYTD!5 zf3Wia{!fET)yM9OwU_XOh!2b+!PHhxF8%(!XRW?$<%Mb`ttWZ$Mcy(2qOoi|Gq+iW zeL0N)g8W{zj^MDUq0%EaCPfX4W%!U{#BCu8gc{>41i7R#NMx)=!z^8m#?sS_tit16 zye`Q^EgeuOthlg06J7!uu_i;Hxw7g?&qb$$^KQ|}KSOuUEzZ!XvFgsF&Df=QOE^3e zYnHG#)1e@jBECo63T*wBxG)jLU<uYGdIBok!74qs9rg1Cyd89~x8BswJUJzD8y!)4c(TiYE2B_Q~6; zKDs)AqD!5*UGg3P4U(aeC8loIQjF%v?T*fz+ zA`c_f1Q_`kAt%A8k_M+%R0SRID(DcSkk>G(rFE>Vr43#`wWD?XuHm=I8_@>8z?*#I zxgBk$TYQsmJ%PAU#c*+1JmK-F;atFVRVW+Ky8FH59s!E+hU zh&`=BZ<4M40PbsmuWkvC1G;BQ@<+}cm`mD%Ds+21k!3pM+L4odP z8PWIw9mIQu;Hi*NUeK++5pO zT;$39G*wYeQc)NuR530Xjdn}~>OZ1QY6Bl`8!p?FMe~c8qnhg4=|f}U_!gQjEP>ud zR8u{O0ttVVRbuTW6#*L4-U6n{9Ye*vG|MKTU8jw&Y=iK#U?~G5OrQUSzN*1((!kev zo^I?d^E}fqd6OB>6JQgqiY{<_Ex0DQSiN?%an zY-byl@r9-ItE|K^FM4sTi-*!B<3taWjC^B;I>n0C&ZPTMI5)+zI;zR1@`ks50_pzYRWD8wjyKabJAEJOZJ#2=# zEUYj)^qDPa_ilS)-uT8BXzgiD869ZxiJetamBB}*?)y1$CaEf|+jX|>p{GBmxzl{B Id9}ImAGKieKL7v# literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/stylesheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/styles/__pycache__/stylesheet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc15947ef0f275c9d17ffa91ea365ca052e95488 GIT binary patch literal 6823 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 Hz1{x-qY#jz literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/alignment.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/alignment.py new file mode 100644 index 00000000..c81e1d04 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/alignment.py @@ -0,0 +1,72 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.compat import safe_string + +from openpyxl.descriptors import Bool, MinMax, Min, Alias, NoneSet +from openpyxl.descriptors.serialisable import Serialisable + + +horizontal_alignments = ( + "general", "left", "center", "right", "fill", "justify", "centerContinuous", + "distributed", ) +vertical_aligments = ( + "top", "center", "bottom", "justify", "distributed", +) + +class Alignment(Serialisable): + """Alignment options for use in styles.""" + + tagname = "alignment" + + __fields__ = ('horizontal', + 'vertical', + 'textRotation', + 'wrapText', + 'shrinkToFit', + 'indent', + 'relativeIndent', + 'justifyLastLine', + 'readingOrder', + ) + horizontal = NoneSet(values=horizontal_alignments) + vertical = NoneSet(values=vertical_aligments) + textRotation = NoneSet(values=range(181)) + textRotation.values.add(255) + text_rotation = Alias('textRotation') + wrapText = Bool(allow_none=True) + wrap_text = Alias('wrapText') + shrinkToFit = Bool(allow_none=True) + shrink_to_fit = Alias('shrinkToFit') + indent = MinMax(min=0, max=255) + relativeIndent = MinMax(min=-255, max=255) + justifyLastLine = Bool(allow_none=True) + readingOrder = Min(min=0) + + def __init__(self, horizontal=None, vertical=None, + textRotation=0, wrapText=None, shrinkToFit=None, indent=0, relativeIndent=0, + justifyLastLine=None, readingOrder=0, text_rotation=None, + wrap_text=None, shrink_to_fit=None, mergeCell=None): + self.horizontal = horizontal + self.vertical = vertical + self.indent = indent + self.relativeIndent = relativeIndent + self.justifyLastLine = justifyLastLine + self.readingOrder = readingOrder + if text_rotation is not None: + textRotation = text_rotation + if textRotation is not None: + self.textRotation = int(textRotation) + if wrap_text is not None: + wrapText = wrap_text + self.wrapText = wrapText + if shrink_to_fit is not None: + shrinkToFit = shrink_to_fit + self.shrinkToFit = shrinkToFit + # mergeCell is vestigial + + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if value is not None and value != 0: + yield attr, safe_string(value) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/borders.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/borders.py new file mode 100644 index 00000000..1214ba00 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/borders.py @@ -0,0 +1,113 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.compat import safe_string +from openpyxl.descriptors import ( + NoneSet, + Typed, + Bool, + Alias, + Sequence, + Integer, +) +from openpyxl.descriptors.serialisable import Serialisable + +from .colors import ColorDescriptor + + +BORDER_NONE = None +BORDER_DASHDOT = 'dashDot' +BORDER_DASHDOTDOT = 'dashDotDot' +BORDER_DASHED = 'dashed' +BORDER_DOTTED = 'dotted' +BORDER_DOUBLE = 'double' +BORDER_HAIR = 'hair' +BORDER_MEDIUM = 'medium' +BORDER_MEDIUMDASHDOT = 'mediumDashDot' +BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot' +BORDER_MEDIUMDASHED = 'mediumDashed' +BORDER_SLANTDASHDOT = 'slantDashDot' +BORDER_THICK = 'thick' +BORDER_THIN = 'thin' + + +class Side(Serialisable): + + """Border options for use in styles. + Caution: if you do not specify a border_style, other attributes will + have no effect !""" + + __fields__ = ('style', + 'color') + + color = ColorDescriptor(allow_none=True) + style = NoneSet(values=('dashDot','dashDotDot', 'dashed','dotted', + 'double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot', + 'mediumDashed', 'slantDashDot', 'thick', 'thin') + ) + border_style = Alias('style') + + def __init__(self, style=None, color=None, border_style=None): + if border_style is not None: + style = border_style + self.style = style + self.color = color + + +class Border(Serialisable): + """Border positioning for use in styles.""" + + tagname = "border" + + __fields__ = ('left', + 'right', + 'top', + 'bottom', + 'diagonal', + 'diagonal_direction', + 'vertical', + 'horizontal') + __elements__ = ('start', 'end', 'left', 'right', 'top', 'bottom', + 'diagonal', 'vertical', 'horizontal') + + # child elements + start = Typed(expected_type=Side, allow_none=True) + end = Typed(expected_type=Side, allow_none=True) + left = Typed(expected_type=Side, allow_none=True) + right = Typed(expected_type=Side, allow_none=True) + top = Typed(expected_type=Side, allow_none=True) + bottom = Typed(expected_type=Side, allow_none=True) + diagonal = Typed(expected_type=Side, allow_none=True) + vertical = Typed(expected_type=Side, allow_none=True) + horizontal = Typed(expected_type=Side, allow_none=True) + # attributes + outline = Bool() + diagonalUp = Bool() + diagonalDown = Bool() + + def __init__(self, left=None, right=None, top=None, + bottom=None, diagonal=None, diagonal_direction=None, + vertical=None, horizontal=None, diagonalUp=False, diagonalDown=False, + outline=True, start=None, end=None): + self.left = left + self.right = right + self.top = top + self.bottom = bottom + self.diagonal = diagonal + self.vertical = vertical + self.horizontal = horizontal + self.diagonal_direction = diagonal_direction + self.diagonalUp = diagonalUp + self.diagonalDown = diagonalDown + self.outline = outline + self.start = start + self.end = end + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if value and attr != "outline": + yield attr, safe_string(value) + elif attr == "outline" and not value: + yield attr, safe_string(value) + +DEFAULT_BORDER = Border(left=Side(), right=Side(), top=Side(), bottom=Side(), diagonal=Side()) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/builtins.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/builtins.py new file mode 100644 index 00000000..27e41a61 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/builtins.py @@ -0,0 +1,1397 @@ +# Copyright (c) 2010-2021 openpyxl + +# Builtins styles as defined in Part 4 Annex G.2 + +from .named_styles import NamedStyle +from openpyxl.xml.functions import fromstring + + +normal = """ + + + + + + + + + + + + + + + + + + + + +""" + +comma = """ + + + _-* #,##0.00\\ _$_-;\\-* #,##0.00\\ _$_-;_-* "-"??\\ _$_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +comma_0 = """ + + + _-* #,##0\\ _$_-;\\-* #,##0\\ _$_-;_-* "-"\\ _$_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +currency = """ + + + _-* #,##0.00\\ "$"_-;\\-* #,##0.00\\ "$"_-;_-* "-"??\\ "$"_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +currency_0 = """ + + + _-* #,##0\\ "$"_-;\\-* #,##0\\ "$"_-;_-* "-"\\ "$"_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +percent = """ + + + 0% + + + + + + + + + + + + + + + + + + +""" + +hyperlink = """ + + + + + + + + + + + + + + + + + + + + """ + +followed_hyperlink = """ + + + + + + + + + + + + + + + + + + + + """ + +title = """ + + + + + + + + + + + + + + + + + + + + + +""" + +headline_1 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +headline_2 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +headline_3 = """ + + + + + + + + + + + + + + + + + + + + + + + + +""" + +headline_4 = """ + + + + + + + + + + + + + + + + + + + + + +""" + +good = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +bad = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +neutral = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +input = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +output = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +calculation = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +linked_cell = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +check_cell = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +warning = """ + + + + + + + + + + + + + + + + + + + + +""" + +note = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +explanatory = """ + + + + + + + + + + + + + + + + + + + + + +""" + +total = """ + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_2 = """ + + + + + + + + + + + + + + + + + + + + + """ + +accent_2_20 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_2_40 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_2_60 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_3 = """ + + + + + + + + + + + + + + + + + + + + + + """ + +accent_3_20 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_3_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" +accent_3_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" +accent_4 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_4_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_4_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_4_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +pandas_highlight = """ + +""" + +styles = dict( + [ + ('Normal', NamedStyle.from_tree(fromstring(normal))), + ('Comma', NamedStyle.from_tree(fromstring(comma))), + ('Currency', NamedStyle.from_tree(fromstring(currency))), + ('Percent', NamedStyle.from_tree(fromstring(percent))), + ('Comma [0]', NamedStyle.from_tree(fromstring(comma_0))), + ('Currency [0]', NamedStyle.from_tree(fromstring(currency_0))), + ('Hyperlink', NamedStyle.from_tree(fromstring(hyperlink))), + ('Followed Hyperlink', NamedStyle.from_tree(fromstring(followed_hyperlink))), + ('Note', NamedStyle.from_tree(fromstring(note))), + ('Warning Text', NamedStyle.from_tree(fromstring(warning))), + ('Title', NamedStyle.from_tree(fromstring(title))), + ('Headline 1', NamedStyle.from_tree(fromstring(headline_1))), + ('Headline 2', NamedStyle.from_tree(fromstring(headline_2))), + ('Headline 3', NamedStyle.from_tree(fromstring(headline_3))), + ('Headline 4', NamedStyle.from_tree(fromstring(headline_4))), + ('Input', NamedStyle.from_tree(fromstring(input))), + ('Output', NamedStyle.from_tree(fromstring(output))), + ('Calculation',NamedStyle.from_tree(fromstring(calculation))), + ('Check Cell', NamedStyle.from_tree(fromstring(check_cell))), + ('Linked Cell', NamedStyle.from_tree(fromstring(linked_cell))), + ('Total', NamedStyle.from_tree(fromstring(total))), + ('Good', NamedStyle.from_tree(fromstring(good))), + ('Bad', NamedStyle.from_tree(fromstring(bad))), + ('Neutral', NamedStyle.from_tree(fromstring(neutral))), + ('Accent1', NamedStyle.from_tree(fromstring(accent_1))), + ('20 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_20))), + ('40 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_40))), + ('60 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_60))), + ('Accent2', NamedStyle.from_tree(fromstring(accent_2))), + ('20 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_20))), + ('40 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_40))), + ('60 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_60))), + ('Accent3', NamedStyle.from_tree(fromstring(accent_3))), + ('20 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_20))), + ('40 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_40))), + ('60 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_60))), + ('Accent4', NamedStyle.from_tree(fromstring(accent_4))), + ('20 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_20))), + ('40 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_40))), + ('60 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_60))), + ('Accent5', NamedStyle.from_tree(fromstring(accent_5))), + ('20 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_20))), + ('40 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_40))), + ('60 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_60))), + ('Accent6', NamedStyle.from_tree(fromstring(accent_6))), + ('20 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_20))), + ('40 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_40))), + ('60 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_60))), + ('Explanatory Text', NamedStyle.from_tree(fromstring(explanatory))), + ('Pandas', NamedStyle.from_tree(fromstring(pandas_highlight))) + ] +) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/cell_style.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/cell_style.py new file mode 100644 index 00000000..fa342652 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/cell_style.py @@ -0,0 +1,202 @@ +# Copyright (c) 2010-2021 openpyxl + +from array import array + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Bool, + Integer, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.utils.indexed_list import IndexedList + + +from .alignment import Alignment +from .protection import Protection + + +class ArrayDescriptor(object): + + def __init__(self, key): + self.key = key + + def __get__(self, instance, cls): + return instance[self.key] + + def __set__(self, instance, value): + instance[self.key] = value + + +class StyleArray(array): + """ + Simplified named tuple with an array + """ + + __slots__ = () + tagname = 'xf' + + fontId = ArrayDescriptor(0) + fillId = ArrayDescriptor(1) + borderId = ArrayDescriptor(2) + numFmtId = ArrayDescriptor(3) + protectionId = ArrayDescriptor(4) + alignmentId = ArrayDescriptor(5) + pivotButton = ArrayDescriptor(6) + quotePrefix = ArrayDescriptor(7) + xfId = ArrayDescriptor(8) + + + def __new__(cls, args=[0]*9): + return array.__new__(cls, 'i', args) + + + def __hash__(self): + return hash(tuple(self)) + + + def __copy__(self): + return StyleArray((self)) + + + def __deepcopy__(self, memo): + return StyleArray((self)) + + +class CellStyle(Serialisable): + + tagname = "xf" + + numFmtId = Integer() + fontId = Integer() + fillId = Integer() + borderId = Integer() + xfId = Integer(allow_none=True) + quotePrefix = Bool(allow_none=True) + pivotButton = Bool(allow_none=True) + applyNumberFormat = Bool(allow_none=True) + applyFont = Bool(allow_none=True) + applyFill = Bool(allow_none=True) + applyBorder = Bool(allow_none=True) + applyAlignment = Bool(allow_none=True) + applyProtection = Bool(allow_none=True) + alignment = Typed(expected_type=Alignment, allow_none=True) + protection = Typed(expected_type=Protection, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('alignment', 'protection') + __attrs__ = ("numFmtId", "fontId", "fillId", "borderId", + "applyAlignment", "applyProtection", "pivotButton", "quotePrefix", "xfId") + + def __init__(self, + numFmtId=0, + fontId=0, + fillId=0, + borderId=0, + xfId=None, + quotePrefix=None, + pivotButton=None, + applyNumberFormat=None, + applyFont=None, + applyFill=None, + applyBorder=None, + applyAlignment=None, + applyProtection=None, + alignment=None, + protection=None, + extLst=None, + ): + self.numFmtId = numFmtId + self.fontId = fontId + self.fillId = fillId + self.borderId = borderId + self.xfId = xfId + self.quotePrefix = quotePrefix + self.pivotButton = pivotButton + self.applyNumberFormat = applyNumberFormat + self.applyFont = applyFont + self.applyFill = applyFill + self.applyBorder = applyBorder + self.alignment = alignment + self.protection = protection + + + def to_array(self): + """ + Convert to StyleArray + """ + style = StyleArray() + for k in ("fontId", "fillId", "borderId", "numFmtId", "pivotButton", + "quotePrefix", "xfId"): + v = getattr(self, k, 0) + if v is not None: + setattr(style, k, v) + return style + + + @classmethod + def from_array(cls, style): + """ + Convert from StyleArray + """ + return cls(numFmtId=style.numFmtId, fontId=style.fontId, + fillId=style.fillId, borderId=style.borderId, xfId=style.xfId, + quotePrefix=style.quotePrefix, pivotButton=style.pivotButton,) + + + @property + def applyProtection(self): + return self.protection is not None or None + + + @property + def applyAlignment(self): + return self.alignment is not None or None + + +class CellStyleList(Serialisable): + + tagname = "cellXfs" + + __attrs__ = ("count",) + + count = Integer(allow_none=True) + xf = Sequence(expected_type=CellStyle) + alignment = Sequence(expected_type=Alignment) + protection = Sequence(expected_type=Protection) + + __elements__ = ('xf',) + + def __init__(self, + count=None, + xf=(), + ): + self.xf = xf + + + @property + def count(self): + return len(self.xf) + + + def __getitem__(self, idx): + return self.xf[idx] + + + def _to_array(self): + """ + Extract protection and alignments, convert to style array + """ + self.prots = IndexedList([Protection()]) + self.alignments = IndexedList([Alignment()]) + styles = [] # allow duplicates + for xf in self.xf: + style = xf.to_array() + if xf.alignment is not None: + style.alignmentId = self.alignments.add(xf.alignment) + if xf.protection is not None: + style.protectionId = self.prots.add(xf.protection) + styles.append(style) + return IndexedList(styles) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/colors.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/colors.py new file mode 100644 index 00000000..cd7048cd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/colors.py @@ -0,0 +1,172 @@ +# Copyright (c) 2010-2021 openpyxl + +import re +from openpyxl.compat import safe_string +from openpyxl.descriptors import ( + String, + Bool, + MinMax, + Integer, + Typed, +) +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.descriptors.serialisable import Serialisable + +# Default Color Index as per 18.8.27 of ECMA Part 4 +COLOR_INDEX = ( + '00000000', '00FFFFFF', '00FF0000', '0000FF00', '000000FF', #0-4 + '00FFFF00', '00FF00FF', '0000FFFF', '00000000', '00FFFFFF', #5-9 + '00FF0000', '0000FF00', '000000FF', '00FFFF00', '00FF00FF', #10-14 + '0000FFFF', '00800000', '00008000', '00000080', '00808000', #15-19 + '00800080', '00008080', '00C0C0C0', '00808080', '009999FF', #20-24 + '00993366', '00FFFFCC', '00CCFFFF', '00660066', '00FF8080', #25-29 + '000066CC', '00CCCCFF', '00000080', '00FF00FF', '00FFFF00', #30-34 + '0000FFFF', '00800080', '00800000', '00008080', '000000FF', #35-39 + '0000CCFF', '00CCFFFF', '00CCFFCC', '00FFFF99', '0099CCFF', #40-44 + '00FF99CC', '00CC99FF', '00FFCC99', '003366FF', '0033CCCC', #45-49 + '0099CC00', '00FFCC00', '00FF9900', '00FF6600', '00666699', #50-54 + '00969696', '00003366', '00339966', '00003300', '00333300', #55-59 + '00993300', '00993366', '00333399', '00333333', #60-63 +) +# indices 64 and 65 are reserved for the system foreground and background colours respectively + +# Will remove these definitions in a future release +BLACK = COLOR_INDEX[0] +WHITE = COLOR_INDEX[1] +#RED = COLOR_INDEX[2] +#DARKRED = COLOR_INDEX[8] +BLUE = COLOR_INDEX[4] +#DARKBLUE = COLOR_INDEX[12] +#GREEN = COLOR_INDEX[3] +#DARKGREEN = COLOR_INDEX[9] +#YELLOW = COLOR_INDEX[5] +#DARKYELLOW = COLOR_INDEX[19] + + +aRGB_REGEX = re.compile("^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$") + + +class RGB(Typed): + """ + Descriptor for aRGB values + If not supplied alpha is 00 + """ + + expected_type = str + + def __set__(self, instance, value): + if not self.allow_none: + m = aRGB_REGEX.match(value) + if m is None: + raise ValueError("Colors must be aRGB hex values") + if len(value) == 6: + value = "00" + value + super(RGB, self).__set__(instance, value) + + +class Color(Serialisable): + """Named colors for use in styles.""" + + tagname = "color" + + rgb = RGB() + indexed = Integer() + auto = Bool() + theme = Integer() + tint = MinMax(min=-1, max=1, expected_type=float) + type = String() + + + def __init__(self, rgb=BLACK, indexed=None, auto=None, theme=None, tint=0.0, index=None, type='rgb'): + if index is not None: + indexed = index + if indexed is not None: + self.type = 'indexed' + self.indexed = indexed + elif theme is not None: + self.type = 'theme' + self.theme = theme + elif auto is not None: + self.type = 'auto' + self.auto = auto + else: + self.rgb = rgb + self.type = 'rgb' + self.tint = tint + + @property + def value(self): + return getattr(self, self.type) + + @value.setter + def value(self, value): + setattr(self, self.type, value) + + def __iter__(self): + attrs = [(self.type, self.value)] + if self.tint != 0: + attrs.append(('tint', self.tint)) + for k, v in attrs: + yield k, safe_string(v) + + @property + def index(self): + # legacy + return self.value + + + def __add__(self, other): + """ + Adding colours is undefined behaviour best do nothing + """ + if not isinstance(other, Color): + return super(Color, self).__add__(other) + return self + + +class ColorDescriptor(Typed): + + expected_type = Color + + def __set__(self, instance, value): + if isinstance(value, str): + value = Color(rgb=value) + super(ColorDescriptor, self).__set__(instance, value) + + +class RgbColor(Serialisable): + + tagname = "rgbColor" + + rgb = RGB() + + def __init__(self, + rgb=None, + ): + self.rgb = rgb + + +class ColorList(Serialisable): + + tagname = "colors" + + indexedColors = NestedSequence(expected_type=RgbColor) + mruColors = NestedSequence(expected_type=Color) + + __elements__ = ('indexedColors', 'mruColors') + + def __init__(self, + indexedColors=(), + mruColors=(), + ): + self.indexedColors = indexedColors + self.mruColors = mruColors + + + def __bool__(self): + return bool(self.indexedColors) or bool(self.mruColors) + + + @property + def index(self): + return [val.rgb for val in self.indexedColors] diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/differential.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/differential.py new file mode 100644 index 00000000..4d336d8f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/differential.py @@ -0,0 +1,95 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.styles import ( + Font, + Fill, + Border, + Alignment, + Protection, + ) +from .numbers import NumberFormat + + +class DifferentialStyle(Serialisable): + + tagname = "dxf" + + __elements__ = ("font", "numFmt", "fill", "alignment", "border", "protection") + + font = Typed(expected_type=Font, allow_none=True) + numFmt = Typed(expected_type=NumberFormat, allow_none=True) + fill = Typed(expected_type=Fill, allow_none=True) + alignment = Typed(expected_type=Alignment, allow_none=True) + border = Typed(expected_type=Border, allow_none=True) + protection = Typed(expected_type=Protection, allow_none=True) + + def __init__(self, + font=None, + numFmt=None, + fill=None, + alignment=None, + border=None, + protection=None, + extLst=None, + ): + self.font = font + self.numFmt = numFmt + self.fill = fill + self.alignment = alignment + self.border = border + self.protection = protection + self.extLst = extLst + + +class DifferentialStyleList(Serialisable): + """ + Dedupable container for differential styles. + """ + + tagname = "dxfs" + + dxf = Sequence(expected_type=DifferentialStyle) + styles = Alias("dxf") + __attrs__ = ("count",) + + + def __init__(self, dxf=(), count=None): + self.dxf = dxf + + + def append(self, dxf): + """ + Check to see whether style already exists and append it if does not. + """ + if not isinstance(dxf, DifferentialStyle): + raise TypeError('expected ' + str(DifferentialStyle)) + if dxf in self.styles: + return + self.styles.append(dxf) + + + def add(self, dxf): + """ + Add a differential style and return its index + """ + self.append(dxf) + return self.styles.index(dxf) + + + def __bool__(self): + return bool(self.styles) + + + def __getitem__(self, idx): + return self.styles[idx] + + + @property + def count(self): + return len(self.dxf) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/fills.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/fills.py new file mode 100644 index 00000000..56bb5ea5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/fills.py @@ -0,0 +1,224 @@ +from __future__ import division +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Float, + Set, + Alias, + NoneSet, + Sequence, + Integer, + MinMax, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.compat import safe_string + +from .colors import ColorDescriptor, Color + +from openpyxl.xml.functions import Element, localname +from openpyxl.xml.constants import SHEET_MAIN_NS + + +FILL_NONE = 'none' +FILL_SOLID = 'solid' +FILL_PATTERN_DARKDOWN = 'darkDown' +FILL_PATTERN_DARKGRAY = 'darkGray' +FILL_PATTERN_DARKGRID = 'darkGrid' +FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal' +FILL_PATTERN_DARKTRELLIS = 'darkTrellis' +FILL_PATTERN_DARKUP = 'darkUp' +FILL_PATTERN_DARKVERTICAL = 'darkVertical' +FILL_PATTERN_GRAY0625 = 'gray0625' +FILL_PATTERN_GRAY125 = 'gray125' +FILL_PATTERN_LIGHTDOWN = 'lightDown' +FILL_PATTERN_LIGHTGRAY = 'lightGray' +FILL_PATTERN_LIGHTGRID = 'lightGrid' +FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal' +FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis' +FILL_PATTERN_LIGHTUP = 'lightUp' +FILL_PATTERN_LIGHTVERTICAL = 'lightVertical' +FILL_PATTERN_MEDIUMGRAY = 'mediumGray' + +fills = (FILL_SOLID, FILL_PATTERN_DARKDOWN, FILL_PATTERN_DARKGRAY, + FILL_PATTERN_DARKGRID, FILL_PATTERN_DARKHORIZONTAL, FILL_PATTERN_DARKTRELLIS, + FILL_PATTERN_DARKUP, FILL_PATTERN_DARKVERTICAL, FILL_PATTERN_GRAY0625, + FILL_PATTERN_GRAY125, FILL_PATTERN_LIGHTDOWN, FILL_PATTERN_LIGHTGRAY, + FILL_PATTERN_LIGHTGRID, FILL_PATTERN_LIGHTHORIZONTAL, + FILL_PATTERN_LIGHTTRELLIS, FILL_PATTERN_LIGHTUP, FILL_PATTERN_LIGHTVERTICAL, + FILL_PATTERN_MEDIUMGRAY) + + +class Fill(Serialisable): + + """Base class""" + + tagname = "fill" + + @classmethod + def from_tree(cls, el): + children = [c for c in el] + if not children: + return + child = children[0] + if "patternFill" in child.tag: + return PatternFill._from_tree(child) + return super(Fill, GradientFill).from_tree(child) + + +class PatternFill(Fill): + """Area fill patterns for use in styles. + Caution: if you do not specify a fill_type, other attributes will have + no effect !""" + + tagname = "patternFill" + + __elements__ = ('fgColor', 'bgColor') + + patternType = NoneSet(values=fills) + fill_type = Alias("patternType") + fgColor = ColorDescriptor() + start_color = Alias("fgColor") + bgColor = ColorDescriptor() + end_color = Alias("bgColor") + + def __init__(self, patternType=None, fgColor=Color(), bgColor=Color(), + fill_type=None, start_color=None, end_color=None): + if fill_type is not None: + patternType = fill_type + self.patternType = patternType + if start_color is not None: + fgColor = start_color + self.fgColor = fgColor + if end_color is not None: + bgColor = end_color + self.bgColor = bgColor + + @classmethod + def _from_tree(cls, el): + attrib = dict(el.attrib) + for child in el: + desc = localname(child) + attrib[desc] = Color.from_tree(child) + return cls(**attrib) + + + def to_tree(self, tagname=None, idx=None): + parent = Element("fill") + el = Element(self.tagname) + if self.patternType is not None: + el.set('patternType', self.patternType) + for c in self.__elements__: + value = getattr(self, c) + if value != Color(): + el.append(value.to_tree(c)) + parent.append(el) + return parent + + +DEFAULT_EMPTY_FILL = PatternFill() +DEFAULT_GRAY_FILL = PatternFill(patternType='gray125') + + +class Stop(Serialisable): + + tagname = "stop" + + position = MinMax(min=0, max=1) + color = ColorDescriptor() + + def __init__(self, color, position): + self.position = position + self.color = color + + +def _assign_position(values): + """ + Automatically assign positions if a list of colours is provided. + + It is not permitted to mix colours and stops + """ + n_values = len(values) + n_stops = sum(isinstance(value, Stop) for value in values) + + if n_stops == 0: + interval = 1 + if n_values > 2: + interval = 1 / (n_values - 1) + values = [Stop(value, i * interval) + for i, value in enumerate(values)] + + elif n_stops < n_values: + raise ValueError('Cannot interpret mix of Stops and Colors in GradientFill') + + pos = set() + for stop in values: + if stop.position in pos: + raise ValueError("Duplicate position {0}".format(stop.position)) + pos.add(stop.position) + + return values + + +class StopList(Sequence): + + expected_type = Stop + + def __set__(self, obj, values): + values = _assign_position(values) + super(StopList, self).__set__(obj, values) + + +class GradientFill(Fill): + """Fill areas with gradient + + Two types of gradient fill are supported: + + - A type='linear' gradient interpolates colours between + a set of specified Stops, across the length of an area. + The gradient is left-to-right by default, but this + orientation can be modified with the degree + attribute. A list of Colors can be provided instead + and they will be positioned with equal distance between them. + + - A type='path' gradient applies a linear gradient from each + edge of the area. Attributes top, right, bottom, left specify + the extent of fill from the respective borders. Thus top="0.2" + will fill the top 20% of the cell. + + """ + + tagname = "gradientFill" + + type = Set(values=('linear', 'path')) + fill_type = Alias("type") + degree = Float() + left = Float() + right = Float() + top = Float() + bottom = Float() + stop = StopList() + + + def __init__(self, type="linear", degree=0, left=0, right=0, top=0, + bottom=0, stop=()): + self.degree = degree + self.left = left + self.right = right + self.top = top + self.bottom = bottom + self.stop = stop + self.type = type + + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if value: + yield attr, safe_string(value) + + + def to_tree(self, tagname=None, namespace=None, idx=None): + parent = Element("fill") + el = super(GradientFill, self).to_tree() + parent.append(el) + return parent diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/fonts.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/fonts.py new file mode 100644 index 00000000..5ce18d27 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/fonts.py @@ -0,0 +1,113 @@ +# Copyright (c) 2010-2021 openpyxl + + +from openpyxl.descriptors import ( + Alias, + Sequence, + Integer +) +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.descriptors.nested import ( + NestedValue, + NestedBool, + NestedNoneSet, + NestedMinMax, + NestedString, + NestedInteger, + NestedFloat, +) +from .colors import ColorDescriptor, Color, BLACK + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element, SubElement +from openpyxl.xml.constants import SHEET_MAIN_NS + + +def _no_value(tagname, value, namespace=None): + if value: + return Element(tagname, val=safe_string(value)) + + +class Font(Serialisable): + """Font options used in styles.""" + + UNDERLINE_DOUBLE = 'double' + UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting' + UNDERLINE_SINGLE = 'single' + UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting' + + name = NestedString(allow_none=True) + charset = NestedInteger(allow_none=True) + family = NestedMinMax(min=0, max=14, allow_none=True) + sz = NestedFloat(allow_none=True) + size = Alias("sz") + b = NestedBool(to_tree=_no_value) + bold = Alias("b") + i = NestedBool(to_tree=_no_value) + italic = Alias("i") + strike = NestedBool(allow_none=True) + strikethrough = Alias("strike") + outline = NestedBool(allow_none=True) + shadow = NestedBool(allow_none=True) + condense = NestedBool(allow_none=True) + extend = NestedBool(allow_none=True) + u = NestedNoneSet(values=('single', 'double', 'singleAccounting', + 'doubleAccounting')) + underline = Alias("u") + vertAlign = NestedNoneSet(values=('superscript', 'subscript', 'baseline')) + color = ColorDescriptor(allow_none=True) + scheme = NestedNoneSet(values=("major", "minor")) + + tagname = "font" + + __elements__ = ('name', 'charset', 'family', 'b', 'i', 'strike', 'outline', + 'shadow', 'condense', 'color', 'extend', 'sz', 'u', 'vertAlign', + 'scheme') + + + def __init__(self, name=None, sz=None, b=None, i=None, charset=None, + u=None, strike=None, color=None, scheme=None, family=None, size=None, + bold=None, italic=None, strikethrough=None, underline=None, + vertAlign=None, outline=None, shadow=None, condense=None, + extend=None): + self.name = name + self.family = family + if size is not None: + sz = size + self.sz = sz + if bold is not None: + b = bold + self.b = b + if italic is not None: + i = italic + self.i = i + if underline is not None: + u = underline + self.u = u + if strikethrough is not None: + strike = strikethrough + self.strike = strike + self.color = color + self.vertAlign = vertAlign + self.charset = charset + self.outline = outline + self.shadow = shadow + self.condense = condense + self.extend = extend + self.scheme = scheme + + + @classmethod + def from_tree(cls, node): + """ + Set default value for underline if child element is present + """ + underline = node.find("{%s}u" % SHEET_MAIN_NS) + if underline is not None and underline.get('val') is None: + underline.set("val", "single") + return super(Font, cls).from_tree(node) + + +DEFAULT_FONT = Font(name="Calibri", sz=11, family=2, b=False, i=False, + color=Color(theme=1), scheme="minor") diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/named_styles.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/named_styles.py new file mode 100644 index 00000000..9f49ed60 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/named_styles.py @@ -0,0 +1,291 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.compat import safe_string + +from openpyxl.descriptors import ( + Typed, + Integer, + Bool, + String, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + +from .fills import PatternFill, Fill +from .fonts import Font +from .borders import Border +from .alignment import Alignment +from .protection import Protection +from .numbers import ( + NumberFormatDescriptor, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, +) +from .cell_style import ( + StyleArray, + CellStyle, +) + + +class NamedStyle(Serialisable): + + """ + Named and editable styles + """ + + font = Typed(expected_type=Font) + fill = Typed(expected_type=Fill) + border = Typed(expected_type=Border) + alignment = Typed(expected_type=Alignment) + number_format = NumberFormatDescriptor() + protection = Typed(expected_type=Protection) + builtinId = Integer(allow_none=True) + hidden = Bool(allow_none=True) + xfId = Integer(allow_none=True) + name = String() + _wb = None + _style = StyleArray() + + + def __init__(self, + name="Normal", + font=Font(), + fill=PatternFill(), + border=Border(), + alignment=Alignment(), + number_format=None, + protection=Protection(), + builtinId=None, + hidden=False, + xfId=None, + ): + self.name = name + self.font = font + self.fill = fill + self.border = border + self.alignment = alignment + self.number_format = number_format + self.protection = protection + self.builtinId = builtinId + self.hidden = hidden + self._wb = None + self._style = StyleArray() + + + def __setattr__(self, attr, value): + super(NamedStyle, self).__setattr__(attr, value) + if getattr(self, '_wb', None) and attr in ( + 'font', 'fill', 'border', 'alignment', 'number_format', 'protection', + ): + self._recalculate() + + + def __iter__(self): + for key in ('name', 'builtinId', 'hidden', 'xfId'): + value = getattr(self, key, None) + if value is not None: + yield key, safe_string(value) + + + @property + def xfId(self): + """ + Index of the style in the list of named styles + """ + return self._style.xfId + + + def _set_index(self, idx): + """ + Allow the containing list to set the index + """ + self._style.xfId = idx + + + def bind(self, wb): + """ + Bind a named style to a workbook + """ + self._wb = wb + self._recalculate() + + + def _recalculate(self): + self._style.fontId = self._wb._fonts.add(self.font) + self._style.borderId = self._wb._borders.add(self.border) + self._style.fillId = self._wb._fills.add(self.fill) + self._style.protectionId = self._wb._protections.add(self.protection) + self._style.alignmentId = self._wb._alignments.add(self.alignment) + fmt = self.number_format + if fmt in BUILTIN_FORMATS_REVERSE: + fmt = BUILTIN_FORMATS_REVERSE[fmt] + else: + fmt = self._wb._number_formats.add(self.number_format) + ( + BUILTIN_FORMATS_MAX_SIZE) + self._style.numFmtId = fmt + + + def as_tuple(self): + """Return a style array representing the current style""" + return self._style + + + def as_xf(self): + """ + Return equivalent XfStyle + """ + xf = CellStyle.from_array(self._style) + xf.xfId = None + xf.pivotButton = None + xf.quotePrefix = None + if self.alignment != Alignment(): + xf.alignment = self.alignment + if self.protection != Protection(): + xf.protection = self.protection + return xf + + + def as_name(self): + """ + Return relevant named style + + """ + named = _NamedCellStyle( + name=self.name, + builtinId=self.builtinId, + hidden=self.hidden, + xfId=self.xfId + ) + return named + + +class NamedStyleList(list): + """ + Named styles are editable and can be applied to multiple objects + + As only the index is stored in referencing objects the order mus + be preserved. + """ + + @property + def names(self): + return [s.name for s in self] + + + def __getitem__(self, key): + if isinstance(key, int): + return super(NamedStyleList, self).__getitem__(key) + + names = self.names + if key not in names: + raise KeyError("No named style with the name{0} exists".format(key)) + + for idx, name in enumerate(names): + if name == key: + return self[idx] + + + def append(self, style): + if not isinstance(style, NamedStyle): + raise TypeError("""Only NamedStyle instances can be added""") + elif style.name in self.names: + raise ValueError("""Style {0} exists already""".format(style.name)) + style._set_index(len(self)) + super(NamedStyleList, self).append(style) + + +class _NamedCellStyle(Serialisable): + + """ + Pointer-based representation of named styles in XML + xfId refers to the corresponding CellStyleXfs + + Not used in client code. + """ + + tagname = "cellStyle" + + name = String() + xfId = Integer() + builtinId = Integer(allow_none=True) + iLevel = Integer(allow_none=True) + hidden = Bool(allow_none=True) + customBuiltin = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + + def __init__(self, + name=None, + xfId=None, + builtinId=None, + iLevel=None, + hidden=None, + customBuiltin=None, + extLst=None, + ): + self.name = name + self.xfId = xfId + self.builtinId = builtinId + self.iLevel = iLevel + self.hidden = hidden + self.customBuiltin = customBuiltin + + +class _NamedCellStyleList(Serialisable): + """ + Container for named cell style objects + + Not used in client code + """ + + tagname = "cellStyles" + + count = Integer(allow_none=True) + cellStyle = Sequence(expected_type=_NamedCellStyle) + + __attrs__ = ("count",) + + def __init__(self, + count=None, + cellStyle=(), + ): + self.cellStyle = cellStyle + + + @property + def count(self): + return len(self.cellStyle) + + + @property + def names(self): + """ + Convert to NamedStyle objects and remove duplicates. + + In theory the highest xfId wins but in practice they are duplicates + so it doesn't matter. + """ + + def sort_fn(v): + return v.xfId + + styles = [] + names = set() + + for ns in sorted(self.cellStyle, key=sort_fn): + if ns.name in names: + continue + + style = NamedStyle( + name=ns.name, + hidden=ns.hidden, + builtinId = ns.builtinId + ) + names.add(ns.name) + style._set_index(len(styles)) # assign xfId + styles.append(style) + + return NamedStyleList(styles) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/numbers.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/numbers.py new file mode 100644 index 00000000..09c43364 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/numbers.py @@ -0,0 +1,200 @@ +# Copyright (c) 2010-2021 openpyxl + +import re + +from openpyxl.descriptors import ( + String, + Sequence, + Integer, +) +from openpyxl.descriptors.serialisable import Serialisable + + +BUILTIN_FORMATS = { + 0: 'General', + 1: '0', + 2: '0.00', + 3: '#,##0', + 4: '#,##0.00', + 5: '"$"#,##0_);("$"#,##0)', + 6: '"$"#,##0_);[Red]("$"#,##0)', + 7: '"$"#,##0.00_);("$"#,##0.00)', + 8: '"$"#,##0.00_);[Red]("$"#,##0.00)', + 9: '0%', + 10: '0.00%', + 11: '0.00E+00', + 12: '# ?/?', + 13: '# ??/??', + 14: 'mm-dd-yy', + 15: 'd-mmm-yy', + 16: 'd-mmm', + 17: 'mmm-yy', + 18: 'h:mm AM/PM', + 19: 'h:mm:ss AM/PM', + 20: 'h:mm', + 21: 'h:mm:ss', + 22: 'm/d/yy h:mm', + + 37: '#,##0_);(#,##0)', + 38: '#,##0_);[Red](#,##0)', + 39: '#,##0.00_);(#,##0.00)', + 40: '#,##0.00_);[Red](#,##0.00)', + + 41: r'_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)', + 42: r'_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)', + 43: r'_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)', + + 44: r'_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)', + 45: 'mm:ss', + 46: '[h]:mm:ss', + 47: 'mmss.0', + 48: '##0.0E+0', + 49: '@', } + +BUILTIN_FORMATS_MAX_SIZE = 164 +BUILTIN_FORMATS_REVERSE = dict( + [(value, key) for key, value in BUILTIN_FORMATS.items()]) + +FORMAT_GENERAL = BUILTIN_FORMATS[0] +FORMAT_TEXT = BUILTIN_FORMATS[49] +FORMAT_NUMBER = BUILTIN_FORMATS[1] +FORMAT_NUMBER_00 = BUILTIN_FORMATS[2] +FORMAT_NUMBER_COMMA_SEPARATED1 = BUILTIN_FORMATS[4] +FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-' +FORMAT_PERCENTAGE = BUILTIN_FORMATS[9] +FORMAT_PERCENTAGE_00 = BUILTIN_FORMATS[10] +FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd' +FORMAT_DATE_YYMMDD = 'yy-mm-dd' +FORMAT_DATE_DDMMYY = 'dd/mm/yy' +FORMAT_DATE_DMYSLASH = 'd/m/y' +FORMAT_DATE_DMYMINUS = 'd-m-y' +FORMAT_DATE_DMMINUS = 'd-m' +FORMAT_DATE_MYMINUS = 'm-y' +FORMAT_DATE_XLSX14 = BUILTIN_FORMATS[14] +FORMAT_DATE_XLSX15 = BUILTIN_FORMATS[15] +FORMAT_DATE_XLSX16 = BUILTIN_FORMATS[16] +FORMAT_DATE_XLSX17 = BUILTIN_FORMATS[17] +FORMAT_DATE_XLSX22 = BUILTIN_FORMATS[22] +FORMAT_DATE_DATETIME = 'yyyy-mm-dd h:mm:ss' +FORMAT_DATE_TIME1 = BUILTIN_FORMATS[18] +FORMAT_DATE_TIME2 = BUILTIN_FORMATS[19] +FORMAT_DATE_TIME3 = BUILTIN_FORMATS[20] +FORMAT_DATE_TIME4 = BUILTIN_FORMATS[21] +FORMAT_DATE_TIME5 = BUILTIN_FORMATS[45] +FORMAT_DATE_TIME6 = BUILTIN_FORMATS[21] +FORMAT_DATE_TIME7 = 'i:s.S' +FORMAT_DATE_TIME8 = 'h:mm:ss@' +FORMAT_DATE_TIMEDELTA = '[hh]:mm:ss' +FORMAT_DATE_YYMMDDSLASH = 'yy/mm/dd@' +FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-' +FORMAT_CURRENCY_USD = '$#,##0_-' +FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-' + + +COLORS = r"\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]" +LITERAL_GROUP = r'".*?"' # anything in quotes +LOCALE_GROUP = r'\[(?!hh?\]|mm?\]|ss?\])[^\]]*\]' # anything in square brackets, except hours or minutes or seconds +STRIP_RE = re.compile(f"{LITERAL_GROUP}|{LOCALE_GROUP}") +TIMEDELTA_RE = re.compile(r'\[hh?\](:mm(:ss(\.0*)?)?)?|\[mm?\](:ss(\.0*)?)?|\[ss?\](\.0*)?', re.I) + + +# Spec 18.8.31 numFmts +# +ve;-ve;zero;text + +def is_date_format(fmt): + if fmt is None: + return False + fmt = fmt.split(";")[0] # only look at the first format + fmt = STRIP_RE.sub("", fmt) # ignore some formats + return re.search(r"[^\\][dmhysDMHYS]", fmt) is not None + + +def is_timedelta_format(fmt): + if fmt is None: + return False + fmt = fmt.split(";")[0] # only look at the first format + return TIMEDELTA_RE.search(fmt) is not None + + +def is_datetime(fmt): + """ + Return date, time or datetime + """ + if not is_date_format(fmt): + return + + DATE = TIME = False + + if any((x in fmt for x in 'dy')): + DATE = True + if any((x in fmt for x in 'hs')): + TIME = True + + if DATE and TIME: + return "datetime" + if DATE: + return "date" + return "time" + + +def is_builtin(fmt): + return fmt in BUILTIN_FORMATS.values() + + +def builtin_format_code(index): + """Return one of the standard format codes by index.""" + try: + fmt = BUILTIN_FORMATS[index] + except KeyError: + fmt = None + return fmt + + +def builtin_format_id(fmt): + """Return the id of a standard style.""" + return BUILTIN_FORMATS_REVERSE.get(fmt) + + +class NumberFormatDescriptor(String): + + def __set__(self, instance, value): + if value is None: + value = FORMAT_GENERAL + super(NumberFormatDescriptor, self).__set__(instance, value) + + +class NumberFormat(Serialisable): + + numFmtId = Integer() + formatCode = String() + + def __init__(self, + numFmtId=None, + formatCode=None, + ): + self.numFmtId = numFmtId + self.formatCode = formatCode + + +class NumberFormatList(Serialisable): + + count = Integer(allow_none=True) + numFmt = Sequence(expected_type=NumberFormat) + + __elements__ = ('numFmt',) + __attrs__ = ("count",) + + def __init__(self, + count=None, + numFmt=(), + ): + self.numFmt = numFmt + + + @property + def count(self): + return len(self.numFmt) + + + def __getitem__(self, idx): + return self.numFmt[idx] diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/protection.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/protection.py new file mode 100644 index 00000000..79947c3a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/protection.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import Bool +from openpyxl.descriptors.serialisable import Serialisable + + +class Protection(Serialisable): + """Protection options for use in styles.""" + + tagname = "protection" + + locked = Bool() + hidden = Bool() + + def __init__(self, locked=True, hidden=False): + self.locked = locked + self.hidden = hidden diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/proxy.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/proxy.py new file mode 100644 index 00000000..9a92f358 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/proxy.py @@ -0,0 +1,62 @@ +# Copyright (c) 2010-2021 openpyxl + +from copy import copy + +from openpyxl.compat import deprecated + + +class StyleProxy(object): + """ + Proxy formatting objects so that they cannot be altered + """ + + __slots__ = ('__target') + + def __init__(self, target): + self.__target = target + + + def __repr__(self): + return repr(self.__target) + + + def __getattr__(self, attr): + return getattr(self.__target, attr) + + + def __setattr__(self, attr, value): + if attr != "_StyleProxy__target": + raise AttributeError("Style objects are immutable and cannot be changed." + "Reassign the style with a copy") + super(StyleProxy, self).__setattr__(attr, value) + + + def __copy__(self): + """ + Return a copy of the proxied object. + """ + return copy(self.__target) + + + def __add__(self, other): + """ + Add proxied object to another instance and return the combined object + """ + return self.__target + other + + + @deprecated("Use copy(obj) or cell.obj = cell.obj + other") + def copy(self, **kw): + """Return a copy of the proxied object. Keyword args will be passed through""" + cp = copy(self.__target) + for k, v in kw.items(): + setattr(cp, k, v) + return cp + + + def __eq__(self, other): + return self.__target == other + + + def __ne__(self, other): + return not self == other diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/styleable.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/styleable.py new file mode 100644 index 00000000..96d40036 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/styleable.py @@ -0,0 +1,152 @@ +# Copyright (c) 2010-2021 openpyxl + +from copy import copy +from warnings import warn + +from .numbers import ( + BUILTIN_FORMATS, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, +) +from .proxy import StyleProxy +from .cell_style import StyleArray +from .named_styles import NamedStyle +from .builtins import styles + + +class StyleDescriptor(object): + + def __init__(self, collection, key): + self.collection = collection + self.key = key + + def __set__(self, instance, value): + coll = getattr(instance.parent.parent, self.collection) + if not getattr(instance, "_style"): + instance._style = StyleArray() + setattr(instance._style, self.key, coll.add(value)) + + + def __get__(self, instance, cls): + coll = getattr(instance.parent.parent, self.collection) + if not getattr(instance, "_style"): + instance._style = StyleArray() + idx = getattr(instance._style, self.key) + return StyleProxy(coll[idx]) + + +class NumberFormatDescriptor(object): + + key = "numFmtId" + collection = '_number_formats' + + def __set__(self, instance, value): + coll = getattr(instance.parent.parent, self.collection) + if value in BUILTIN_FORMATS_REVERSE: + idx = BUILTIN_FORMATS_REVERSE[value] + else: + idx = coll.add(value) + BUILTIN_FORMATS_MAX_SIZE + + if not getattr(instance, "_style"): + instance._style = StyleArray() + setattr(instance._style, self.key, idx) + + + def __get__(self, instance, cls): + if not getattr(instance, "_style"): + instance._style = StyleArray() + idx = getattr(instance._style, self.key) + if idx < BUILTIN_FORMATS_MAX_SIZE: + return BUILTIN_FORMATS.get(idx, "General") + coll = getattr(instance.parent.parent, self.collection) + return coll[idx - BUILTIN_FORMATS_MAX_SIZE] + + +class NamedStyleDescriptor(object): + + key = "xfId" + collection = "_named_styles" + + + def __set__(self, instance, value): + if not getattr(instance, "_style"): + instance._style = StyleArray() + coll = getattr(instance.parent.parent, self.collection) + if isinstance(value, NamedStyle): + style = value + if style not in coll: + instance.parent.parent.add_named_style(style) + elif value not in coll.names: + if value in styles: # is it builtin? + style = styles[value] + if style not in coll: + instance.parent.parent.add_named_style(style) + else: + raise ValueError("{0} is not a known style".format(value)) + else: + style = coll[value] + instance._style = copy(style.as_tuple()) + + + def __get__(self, instance, cls): + if not getattr(instance, "_style"): + instance._style = StyleArray() + idx = getattr(instance._style, self.key) + coll = getattr(instance.parent.parent, self.collection) + return coll.names[idx] + + +class StyleArrayDescriptor(object): + + def __init__(self, key): + self.key = key + + def __set__(self, instance, value): + if instance._style is None: + instance._style = StyleArray() + setattr(instance._style, self.key, value) + + + def __get__(self, instance, cls): + if instance._style is None: + return False + return bool(getattr(instance._style, self.key)) + + +class StyleableObject(object): + """ + Base class for styleble objects implementing proxy and lookup functions + """ + + font = StyleDescriptor('_fonts', "fontId") + fill = StyleDescriptor('_fills', "fillId") + border = StyleDescriptor('_borders', "borderId") + number_format = NumberFormatDescriptor() + protection = StyleDescriptor('_protections', "protectionId") + alignment = StyleDescriptor('_alignments', "alignmentId") + style = NamedStyleDescriptor() + quotePrefix = StyleArrayDescriptor('quotePrefix') + pivotButton = StyleArrayDescriptor('pivotButton') + + __slots__ = ('parent', '_style') + + def __init__(self, sheet, style_array=None): + self.parent = sheet + if style_array is not None: + style_array = StyleArray(style_array) + self._style = style_array + + + @property + def style_id(self): + if self._style is None: + self._style = StyleArray() + return self.parent.parent._cell_styles.add(self._style) + + + @property + def has_style(self): + if self._style is None: + return False + return any(self._style) + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/stylesheet.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/stylesheet.py new file mode 100644 index 00000000..e22da5f6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/stylesheet.py @@ -0,0 +1,263 @@ +# Copyright (c) 2010-2021 openpyxl + +from warnings import warn + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, +) +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.xml.constants import ARC_STYLE, SHEET_MAIN_NS +from openpyxl.xml.functions import fromstring + +from .builtins import styles +from .colors import ColorList, COLOR_INDEX +from .differential import DifferentialStyle +from .table import TableStyleList +from .borders import Border +from .fills import Fill +from .fonts import Font +from .numbers import ( + NumberFormatList, + BUILTIN_FORMATS, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, + is_date_format, + is_timedelta_format, + builtin_format_code +) +from .named_styles import ( + _NamedCellStyleList +) +from .cell_style import CellStyle, CellStyleList + + +class Stylesheet(Serialisable): + + tagname = "styleSheet" + + numFmts = Typed(expected_type=NumberFormatList) + fonts = NestedSequence(expected_type=Font, count=True) + fills = NestedSequence(expected_type=Fill, count=True) + borders = NestedSequence(expected_type=Border, count=True) + cellStyleXfs = Typed(expected_type=CellStyleList) + cellXfs = Typed(expected_type=CellStyleList) + cellStyles = Typed(expected_type=_NamedCellStyleList) + dxfs = NestedSequence(expected_type=DifferentialStyle, count=True) + tableStyles = Typed(expected_type=TableStyleList, allow_none=True) + colors = Typed(expected_type=ColorList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('numFmts', 'fonts', 'fills', 'borders', 'cellStyleXfs', + 'cellXfs', 'cellStyles', 'dxfs', 'tableStyles', 'colors') + + def __init__(self, + numFmts=None, + fonts=(), + fills=(), + borders=(), + cellStyleXfs=None, + cellXfs=None, + cellStyles=None, + dxfs=(), + tableStyles=None, + colors=None, + extLst=None, + ): + if numFmts is None: + numFmts = NumberFormatList() + self.numFmts = numFmts + self.number_formats = IndexedList() + self.fonts = fonts + self.fills = fills + self.borders = borders + if cellStyleXfs is None: + cellStyleXfs = CellStyleList() + self.cellStyleXfs = cellStyleXfs + if cellXfs is None: + cellXfs = CellStyleList() + self.cellXfs = cellXfs + if cellStyles is None: + cellStyles = _NamedCellStyleList() + self.cellStyles = cellStyles + + self.dxfs = dxfs + self.tableStyles = tableStyles + self.colors = colors + + self.cell_styles = self.cellXfs._to_array() + self.alignments = self.cellXfs.alignments + self.protections = self.cellXfs.prots + self._normalise_numbers() + self.named_styles = self._merge_named_styles() + + + @classmethod + def from_tree(cls, node): + # strip all attribs + attrs = dict(node.attrib) + for k in attrs: + del node.attrib[k] + return super(Stylesheet, cls).from_tree(node) + + + def _merge_named_styles(self): + """ + Merge named style names "cellStyles" with their associated styles + "cellStyleXfs" + """ + named_styles = self.cellStyles.names + + for style in named_styles: + self._expand_named_style(style) + + return named_styles + + + def _expand_named_style(self, named_style): + """ + Bind format definitions for a named style from the associated style + record + """ + xf = self.cellStyleXfs[named_style.xfId] + named_style.font = self.fonts[xf.fontId] + named_style.fill = self.fills[xf.fillId] + named_style.border = self.borders[xf.borderId] + if xf.numFmtId < BUILTIN_FORMATS_MAX_SIZE: + formats = BUILTIN_FORMATS + else: + formats = self.custom_formats + if xf.numFmtId in formats: + named_style.number_format = formats[xf.numFmtId] + if xf.alignment: + named_style.alignment = xf.alignment + if xf.protection: + named_style.protection = xf.protection + + + def _split_named_styles(self, wb): + """ + Convert NamedStyle into separate CellStyle and Xf objects + """ + for style in wb._named_styles: + self.cellStyles.cellStyle.append(style.as_name()) + self.cellStyleXfs.xf.append(style.as_xf()) + + + @property + def custom_formats(self): + return dict([(n.numFmtId, n.formatCode) for n in self.numFmts.numFmt]) + + + def _normalise_numbers(self): + """ + Rebase custom numFmtIds with a floor of 164 when reading stylesheet + And index datetime formats + """ + date_formats = set() + timedelta_formats = set() + custom = self.custom_formats + formats = self.number_formats + for idx, style in enumerate(self.cell_styles): + if style.numFmtId in custom: + fmt = custom[style.numFmtId] + if fmt in BUILTIN_FORMATS_REVERSE: # remove builtins + style.numFmtId = BUILTIN_FORMATS_REVERSE[fmt] + else: + style.numFmtId = formats.add(fmt) + BUILTIN_FORMATS_MAX_SIZE + else: + fmt = builtin_format_code(style.numFmtId) + if is_date_format(fmt): + # Create an index of which styles refer to datetimes + date_formats.add(idx) + if is_timedelta_format(fmt): + # Create an index of which styles refer to timedeltas + timedelta_formats.add(idx) + self.date_formats = date_formats + self.timedelta_formats = timedelta_formats + + + def to_tree(self, tagname=None, idx=None, namespace=None): + tree = super(Stylesheet, self).to_tree(tagname, idx, namespace) + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + +def apply_stylesheet(archive, wb): + """ + Add styles to workbook if present + """ + try: + src = archive.read(ARC_STYLE) + except KeyError: + return wb + + node = fromstring(src) + stylesheet = Stylesheet.from_tree(node) + + if stylesheet.cell_styles: + + wb._borders = IndexedList(stylesheet.borders) + wb._fonts = IndexedList(stylesheet.fonts) + wb._fills = IndexedList(stylesheet.fills) + wb._differential_styles.styles = stylesheet.dxfs + wb._number_formats = stylesheet.number_formats + wb._protections = stylesheet.protections + wb._alignments = stylesheet.alignments + wb._table_styles = stylesheet.tableStyles + + # need to overwrite openpyxl defaults in case workbook has different ones + wb._cell_styles = stylesheet.cell_styles + wb._named_styles = stylesheet.named_styles + wb._date_formats = stylesheet.date_formats + wb._timedelta_formats = stylesheet.timedelta_formats + + for ns in wb._named_styles: + ns.bind(wb) + + else: + warn("Workbook contains no stylesheet, using openpyxl's defaults") + + if not wb._named_styles: + normal = styles['Normal'] + wb.add_named_style(normal) + warn("Workbook contains no default style, apply openpyxl's default") + + if stylesheet.colors is not None: + wb._colors = stylesheet.colors.index + + +def write_stylesheet(wb): + stylesheet = Stylesheet() + stylesheet.fonts = wb._fonts + stylesheet.fills = wb._fills + stylesheet.borders = wb._borders + stylesheet.dxfs = wb._differential_styles.styles + stylesheet.colors = ColorList(indexedColors=wb._colors) + + from .numbers import NumberFormat + fmts = [] + for idx, code in enumerate(wb._number_formats, BUILTIN_FORMATS_MAX_SIZE): + fmt = NumberFormat(idx, code) + fmts.append(fmt) + + stylesheet.numFmts.numFmt = fmts + + xfs = [] + for style in wb._cell_styles: + xf = CellStyle.from_array(style) + + if style.alignmentId: + xf.alignment = wb._alignments[style.alignmentId] + + if style.protectionId: + xf.protection = wb._protections[style.protectionId] + xfs.append(xf) + stylesheet.cellXfs = CellStyleList(xf=xfs) + + stylesheet._split_named_styles(wb) + stylesheet.tableStyles = wb._table_styles + + return stylesheet.to_tree() diff --git a/.venv/lib/python3.9/site-packages/openpyxl/styles/table.py b/.venv/lib/python3.9/site-packages/openpyxl/styles/table.py new file mode 100644 index 00000000..b0dd5403 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/styles/table.py @@ -0,0 +1,94 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Bool, + Set, + Integer, + NoneSet, + String, + Sequence +) + +from .colors import Color + + +class TableStyleElement(Serialisable): + + tagname = "tableStyleElement" + + type = Set(values=(['wholeTable', 'headerRow', 'totalRow', 'firstColumn', + 'lastColumn', 'firstRowStripe', 'secondRowStripe', 'firstColumnStripe', + 'secondColumnStripe', 'firstHeaderCell', 'lastHeaderCell', + 'firstTotalCell', 'lastTotalCell', 'firstSubtotalColumn', + 'secondSubtotalColumn', 'thirdSubtotalColumn', 'firstSubtotalRow', + 'secondSubtotalRow', 'thirdSubtotalRow', 'blankRow', + 'firstColumnSubheading', 'secondColumnSubheading', + 'thirdColumnSubheading', 'firstRowSubheading', 'secondRowSubheading', + 'thirdRowSubheading', 'pageFieldLabels', 'pageFieldValues'])) + size = Integer(allow_none=True) + dxfId = Integer(allow_none=True) + + def __init__(self, + type=None, + size=None, + dxfId=None, + ): + self.type = type + self.size = size + self.dxfId = dxfId + + +class TableStyle(Serialisable): + + tagname = "tableStyle" + + name = String() + pivot = Bool(allow_none=True) + table = Bool(allow_none=True) + count = Integer(allow_none=True) + tableStyleElement = Sequence(expected_type=TableStyleElement, allow_none=True) + + __elements__ = ('tableStyleElement',) + + def __init__(self, + name=None, + pivot=None, + table=None, + count=None, + tableStyleElement=(), + ): + self.name = name + self.pivot = pivot + self.table = table + self.count = count + self.tableStyleElement = tableStyleElement + + +class TableStyleList(Serialisable): + + tagname = "tableStyles" + + defaultTableStyle = String(allow_none=True) + defaultPivotStyle = String(allow_none=True) + tableStyle = Sequence(expected_type=TableStyle, allow_none=True) + + __elements__ = ('tableStyle',) + __attrs__ = ("count", "defaultTableStyle", "defaultPivotStyle") + + def __init__(self, + count=None, + defaultTableStyle="TableStyleMedium9", + defaultPivotStyle="PivotStyleLight16", + tableStyle=(), + ): + self.defaultTableStyle = defaultTableStyle + self.defaultPivotStyle = defaultPivotStyle + self.tableStyle = tableStyle + + + @property + def count(self): + return len(self.tableStyle) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/utils/__init__.py new file mode 100644 index 00000000..fdcd1faf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/utils/__init__.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2021 openpyxl + + +from .cell import ( + absolute_coordinate, + cols_from_range, + column_index_from_string, + coordinate_to_tuple, + get_column_letter, + get_column_interval, + quote_sheetname, + range_boundaries, + range_to_tuple, + rows_from_range, +) + +from .formulas import FORMULAE diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0e726b39896df6d7d84c7fe80b8c5ed9eb5c6ce GIT binary patch literal 524 zcmYk2y-p)B5XX0uO|tnSpftQf3SuNGBy_ry0-}He5z&~!a(0Kr!ej5*-jF;CPe9Gv zxTU&Rxaw?>AS3xF&woBOd^qeS!RBu-e3mB3dvtcK5uBgl*@!YJQ<-2&Q*6)%Hfa+x znqiB!uua?8p&iU=j$PWt9_^(uzajfLpo8#l&XY;@H_S*TJwN6Pt7Ywg6eD3RbC7(d>TgbK=< zOF#Nk>oD1_0G#3oe*a?}Tj*Mq+z7CK?^F3|Ju>=vceP3C^NX9i>-(!;=f5XIpA{gb ziN`X{h%BNN(T?auxRlanA8Bwl`r@*fJDW3lM4Yx^6Yx&@0)LCS+CbYAj5B;*v|l=@BXqY5nP_au>=%H z94oZIBaG1t!~-6lBOVHT7LEcQ4N*V-0bZz&>n;})Rw&11>Qeu0SA0${b^uDC5#|Wu z@hIf23pBwz@m!>ixUBh@&ZN3Z`D#KKWS9T~oeIL7Gjd!w;S)1!sMo0YVX(cQIVURVbeceo!ktsSQ@+O2r{ z{W*lwjJ3iQqR3$vROl?2hgBd^6@JAZVayy2(Ir0aNA>=;kvg_TE{q8wLEWZQYUwBi zm&gh=F|q#~F*CJwaI(6@^**Jkf_^EzM8DExA-H78OJ?|ykxP-hDQ7Oz$uON|nMwvr zp_hr0<0LO(2>RjRQDUVN4|0~Cu&J;~mJ6Mi?^RMbscbU#>p=fZe2|x>1rTod_L^10 zd-(L$KU*`ng8dgYz9S#vpY}c|2ef!nJ_i;4f literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/cell.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/cell.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7751e2e8e1c0c58fc9d716c0a38166e33798878e GIT binary patch literal 6326 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 I+iU&ozqnDZ+yDRo literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/datetime.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/datetime.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6abe4db3d5ab6597941f0cb33f4363ef6d4041e GIT binary patch literal 3802 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 Jy@s>+`~$$Elh*(M literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/formulas.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/formulas.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd76ad0c7c3d31542cf021b296d30e4026b8c218 GIT binary patch literal 3177 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 W=$eU5Qiii(So&Z3ilvz)v-A&UI+pnW literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/protection.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/protection.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2f37ac4a89d46682b2f4b8a1ebc4a01d41ec642 GIT binary patch literal 970 zcmYjQy>AmS6!(3kgs3ghjm>L@oTGLDghHXMgoH?|1_@MAMb6pDS@qe6?W9R{5kugw zV1WrS^MCLM+{)CMjRl?$TGf`{*S+8K`|#0?jTVA6`2LCZB7}Z=<>mq#+J$2`fv}hb zEc_6hAQlZ!yHTMu0Q1#a10ja92ggbvELa8(m57C_a37s^--gR@8M&a0RzVr|&@wJ# z7Ck}B2JrZ$*Ml-H8_!T5ErWNPW#GaRtpcx=0c&{fA_rb8*q<9;5L;unK8E)ZWYeTy zA(ZyE>9q za*^OynXvd=NQsqpwMUKPDm9DZ25zMFS<PSAJ>7wHs4UOzv*VqxNqoIt>JeLWz#k{`n z5?%+o^pEHA4iNF@J&+eaKn+3dx7VuMTupOsV2)J;6RaZ1RTaV1swm_0sxd7JZrW=n zTZN=TIX-v8t7%uwYbg?JUd3rfP1TrDIprtoFl-3Qh9lwZH)QU@eZNmh&Y7U(IW=tG z`09=pnbV34(oAbf5_mB)BE^Ul3mB=|Ngfgl&FK6egeAVjVlkIw>V&l9>Pcyp6pL!Z zcjY=g|BKiyAgC1wTfutJguPjFqQd9}{)2Dt~es`)tAY$|!r2m1vF{ROv` B8!rF= literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/units.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/utils/__pycache__/units.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..540ba18daad0c0b26dd2b59ed116eb9f08262d41 GIT binary patch literal 2575 zcma)8&2JM|5Z||62Pck`kQfpOg)LvL5yT`A+DI)V5JRZIKoUx|JuI$gu~)3U*4uSN zEYnNiujr*ca;wx!|Bv2$LaJ2B?XkC>`kS|2CrTW|TkXv3`|xjO-h8-}$&6?i8^8Tj zl92x7kG&&-k7c~UuOOt6@U)t+gkV|sbW5j%l_0}19D|b6nw6v>Ylx0mM<`{bXqZOc zXx1>LX%u{fGBgIBrg548AEl!-37(-T%7Ty4F**)DPSZ34K0znwB=}L9rBmRO^f{fT zGjD`7ML9C*Q%G4lOXsk5jLy>q@Z)rmJ_Da7eM8G#>OyNRCpsx|x7@(>>%|JsXxiLz zHwD&Bf>d6p@z<@&lg@0rw9vg(xu(V+x5|Py_Xf-Ck2`pS36M~0!y{REBCG8P;3Znw zdDE?ztLE3{7YpVkbK$BHogV*Bo+7`L&Xy zRq1Xy`N!MU(68q=%2nU@@^f2GeJk&|Kja(RVb!l+n_J2UZs=TTl*%tl6(`914X57N ze&ywxq3Z?tX59^gxyE)U&Fa{pZ$pnOfC_XFO_v$a1J?1HvThUt+cpc^W;M2~D2Fg7 z*mW6onD0uyo!@&T~EEBPo1cIFP82{64_S^xd?+kc)I%emuaDrT26`l;4g@{@Z(>_~-5h}*gFYJtvR;%^IKgo${brp8qxIYqf!^+; z2|4|dXuY)2Ta)uAk$c(*BH@*p1nSkJ=b~ivw}ko`HM-pKFk+QpX{LS}1ZR4dr&!MCR8 zO>!#IaS+u}P?zcz&k4HE|3*jBm0FSw7#MaF(OH$r>YUxzN0;XZ>C>;Kg9G3k&UE?W z4h?|LL<9>6gaJzL!b1VI#^Qe-hW?db<9?4RaR3KT76WE3V8|N zXd9pGVKbc(+kVjuo6@musoY><*_vD}++BJ4VAEcI^vqr>+`qTBX^q`p*(lg|9zC$1 z-Cx~Yv$F9XOg(*A?9EQavkwY)H|>Wj>-X*#cYkj4(c^e-EZ(?QSXnKst2Uzgq2xxT zx`O25tZJsJVv?(i%5s&Hl5;?E462J)H=%6Y=RBjQ;^Cddubj literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/bound_dictionary.py b/.venv/lib/python3.9/site-packages/openpyxl/utils/bound_dictionary.py new file mode 100644 index 00000000..443c2df9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/utils/bound_dictionary.py @@ -0,0 +1,26 @@ +# Copyright (c) 2010-2021 openpyxl + +from collections import defaultdict + + +class BoundDictionary(defaultdict): + """ + A default dictionary where elements are tightly coupled. + + The factory method is responsible for binding the parent object to the child. + + If a reference attribute is assigned then child objects will have the key assigned to this. + + Otherwise it's just a defaultdict. + """ + + def __init__(self, reference=None, *args, **kw): + self.reference = reference + super(BoundDictionary, self).__init__(*args, **kw) + + + def __getitem__(self, key): + value = super(BoundDictionary, self).__getitem__(key) + if self.reference is not None: + setattr(value, self.reference, key) + return value diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/cell.py b/.venv/lib/python3.9/site-packages/openpyxl/utils/cell.py new file mode 100644 index 00000000..10fb6d2a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/utils/cell.py @@ -0,0 +1,227 @@ +# Copyright (c) 2010-2021 openpyxl + +""" +Collection of utilities used within the package and also available for client code +""" +import re +from string import digits + +from .exceptions import CellCoordinatesException + +# constants +COORD_RE = re.compile(r'^[$]?([A-Za-z]{1,3})[$]?(\d+)$') +COL_RANGE = """[A-Z]{1,3}:[A-Z]{1,3}:""" +ROW_RANGE = r"""\d+:\d+:""" +RANGE_EXPR = r""" +[$]?(?P[A-Za-z]{1,3})? +[$]?(?P\d+)? +(:[$]?(?P[A-Za-z]{1,3})? +[$]?(?P\d+)?)? +""" +ABSOLUTE_RE = re.compile('^' + RANGE_EXPR +'$', re.VERBOSE) +SHEET_TITLE = r""" +(('(?P([^']|'')*)')|(?P[^'^ ^!]*))!""" +SHEETRANGE_RE = re.compile("""{0}(?P{1})(?=,?)""".format( + SHEET_TITLE, RANGE_EXPR), re.VERBOSE) + + +def get_column_interval(start, end): + """ + Given the start and end columns, return all the columns in the series. + + The start and end columns can be either column letters or 1-based + indexes. + """ + if isinstance(start, str): + start = column_index_from_string(start) + if isinstance(end, str): + end = column_index_from_string(end) + return [get_column_letter(x) for x in range(start, end + 1)] + + +def coordinate_from_string(coord_string): + """Convert a coordinate string like 'B12' to a tuple ('B', 12)""" + match = COORD_RE.match(coord_string) + if not match: + msg = f"Invalid cell coordinates ({coord_string})" + raise CellCoordinatesException(msg) + column, row = match.groups() + row = int(row) + if not row: + msg = f"There is no row 0 ({coord_string})" + raise CellCoordinatesException(msg) + return column, row + + +def absolute_coordinate(coord_string): + """Convert a coordinate to an absolute coordinate string (B12 -> $B$12)""" + m = ABSOLUTE_RE.match(coord_string) + if not m: + raise ValueError(f"{coord_string} is not a valid coordinate range") + + d = m.groupdict('') + for k, v in d.items(): + if v: + d[k] = f"${v}" + + if d['max_col'] or d['max_row']: + fmt = "{min_col}{min_row}:{max_col}{max_row}" + else: + fmt = "{min_col}{min_row}" + return fmt.format(**d) + + +def _get_column_letter(col_idx): + """Convert a column number into a column letter (3 -> 'C') + + Right shift the column col_idx by 26 to find column letters in reverse + order. These numbers are 1-based, and can be converted to ASCII + ordinals by adding 64. + + """ + # these indicies corrospond to A -> ZZZ and include all allowed + # columns + if not 1 <= col_idx <= 18278: + raise ValueError("Invalid column index {0}".format(col_idx)) + letters = [] + while col_idx > 0: + col_idx, remainder = divmod(col_idx, 26) + # check for exact division and borrow if needed + if remainder == 0: + remainder = 26 + col_idx -= 1 + letters.append(chr(remainder+64)) + return ''.join(reversed(letters)) + + +_COL_STRING_CACHE = {} +_STRING_COL_CACHE = {} +for i in range(1, 18279): + col = _get_column_letter(i) + _STRING_COL_CACHE[i] = col + _COL_STRING_CACHE[col] = i + + +def get_column_letter(idx,): + """Convert a column index into a column letter + (3 -> 'C') + """ + try: + return _STRING_COL_CACHE[idx] + except KeyError: + raise ValueError("Invalid column index {0}".format(idx)) + + +def column_index_from_string(str_col): + """Convert a column name into a numerical index + ('A' -> 1) + """ + # we use a function argument to get indexed name lookup + try: + return _COL_STRING_CACHE[str_col.upper()] + except KeyError: + raise ValueError("{0} is not a valid column name".format(str_col)) + + +def range_boundaries(range_string): + """ + Convert a range string into a tuple of boundaries: + (min_col, min_row, max_col, max_row) + Cell coordinates will be converted into a range with the cell at both end + """ + msg = "{0} is not a valid coordinate or range".format(range_string) + m = ABSOLUTE_RE.match(range_string) + if not m: + raise ValueError(msg) + + min_col, min_row, sep, max_col, max_row = m.groups() + + if sep: + cols = min_col, max_col + rows = min_row, max_row + + if not ( + all(cols + rows) or + all(cols) and not any(rows) or + all(rows) and not any(cols) + ): + raise ValueError(msg) + + if min_col is not None: + min_col = column_index_from_string(min_col) + + if min_row is not None: + min_row = int(min_row) + + if max_col is not None: + max_col = column_index_from_string(max_col) + else: + max_col = min_col + + if max_row is not None: + max_row = int(max_row) + else: + max_row = min_row + + return min_col, min_row, max_col, max_row + + +def rows_from_range(range_string): + """ + Get individual addresses for every cell in a range. + Yields one row at a time. + """ + min_col, min_row, max_col, max_row = range_boundaries(range_string) + rows = range(min_row, max_row + 1) + cols = [get_column_letter(col) for col in range(min_col, max_col + 1)] + for row in rows: + yield tuple('{0}{1}'.format(col, row) for col in cols) + + +def cols_from_range(range_string): + """ + Get individual addresses for every cell in a range. + Yields one row at a time. + """ + min_col, min_row, max_col, max_row = range_boundaries(range_string) + rows = range(min_row, max_row+1) + cols = (get_column_letter(col) for col in range(min_col, max_col+1)) + for col in cols: + yield tuple('{0}{1}'.format(col, row) for row in rows) + + +def coordinate_to_tuple(coordinate): + """ + Convert an Excel style coordinate to (row, colum) tuple + """ + for idx, c in enumerate(coordinate): + if c in digits: + break + col = coordinate[:idx].upper() + row = coordinate[idx:] + return int(row), _COL_STRING_CACHE[col] + + +def range_to_tuple(range_string): + """ + Convert a worksheet range to the sheetname and maximum and minimum + coordinate indices + """ + m = SHEETRANGE_RE.match(range_string) + if m is None: + raise ValueError("Value must be of the form sheetname!A1:E4") + sheetname = m.group("quoted") or m.group("notquoted") + cells = m.group("cells") + boundaries = range_boundaries(cells) + return sheetname, boundaries + + +def quote_sheetname(sheetname): + """ + Add quotes around sheetnames if they contain spaces. + """ + if "'" in sheetname: + sheetname = sheetname.replace("'", "''") + + sheetname = u"'{0}'".format(sheetname) + return sheetname diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/dataframe.py b/.venv/lib/python3.9/site-packages/openpyxl/utils/dataframe.py new file mode 100644 index 00000000..a48e5ff6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/utils/dataframe.py @@ -0,0 +1,92 @@ +# Copyright (c) 2010-2021 openpyxl + +from itertools import accumulate +import operator + +from openpyxl.compat.product import prod + + +def dataframe_to_rows(df, index=True, header=True): + """ + Convert a Pandas dataframe into something suitable for passing into a worksheet. + If index is True then the index will be included, starting one row below the header. + If header is True then column headers will be included starting one column to the right. + Formatting should be done by client code. + """ + import numpy + from pandas import Timestamp + blocks = df._data.blocks + ncols = sum(b.shape[0] for b in blocks) + data = [None] * ncols + + for b in blocks: + values = b.values + + if b.dtype.type == numpy.datetime64: + values = numpy.array([Timestamp(v) for v in values.ravel()]) + values = values.reshape(b.shape) + + result = values.tolist() + + for col_loc, col in zip(b.mgr_locs, result): + data[col_loc] = col + + if header: + if df.columns.nlevels > 1: + rows = expand_index(df.columns, header) + else: + rows = [list(df.columns.values)] + for row in rows: + n = [] + for v in row: + if isinstance(v, numpy.datetime64): + v = Timestamp(v) + n.append(v) + row = n + if index: + row = [None]*df.index.nlevels + row + yield row + + if index: + yield df.index.names + + expanded = ([v] for v in df.index) + if df.index.nlevels > 1: + expanded = expand_index(df.index) + + for idx, v in enumerate(expanded): + row = [data[j][idx] for j in range(ncols)] + if index: + row = v + row + yield row + + +def expand_index(index, header=False): + """ + Expand axis or column Multiindex + For columns use header = True + For axes use header = False (default) + """ + + shape = index.levshape + depth = prod(shape) + row = [None] * index.nlevels + lengths = [depth / size for size in accumulate(shape, operator.mul)] # child index lengths + columns = [ [] for l in index.names] # avoid copied list gotchas + + for idx, entry in enumerate(index): + row = [None] * index.nlevels + for level, v in enumerate(entry): + length = lengths[level] + if idx % length: + v = None + row[level] = v + if header: + columns[level].append(v) + + if not header: + yield row + + if header: + for row in columns: + yield row diff --git a/.venv/lib/python3.9/site-packages/openpyxl/utils/datetime.py b/.venv/lib/python3.9/site-packages/openpyxl/utils/datetime.py new file mode 100644 index 00000000..17be1a3c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/utils/datetime.py @@ -0,0 +1,140 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Manage Excel date weirdness.""" + +# Python stdlib imports +import datetime +from math import isnan +import re + + +# constants +MAC_EPOCH = datetime.datetime(1904, 1, 1) +WINDOWS_EPOCH = datetime.datetime(1899, 12, 30) +CALENDAR_WINDOWS_1900 = 2415018.5 # Julian date of WINDOWS_EPOCH +CALENDAR_MAC_1904 = 2416480.5 # Julian date of MAC_EPOCH +CALENDAR_WINDOWS_1900 = WINDOWS_EPOCH +CALENDAR_MAC_1904 = MAC_EPOCH +SECS_PER_DAY = 86400 + +ISO_FORMAT = '%Y-%m-%dT%H:%M:%SZ' +ISO_REGEX = re.compile(r''' +(?P(?P\d{4})-(?P\d{2})-(?P\d{2}))?T? +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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/function_group.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/function_group.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2985e9315e5e4dc1da4fdc4e73ad447039d79284 GIT binary patch literal 1186 zcmZ`&y>8S%5Z<+YKId{55(p`108zQ%T!aP*A*4ivNa%!Qs%=(2@8)8~-gS0;NiIqk z+B^YIkeat~OO;ok0A}{M=t5wn9naYF^UZwYY%u5%IO(^q*$bbL?`SNa0E~BV?JfvG z1T9HL8KtOurC0gPuL2fS9oC^_j|g7`Ux)}Kz4TcKc1MI@htBSbZc5_l1~QSDw*6F^ zA}b4^Fyu6WOyRgNm3T$q@Fd0T5S(s zC$?zh%R0-CvWc{buBEDHXJvA#&Cyuvqhx`UPZ0UAp0yF@g(@1(*AbzE?w@bnX>ho3 zi2-pJBM>v^ZIAOxi)o2^pY!8sR{r_YZL$ff1_^gvnE`6p*pld@8G^vc*a$@A1zzZr zU+(QaqHfpc6}a9dN_fJ*qO2oB*#$kg@>nD~Y(=b!NUQ*PJ<$X5`t8P@-aZz#VQY)A zI6m!Xprx0Ur9S0KDvp?UY9X(ZzOB?$JM`E^6V4c5*hSPis*WyS>5YqD}_xif!Z4`Uv zF}S%T@v2!v{Q(Mxegm}*3Re!XyYaii42YGbtfXoz=l^H%Aw*%g#pK9hkM_O5CzSj& z_-zzN?D=9LheF!i6m_GG9onV;*5J_C=D+-m!{zqLVa2s^nU_Z6)(e-ajo!>R^DRbM L^zGBlaJ~N1bYv_M;a8mQgS@}p2n61ORtN!?I~GNX%8w3}FDEt!?% z)E%8@kMu9#!f@-E|7K6jFvCm_oVdY-_cpeiG%17`AkPyluD)oNBid= zoF`R9`4ydmM-H4NI70>?3Q?KTQ*Bj6JHs-)tex%U>|8Hz=X;v1^>kbB8MYy>XIY_V z+NP@9R!ENIA1fqJ)kj&o2);&i@O9~1;2Wd>enI*rVz!l9@fpmh)Kp=$Dfb-ah0YeE zHBD&kh9Gn765nWZpR*Raj1jGEI3PWhQ9!) zVyi^4GeotsG)FQd`$V<#sijdJm%o!C8nhc}dm**V)GDTym0G3LDpP$2HvN+-N1CG3 zqq!<+kD1h(O|5Eb&860SYMn~0)AY<}yaiGu1!6u{?X$#yP+DTNkms~PnA zPULNQ%!`Ji7eCQ15tp_lM@33{x-@PwGDvEI* zmxV2%Fx-I9meZqRrt1;9;SGXl)uW88dtoH1ID>3!b{!5Ea7CgbAKpE>&1u;EBBp#O ztbV)&Mbll2XfG5qZkM{dhxc7|;wZo-xGCK5+$aV^%<=tEmKkdXz7PACJDVkR+lg7U z*`+;tA2I@3YgLhh5N&fTCyo!C&mra!rx2$RXAldBvxswumk=)_UO}8kTtHkzyoz`Y z@jBqC!rz3yKfiADDDj-e=ME<~;Th1z#;_X&exvPnq4XQ|0rdwB=500l!>AkhSL)Xq zp%>9heRu-S4hg*hi8x0 zSA9HIZMJpvsGY;fe0a*8jxal&UO-}ow$k`bggj42YJ_EzTHsrF`2uKnv{|?BKC~wE03*g=L z*8_(L9n?Zz(4o*cXqd1@Ml*>0DD`eq7C9iGaQ6nMu7~m=N;oVE5Tl@?!bJD3-v*5o zWjFBMm~-k!IGdQ8_;+Lg_lGZS2i&Erqczpa9BY=phq!{cig+LK0pdf%M~G{Pj}f0B zt|LB0+(1mSUM4(N0)aC`5mTmKqz4aA;Zi40eWCy*z6dwAHYYkzo9Ays6MqNsF5(hG zR-atHtZ=ypS^CFq1i#HrvYQtcavcW11PU=ESjd_zSq$2N0 z=W3Z`e_rTLzc1gzCUe5*7Jh}74bwx54XFb!WutRR{QslUhM z)%S2Ql;7a`IIkX74{UIfM0uG#HS5^Kks7~*6FoO?A)Wk%Y490z`3}wy-_~P2e@?Ui z;@GQIE87>wFVQ-oq07B~6!5SPAB5>QI>&h1)#LhcxlWn5GSy{n%Tzb;o`vJ?snZ88 oWn69q10@qqF6Tg<%E)4|3gi0zyYvQ5B_nI8=L=SLdS&|Z-%#=axBvhE literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/protection.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/protection.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d44fcefc1f27fac911d3bdc22eacfe2d177e725 GIT binary patch literal 4288 zcmbVQOLH5?5#ASo#gpJuFG_+Qwgg88oWx~0iIcKLnxazw0fJ`O=B9-sYVQ{|G^+8v8X{xtW3?< zI;k+5TD0_?PO88u1E*GS>Z~ytYrpwO-lNy( zI^6)7TOf0RUQ{x-*=>-yRP^5aR@>Lnx&CDVCU;{_mw*4!Y7Upf)kST1vR?4x1DqltuU2C?@7++*Y=BhF+U zyHEXy#xI3zVCV7Vm9r(j0``-eZ!fZS=yKOf7zgj+>nL*d+%j`A8z95serfz7N^ca> zUjzp+_mjhL&kdPeDfqkKRljqC(JXJ^2FW?z(2us_QIyE_T*>(Y5pK>K-+>QB7hVed8G4Js;8Qxr3Rp$nu>QbHKAvu7ND7y0IjqPSV}8^<@;HcB*SCE=N zW4?*Js|eQ+t|N31ZXn!5xCL;g@%Q1+zn}F(Mt!%x>vFoy{bSaDG(Jq?s6X%yA!77< z$1FPT2mbT^aGV^*(NB9n>kB_&9}Zpbg?qq6KOVAZIDQrM^Aqn63%~6R$I^BjKk^gD zxex0JR6tv{^)mc2MC$F|$(5&tujhB5m(L@$9nZvRCJ)kuk?=H9Lo^aF)ocA5T^UNJ zI@~Pdqyv82aXWd`>cmkeIrK#**SxpaH8|Q_t~fcbh;e5oe-DRB$I3rjZorvK#|{QU zM%8I%a0jK<1xwD)L10Lf0W`yc?VsFTu+4=pdJsm72Wf0u(9~?Hb_Cwi)lcp}W^lK0 zSFG67bh}FNdmYXWz}Nv3q7(0TQ2kz4=O4giXS&+rTYFJn_YBv&)+bgW|sLRvrInARA=gQ2!X-)US~h%9%MGKAFA9D2^)gF zbl!vc&Wud)dq6MJo&kTN{SS1VZl+)!sHO_O+lN-2Tv`-P#D8>NL%D$_KQDWPO3ye; zcfqkQe)IqImQQEArC5Htv;XQYKgNCZ&u>NrrXgxRahBT)v@RC=f!=a|-WX!4^Z$02 zpPvHab(SL!kCT% zq#KU&$B`RkBPuL$bT(N@+yi8CGy*9y_w;I&fUlAxDT<%zph}fCN??W{Ev$DOkrzX% zMj@&Qa$aU7k*ZjTs+o$rnI%`OZD%rU1-tJ`vv{U%l5jlJyC|q^VMB%lYb9f4A6bT5 z3`LbPOjcIal^sF`8J8*x$mYa09t(opKA6DyrwGbYXnQQP#B%`Kuno(w^%|s^+61RP za zH{}6NHr=_3w~9F|D{8BvsBfzxFGIU_me96gMgAqi4#Go(U4*X?evP2KK!NfC^s>J| z6D#$rvSOU=kO$K8`l#Y>~a4@7TT z1FM2kT^7>bj=d51OEO^I2tI$tYG;cMNZOxe0r6KR%&uZ@L%&3_G>7z literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/smart_tags.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/smart_tags.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..beb0e27923db8100cd54b50cef51fdacfebb3e0e GIT binary patch literal 1616 zcmZ`(OK;Oa5Z?7WX;RwK@)Ss1kc=Rf=mjK%DnJ4xWK~L#&|Zw|T~e!lq`OYrRK3BK zUx4@lJ@apT<-}h=;xV&ntn{_fPG;=go!M``&vCcgCNTOxzQx}yLVjW}Kj0bJfT=!! z;e^wS4Xbi?YmWH!KU$7gd7$=7$Nv z22Aw?j3A71!VFHCDJ*Vq^DAX`<2jAzLQPmDWgfSn9uE@dv8)F1uI!!c^D59MK~Zks|{vJu<->ob<^t4Sloj6mc%pC{DzCnbsCY zwKq^bE^rQ}~&L!talMdyWAOV;{hl(+m){=77N*ZZVg)xWirD za}PNPnY*x(QCv_-xpIH+yA-1p6CC+@&{iFZFsL~O@8Or-^HfgEn1FS}`rZ=fzN zY>ADB>oh2>X|7A@e+!m8#H^@+Dg76b{C_3B3H>&^HHPMy^J866 ze#gq;l7q@R9Pt1^6r%b{pxUa6I^$=8tep*Vb}q=<`JiAI0?pO}-PVJmT?`D{kmqIn zQefItSI$M<4q%cl)p zZ|s!FP=shUewExq16tWo3d11vPXi8*s!A0g0L@JtfHi)g%kd zBspL<$pdpq0hmuTU?I_gT2ch+i2*DoC7_X*z*1rXO`>#^eYM@Ngdv~F^F2Nm#lQ=_ zz}tfrQjZ7G&Su2Cy(r|4-*H*w`=W-ek0|4wo3>fl))`RByUtcDwB5&ENTMB~ji;it zBO4xjgb#!{sccfOKj1>=&elVRdyz1ncr51fGNS0X+}oybIkD)Bcy!0}Ic2vUPVcg4 zG=$~asEAxl{U>Y%BEwb@YlstwlZaD@(}**Ovxswu^N0(GivUreyZnC4*&?g?a%+`&R@rlZP}RnzTu+fdjN zjF)RiqT6n);0&P?&|Xg%y#D4;r6W(I>Lfh{&YA;fML#`FPhm>vXJqXRJxkBQ^XTAr=Sk7NK+41< z7Wmyo>30=T+Ze?>3LYQwI=ki`Y3s5Ygs_pdbvFtEObo(8buT2~20UW++_d_2NH(AY zUOTJ85RnBlw9TRKxO5QtgtD07>y|5^%{~C>%AEiL3+mMReuBz>Ykf%>4A4p{z;aRr zR+2ejHK_sTl6hb)sRQSe1z)80~C8axMnW?!>KUZrtPctgt5bD!uj|5oPr6I}r;;zH`fAV&doA z;iGs9swQ?%UuP^zSo?PCDP*&_vC1w%5ZFhEtB7lej}f0BK1F;{hb5`p%>zCx@c&|(hxiL?^*7!QsZSCz6h+gOil-CH}l6W&!NmoGrZ1;5|z)mfvPbl0W+B%jGwhlxtw*r8=7KOAuF3H`<--QL9b#yEkg LUN)+Q>b1WC;yT02 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/web.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/web.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ef1058852b8671e3faba23df1997d9d3cadaab6 GIT binary patch literal 2448 zcma)8OOG2x5T5sp$M(iaHi0BOf{1_tVtXBDvq31V0ELH;vx*}j`ZAiGZoHG7htt#E zcvrq{uKWUS+;ZmM^pz9804WDVRgdj;5=Ww~{-&y@yQ{k9t7=EhrmsO8{`q_K5Tw6Q zS#1_5yUDqT1HL3MXqZZRo zP2z)YQwMZM>2=Uu>VfVly+Lb3E%5&aOIo1I#*pzON|Q3$Pg&qf`_b{3QRxlYlL^aX zCY_<+Nj{SH{j`XLbbrVN8!;}euZtp;?x4uokck+#TWt;~yU@!Y0hmT~su6?g#AMc~ zMr`H)y3C;lHGk8Ir}WxV_gQ`ZrUB05adgt87C7$7*3WEzZ?X?AA3WH9%wj<W9>0>VXr=Ni8Rj~BmmGDefA^L@nWw>&vwoxS4&QRJOrd;njd(>`MP zQ729IJL6+c}eepZF@@k;li}`^wP%YTNJ@P1HVJQ7D%nCY5QEr6clS!1$-?$GzV`5H4#Shb7G?*Y8b z%sJ%j+Nn-}lfcHN+$o)R|*+bau!3N*a{V5sPQJK{5^ocr}5^t;9>rd}&?|`&*yEon0>CL6P@V2(q z+v!em3WzX$y46#oxO%JK#|d{~M;rx)J4aDEVda3|fat`?)e)7nJQmwUmwcAmR}uCx=@t@GBC31b*_55%99$0Rl3He}?cm!cBxW!kT=0 z0ON{XT76Mn7tfQ+FMYse;srHTVO0P@3NEZo0#z;F=pAyja#1DAnV+iqDretVWIu$p zU!mK0;(1a)k;UJJsh+E)wko>+a9lMAn&gAUozsRJG3Low6ufMgD}OaOl7MWko5!)# uRavbZ#lI3&x~t==Qz4C1Vk(m>F<+*&^uDaFqkFhriAx*$je6@^>(YN7UJ#)G literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/workbook.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/__pycache__/workbook.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3acf50a08ea395ade28a1d089f5f3897f021d2ce GIT binary patch literal 14717 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 z zx@4XglBimTcj}Y+i5z#f_V3UMO0NlaAkAxyCGBfL6HjW%DYstfudMOi65>=OnCj8) z-hm$U>YHBJrf)AZEA!0c2&(_;ymwOE6JbBfEdk^JULcxkkeg8?R~G1(+xzea6bDUC2#LQ51sjq^Y$I4 z&ydx_pP)IfKAxeO74d&GD{ebQ*4!6o9iT*=Ui9As(Xobg$~2JWe8D zTIurtJg|-=^QEnL#Gm{(B!1Tqb4367NSZVs{a=OXT{Zt-1$h>=@+c)2DLF=+(oN$3 zfM7X(T?Z5$ryeILIZ4SWBzPm&4&-gh6+26-Frh4OJ1K~;bV(`D%cR8oYe@3Y&C2|f ZmZsbU{ibzPj{l)HQ_?HBeM3d#e*rK$rS1R# literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/_writer.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/_writer.py new file mode 100644 index 00000000..458d3a37 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/_writer.py @@ -0,0 +1,191 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Write the workbook global settings to the archive.""" + +from copy import copy + +from openpyxl.utils import absolute_coordinate, quote_sheetname +from openpyxl.xml.constants import ( + ARC_APP, + ARC_CORE, + ARC_WORKBOOK, + PKG_REL_NS, + CUSTOMUI_NS, + ARC_ROOT_RELS, +) +from openpyxl.xml.functions import tostring, fromstring + +from openpyxl.packaging.relationship import Relationship, RelationshipList +from openpyxl.workbook.defined_name import DefinedName +from openpyxl.workbook.external_reference import ExternalReference +from openpyxl.packaging.workbook import ChildSheet, WorkbookPackage, PivotCache +from openpyxl.workbook.properties import WorkbookProperties +from openpyxl.utils.datetime import CALENDAR_MAC_1904 + + +def get_active_sheet(wb): + """ + Return the index of the active sheet. + If the sheet set to active is hidden return the next visible sheet or None + """ + visible_sheets = [idx for idx, sheet in enumerate(wb._sheets) if sheet.sheet_state == "visible"] + if not visible_sheets: + raise IndexError("At least one sheet must be visible") + + idx = wb._active_sheet_index + sheet = wb.active + if sheet and sheet.sheet_state == "visible": + return idx + + for idx in visible_sheets[idx:]: + wb.active = idx + return idx + + return None + + +class WorkbookWriter: + + def __init__(self, wb): + self.wb = wb + self.rels = RelationshipList() + self.package = WorkbookPackage() + self.package.workbookProtection = wb.security + self.package.calcPr = wb.calculation + + + def write_properties(self): + + props = WorkbookProperties() # needs a mapping to the workbook for preservation + if self.wb.code_name is not None: + props.codeName = self.wb.code_name + if self.wb.excel_base_date == CALENDAR_MAC_1904: + props.date1904 = True + self.package.workbookPr = props + + + def write_worksheets(self): + for idx, sheet in enumerate(self.wb._sheets, 1): + sheet_node = ChildSheet(name=sheet.title, sheetId=idx, id="rId{0}".format(idx)) + rel = Relationship(type=sheet._rel_type, Target=sheet.path) + self.rels.append(rel) + + if not sheet.sheet_state == 'visible': + if len(self.wb._sheets) == 1: + raise ValueError("The only worksheet of a workbook cannot be hidden") + sheet_node.state = sheet.sheet_state + self.package.sheets.append(sheet_node) + + + def write_refs(self): + for link in self.wb._external_links: + # need to match a counter with a workbook's relations + rId = len(self.wb.rels) + 1 + rel = Relationship(type=link._rel_type, Target=link.path) + self.rels.append(rel) + ext = ExternalReference(id=rel.id) + self.package.externalReferences.append(ext) + + + def write_names(self): + defined_names = copy(self.wb.defined_names) + + # Defined names -> autoFilter + for idx, sheet in enumerate(self.wb.worksheets): + auto_filter = sheet.auto_filter.ref + + if auto_filter: + name = DefinedName(name='_FilterDatabase', localSheetId=idx, hidden=True) + name.value = u"{0}!{1}".format(quote_sheetname(sheet.title), + absolute_coordinate(auto_filter) + ) + defined_names.append(name) + + # print titles + if sheet.print_titles: + name = DefinedName(name="Print_Titles", localSheetId=idx) + name.value = ",".join([u"{0}!{1}".format(quote_sheetname(sheet.title), r) + for r in sheet.print_titles.split(",")]) + defined_names.append(name) + + # print areas + if sheet.print_area: + name = DefinedName(name="Print_Area", localSheetId=idx) + name.value = ",".join([u"{0}!{1}".format(quote_sheetname(sheet.title), r) + for r in sheet.print_area]) + defined_names.append(name) + + self.package.definedNames = defined_names + + + def write_pivots(self): + pivot_caches = set() + for pivot in self.wb._pivots: + if pivot.cache not in pivot_caches: + pivot_caches.add(pivot.cache) + c = PivotCache(cacheId=pivot.cacheId) + self.package.pivotCaches.append(c) + rel = Relationship(Type=pivot.cache.rel_type, Target=pivot.cache.path) + self.rels.append(rel) + c.id = rel.id + #self.wb._pivots = [] # reset + + + def write_views(self): + active = get_active_sheet(self.wb) + if self.wb.views: + self.wb.views[0].activeTab = active + self.package.bookViews = self.wb.views + + + def write(self): + """Write the core workbook xml.""" + + self.write_properties() + self.write_worksheets() + self.write_names() + self.write_pivots() + self.write_views() + self.write_refs() + + return tostring(self.package.to_tree()) + + + def write_rels(self): + """Write the workbook relationships xml.""" + + styles = Relationship(type='styles', Target='styles.xml') + self.rels.append(styles) + + theme = Relationship(type='theme', Target='theme/theme1.xml') + self.rels.append(theme) + + if self.wb.vba_archive: + vba = Relationship(type='', Target='vbaProject.bin') + vba.Type ='http://schemas.microsoft.com/office/2006/relationships/vbaProject' + self.rels.append(vba) + + return tostring(self.rels.to_tree()) + + + def write_root_rels(self): + """Write the package relationships""" + + rels = RelationshipList() + + rel = Relationship(type="officeDocument", Target=ARC_WORKBOOK) + rels.append(rel) + rel = Relationship(Type=f"{PKG_REL_NS}/metadata/core-properties", Target=ARC_CORE) + rels.append(rel) + + rel = Relationship(type="extended-properties", Target=ARC_APP) + rels.append(rel) + + if self.wb.vba_archive is not None: + # See if there was a customUI relation and reuse it + xml = fromstring(self.wb.vba_archive.read(ARC_ROOT_RELS)) + root_rels = RelationshipList.from_tree(xml) + for rel in root_rels.find(CUSTOMUI_NS): + rels.append(rel) + + return tostring(rels.to_tree()) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py new file mode 100644 index 00000000..5035d465 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/child.py @@ -0,0 +1,166 @@ +# Copyright (c) 2010-2021 openpyxl + +import re +import warnings + +from openpyxl.worksheet.header_footer import HeaderFooter + +""" +Base class for worksheets, chartsheets, etc. that can be added to workbooks +""" + +INVALID_TITLE_REGEX = re.compile(r'[\\*?:/\[\]]') + + +def avoid_duplicate_name(names, value): + """ + Naive check to see whether name already exists. + If name does exist suggest a name using an incrementer + Duplicates are case insensitive + """ + # Check for an absolute match in which case we need to find an alternative + match = [n for n in names if n.lower() == value.lower()] + if match: + names = u",".join(names) + sheet_title_regex = re.compile(f'(?P{re.escape(value)})(?P<count>\\d*),?', re.I) + matches = sheet_title_regex.findall(names) + if matches: + # use name, but append with the next highest integer + counts = [int(idx) for (t, idx) in matches if idx.isdigit()] + highest = 0 + if counts: + highest = max(counts) + value = u"{0}{1}".format(value, highest + 1) + return value + + +class _WorkbookChild(object): + + __title = "" + _id = None + _path = "{0}" + _parent = None + _default_title = "Sheet" + + def __init__(self, parent=None, title=None): + self._parent = parent + self.title = title or self._default_title + self.HeaderFooter = HeaderFooter() + + + def __repr__(self): + return '<{0} "{1}">'.format(self.__class__.__name__, self.title) + + + @property + def parent(self): + return self._parent + + + @property + def encoding(self): + return self._parent.encoding + + + @property + def title(self): + return self.__title + + + @title.setter + def title(self, value): + """ + Set a sheet title, ensuring it is valid. + Limited to 31 characters, no special characters. + Duplicate titles will be incremented numerically + """ + if not self._parent: + return + + if not value: + raise ValueError("Title must have at least one character") + + if hasattr(value, "decode"): + if not isinstance(value, str): + try: + value = value.decode("ascii") + except UnicodeDecodeError: + raise ValueError("Worksheet titles must be str") + + m = INVALID_TITLE_REGEX.search(value) + if m: + msg = "Invalid character {0} found in sheet title".format(m.group(0)) + raise ValueError(msg) + + if self.title is not None and self.title != value: + value = avoid_duplicate_name(self.parent.sheetnames, value) + + if len(value) > 31: + warnings.warn("Title is more than 31 characters. Some applications may not be able to read the file") + + self.__title = value + + + @property + def oddHeader(self): + return self.HeaderFooter.oddHeader + + + @oddHeader.setter + def oddHeader(self, value): + self.HeaderFooter.oddHeader = value + + + @property + def oddFooter(self): + return self.HeaderFooter.oddFooter + + + @oddFooter.setter + def oddFooter(self, value): + self.HeaderFooter.oddFooter = value + + + @property + def evenHeader(self): + return self.HeaderFooter.evenHeader + + + @evenHeader.setter + def evenHeader(self, value): + self.HeaderFooter.evenHeader = value + + + @property + def evenFooter(self): + return self.HeaderFooter.evenFooter + + + @evenFooter.setter + def evenFooter(self, value): + self.HeaderFooter.evenFooter = value + + + @property + def firstHeader(self): + return self.HeaderFooter.firstHeader + + + @firstHeader.setter + def firstHeader(self, value): + self.HeaderFooter.firstHeader = value + + + @property + def firstFooter(self): + return self.HeaderFooter.firstFooter + + + @firstFooter.setter + def firstFooter(self, value): + self.HeaderFooter.firstFooter = value + + + @property + def path(self): + return self._path.format(self._id) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/defined_name.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/defined_name.py new file mode 100644 index 00000000..db5e8ade --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/defined_name.py @@ -0,0 +1,266 @@ +# Copyright (c) 2010-2021 openpyxl + +import re + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + String, + Float, + Integer, + Bool, + NoneSet, + Set, + Sequence, + Descriptor, +) +from openpyxl.compat import safe_string +from openpyxl.formula import Tokenizer +from openpyxl.utils.cell import ( + SHEETRANGE_RE, + SHEET_TITLE, +) + +RESERVED = frozenset(["Print_Area", "Print_Titles", "Criteria", + "_FilterDatabase", "Extract", "Consolidate_Area", + "Sheet_Title"]) + +_names = "|".join(RESERVED) +RESERVED_REGEX = re.compile(r"^_xlnm\.(?P<name>{0})".format(_names)) +COL_RANGE = r"""(?P<cols>[$]?[a-zA-Z]{1,3}:[$]?[a-zA-Z]{1,3})""" +COL_RANGE_RE = re.compile(COL_RANGE) +ROW_RANGE = r"""(?P<rows>[$]?\d+:[$]?\d+)""" +ROW_RANGE_RE = re.compile(ROW_RANGE) +TITLES_REGEX = re.compile("""{0}{1}?,?{2}?""".format(SHEET_TITLE, ROW_RANGE, COL_RANGE), + re.VERBOSE) + + +### utilities + +def _unpack_print_titles(defn): + """ + Extract rows and or columns from print titles so that they can be + assigned to a worksheet + """ + scanner = TITLES_REGEX.finditer(defn.value) + kw = dict((k, v) for match in scanner + for k, v in match.groupdict().items() if v) + + return kw.get('rows'), kw.get('cols') + + +def _unpack_print_area(defn): + """ + Extract print area + """ + new = [] + for m in SHEETRANGE_RE.finditer(defn.value): # can be multiple + coord = m.group("cells") + if coord: + new.append(coord) + return new + + +class DefinedName(Serialisable): + + tagname = "definedName" + + name = String() # unique per workbook/worksheet + comment = String(allow_none=True) + customMenu = String(allow_none=True) + description = String(allow_none=True) + help = String(allow_none=True) + statusBar = String(allow_none=True) + localSheetId = Integer(allow_none=True) + hidden = Bool(allow_none=True) + function = Bool(allow_none=True) + vbProcedure = Bool(allow_none=True) + xlm = Bool(allow_none=True) + functionGroupId = Integer(allow_none=True) + shortcutKey = String(allow_none=True) + publishToServer = Bool(allow_none=True) + workbookParameter = Bool(allow_none=True) + attr_text = Descriptor() + value = Alias("attr_text") + + + def __init__(self, + name=None, + comment=None, + customMenu=None, + description=None, + help=None, + statusBar=None, + localSheetId=None, + hidden=None, + function=None, + vbProcedure=None, + xlm=None, + functionGroupId=None, + shortcutKey=None, + publishToServer=None, + workbookParameter=None, + attr_text=None + ): + self.name = name + self.comment = comment + self.customMenu = customMenu + self.description = description + self.help = help + self.statusBar = statusBar + self.localSheetId = localSheetId + self.hidden = hidden + self.function = function + self.vbProcedure = vbProcedure + self.xlm = xlm + self.functionGroupId = functionGroupId + self.shortcutKey = shortcutKey + self.publishToServer = publishToServer + self.workbookParameter = workbookParameter + self.attr_text = attr_text + + + @property + def type(self): + tok = Tokenizer("=" + self.value) + parsed = tok.items[0] + if parsed.type == "OPERAND": + return parsed.subtype + return parsed.type + + + @property + def destinations(self): + if self.type == "RANGE": + tok = Tokenizer("=" + self.value) + for part in tok.items: + if part.subtype == "RANGE": + m = SHEETRANGE_RE.match(part.value) + sheetname = m.group('notquoted') or m.group('quoted') + yield sheetname, m.group('cells') + + + @property + def is_reserved(self): + m = RESERVED_REGEX.match(self.name) + if m: + return m.group("name") + + + @property + def is_external(self): + return re.compile(r"^\[\d+\].*").match(self.value) is not None + + + def __iter__(self): + for key in self.__attrs__: + if key == "attr_text": + continue + v = getattr(self, key) + if v is not None: + if v in RESERVED: + v = "_xlnm." + v + yield key, safe_string(v) + + +class DefinedNameList(Serialisable): + + tagname = "definedNames" + + definedName = Sequence(expected_type=DefinedName) + + + def __init__(self, definedName=()): + self.definedName = definedName + + + def _cleanup(self): + """ + Strip invalid definitions and remove special hidden ones + """ + valid_names = [] + for n in self.definedName: + if n.name in ("_xlnm.Print_Titles", "_xlnm.Print_Area") and n.localSheetId is None: + continue + elif n.name == "_xlnm._FilterDatabase": + continue + valid_names.append(n) + self.definedName = valid_names + + + def _duplicate(self, defn): + """ + Check for whether DefinedName with the same name and scope already + exists + """ + for d in self.definedName: + if d.name == defn.name and d.localSheetId == defn.localSheetId: + return True + + + def append(self, defn): + if not isinstance(defn, DefinedName): + raise TypeError("""You can only append DefinedNames""") + if self._duplicate(defn): + raise ValueError("""DefinedName with the same name and scope already exists""") + names = self.definedName[:] + names.append(defn) + self.definedName = names + + + def __len__(self): + return len(self.definedName) + + + def __contains__(self, name): + """ + See if a globaly defined name exists + """ + for defn in self.definedName: + if defn.name == name and defn.localSheetId is None: + return True + + + def __getitem__(self, name): + """ + Get globally defined name + """ + defn = self.get(name) + if not defn: + raise KeyError("No definition called {0}".format(name)) + return defn + + + def get(self, name, scope=None): + """ + Get the name assigned to a specicic sheet or global + """ + for defn in self.definedName: + if defn.name == name and defn.localSheetId == scope: + return defn + + + def __delitem__(self, name): + """ + Delete a globally defined name + """ + if not self.delete(name): + raise KeyError("No globally defined name {0}".format(name)) + + + def delete(self, name, scope=None): + """ + Delete a name assigned to a specific or global + """ + for idx, defn in enumerate(self.definedName): + if defn.name == name and defn.localSheetId == scope: + del self.definedName[idx] + return True + + + def localnames(self, scope): + """ + Provide a list of all names for a particular worksheet + """ + return [defn.name for defn in self.definedName if defn.localSheetId == scope] diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__init__.py new file mode 100644 index 00000000..4f7cabbe --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) 2010-2021 openpyxl + +from .external import ExternalLink diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf3f31e21a00f34f0af0417769af824a2d8ad13f GIT binary patch literal 244 zcmYe~<>g`kg5b@w6WxLIV-N=!FabFZKwK;UBvKes7;_kM8KW2(8B&;n88n$+G6ID) z8E^5pR+OX`<t66$Waef2X);A|q(TLXKqjnYC}IXuVB%Mfer{??W}?1FVo{1qQD#}H zen4eLNq(MwaB@a|evZCgS!!OHeokhRenDkPMt+{Lo~3?qW=X1UL1J=tVtQ(^ettn} zUO{C=j(&N5QFc;(ezrc$w)h;N{rd6onR%Hd@$q^EmA5!-a`RJ4b5iX<4)_ej3;?-R BLuCK} literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__pycache__/external.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/__pycache__/external.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d51ff26d70caff99bb7447aa2cc673e99115dc6e GIT binary patch literal 5173 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/external.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/external.py new file mode 100644 index 00000000..0e2f1d50 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_link/external.py @@ -0,0 +1,190 @@ +# Copyright (c) 2010-2021 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Bool, + Integer, + NoneSet, + Sequence, +) +from openpyxl.descriptors.excel import Relation, ExtensionList +from openpyxl.descriptors.nested import NestedText +from openpyxl.descriptors.sequence import NestedSequence, ValueSequence + +from openpyxl.packaging.relationship import ( + Relationship, + get_rels_path, + get_dependents + ) +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import fromstring + + +"""Manage links to external Workbooks""" + + +class ExternalCell(Serialisable): + + r = String() + t = NoneSet(values=(['b', 'd', 'n', 'e', 's', 'str', 'inlineStr'])) + vm = Integer(allow_none=True) + v = NestedText(allow_none=True, expected_type=str) + + def __init__(self, + r=None, + t=None, + vm=None, + v=None, + ): + self.r = r + self.t = t + self.vm = vm + self.v = v + + +class ExternalRow(Serialisable): + + r = Integer() + cell = Sequence(expected_type=ExternalCell) + + __elements__ = ('cell',) + + def __init__(self, + r=(), + cell=None, + ): + self.r = r + self.cell = cell + + +class ExternalSheetData(Serialisable): + + sheetId = Integer() + refreshError = Bool(allow_none=True) + row = Sequence(expected_type=ExternalRow) + + __elements__ = ('row',) + + def __init__(self, + sheetId=None, + refreshError=None, + row=(), + ): + self.sheetId = sheetId + self.refreshError = refreshError + self.row = row + + +class ExternalSheetDataSet(Serialisable): + + sheetData = Sequence(expected_type=ExternalSheetData, ) + + __elements__ = ('sheetData',) + + def __init__(self, + sheetData=None, + ): + self.sheetData = sheetData + + +class ExternalSheetNames(Serialisable): + + sheetName = ValueSequence(expected_type=str) + + __elements__ = ('sheetName',) + + def __init__(self, + sheetName=(), + ): + self.sheetName = sheetName + + +class ExternalDefinedName(Serialisable): + + tagname = "definedName" + + name = String() + refersTo = String(allow_none=True) + sheetId = Integer(allow_none=True) + + def __init__(self, + name=None, + refersTo=None, + sheetId=None, + ): + self.name = name + self.refersTo = refersTo + self.sheetId = sheetId + + +class ExternalBook(Serialisable): + + tagname = "externalBook" + + sheetNames = Typed(expected_type=ExternalSheetNames, allow_none=True) + definedNames = NestedSequence(expected_type=ExternalDefinedName) + sheetDataSet = Typed(expected_type=ExternalSheetDataSet, allow_none=True) + id = Relation() + + __elements__ = ('sheetNames', 'definedNames', 'sheetDataSet') + + def __init__(self, + sheetNames=None, + definedNames=(), + sheetDataSet=None, + id=None, + ): + self.sheetNames = sheetNames + self.definedNames = definedNames + self.sheetDataSet = sheetDataSet + self.id = id + + +class ExternalLink(Serialisable): + + tagname = "externalLink" + + _id = None + _path = "/xl/externalLinks/externalLink{0}.xml" + _rel_type = "externalLink" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml" + + externalBook = Typed(expected_type=ExternalBook, allow_none=True) + file_link = Typed(expected_type=Relationship, allow_none=True) # link to external file + + __elements__ = ('externalBook', ) + + def __init__(self, + externalBook=None, + ddeLink=None, + oleLink=None, + extLst=None, + ): + self.externalBook = externalBook + # ignore other items for the moment. + + + def to_tree(self): + node = super(ExternalLink, self).to_tree() + node.set("xmlns", SHEET_MAIN_NS) + return node + + + @property + def path(self): + return self._path.format(self._id) + + +def read_external_link(archive, book_path): + src = archive.read(book_path) + node = fromstring(src) + book = ExternalLink.from_tree(node) + + link_path = get_rels_path(book_path) + deps = get_dependents(archive, link_path) + book.file_link = deps.Relationship[0] + + return book diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_reference.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_reference.py new file mode 100644 index 00000000..15d27198 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/external_reference.py @@ -0,0 +1,18 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence +) +from openpyxl.descriptors.excel import ( + Relation, +) + +class ExternalReference(Serialisable): + + tagname = "externalReference" + + id = Relation() + + def __init__(self, id): + self.id = id diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/function_group.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/function_group.py new file mode 100644 index 00000000..1a22aa8c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/function_group.py @@ -0,0 +1,36 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + String, + Integer, +) + +class FunctionGroup(Serialisable): + + tagname = "functionGroup" + + name = String() + + def __init__(self, + name=None, + ): + self.name = name + + +class FunctionGroupList(Serialisable): + + tagname = "functionGroups" + + builtInGroupCount = Integer(allow_none=True) + functionGroup = Sequence(expected_type=FunctionGroup, allow_none=True) + + __elements__ = ('functionGroup',) + + def __init__(self, + builtInGroupCount=16, + functionGroup=(), + ): + self.builtInGroupCount = builtInGroupCount + self.functionGroup = functionGroup diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/properties.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/properties.py new file mode 100644 index 00000000..f904f619 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/properties.py @@ -0,0 +1,151 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + String, + Float, + Integer, + Bool, + NoneSet, + Set, +) + +from openpyxl.descriptors.excel import Guid + + +class WorkbookProperties(Serialisable): + + tagname = "workbookPr" + + date1904 = Bool(allow_none=True) + dateCompatibility = Bool(allow_none=True) + showObjects = NoneSet(values=(['all', 'placeholders'])) + showBorderUnselectedTables = Bool(allow_none=True) + filterPrivacy = Bool(allow_none=True) + promptedSolutions = Bool(allow_none=True) + showInkAnnotation = Bool(allow_none=True) + backupFile = Bool(allow_none=True) + saveExternalLinkValues = Bool(allow_none=True) + updateLinks = NoneSet(values=(['userSet', 'never', 'always'])) + codeName = String(allow_none=True) + hidePivotFieldList = Bool(allow_none=True) + showPivotChartFilter = Bool(allow_none=True) + allowRefreshQuery = Bool(allow_none=True) + publishItems = Bool(allow_none=True) + checkCompatibility = Bool(allow_none=True) + autoCompressPictures = Bool(allow_none=True) + refreshAllConnections = Bool(allow_none=True) + defaultThemeVersion = Integer(allow_none=True) + + def __init__(self, + date1904=None, + dateCompatibility=None, + showObjects=None, + showBorderUnselectedTables=None, + filterPrivacy=None, + promptedSolutions=None, + showInkAnnotation=None, + backupFile=None, + saveExternalLinkValues=None, + updateLinks=None, + codeName=None, + hidePivotFieldList=None, + showPivotChartFilter=None, + allowRefreshQuery=None, + publishItems=None, + checkCompatibility=None, + autoCompressPictures=None, + refreshAllConnections=None, + defaultThemeVersion=None, + ): + self.date1904 = date1904 + self.dateCompatibility = dateCompatibility + self.showObjects = showObjects + self.showBorderUnselectedTables = showBorderUnselectedTables + self.filterPrivacy = filterPrivacy + self.promptedSolutions = promptedSolutions + self.showInkAnnotation = showInkAnnotation + self.backupFile = backupFile + self.saveExternalLinkValues = saveExternalLinkValues + self.updateLinks = updateLinks + self.codeName = codeName + self.hidePivotFieldList = hidePivotFieldList + self.showPivotChartFilter = showPivotChartFilter + self.allowRefreshQuery = allowRefreshQuery + self.publishItems = publishItems + self.checkCompatibility = checkCompatibility + self.autoCompressPictures = autoCompressPictures + self.refreshAllConnections = refreshAllConnections + self.defaultThemeVersion = defaultThemeVersion + + +class CalcProperties(Serialisable): + + tagname = "calcPr" + + calcId = Integer() + calcMode = NoneSet(values=(['manual', 'auto', 'autoNoTable'])) + fullCalcOnLoad = Bool(allow_none=True) + refMode = NoneSet(values=(['A1', 'R1C1'])) + iterate = Bool(allow_none=True) + iterateCount = Integer(allow_none=True) + iterateDelta = Float(allow_none=True) + fullPrecision = Bool(allow_none=True) + calcCompleted = Bool(allow_none=True) + calcOnSave = Bool(allow_none=True) + concurrentCalc = Bool(allow_none=True) + concurrentManualCount = Integer(allow_none=True) + forceFullCalc = Bool(allow_none=True) + + def __init__(self, + calcId=124519, + calcMode=None, + fullCalcOnLoad=True, + refMode=None, + iterate=None, + iterateCount=None, + iterateDelta=None, + fullPrecision=None, + calcCompleted=None, + calcOnSave=None, + concurrentCalc=None, + concurrentManualCount=None, + forceFullCalc=None, + ): + self.calcId = calcId + self.calcMode = calcMode + self.fullCalcOnLoad = fullCalcOnLoad + self.refMode = refMode + self.iterate = iterate + self.iterateCount = iterateCount + self.iterateDelta = iterateDelta + self.fullPrecision = fullPrecision + self.calcCompleted = calcCompleted + self.calcOnSave = calcOnSave + self.concurrentCalc = concurrentCalc + self.concurrentManualCount = concurrentManualCount + self.forceFullCalc = forceFullCalc + + +class FileVersion(Serialisable): + + tagname = "fileVersion" + + appName = String(allow_none=True) + lastEdited = String(allow_none=True) + lowestEdited = String(allow_none=True) + rupBuild = String(allow_none=True) + codeName = Guid(allow_none=True) + + def __init__(self, + appName=None, + lastEdited=None, + lowestEdited=None, + rupBuild=None, + codeName=None, + ): + self.appName = appName + self.lastEdited = lastEdited + self.lowestEdited = lowestEdited + self.rupBuild = rupBuild + self.codeName = codeName diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/protection.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/protection.py new file mode 100644 index 00000000..6123238e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/protection.py @@ -0,0 +1,163 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + String, + Float, + Integer, + Bool, + NoneSet, + Set, +) +from openpyxl.descriptors.excel import ( + ExtensionList, + HexBinary, + Guid, + Relation, + Base64Binary, +) +from openpyxl.utils.protection import hash_password + + +class WorkbookProtection(Serialisable): + + _workbook_password, _revisions_password = None, None + + tagname = "workbookPr" + + workbook_password = Alias("workbookPassword") + workbookPasswordCharacterSet = String(allow_none=True) + revision_password = Alias("revisionsPassword") + revisionsPasswordCharacterSet = String(allow_none=True) + lockStructure = Bool(allow_none=True) + lock_structure = Alias("lockStructure") + lockWindows = Bool(allow_none=True) + lock_windows = Alias("lockWindows") + lockRevision = Bool(allow_none=True) + lock_revision = Alias("lockRevision") + revisionsAlgorithmName = String(allow_none=True) + revisionsHashValue = Base64Binary(allow_none=True) + revisionsSaltValue = Base64Binary(allow_none=True) + revisionsSpinCount = Integer(allow_none=True) + workbookAlgorithmName = String(allow_none=True) + workbookHashValue = Base64Binary(allow_none=True) + workbookSaltValue = Base64Binary(allow_none=True) + workbookSpinCount = Integer(allow_none=True) + + __attrs__ = ('workbookPassword', 'workbookPasswordCharacterSet', 'revisionsPassword', + 'revisionsPasswordCharacterSet', 'lockStructure', 'lockWindows', 'lockRevision', + 'revisionsAlgorithmName', 'revisionsHashValue', 'revisionsSaltValue', + 'revisionsSpinCount', 'workbookAlgorithmName', 'workbookHashValue', + 'workbookSaltValue', 'workbookSpinCount') + + def __init__(self, + workbookPassword=None, + workbookPasswordCharacterSet=None, + revisionsPassword=None, + revisionsPasswordCharacterSet=None, + lockStructure=None, + lockWindows=None, + lockRevision=None, + revisionsAlgorithmName=None, + revisionsHashValue=None, + revisionsSaltValue=None, + revisionsSpinCount=None, + workbookAlgorithmName=None, + workbookHashValue=None, + workbookSaltValue=None, + workbookSpinCount=None, + ): + if workbookPassword is not None: + self.workbookPassword = workbookPassword + self.workbookPasswordCharacterSet = workbookPasswordCharacterSet + if revisionsPassword is not None: + self.revisionsPassword = revisionsPassword + self.revisionsPasswordCharacterSet = revisionsPasswordCharacterSet + self.lockStructure = lockStructure + self.lockWindows = lockWindows + self.lockRevision = lockRevision + self.revisionsAlgorithmName = revisionsAlgorithmName + self.revisionsHashValue = revisionsHashValue + self.revisionsSaltValue = revisionsSaltValue + self.revisionsSpinCount = revisionsSpinCount + self.workbookAlgorithmName = workbookAlgorithmName + self.workbookHashValue = workbookHashValue + self.workbookSaltValue = workbookSaltValue + self.workbookSpinCount = workbookSpinCount + + def set_workbook_password(self, value='', already_hashed=False): + """Set a password on this workbook.""" + if not already_hashed: + value = hash_password(value) + self._workbook_password = value + + @property + def workbookPassword(self): + """Return the workbook password value, regardless of hash.""" + return self._workbook_password + + @workbookPassword.setter + def workbookPassword(self, value): + """Set a workbook password directly, forcing a hash step.""" + self.set_workbook_password(value) + + def set_revisions_password(self, value='', already_hashed=False): + """Set a revision password on this workbook.""" + if not already_hashed: + value = hash_password(value) + self._revisions_password = value + + @property + def revisionsPassword(self): + """Return the revisions password value, regardless of hash.""" + return self._revisions_password + + @revisionsPassword.setter + def revisionsPassword(self, value): + """Set a revisions password directly, forcing a hash step.""" + self.set_revisions_password(value) + + @classmethod + def from_tree(cls, node): + """Don't hash passwords when deserialising from XML""" + self = super(WorkbookProtection, cls).from_tree(node) + if self.workbookPassword: + self.set_workbook_password(node.get('workbookPassword'), already_hashed=True) + if self.revisionsPassword: + self.set_revisions_password(node.get('revisionsPassword'), already_hashed=True) + return self + +# Backwards compatibility +DocumentSecurity = WorkbookProtection + + +class FileSharing(Serialisable): + + tagname = "fileSharing" + + readOnlyRecommended = Bool(allow_none=True) + userName = String(allow_none=True) + reservationPassword = HexBinary(allow_none=True) + algorithmName = String(allow_none=True) + hashValue = Base64Binary(allow_none=True) + saltValue = Base64Binary(allow_none=True) + spinCount = Integer(allow_none=True) + + def __init__(self, + readOnlyRecommended=None, + userName=None, + reservationPassword=None, + algorithmName=None, + hashValue=None, + saltValue=None, + spinCount=None, + ): + self.readOnlyRecommended = readOnlyRecommended + self.userName = userName + self.reservationPassword = reservationPassword + self.algorithmName = algorithmName + self.hashValue = hashValue + self.saltValue = saltValue + self.spinCount = spinCount diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/smart_tags.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/smart_tags.py new file mode 100644 index 00000000..3da0aef9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/smart_tags.py @@ -0,0 +1,56 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + String, + Bool, + NoneSet, + +) + +class SmartTag(Serialisable): + + tagname = "smartTagType" + + namespaceUri = String(allow_none=True) + name = String(allow_none=True) + url = String(allow_none=True) + + def __init__(self, + namespaceUri=None, + name=None, + url=None, + ): + self.namespaceUri = namespaceUri + self.name = name + self.url = url + + +class SmartTagList(Serialisable): + + tagname = "smartTagTypes" + + smartTagType = Sequence(expected_type=SmartTag, allow_none=True) + + __elements__ = ('smartTagType',) + + def __init__(self, + smartTagType=(), + ): + self.smartTagType = smartTagType + + +class SmartTagProperties(Serialisable): + + tagname = "smartTagPr" + + embed = Bool(allow_none=True) + show = NoneSet(values=(['all', 'noIndicator'])) + + def __init__(self, + embed=None, + show=None, + ): + self.embed = embed + self.show = show diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/views.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/views.py new file mode 100644 index 00000000..dcf4c673 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/views.py @@ -0,0 +1,155 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + String, + Float, + Integer, + Bool, + NoneSet, + Set, +) +from openpyxl.descriptors.excel import ( + ExtensionList, + Guid, +) + + +class BookView(Serialisable): + + tagname = "workbookView" + + visibility = NoneSet(values=(['visible', 'hidden', 'veryHidden'])) + minimized = Bool(allow_none=True) + showHorizontalScroll = Bool(allow_none=True) + showVerticalScroll = Bool(allow_none=True) + showSheetTabs = Bool(allow_none=True) + xWindow = Integer(allow_none=True) + yWindow = Integer(allow_none=True) + windowWidth = Integer(allow_none=True) + windowHeight = Integer(allow_none=True) + tabRatio = Integer(allow_none=True) + firstSheet = Integer(allow_none=True) + activeTab = Integer(allow_none=True) + autoFilterDateGrouping = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + visibility="visible", + minimized=False, + showHorizontalScroll=True, + showVerticalScroll=True, + showSheetTabs=True, + xWindow=None, + yWindow=None, + windowWidth=None, + windowHeight=None, + tabRatio=600, + firstSheet=0, + activeTab=0, + autoFilterDateGrouping=True, + extLst=None, + ): + self.visibility = visibility + self.minimized = minimized + self.showHorizontalScroll = showHorizontalScroll + self.showVerticalScroll = showVerticalScroll + self.showSheetTabs = showSheetTabs + self.xWindow = xWindow + self.yWindow = yWindow + self.windowWidth = windowWidth + self.windowHeight = windowHeight + self.tabRatio = tabRatio + self.firstSheet = firstSheet + self.activeTab = activeTab + self.autoFilterDateGrouping = autoFilterDateGrouping + + +class CustomWorkbookView(Serialisable): + + tagname = "customWorkbookView" + + name = String() + guid = Guid() + autoUpdate = Bool(allow_none=True) + mergeInterval = Integer(allow_none=True) + changesSavedWin = Bool(allow_none=True) + onlySync = Bool(allow_none=True) + personalView = Bool(allow_none=True) + includePrintSettings = Bool(allow_none=True) + includeHiddenRowCol = Bool(allow_none=True) + maximized = Bool(allow_none=True) + minimized = Bool(allow_none=True) + showHorizontalScroll = Bool(allow_none=True) + showVerticalScroll = Bool(allow_none=True) + showSheetTabs = Bool(allow_none=True) + xWindow = Integer(allow_none=True) + yWindow = Integer(allow_none=True) + windowWidth = Integer() + windowHeight = Integer() + tabRatio = Integer(allow_none=True) + activeSheetId = Integer() + showFormulaBar = Bool(allow_none=True) + showStatusbar = Bool(allow_none=True) + showComments = NoneSet(values=(['commNone', 'commIndicator', + 'commIndAndComment'])) + showObjects = NoneSet(values=(['all', 'placeholders'])) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + name=None, + guid=None, + autoUpdate=None, + mergeInterval=None, + changesSavedWin=None, + onlySync=None, + personalView=None, + includePrintSettings=None, + includeHiddenRowCol=None, + maximized=None, + minimized=None, + showHorizontalScroll=None, + showVerticalScroll=None, + showSheetTabs=None, + xWindow=None, + yWindow=None, + windowWidth=None, + windowHeight=None, + tabRatio=None, + activeSheetId=None, + showFormulaBar=None, + showStatusbar=None, + showComments="commIndicator", + showObjects="all", + extLst=None, + ): + self.name = name + self.guid = guid + self.autoUpdate = autoUpdate + self.mergeInterval = mergeInterval + self.changesSavedWin = changesSavedWin + self.onlySync = onlySync + self.personalView = personalView + self.includePrintSettings = includePrintSettings + self.includeHiddenRowCol = includeHiddenRowCol + self.maximized = maximized + self.minimized = minimized + self.showHorizontalScroll = showHorizontalScroll + self.showVerticalScroll = showVerticalScroll + self.showSheetTabs = showSheetTabs + self.xWindow = xWindow + self.yWindow = yWindow + self.windowWidth = windowWidth + self.windowHeight = windowHeight + self.tabRatio = tabRatio + self.activeSheetId = activeSheetId + self.showFormulaBar = showFormulaBar + self.showStatusbar = showStatusbar + self.showComments = showComments + self.showObjects = showObjects diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/web.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/web.py new file mode 100644 index 00000000..0bb08b84 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/web.py @@ -0,0 +1,98 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + String, + Float, + Integer, + Bool, + NoneSet, +) + + +class WebPublishObject(Serialisable): + + tagname = "webPublishingObject" + + id = Integer() + divId = String() + sourceObject = String(allow_none=True) + destinationFile = String() + title = String(allow_none=True) + autoRepublish = Bool(allow_none=True) + + def __init__(self, + id=None, + divId=None, + sourceObject=None, + destinationFile=None, + title=None, + autoRepublish=None, + ): + self.id = id + self.divId = divId + self.sourceObject = sourceObject + self.destinationFile = destinationFile + self.title = title + self.autoRepublish = autoRepublish + + +class WebPublishObjectList(Serialisable): + + tagname ="webPublishingObjects" + + count = Integer(allow_none=True) + webPublishObject = Sequence(expected_type=WebPublishObject) + + __elements__ = ('webPublishObject',) + + def __init__(self, + count=None, + webPublishObject=(), + ): + self.webPublishObject = webPublishObject + + + @property + def count(self): + return len(self.webPublishObject) + + +class WebPublishing(Serialisable): + + tagname = "webPublishing" + + css = Bool(allow_none=True) + thicket = Bool(allow_none=True) + longFileNames = Bool(allow_none=True) + vml = Bool(allow_none=True) + allowPng = Bool(allow_none=True) + targetScreenSize = NoneSet(values=(['544x376', '640x480', '720x512', '800x600', + '1024x768', '1152x882', '1152x900', '1280x1024', '1600x1200', + '1800x1440', '1920x1200'])) + dpi = Integer(allow_none=True) + codePage = Integer(allow_none=True) + characterSet = String(allow_none=True) + + def __init__(self, + css=None, + thicket=None, + longFileNames=None, + vml=None, + allowPng=None, + targetScreenSize='800x600', + dpi=None, + codePage=None, + characterSet=None, + ): + self.css = css + self.thicket = thicket + self.longFileNames = longFileNames + self.vml = vml + self.allowPng = allowPng + self.targetScreenSize = targetScreenSize + self.dpi = dpi + self.codePage = codePage + self.characterSet = characterSet diff --git a/.venv/lib/python3.9/site-packages/openpyxl/workbook/workbook.py b/.venv/lib/python3.9/site-packages/openpyxl/workbook/workbook.py new file mode 100644 index 00000000..451c6468 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/workbook/workbook.py @@ -0,0 +1,459 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Workbook is the top-level container for all document information.""" +from copy import copy + +from openpyxl.compat import deprecated +from openpyxl.worksheet.worksheet import Worksheet +from openpyxl.worksheet._read_only import ReadOnlyWorksheet +from openpyxl.worksheet._write_only import WriteOnlyWorksheet +from openpyxl.worksheet.copier import WorksheetCopy + +from openpyxl.utils import quote_sheetname +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.utils.datetime import WINDOWS_EPOCH, MAC_EPOCH +from openpyxl.utils.exceptions import ReadOnlyWorkbookException + +from openpyxl.writer.excel import save_workbook + +from openpyxl.styles.cell_style import StyleArray +from openpyxl.styles.named_styles import NamedStyle +from openpyxl.styles.differential import DifferentialStyleList +from openpyxl.styles.alignment import Alignment +from openpyxl.styles.borders import DEFAULT_BORDER +from openpyxl.styles.fills import DEFAULT_EMPTY_FILL, DEFAULT_GRAY_FILL +from openpyxl.styles.fonts import DEFAULT_FONT +from openpyxl.styles.protection import Protection +from openpyxl.styles.colors import COLOR_INDEX +from openpyxl.styles.named_styles import NamedStyleList +from openpyxl.styles.table import TableStyleList + +from openpyxl.chartsheet import Chartsheet +from .defined_name import DefinedName, DefinedNameList +from openpyxl.packaging.core import DocumentProperties +from openpyxl.packaging.relationship import RelationshipList +from .child import _WorkbookChild +from .protection import DocumentSecurity +from .properties import CalcProperties +from .views import BookView + + +from openpyxl.xml.constants import ( + XLSM, + XLSX, + XLTM, + XLTX +) + +INTEGER_TYPES = (int,) + +class Workbook(object): + """Workbook is the container for all other parts of the document.""" + + _read_only = False + _data_only = False + template = False + path = "/xl/workbook.xml" + + def __init__(self, + write_only=False, + iso_dates=False, + ): + self._sheets = [] + self._pivots = [] + self._active_sheet_index = 0 + self.defined_names = DefinedNameList() + self._external_links = [] + self.properties = DocumentProperties() + self.security = DocumentSecurity() + self.__write_only = write_only + self.shared_strings = IndexedList() + + self._setup_styles() + + self.loaded_theme = None + self.vba_archive = None + self.is_template = False + self.code_name = None + self.epoch = WINDOWS_EPOCH + self.encoding = "utf-8" + self.iso_dates = iso_dates + + if not self.write_only: + self._sheets.append(Worksheet(self)) + + self.rels = RelationshipList() + self.calculation = CalcProperties() + self.views = [BookView()] + + + def _setup_styles(self): + """Bootstrap styles""" + + self._fonts = IndexedList() + self._fonts.add(DEFAULT_FONT) + + self._alignments = IndexedList([Alignment()]) + + self._borders = IndexedList() + self._borders.add(DEFAULT_BORDER) + + self._fills = IndexedList() + self._fills.add(DEFAULT_EMPTY_FILL) + self._fills.add(DEFAULT_GRAY_FILL) + + self._number_formats = IndexedList() + self._date_formats = {} + self._timedelta_formats = {} + + self._protections = IndexedList([Protection()]) + + self._colors = COLOR_INDEX + self._cell_styles = IndexedList([StyleArray()]) + self._named_styles = NamedStyleList() + self.add_named_style(NamedStyle(font=copy(DEFAULT_FONT), border=copy(DEFAULT_BORDER), builtinId=0)) + self._table_styles = TableStyleList() + self._differential_styles = DifferentialStyleList() + + + @property + def epoch(self): + if self._epoch == WINDOWS_EPOCH: + return WINDOWS_EPOCH + return MAC_EPOCH + + + @epoch.setter + def epoch(self, value): + if value not in (WINDOWS_EPOCH, MAC_EPOCH): + raise ValueError("The epoch must be either 1900 or 1904") + self._epoch = value + + + @property + def read_only(self): + return self._read_only + + @property + def data_only(self): + return self._data_only + + @property + def write_only(self): + return self.__write_only + + + @property + def excel_base_date(self): + return self.epoch + + @property + def active(self): + """Get the currently active sheet or None + + :type: :class:`openpyxl.worksheet.worksheet.Worksheet` + """ + try: + return self._sheets[self._active_sheet_index] + except IndexError: + pass + + @active.setter + def active(self, value): + """Set the active sheet""" + if not isinstance(value, (_WorkbookChild, INTEGER_TYPES)): + raise TypeError("Value must be either a worksheet, chartsheet or numerical index") + if isinstance(value, INTEGER_TYPES): + self._active_sheet_index = value + return + #if self._sheets and 0 <= value < len(self._sheets): + #value = self._sheets[value] + #else: + #raise ValueError("Sheet index is outside the range of possible values", value) + if value not in self._sheets: + raise ValueError("Worksheet is not in the workbook") + if value.sheet_state != "visible": + raise ValueError("Only visible sheets can be made active") + + idx = self._sheets.index(value) + self._active_sheet_index = idx + + + def create_sheet(self, title=None, index=None): + """Create a worksheet (at an optional index). + + :param title: optional title of the sheet + :type title: str + :param index: optional position at which the sheet will be inserted + :type index: int + + """ + if self.read_only: + raise ReadOnlyWorkbookException('Cannot create new sheet in a read-only workbook') + + if self.write_only : + new_ws = WriteOnlyWorksheet(parent=self, title=title) + else: + new_ws = Worksheet(parent=self, title=title) + + self._add_sheet(sheet=new_ws, index=index) + return new_ws + + + def _add_sheet(self, sheet, index=None): + """Add an worksheet (at an optional index).""" + + if not isinstance(sheet, (Worksheet, WriteOnlyWorksheet, Chartsheet)): + raise TypeError("Cannot be added to a workbook") + + if sheet.parent != self: + raise ValueError("You cannot add worksheets from another workbook.") + + if index is None: + self._sheets.append(sheet) + else: + self._sheets.insert(index, sheet) + + + def move_sheet(self, sheet, offset=0): + """ + Move a sheet or sheetname + """ + if not isinstance(sheet, Worksheet): + sheet = self[sheet] + idx = self._sheets.index(sheet) + del self._sheets[idx] + new_pos = idx + offset + self._sheets.insert(new_pos, sheet) + + + def remove(self, worksheet): + """Remove `worksheet` from this workbook.""" + idx = self._sheets.index(worksheet) + localnames = self.defined_names.localnames(scope=idx) + for name in localnames: + self.defined_names.delete(name, scope=idx) + self._sheets.remove(worksheet) + + + @deprecated("Use wb.remove(worksheet) or del wb[sheetname]") + def remove_sheet(self, worksheet): + """Remove `worksheet` from this workbook.""" + self.remove(worksheet) + + + def create_chartsheet(self, title=None, index=None): + if self.read_only: + raise ReadOnlyWorkbookException("Cannot create new sheet in a read-only workbook") + cs = Chartsheet(parent=self, title=title) + + self._add_sheet(cs, index) + return cs + + + @deprecated("Use wb[sheetname]") + def get_sheet_by_name(self, name): + """Returns a worksheet by its name. + + :param name: the name of the worksheet to look for + :type name: string + + """ + return self[name] + + def __contains__(self, key): + return key in self.sheetnames + + + def index(self, worksheet): + """Return the index of a worksheet.""" + return self.worksheets.index(worksheet) + + + @deprecated("Use wb.index(worksheet)") + def get_index(self, worksheet): + """Return the index of the worksheet.""" + return self.index(worksheet) + + def __getitem__(self, key): + """Returns a worksheet by its name. + + :param name: the name of the worksheet to look for + :type name: string + + """ + for sheet in self.worksheets + self.chartsheets: + if sheet.title == key: + return sheet + raise KeyError("Worksheet {0} does not exist.".format(key)) + + def __delitem__(self, key): + sheet = self[key] + self.remove(sheet) + + def __iter__(self): + return iter(self.worksheets) + + + @deprecated("Use wb.sheetnames") + def get_sheet_names(self): + return self.sheetnames + + @property + def worksheets(self): + """A list of sheets in this workbook + + :type: list of :class:`openpyxl.worksheet.worksheet.Worksheet` + """ + return [s for s in self._sheets if isinstance(s, (Worksheet, ReadOnlyWorksheet, WriteOnlyWorksheet))] + + @property + def chartsheets(self): + """A list of Chartsheets in this workbook + + :type: list of :class:`openpyxl.chartsheet.chartsheet.Chartsheet` + """ + return [s for s in self._sheets if isinstance(s, Chartsheet)] + + @property + def sheetnames(self): + """Returns the list of the names of worksheets in this workbook. + + Names are returned in the worksheets order. + + :type: list of strings + + """ + return [s.title for s in self._sheets] + + def create_named_range(self, name, worksheet=None, value=None, scope=None): + """Create a new named_range on a worksheet""" + defn = DefinedName(name=name, localSheetId=scope) + if worksheet is not None: + defn.value = "{0}!{1}".format(quote_sheetname(worksheet.title), value) + else: + defn.value = value + + self.defined_names.append(defn) + + + def add_named_style(self, style): + """ + Add a named style + """ + self._named_styles.append(style) + style.bind(self) + + + @property + def named_styles(self): + """ + List available named styles + """ + return self._named_styles.names + + + @deprecated("Use workbook.defined_names.definedName") + def get_named_ranges(self): + """Return all named ranges""" + return self.defined_names.definedName + + + @deprecated("Use workbook.defined_names.append") + def add_named_range(self, named_range): + """Add an existing named_range to the list of named_ranges.""" + self.defined_names.append(named_range) + + + @deprecated("Use workbook.defined_names[name]") + def get_named_range(self, name): + """Return the range specified by name.""" + return self.defined_names[name] + + + @deprecated("Use del workbook.defined_names[name]") + def remove_named_range(self, named_range): + """Remove a named_range from this workbook.""" + del self.defined_names[named_range] + + + @property + def mime_type(self): + """ + The mime type is determined by whether a workbook is a template or + not and whether it contains macros or not. Excel requires the file + extension to match but openpyxl does not enforce this. + + """ + ct = self.template and XLTX or XLSX + if self.vba_archive: + ct = self.template and XLTM or XLSM + return ct + + + def save(self, filename): + """Save the current workbook under the given `filename`. + Use this function instead of using an `ExcelWriter`. + + .. warning:: + When creating your workbook using `write_only` set to True, + you will only be able to call this function once. Subsequents attempts to + modify or save the file will raise an :class:`openpyxl.shared.exc.WorkbookAlreadySaved` exception. + """ + if self.read_only: + raise TypeError("""Workbook is read-only""") + if self.write_only and not self.worksheets: + self.create_sheet() + save_workbook(self, filename) + + + @property + def style_names(self): + """ + List of named styles + """ + return [s.name for s in self._named_styles] + + + def copy_worksheet(self, from_worksheet): + """Copy an existing worksheet in the current workbook + + .. warning:: + This function cannot copy worksheets between workbooks. + worksheets can only be copied within the workbook that they belong + + :param from_worksheet: the worksheet to be copied from + :return: copy of the initial worksheet + """ + if self.__write_only or self._read_only: + raise ValueError("Cannot copy worksheets in read-only or write-only mode") + + new_title = u"{0} Copy".format(from_worksheet.title) + to_worksheet = self.create_sheet(title=new_title) + cp = WorksheetCopy(source_worksheet=from_worksheet, target_worksheet=to_worksheet) + cp.copy_worksheet() + return to_worksheet + + + def close(self): + """ + Close workbook file if open. Only affects read-only and write-only modes. + """ + if hasattr(self, '_archive'): + self._archive.close() + + + def _duplicate_name(self, name): + """ + Check for duplicate name in defined name list and table list of each worksheet. + Names are not case sensitive. + """ + name = name.lower() + for sheet in self.worksheets: + for t in sheet.tables: + if name == t.lower(): + return True + + if name in self.defined_names: + return True + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__init__.py new file mode 100644 index 00000000..843d4997 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2010-2021 openpyxl diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e87a64bfe62f0c5c89b704a5eb164cac64a12af GIT binary patch literal 186 zcmYe~<>g`kg5b@w6P1DVV-N=!FakLaKwQiMBvKfH88jLFRx%WUgb~EAc>Ubel*~kZ zkHn%Bm!izFRQ-U;jFS93{ov$`{QMk!y|UE2GX0#)B>jTQl8pR3V?9g#;>?m%-GapA z?8NlcV*UJr)VzYqiX8p&{G#mQjMUT;{rLFIyv&mLc)fzkTO2mI`6;D2sdgZXKLarX E0EfXbF#rGn literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_read_only.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_read_only.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..240c9407b53807e7e5c16286fc4712ed1d2d88e0 GIT binary patch literal 5019 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_reader.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_reader.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c8bc8b1ebf060f5ef9acdd63ec8aab61308a872 GIT binary patch literal 13611 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_write_only.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_write_only.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fb70a746bdd05a92eb30b1d33f093eb54baf173 GIT binary patch literal 4138 zcmZ`+TW{RP73Pp!E_b!MSeEap!#HlzEnLaH07cTYNMWaTfkFu&!%hcn32J8~QRZbm zGb?E~tD=Yuq;Gu-P!t8EN57{3qECJ1YoGEL`jUQUxVx5YnFJ4qb2&3}zH`1gOy=j? z4z7)V{UbSEa-9Ft$LY(*$H#cIfDjIM1QTw?a+kY|%3kK>KKJtmZ`f}?3-Ts!qTCQc z7UnJ9vh~fZozL+(TOVefe4fuU=Yb<yqW#<vZP`5X_=1?*aJrq*FP^B>$Y{S*PxX$J zMn|TM4)QEgvah6;g-J|W7CK6cC@G?{Z_+#+NfGTQ!*8)nr^~9jRBy{ds>GD4>sJ0f znPt_yRs3m@4e1lT7UCzEWV0-v-rGqt@hz(dx2V=m{eo31(=NZxluX2MBiWMzV;5(g ztX9>dnNMHd<U{`l1?g}G^SOd?Pq^F{9&g|YWOFy<AtVe1+2%0CE(`_P;T=dA3UYxj zKrZq{$R)l6d4XSmT;|J=7x_iV6}|#_iC==e%r8S;;a9$LwwUlm<9W!h%4;GJ&F9V$ z<JUzfTF)JRL$t*l-fxMHn8*8jVnHn8eN!xn3wXaRmc>Q9Be5bb;r-56j_>eWfa!9z zJhSG^5~F{&BU~E>mdev&`z3~}(We$B4VZwYl48)4Q7-dR4WrFrv=7K>fGYQOv^&rS z;96b4R<7C-VWy)V5Z{!MHp2|6l0rl~03_;_c@D?Wm>Uf`+A3A8i+c3RY>*e)YRO4A z6eRtAmO{g(L{~W5Wd0saVmtn3y*EXWfTS`71HJVRA&g_#*g0~K-7y=xCpH|$PV5QP z#{SqjZk#x9cL%m|*Om3GplawrUn(_+@l_bdX_1;Z=3$&C2QgMTDZvO;5Nl}${jOK} zT4q~S&`%ULST#&)G6}zI#c?l7w2tE!&i^*oaw*be?O~$CeU<LXwa<n-rYzPrdOKyA zt*!3KVs9-=H`n?@vr`s7UHxE9BglT#PkK+2ZK>DFzAXB~gKTYo>icUk8AZk=;$^iz zRP$K$D(yamaM&^naF1@h?ggvUjrC~P(~HXV+{2>-h_SN^<Gf(&2&U`t>uTayQ>eXQ zY7xEF%1m5BsS0{o31|8l%G!l++|jkyb*$FC&MbsbY~ktQ(ceIfmT4)w>=@B`!VcLJ zr?~Dr#v9&YSTVywkA1@O_fFy7b;j(tA>0%8x6gsDcj%7p3jeWl=%H6I_Q(^cyz3k{ z;TQDnL4zy2pF4-{vyY*B8M?#aUuxa8&L@uWeYn>V`W8z+cJ$YG?aVYM%|BV4f5I@Q z507e$eqx=}Mw^6-+qgksnVs5~rFvf@NGJ|x9Ut+QCAIsBNfaTl-fdOQ8f8_v2V3N7 zg{bB?OxgdfA@T&xf+Ulg`D#3>rpzS88XESJY#?=P${6$WR_!n3z4H&I<on4%YPx<6 z;SJ1LHPIfY;-K<Scoo&TiO-=)e?a)W1>yKEysg6)+z#`Z>jo$<u?5K6?&#(@pr2B5 z26=Kn@)hfXw@@I<3TNP`n>Lmya&6tQOL$Z4s>|rs_0)B|6dje*UBX1_DpkJXmviy9 zG!vVChIZ@f4r}8+f2}XOzj>{XporG07T{%!)`quTBWgXngF7hsHRlv<syz<seyDBE zO1922)RdVXbwhQ9_HmQM_aUkVp{ebbd3%Pbt{OI&rd1|Mv-2#V-iMCIc=R%a1C9vY z&~3Ah+oqe1uAYOK)!egjOoMPyxq-)G^*IU#k$1%OEg;2=Ydqs`c}K3k1xqx>h+Auu z>7JW{0o8|S3r}FHxM^CjN%P3|+HD@U>k)zxJCFV}3y}}OvPn+S^kD?<Q^{sVXA3e8 zP&JOH+TrO0!})<&c{$Jw`||9#S=)MdPSJL%cB<1to22N;%Fj|wY(D5`vTCTL*p^iX z2LfhD1p#2OQ14-!x=n-qLLL}J@z8baaC(>8UU9#mUe`97YWoKvTlmiP+7<(N;rTRK zk2-VU5kbso@@sqm0Hh9w9swZOk$d0y<DdWF9{PukF$fXnzWr=@)E+m+UhIPA-m?_? zh*=^t{<vWpN3QzaxN+zezVW_f6FT+LKN!1^&9S!|LK9PaD7EM-ZjZgk<nG??oc=4- zsO9N8o-GR(@Oh&rGfUqqi#@4Kw7-)Wi4t-HjT2rXPqc91yiW@PkMe^&(u34kgehB~ zbI&^dOxuJbJBm(>swIm-PMiq1tQ%DA$3%ahC{-%;19VXy^$Y53LlHW>wF9?MSz0{h z?Os_bkrt%DQ*sM!iH>a^(+w=3RZzQ^B5JC9N=0}R+K4UHU7Fx5*eXKZ7kD%caQx6+ zW+8KNBiir;h&P<kh&jBy4ja95PUM~C@HbOX2QwIC{739dB(gTC89k)LkFsZL=gBEi z+uI)9Ha$baR+Kj7%1e=MQO*TxPuqt`0OhKpl;T`6Ay)c3p8AY`!xqk*Pj6-sqH@~8 zG5CYH<mAj|9#}M^S$-H%fZmC0fZmC0fYQQ~X!c(bZ44$!q{S^tb@avELu72NM?l^B zMHP^vC8qL;A0~lYg;Q#I70^3a;Xi4&6nsRT!FQv#&jalgPjug<OtMIF8OK#Sj$!3N zM&(W%KN}?3q@@|hqU^<SP4@OUv)t0eM7$L%nZ>k3kbgfhJG_-6@3u9_NfTiiiO<<m z?ccmbwY{B0nYx(f$Rq2b4Jsbkm*$<mLeRxFZ`FM1)EA|^uhh`V!#i6_%8|rM3aNR! z9)a{X;cc71#CxgSR|{CVO-ie<uaLefGgQ~8j_9&xPdbVe<*YVT>LZS9T+L6CXNob{ z<YipZnWZSfv^hHdh#mbAM9BQ6kL1I*NpIj$>U-|(b^?T-A6^NA@Itr}-VA-KX}!Cs zehEcY6DL4l_KZCd`1~~OUZs^-0<mnvugn^Vmng<od-RK{=uAWoGELHz#qZjz_rRo? wUX=$uiQg^!BP*itm)VP5%c<?!joW-W{HS)EUlG}9vaRoS+$9hRZnxw89|Bb)Z2$lO literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_writer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/_writer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e156e435cf636b562890aeced8301e45977effe GIT binary patch literal 11759 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 F{{yKj)6D<? literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/cell_range.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/cell_range.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d88ff27b7a2fa6bed6326c784660be6191af6221 GIT binary patch literal 16100 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 IY4ktx|FDU~-v9sr literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/cell_watch.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/cell_watch.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be99e4444e26422f65f0e35d56cc09a817951e25 GIT binary patch literal 1045 zcma)5y^ho{5Vn(SHv0z$oG1}=Y{90WLPF@ILxSprlmf|(@x{Y#bk5J&xxHI--O=&{ zJi*qyO<F2mfd*#0>}ruX2}?7YaqRJY-#1Z{$&lg7zI|060>-{mvcDosj?f%A%``Wx z<brdG0~3^?2+K%BoV{T>)X`_ABj7io=waT|G3K$)W8Kf#H2Hy5*pzqU3~ZrH;nb;t zY0xDZoL@kdLl<YQEvk7=2lrQq$q|}+j1HLKnu$Pj5khpsL=PXM=edY=fDgsp;24Z~ zuiAXk4f1Vxk|W05qL}O$%@GaP{F+Hbma#QI!7jPQ)*=VyqfPMhnpryaQ>7*7LZxq& z)vs-_g!J8N(biR(<%_yD>1+wrGBw3%+N|0|T|J+@NL|swvqt4-Y7Q=~8>pJql}Rsa zd*&7ZZAv}LOX_#lthz+XqAFS`52!cabolB14$^G<d5V4Z>2#$^kg^*}S=Rc(P&}6M z{6d*OU;3?@Q)o$1566xvB1C&`L}?El?K?u91W^!&?3drYU6!YK^lw?9r-LsfNGSR` z@zcOX;^$z{jdobTiP26Va7g<Et{TW&(6U`Mu;w<#+P=sCmCyF+#shTIzz!(hmDphQ zFbV%7v@O4fdp+$K#yRj;rUN^nLU+jBB}dX&8cWZNe?cJUV4#GmbyD6=@IAcs5Y0~= z5j+ecqL}tkQ1N6tATtdvw?)&|*3I1BKVxYo#NofrZF=%DlaRjXp2A*m!rvt?H?#7J NYB^$P%n$kp<KLJO;12)* literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/controls.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/controls.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6fde0ee16532effebda5c31a4487a631b909337 GIT binary patch literal 2356 zcmb7FOK;mo5a#<O%90%?ah$YG`exgRMSJQ4h0_#jf)=U~sTW=dn!A=+FG*!d*_Odu za?LO3FYu{{-upxL+EZ>l7bw~pQeuqSHP99I<F{OLX86tQDC%|{3C8%>pQB%Z{f>+C zLxaT*%%TJ!5|Npd%7H8+SD2EjftqT8mg<3?8iA3Tfm!dV%u4OR2CfmEIcX>8$kMkG zF^KtGA|{oeseub?i`cNX>vb2_4(Y(UQ?J*EJC?leOYkgtvT(<gClO1EXpd1(7y4u| zqeN(5=Q$JR<E*4p%7rm5d6G?qHKtGJG>a*`X+5DVDw8~WDKAa^u=f*<%LiF}kn<Sr zpC5f#?7%F31E5kM6Dd%L9H>;II#FK8fk9285)GKuGP`9?%Q`J{Th?vaTFZJZ>(lk6 z<&BnIYuRSYuD9$4y}5j2i|D~E(jhi+p3A{DF+e?6^d9C}$#b^L^BLvkLfG-@#+fX- z^kfD~P!g7)<^=9I5o7srnB^HtXGFC|qoL4dNnFP3AjxVVS71JbSrC9quB1?^6hOIB z0hLMvR4W}&s|-NBG69Xs0yHZd(5f6jyXpWsl?&LZx`1xA2H357fNNDBut%h^bS96y zK1bKWV0nB970@AcKbo_0oSaf&gfqy0DJ;?y3K1@+am3;W>^NE!!kQ;d@`V-6%Y2wH z*0ArBj5cf+K2+#w6!Tn|5i2M3q!bQIGH~{g!t-4y!V=$n5@k~=lz3k#3Gvp2R#3KI z2gf%dLVg|L2Et8*EreSL+X%N2?jXE|@H)a92zL?gA-oB2Ch@o7@@MF$lq8Y=DB|QB zo*YqscX3eWnLmaq&Y3?rqS=wpl0AR6C=c@N)4^wck(BiQEQ$}KDJ^^`fNZunVg7N> z4~qjzOMh7l#bCA&Rv0E(QikDs;I6m}AZ-}(Rln1%e-+nYWy0%^CX>l<I0PKQ;2U6V z<lROmS(MT+6iyhXIhix$ZWunDM{K!cmeCX`SK%d&4XV);o6-Lh*1$LZQ(X&pQDXE@ z8BJ+c7GcQW#rvrFId}(`9RTbO#Sp+!G{upx{3)6$$yfgVsBK<oLHMOR=;(8Ri!F$# zFBEJ-&m?F-Hc{$U<iPRA!*3D`j=x(Ue@B?h3<-0Y>WQcDHL%L@LwFCsler6*QH)+! z19^M6v4k%kZaj|s8f<xLZ9tgCK{TU}p(kVZJdM8voAnkSAoCGk=~_#QTK@>Mz*I>( zR@-TR8S|@ZQlW&rF2};;?<05!A0X5<f{AGy>BwqtXs~hlAu@Dy&Uqb19l=Mi@g2+p zOB9ppLJZC-b$@Z(zmOpGx&yPoXf7rQ|09SRKq`n%3`mDXOz6h8u$G?&jcA#Vf_K5h znauCQr4BGg-B$y7n+H4!O&(h9`6qA>Qd&#hnrJ?>6?h8I6tz|LbbS>;tL_L20Oj!P zO9eJzs}`Y&@M<wXhKED+hN0Fc1cm&Y<NcA>3+}CY(16e)=E<zgc`+!?{j+n7GuXWB zxhB_GnY>POonZ_w*uL!X0X>N+<9NygHD|QxBz37^PV1O1s|aCz*)*oFFv|s=4OhNn Mxw5PFzvyrN0~9*)FaQ7m literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/copier.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/copier.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1676d8e680b802a0512aa47c47b519fde41513e2 GIT binary patch literal 2311 zcmZuyOOM+&5GE;EmTft3`p5$)8Wp|NK)O1#KwBUv0wjH)C>8;>XaFGrp%uBYym~E3 z@79KW>E_x$&|cDG&iyyK_LRTSOFKhp?cHrCkenG(9O|2IM#*?QB+w52`XjkLAmndU zE;kpHhtTzX7z7bCCk2TKC4vbjrv-}{CEpR@irz~ideS*}Vpn(vWZHiNZ;&Z%+_b8f zZ)mFwo>uBu&!jXdE?;g8l!wsumoP{YQ$b=TXzZY?mjsTrPU4>ML?3id_+kKaUkt?v zW?uy0cHE4&E*^p7)$hX)8j7Kr$?!DE7gFC3wOQski>#2P&Z-jRLaG;1gsIGP)Dra~ zLvvCJdq%CY(nNLgLWWwJMI9cgstBu6hG!Qa6`BgRSKvs6XPKF0rFE*4LfRv6<6%`D z?^!K%;7czW$_T+D==u%}Yq}<!tQm|Ty<&eN>g0gDru$Q;@pQFNscbw0*OaEY$xo%q zj+R`>c16P`SIhh;p0ozOy=6RUU)ot-6Ey;t|2&ThDY7K`K2hSa%1&kUWH~cc86Bjv zs>-9iQ(2xyd3G4pOEas=&-T8GbY|qIb&?(fFgmJgS=P&6@@VTVg7{}r?bXZ1=R7Mj z!}$b1(u09e51!Snrxyrfp!P1drO29#hBAU=0eS+0A!{<HhMhAVTG{DjSI~3%D}Xk~ z?ktG<-1G#yu+2TR&b?zt{Rnmk?Fj7D-Cx~#l#~$Cd>O*6m%G5~)km-bk!|hCZUsZ0 z83cOu#pNxB(ws?I?l$U{FCsfSl1i4wt{@rowBHOf4O}xxnM&1%Z&R3f%7>vD{Ism) zH%e8i8UBpyZDp@c@RZk7VFugqwL^d^<TAMhT_e(j1$6i?V?NA2o6yz0Yj9tu&i`=V z64StGKt_UOgqh-OfTDn>dyZ)LxZCMor+b~=@AQ7B`<?E0`k>PXoj&aJA;v}_S*D|A z#4#@1rf4&1m0b!eyr;!44s4O(N0llPV}XP`z`Db01sqi-lR6ICjBk&N159n+rF}fK znoab0Y*U|CwZTH6-a#*zFFU}S8}?pX(tixAE%wy6m``Z?tUkDk{ngj50N;Z%uv@~n zz|VowSP34J=ZDYOGq#}{wsBhcp;Ayme`6bW?aX^?m-k@yVD@0{s~?OHGlJ~$0Z2pj z%vum;myf^_sK8nRYY9#Qn-pw+${H`N@<mZLj;hY4;}&7Bi$V|bztAmLT?|a)aa(2M zfk;fk4bV3p%o5FQ&9yOtEmi?Ltg1XKkK-ZV%1uA53aHCw+H2W;8(?T0xI{eaiWxeJ zM_nC5<yt`0yYNZ`UB3qdp?BznVI({nbbWRAY9uZ(>q;1qdD}2x-4PVv|8_&$`m?6z zY{RS!0RY)qvsaD{Q3wHYH$CI7djP4kb{}BHVkq4uym#`+iMxe3z5&qk9hKvoE##L> zQ?KbmsZWeiZH>V4pzv4GVmjEeZ|Ygt8W;CAj#g<K9_Lst;eVPH*TF)J9zxexJP1%W zfM)`I_2Jt<+Ff&hI>4%4!Viw~X2^L_iA9cb!1>7{$-9+4=b}nES7SKeRwwm7N*E=D z-Y?zAW|pmD2s>6Xdo7>3p6~f%Kk#oVT-gV9*o41_?_<Giyy|c+Q)8Fkc4Ff{XfOCR LZr3;jEO7n<<;YXV literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/custom.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/custom.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d7a1b5bd48961ea35a7694b4d4554d761df777f GIT binary patch literal 1079 zcma)5&2AGh5Vm(Wn@veV_>n43z>OTp9(qDTs8W&OR8`uWC1h>aL$d1r)pkOIDsd=R zo`5IFnYY<1CtiUI%s8Y?5S6ervmSeFf8RH=>0q$Oa3tTprk^~<zEiM00t^n(3^~OV zFIdS1=QQ^Uuk?jq1|s0>4O6}fJ~I^nzw$)~<Bke34qY6oZo<aV54?qqdD~Aw=V_6f z^t6Dn*M^DJc{OdL1kUGBWsp(g_V6({L^Ds&0TWy?;VCYB2(Fmu;A8YW7oqa-p|Bku z&5f<g6J0l;?NSW0o%lFIw%3E^WJhR*1i9pwOd`jGt@ttG4XU&xoPmMjz3yVhis>GH ze!h!KP<a}^O|^Qh^995w%bBgKILT&pUBr_GRExOCPvd53XLWTjc@dl3!t*B0&eAEE z7y(wz@}h`8)cVZK0BpR*I%$?|BxPRZR?2$>&0+RG?(cG&Y;wkkwM$0`t(5Jalx3~v z1<m_Xp3l?b&ra8-Q<`d$)$!4HXhxK_97&-&=xEbK=prxhLZAI|tG5^F8K&+Q2|CLA zLgn#AS4FNjxQJX=^xDx5p&X1D?UV~wum}UVXdttoq+K?!;%&4^a*+SGqB=xy`aZg` zr-_q(K<<XRV@-@7qir;Q^f$z7)v-U^;c-v*>7oI-A-TKc=nrygxn~5{0SYLgvPR0= zMIYg*eKgmNB>J8gknFUMMHWvtT{BT&GMzWJ)@EY1{wht)A|C!LZ(a2pV07sgf2RbA U&_*xU{qu_MGNfCdkD^ikH~RVUiU0rr literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/datavalidation.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/datavalidation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f90440541741965abe9cd38691bb03a76a326571 GIT binary patch literal 5817 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 anotvhB*J7}eW_B{=FRGhR=uuIul^598jo85 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/dimensions.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/dimensions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6918c7e36fa575f8fdc0dfa44a229ecccdcf73ea GIT binary patch literal 7759 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 TB>I^fZ?C<#c4zJVwQv1DAT7>< literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/drawing.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/drawing.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1037b7528ea49306bfcdcb64ba8c65881023950b GIT binary patch literal 646 zcmZvZv2NQi5Qa%gvedu^3UtfVjs{wB=oBC*0yID~89}{qD8YzVn+PSUq~gRuw&X4H z1ex<TT08X>I+Z8efQBA$r~i0I+;^gy&5lU4<?FM0M+y0j$=K4EoJ9{GW6(rXOKK`8 zMa?X$6OmBzjp#(DPei9c9}|)4Y)R&mKk){c(|)=HZ<IAbU0H~E@g1z{jB6@v7+VsP zv*_V{3_t|cL@-T70_h_Wna+kWR7^C(0eyb%)vak(BG)^ys93ytFx2Q@qX%5`fj$r! z$>U|Zh$PJDIqTB^_NUKc{e}2Ee*XU8HE5&wh4T8`n+@>G?YeUfUsh}9EH5|EY`8U7 zyxn$d*L*HdcrYD&YE^ZuRuH&rp=q~wmft#m9o7IH-y@dowlAbKjp?MEA=!XT@89m1 zDGx<t%(1soHAvYXNm)C6V^L3~{B@)3zLIxph0@~>95(T9P~kG8P+*D=isxYxL%~uu zVZ+&Co{0|!>{3IhylFe<Lm5WT=Mh#M|F;kBDzG~Q95Na_aNzX5INh!A1xW+8P3biG E2UG5ujsO4v literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/errors.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/errors.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..341f933b78b289ee746445a4dc71eccb20767f79 GIT binary patch literal 2365 zcmb7F%WmXE6m|J+w|mkPX2LK$hu2^P-3t&B5<-Dw0!X74q9YI`A<J=9I#&GXRJrp~ z?`F311$;r){7r3H@dGSa0QXj>J)KEMg0kxP-g4Q#b?>QDX}231&_=)gnm!2(<9BqH zn+?t;blCtfgOS202_*z=voK4GTBS|x(xFc2Qdf^#g;)C2C&o7hvzhbMU=AlwEE<6C zG7o%D`yu!~3&0Pw-vK{l9q>Eak61J^;_e^tXCo#m8gY@QMK065g2%RUcF!i9DR<O} zyc#R}>$)zKKjKGIUS%9!2)1}p{E${-p5Z5!n+47$bonj-Hz;8SH5s86x0%VTXM{S; zV-9nl66!J=!thn_=(ORL%<D=8d|~ch5EZ6HQJ*ALUGZUN!Uq=(ZR}gnCFXfXW<~<3 z8yOd52oYLSk;kU8B`@|x7baX7VhwJ8{W2&y%hSQPsbJe8Kjwq)&kmZp8jP|7SmvOA z%&X%;k?##AXU#!febWDYAoGTQJV~>|bj;<Tp73gNc3KQhYH=tJIBy1Ah+4@0<V^WV zl2>_?B<mQN4%s?iZ`apfJdPns4`)|t$&*BdNmABqTA&>z$<Z_|E}wW!I!03<Vf0eO zZM1MTOVC5t2Z*3cG<?%GL(BL_kKSCT4`AxmI&p`YP$!pJ)B_sLYvnT!Y89w%yOzhf zY-qQw7rIa~7?|*^;Vfx@L@;2%3?1ixEug>^h*tn&Q`|!PKEm_$>ZM{19zb8@c;iZ3 zZJvP(`pq~HSP}6O!X1RW2rdF9OaB8i&kJ7is*y=@bDB5cg?rF7^B~Pu2ptlQ<JS_6 z?A*l3rvO(IO&#uDZVIop{MHIuD{QS!YelWqZLM3awFZSlMIf}NH(8*f@5WUvID3R# zRZ+g=?FtNXxRZ6@wrFU#TWk}Y`4*d*u+hlO0yJkfpfz&<?U@Vc%sfDM<^y`O0MMU> zfB|kyUDr5Njyw{4A9nh2TFis#H7E6UKF*t6;L6WD6>7bjmU~=`8jScWiQ~%KuSGd6 zQWaeqT7cBrv?}WCkh7i3$LqYDG-q0&_&ygXS!x}`t=UeS^hc06mNqCgy(}#<_#SX| zt1hNx6?f)zdjl3L-b8o{0a+&AMtBDSbxpj7@IJyrgcaQU48|oA&4^s`;&py^xfm^3 z<8ZjMXz}fpg!S-uIzR`l{}ttpvhf;x3fCb&0zlcYN%-Pr8RXjar@%L_F}^+7K82@O z8NZMg?s1=4%zkFlfH^dT(+STjLOWV!I;y*zAW6H+bd};|`0y^M18zcrM7=P@>u^&p z5aqF)>&-B;U}(|MZt08)vP|ceo+5@Pu{Gxbeo=ggu%f;{!MMbg8n#Q$H?D;Hyr8Cj zbr?r0i>cKIQ>cR%7^2pY`A=U(_r{u`Y!9JJR7frLBtjJ;#@|9ei@Wr};;8B~E;Er& zU^~gaTsrIpri9*EHE$k{K6EtIFj#tj)yzIW&3GX+HMN$a4r*%XNS3mv^N7h;{ulG( V?n?|nqT)y7o*P-csJGU8_%EvX?Y#g1 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/filters.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/filters.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6f98bddba365050bf603b239f3d8a9f70dc1082 GIT binary patch literal 9069 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 V(4FgE>2|yG-E-a7yO+Ax{|`hDZk+%C literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/header_footer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/header_footer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..118092167fcbe9f2ee3344fadae6298762fed219 GIT binary patch literal 7527 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 Pgu$%}mUFIpzq<MVw4Vq9 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/hyperlink.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/hyperlink.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cf78abe1fcf66dd13886c054c51d0d6c6d41e06 GIT binary patch literal 1849 zcmb7FPj4JG6t_Kpce5KpNsEdov=_ukuu};RAXO+*ktkBNLS0UCF_W>Aoo04sYCCB* zOiw9CJ^`0<%$aZFD<{4}PrT>pZnGgh;E{iRZ~bQ8AOD^=ACHFw#_X>@@+KhUZ&WT1 zACx_q`Z0hYf>vZfGfGir6<c_jx9~IH>0T8q!YriZ2O@kCyd)x!^xVrL&_fY{9yvW0 z@r)#cf8dKGp>{Nrs?4iW=lhjRm<?w}mGz;GXY$#xtP2S?@e^6)rflj0U%EU(Q1)Qz zCjgRURFI4bnt9ScCs`nU!G!la&BC=5t)*BFz-GACw}hXKMC^j4*;oV^rX4(5wo+AP zePjpomH0CRHO#B3dCu#mmPueeB@b*|HHC}QMy6>hQ?@n|rEaTyX;~?zMFdf<2FeWP zLzwz=fR1*Ala2w>&I4qf59oCPpx=dnfgm$-L8nQm2JnW$(^FdrBZM)44Na~NrAZ>| zYgrw<(@?z!))z#*56kOc(uEXdo<7Qz_+FJKGW}^eH%*<+iaBJGPEKTfl2+w@+Aht! zslS?ho9fcYFWbC0$`7SZn^xBC^0Z2yH|j{wr8McfT$6TbW6sOEG@Rc8e;UIjaY$Fs z*^PCXCo97woVt;0<I8ovkeu5g=Zi)hS16A-e|DT#Z+0S+AEKx{`1G||1H=0oxzz1| z^V}GvIaeRx4i@4P+&~rig_YKy0L08^AsxC0R_1$zuAkS=_CEy0VD|6>4*)g^Dg(ig znqutwg=SITCmD2z7`!CeKn%qetV1yZDsJ2Hx<-#nZL;CIz8dH_mZz;Oj1=4;OEH-O z>9ratujz)o>FI`qQrn;=R3Rf1rgmU)^3`HsmA-(f-FNBPt&Mor=T=a#Sq+po+#1r6 zr}Uhz+%oI$1H+ud3)(w>dlGmEc=~9<`F+sd>AW}jzs|i%)_t-c!^f{=Y$MKLpYyLT zIlg%{YiF@hYdmzkIv?U|-vB_$cx95`*lqHHb?g=G=$~+ncJwLJ6G2~j#_!lU{MJ!u zd%Oa`8V;RJ&qDR1`1SsaByeHcFmIvL#G76}Xw)J%3fY->u6Q;$$*W^&L$~NF8L7Kq zi4kdc@zZS}b4YjS+3sdmy^|@B0?#7ZQoFFIn+Vvy>O%xf(XqZSplhN#_}ixlcM;y6 zq?24v-G+_NV7f~HduqsrehiPoooO=2?yWA9iIBQbWosIxC;HMKg+fkcyVu$E{g1I_ oA6>UUk*9^Ml)DVDy`9(V0=4mj{;K&7Nvval5#8}e-p-wW0Xyl2vj6}9 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/merge.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/merge.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c59f5ad57320e526995a79ff48a302a42bec8173 GIT binary patch literal 4394 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/ole.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/ole.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..095aa8a99400711e616ccc61f42939bc23b405d2 GIT binary patch literal 3035 zcma)8OLN;c5avT7DT<P1J4u^9oi=^2ZKO`qS7zFdRX-BXxU?rynh7o(MnEjad?+C8 z*dARv$)T5AdhIXpx&NX+0oR^-@1^ab-329Dv6D>5!N*+?kHzBK1%gJyR^aLV_G|Ea zLs9-fW%j9nvJ55r8GtHOjg(j=s*17}X|Yc9xI!v%l~iMc7_mvr*dmtvuSd1mCbpC- zQ9X8uqbk=GTBXKgg&ItKq>~2dCbdAfq}~L*Ms3h-sn3C4rw-_j)LXRCQ{3jCV2$Fc z!s#&{22q#=cO&K+qS`+kF)GZBBxgH}3!|6wFxe4$kL9BBeVRtX>ahoVEEzED-c1I> zl>ZR$UB*Fg^=_=K_5IbG-P;@9b>Hg^aGu$x3d%B+>?QzHh)NZrQI+Vd!m7-m+7peK zGqS~MRHv0EDzQtc&K#)IX%*Tws7)<ed#sWsHDG0R;cVT#&jxv6nJ@xrp{t1oI~YMd zqkax+6tyIXSvCp=tbeQuJBXt6p`WA)b2Z)q+lW0p91U3#Ko4(yU_jOML0N;c0wsF~ zV62W6Um0tF>R1QV#uY$)Tm`I9rKcRLo?8`F{~k|cq2;M)#OXe}9p=Lo7Dbt8WZ}__ zY99G1r{GdUR5BLb;|nkgUqm>Ea30|Tz_G$#h0i~v6EhkHof`qCt32Fio$bS6o+h0h z>`xkX+WRco??mC<&gd{7rpeXzwN4i1><TzwH`rlWCmpe5ba)VT9;SRZ8#0!6(ulQ3 zhr;sxFbQ+te*<R9B;8FzE#9N^v)*n`Te?-xTVMCwIqZy%_I+Xdew@<12xZ6jAM6Fu z<d2yLJ1BA;msLZ+S!Nb(qI$A&3@W*N{u)Z?ff=}nY8_x6N`^lzt)e-qquTH~6-AxA z|H@WpDC$6uW$gM3z-fvCK~-Wfb3#h1lxn49mr}iyoKk9(QnQriSnH8O=2>ep>O%Qv zkygpMlH%tl6t@beZ*yU#lI+|3Smls@ls=yWbXAUv0NP^R^g+3#?ItKMrtR1O@>a(t zpfR=p&2bIT8ry)iaUIYeJAid0I7er@j+|E*Q99U#G*YMRUa%MCJs`I*{1K#*Tv)Wo zHdI)9VX+Wl1$%kA9!8K|P}&R=R!H07AQ#m*81Ph>L6rB|K`wMkncLv6z>IP}jxosJ zMYxRc9>V(wA0T{);39m4@G-(C2q(OeltC_i3q?{!X-+Br^!I3f>iZM2J6^xvUtb3R z*1H$@>o_<D3x5;gEd)tzBxPZ-B=pOgjXCp_?HEBUVlhkd%=e!k25&=GTy^#(0EPjE zz|*gRx>nK4u$YEN@%@YA7-xb67qAQ^yAN<SNT?3E8N)=Axf`=MoLV~?HDxBB*4LpP zviGFkVU0=qCYytRXi=NY(>dCpO~~&Hv<~^bC2Cs{o6x2P8LANbkhPKDGIh%s5Utf8 zyS?qTmA=2S<@MLRK53LS8&Czum9Za0dn_ZZt<AMt+pFFFn%~{L-M!PBt=aNcx|^F2 zQcEyCgp>uJ>4S0^Qf*LPKBP{9#*`xqW5m;)4TuW5-_7u(5|t=Sb_Fi-)`-G6GzpG0 zfCD-k1|zmXU8@L#C77FkhHw?(bA&GtaNqe?2+v^q4%BB&0Hrp|@}q^)#U~^;J&%71 zErH^myZC&LBU|KxxTLML2@lNEhRFz!*2!!xO}v8bo}1FqzjvU>BezKDw%P>tVlhrL z_-~Hw&+r!Ga2W@|tZ<gMnB1vO;6^2HSt_xp39PFLyL3rL?2>jFzXZcT?(M)wdU$|z zm_8_%^uQynphvm&{2J5&b7dwghbVTcOi4dMn@o<XA1$6(QSJ;7S@v*i&)gKGsB}eP z<BMJVH8eiN`OsLIF3_$2ajf^;d2(rbOK4M;4R|=pQ=YZ6nL8v$OUb!swJUO<+-*q> z$qGpWw2@ps>BEDEaOrGkBhCUU?;<`e>g4T!Tqv6!#8G>YCRrXpA}nqVGRaC7A_1i7 iGu)R^fW8&hH^p`2JEVk+0!Lk{!8Ji|U2Jt)OaBAhtaqOP literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/page.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/page.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6be8a445493b482286e7dcd86900a758d4bb0d0d GIT binary patch literal 4378 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/pagebreak.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/pagebreak.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20c37b5d5ae1dcdf1cfb4127b38a50c78b4de0db GIT binary patch literal 2371 zcmb7GTW{k;6rLGh<0fso?QVC)E!#x`3y~lpAwXy;TO<|<MXODPkY9}J8CtiF?H#+9 zMA@hIwZDKDc<eL(#;-i}FMtr7GmewAv_gz^zMQ$(XXc!5&PmW}IRx6^x1WPGi;&;3 zv)FX#Y=PuI0C2)-Oor5>6m=G}q2_5r-P4DLXHfDD;TqS^3D*TZ(>xRU1~;K^s=g&G z&xSFJ+c0Je3)VDvV?bQz3cgNUS~dqlMnN3q!EP*Et+c*P3b7|-sXt58xU>i2)saX- z5#svAW<X~PB>xUT5RY==F-|>A=$vuwf_lbGGiRD5?CES{ra8Q!c1XP?ZsOib<Czq} zVX5uP!<|c7Izb$#Cw`J9LQ=4H_4k*A^g|QWx}nZMwn6eufC-%tpG+7aooIk;q62CZ z15oE=KrU(DHA)uoQX57IKyZqXlq@S;v($4D|0J96DcM3;Mp!{;16&e$4}Sjmu{RVv z3VP22$-kD-vFN=R?H6g%8-)Adzh3uPB*(ot+U;edVn0nj?ta$Gqe6U`1>s?^C-PpJ zi6k4H#=VnN9_IT(6uk^4cF_afY*gC5A0<)Y`}e_KuDD~Fw0g$N%2C~ViEAkTRDer& z`fd}ykOV{F`=#Ui!;~Mzs5gE8)lm>nXRIRFLse?<*3DBRCFe(%>)6H9FTfghF$hZ_ z`9lDk>CB<iM<aCl{Ha#`i%m1u7x3zTvZ-Q`e!+mvhUah-m~$Uk<eXEl$(MNZoOmtX z;>+;6ELQjmZ=aJhWaJ88<@c0s@&|m4ufrQ{ZUO5yO8aGcqWtMK<W$RdqzxN#S=kb& znFtHP{bG~}*?`eYD&Yd-q!Fe^Nl~_eJ>XLHjcpw1D-VTuxVoV}BPxbSkrOWWfJpS6 zd;`GM<tplLBD{rwhHknW<In}k@q5G=uh#pY@gc<9Qa99RmXXZ`nUDi|Mmasy!2ztV zcyq~0Ef$Hg2q#qgm<Jo<jD*?+a{K-#(5tM|Yu2mTyRGB7YwPv7--a>SLAYVf@_pEk z?|%++wXycXQPt$#wlIEZ>x@n<!J*zkGaZEMwv@x4!gytejhAP3RD}PB75U^Y8yHWO zrc~J2_|eS9P5Ojt1uVyg7ZXLj`Y9;217EX(O=zK=vHaZvGTnjB=?2t_3;LQ)*e}L} z{tDHj%7k2lK<nd8r!#F|ao!0!m~c8O<IE<mkS;D&si2SP5K2U$NKrab9wm7ZKvhte zS$VBAgA6hmceRQ#NZ3+84&wZ#kKcot=O9%s(=kJ|LB=aHKh$69SNF$-M7=68{mBOi z8wi+Us$@04Bv_KF9iep(GMtFTP$WeT`L5ynK~YFhs|5BK7jFWTb|xVS%VH#PYYW>| z<WQOI5llP*QKhBMY)z(9y~=EVIkv6aI-`yfiR(j<YYjcR1(LrHa6QKzKs6Wof$2e| zJg__q(DtBGLbSBf-cGBW;x)plmiOJptsX(cH`9iTV{@X<PxS#zd=H``fu~Ycg-IOm zyN>s`u1#Go@=!)uk;=TAFZ^MU$WU+nwslo-ROy(<1-IulUI5)xl2n#%+gfRVQ5CAE QTD`v}UANi~+aKxLKgN~J{Qv*} literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/picture.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/picture.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbd3285f44d6261f399c89df6c05cfbb31888169 GIT binary patch literal 463 zcmZuty-ve05Vn&5p=u?j&M;;m9+;32st^)PRi$hhu-y36SasrHCxy)L7CZqn6R(n$ ziC19al7i^KNq4^Qe!sJrOhyDHd3zD>_<r<YR{{hxRCkO<5=k{FXi6!$A<~onGm$>f zCqMOMkdSEa6RSu>yKw?m39X!1YRLNFu22LsRCk01NJ=Ha0W|fcheP~sn&bfWmB`jB zYnoCnR8}_@(y9A5K97Pf<h&FGaNdnLFN|z-?~ggZZ-oBI9ModfyRXECxOgyMcdP)Z z1iKYhUR$*RwrKO(lq|_|V>F95P;Qu3OIEdYZpyRxoH<p)X@&H}3LG;PlvVqv*@Lm` zfg!85gyX8UK5p9YZ}rfG-hk(kpPu}-#S&a*RaG17Vz={cw(l6~|LAVLLriqx#rDk0 KzHFF24!!}5J$a`9 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/properties.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/properties.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de3c2b7d4be12157e53ada7ab53f1256cf281164 GIT binary patch literal 2378 zcma)8&2QsG6t^A6b{wb4w%zUmD@gg2B}fiPNJt2kc7eSRs!bLoUo6KnZLIo>nMu2; z%xSsqKLCG0&-^!Y<;0y6;=p^8#EG<7!C3Rld(Y$fcptw>)akeywDIr1MO{zR{y=5B znV_6N=LCRgfkt#<q<UrqhK_P0ZDeL(X3e0PS%IZ&W@=|n;ON@78fg;i7mZldc+m)2 zpxeX&-BG#=dW*QAyGn19_E_`1+ru9VcEx9uivBq(O3Fk+`Jb@9=IhcMQ<g+&!lP+Q z{f4y0f+hK-G*62nmCebbqy&a_&x*8Qk13B?Qi_7b`1y9L4|WPxss%b6&LDcwpe8X$ z<E0)nYsso5yOta{3LZ`mTEv8HJJSAsF482YvQ;dFb2hotr5mMbaTDf6PDgQbYYQkF z(9fXrM*x*xX`xmbfO^#cG%6FYLA0@Ur;mJ7wxhC47h|zVDVH9fXIaD+rxfO|wWrDD zOvrscD{fZ%885PFk#fH&O-|Db24U7%2VoDP3vj2gd+_sjG|VVTqT!Q>k;g2#ro;2a zOceQW9M8bL!@)JpuZL+e9hM6*EAmeVpAUH==qF_qUqzRc4<TH6xp<xqZ`Ls!mURpV z<w81PnB<8F!$Vk*I{=zx>G<6CRvR9yhxq0Qn(yLAd6dyGlx`Si1(~NPdtvy~JW5w@ zY!O|e$S|_I4#^Ji1)g&Y_EEI~unaiv$uUgB)zxEax_bUmBVI|N28=$z&jtYhl|ps8 z*sF9oI0uJ~shF3WB$w@SX?zC832VW;qZpT2H<as~>4CDHGngl=4MyLPd(m7JxWXx; z(G{1ii$qM6!EZ49kiCiU7Q)6Q${XmWkDzmOq=tccv}3EAAQ=!MhoS4(eS{-~2M8(> zn{{66|A#QR4_&3h%KrvK>nlFT<UPTmzXH6*=hmu#ZMv46T58pjTbFXXwt2PGsii&I zUCq~{_vk*z=1U_u0Qqnw-zUxB2$&4HO%EOr3+TX=2Q`ar;v3kC*4D9wICbdrlE#9P zPyll=%t9bFM?_OaU<?a{y#X+av1Hc`W%TYDbj|>_^I)4hswOaqSy_P1$_BJ52hgrs zfKKHCwyHMK=Ng*`7Y>BnhYZxtt*VW`m8ag$g!e-G*{eLTb%+i}G;h8BR|42~Iv14p zM-$)J(2Bn&owy)$1QjWdX^w3DO~JBAoE14qL{jAZEK1{f8lf!RizF44eFvY&Rym&n z!Otj>dwh|{PYRaY7P*L0X`}v>UI5*J@B>~AF_;epi*g_$nCa{5D4j3o>~2|e!4~Xo zaFi<LrCTw<lyCjcvLxO?Lm%N7;a!CH5Z*`l0O3Oftd8Ymb$E6a(FqLWeE_YyWVkIf zEZxz2hGjUaz;@)yrocR8!FBT=?kqJV2B!9}iVj!(KZmezoN^1^M!S3y0L#*pW;7Q( z3}4Toc!7Nkoqq<f#i2_kU7@FYhPh_dCAV(yFNSlBd{5e8NQyWNgX8seG9dL$&If$! z9{@u(3=VgVW6CGW9eB9ls2;@C1v-fFF9K)E2Y3Z2#!;Ktil!n*<VERxvAlV{#CbUq Pk!g7Pq3$($M?LRft~(Eh literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/protection.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/protection.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00d58a7de93deebc2fc3a053935a736ee6b14737 GIT binary patch literal 3441 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 W*`EE@qy!4J+ir`sDy_=q?*9Sy(<F5O literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/related.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/related.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6e0dad52154858996077ee2bae5328c7a7dbe06 GIT binary patch literal 816 zcmZuvv5wO~5Z(1Ui4!HdtLZ2$fn87`A%p}HR1w0Rg;?!*=Ps+;*k;!Wms7e#x{siN zGBtnWmWp4XV#Yp8DHv%cGoJCh_r_6+#f+h4zrL%_oUz|joLh*+8+88+gJzmrw&pqK zlm|9gi(GK_o@t@OBhw-9gUCZY%2+)9gKw~yxAP2~QPy~MWdZBx6Ij)lsw`;W+z5=! zCm4X`T(dj?abUP|tOG5MJRj-F1H0xLL5|aW{J=t%PY^C@C*5Aq%OdEu^nr5nx9Fa< z?)jccq@S@9Um~5r=s0LY5BA!Pv43JN!k@oi(luzK(hth%cg}1f{kU5-RheeRs;X?7 zY@yty)?B6au31&(i{xeMO#{zsRoto@@M%>;S?}&_`mJ)eeg)8^j--Q5>RlU2X-d;b zIVWZvWqyD8s394eg~0A$ln?GNv1nL>Y7W7VV9$T@jJXj$j72;0n;M*>d$yCNl8pn1 zg`4!Xx+&EfT4D6viMLZJ3#+`BveWIRIOK>E0Svq@242H5I^7bFjw)!Gl<iE)b)`3! z^0|~>H_G-MN0htMI=X+iyInk@7GUjwOlNl&6gm#Xo)~-jDp4>Nv#?XNj7Ry?;kJne vUpP}YmGg-|`=dF!X#V8Cb8v^SJuL$1b$94pAoMnR)vI|;>^+Um`CR-1lxV&0 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/scenario.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/scenario.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52e84589ef50653f96e48f865756dfe40e459343 GIT binary patch literal 2564 zcmb7`&2!sC6u@`&ZCQ4lriB6pS|}eHs67K*m|>U<DS<NJVQ`sYbkQi<jV+=NXID;| zj5#IO{smm%m^1&4uAKTO@NwY1l`W@ka$q(4^j2E!zW4U`b`^CxE`c)o<JTx^5%MQC zE*AqD+fd~M1S5>5B%=YPsB5W~>4BaZfsvVknOT9A*?|r3bY`SZ)(Tpbd_|bataHLF zPS5nfg|^KcXgjLihIWg&&~{bZW9<>~y=O2R@oD9axJaTjk<nhteY3JgrAYE|Wk1SG zKIWn_z9@>caz^}c#`Bn0?VTb&;-XBT|1(+-(*16hmdOrJ(;uRI%wrt;axtN?4OKn_ z!AU?F2{c9nof~H)FuBDvraz^D-Dr+_)8g*pO`AE)V)i)=JZ6CXt;&6rPiH00DJwT= z^v4&pa-%dYj>Ejj`EWP3V9sShU4yy<RlWscPUj>fa}6Y&>maqc0aBlvAPq)F<bn<% zEL2oB<0&sWtE^ehVCYIeiXbrgEZfb>N6fb>L-KTAY{0Ohhhh`O4HR1-E{M1Vx4*+d z##s^#zK#TYD3T*S`2J*47WrTlPaxET{t?fQ25GW4n4XlABLBGm=|CnWe=v>WgJ{g< zpqTP}dh#S492eq1PB<?IGUj<ClA=F7shluO@}vyI+c2F}^w}1zuhXqn*!s(UzB%08 z9S;2tews%a55vk0!>nMl6m>5Q4`)%jcw?8*7*(O8XUbb#-*uOfYvs3i88vkMl6VQ5 zXj&JlL_;0T&@Af01yUz;as8uuy+D)=Uu@&2`yietiknOWmMm@qTMlnwu0Y#uv^KXF z&%8$KFf-^fE7)LN=CU?$*5jMZW1VwyMuQv70p>O!3l?FnoMpfPw+??Y<uQ;EmM2sG zSa@KPPemJV00qXXveF_xKz1exV?3{nnS|W3<08v=URGvY%<@vj2Z(jx%Vj~mA@+S} zTuZEl#LjJyCUPq<vc9Fv5O+Zr_fR18;%yZ7LHLfii8|W4-sv5D*7RIoJ6zOHpi1<X zw3d8X^mKC>q=v5;w~1vzy>SQ$D9N1sOwZ`jWUbOuo-30sXl>pK9>on>_9aw_;}Gq% z+svrK=-Z1x0@Fi(OCVk16%?<ccnt+^1d6QMb%pX>e5O#oB2t(HkpH+^5>lcShEZ7x zP!-;lGZm1PqC62QY%7D6rmVgL!@mnvqCnae>56z)GpMm*9@+oP-PEYSZ(d-(IWc~M zx7V@1AlzvPhrN`y;i>slgFVy=I&4Fo3lz9-Gw+-L`yJMWdr#5cgQH<V`?rZKgVvIG zDc*p27np0}O=yS?2tfXI)Y0Q%jB#8R)SD$om#$-Y#jT;nf&`fffeB*BNpW16@=)-7 z->j)nvwaA|i3ce9C{}@2F-J;&gep}^S&KdJ)@8?5lq#B_GZq8_>kd?j*Z`yC7j3Tn zM(6Z*IGxpz0cUhWZEdg>O<{9pzP>WCqH^7=0Sp-7$cQ1q4Ue8~J!e!s%>PXsV-F6H zdKKP-f!C(R-5ooG;TY#u?$XosVAMT)qh3cypv~(O2-mWnPVM%qwZQFJz~9bdRdC;` zj{)>te1u}PQc6?H^#Q#22C9m9og#Q3cv_Pj|GJ)I3G4^QRd8>4680IFu}G$6A!HwR z*5;2!pqIhTtNPXFmoi)>z1kAc@W74XuObxqD^ojx+my;hHS<-bSI%ekarrq?qYhn< M-myKsce{80Kl^I`Y5)KL literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/smart_tag.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/smart_tag.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47dd59e19ed9cc0368bc0c03703353de7fd95ee9 GIT binary patch literal 2032 zcmb7FOOM+&5GM7OY;QJc9(|`dw5SfXP~?;%C>EO{O?xl`I453IMPzMZ^>9e-X1(w& zz4jOM7wkFzhS#3@7m6b747Il9HV#l49C0X$GxL2j<7_mF2#o3L-?AqjA-|*29}Xx- zFx4CcC!Cg~qKr~pTcuUm%&r{fR4#KXk9m}QPq@vUUkG;uePuHrbeDUedqxjH_jv$% zVDu35kVl|LMvwSlO5))kV2#9752r#FSy`y;c`0H?JI75^YX6726*D2VH*IB6&vY;q zFHS|BiyXh}4;z#tnCc4<f-uSnvp8k8a9$DS8tH+D=rd(Lx4^om4<3oKoK~4^pJlVB zQXk|y`Xt9|*8|rU%ttWQA&3=Sk(8_~kYH{~F6kt;w0$C$+CIz5*w&5`<<An4mje)& zM2_I`=TAu`c#$PfGReP_#hFN+F6V7iC)0cmaVO(5QJ*Dc@jO{9+j&#pA3sP`(TcAY zS$>kugi4x)s29ugGI`m^6Ezp2O%#Taw%KgFSn426i@Ipj^aj{dNG@?adU0nr-tqcW zY)vL{fM3;FCDK$!X<9Y>w8Zr=O<$a5<<%P>EZ|bQaOXBOc@0&Jt|tyq^+90pY6v2* z9LuAgO(=O|-sG^kGd78M4BNXz3YS~IQsxOCC=Eo2WP%=TRs-&`As?7Tqin=|Offy| z2d#&FiZ7`iiSvcXTfx(Ixe(7F7o)5!o0n<b)FP%b05?oXlgBxRydJpTWW&dx^s`}Q zLrz#L2c(UeAwxJ2JLWR&pdsyZQ3^1tgY&99&XnM>D-oj3;s?0;5XDC*w!j)p5$w-l zD)f$cfGGSfZuJA*a_d~V|2RI7n6~md3QP?79tvm&9ZQ{MBtRPTnDAR4Fb}}xO4O}N z)BSwkgs;$XbsGfo9f5BVjVuRQ$KPKP&-{mZG<O73jX}JNd2X|i2i)U6lybyfU^&#I ze(*|<`ZOzvz5toG;L)M+|0=uV&obX@Dr382%3ODMeP>6~hM#X>e_MICcih{OV|DJt zgZ<PXSe@sl<cvK`xcwA-0&l-Y^XSYK1s(ivB);1Z{g?3e?a%}0(9cberz|j~73y%^ zI~5B}|A98zfqhBkr*M~vF2_1q4_t5R20LR{H=8Pv_h2u!I)FD<{0rM3;fyl3+7~xA zK5S5(0M%GAakw8<#G!NX-B^AG=Z;}2^wnUANd7Hxe-aPay>%-dbD?rsEZRn@vFiPu yNpzImeA_u`O3YN8;dWb!hIHd28tTH|Z2~%Y*tO_y@qXjpkiH)rUOT*Vc>N!y6o_d6 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/table.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/table.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f02b871e09994748cdb38688798432e72d17ca7a GIT binary patch literal 10511 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 H6i@yyLBHqM literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/views.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/views.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99aa11724ffed477da01614cc12b0f3d760b4449 GIT binary patch literal 3530 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 H>h1g&+q4Qr literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/worksheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/__pycache__/worksheet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78b3dcd53ea1fc653ffeef2581436c05400d17d4 GIT binary patch literal 25879 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 C8+1Sb literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_read_only.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_read_only.py new file mode 100644 index 00000000..fddf492d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_read_only.py @@ -0,0 +1,186 @@ +# Copyright (c) 2010-2021 openpyxl + +""" Read worksheets on-demand +""" + +from .worksheet import Worksheet +from openpyxl.cell.read_only import ReadOnlyCell, EMPTY_CELL +from openpyxl.utils import get_column_letter + +from ._reader import WorkSheetParser + + +def read_dimension(source): + parser = WorkSheetParser(source, []) + return parser.parse_dimensions() + + +class ReadOnlyWorksheet(object): + + _min_column = 1 + _min_row = 1 + _max_column = _max_row = None + + # from Standard Worksheet + # Methods from Worksheet + cell = Worksheet.cell + iter_rows = Worksheet.iter_rows + values = Worksheet.values + rows = Worksheet.rows + __getitem__ = Worksheet.__getitem__ + __iter__ = Worksheet.__iter__ + + + def __init__(self, parent_workbook, title, worksheet_path, shared_strings): + self.parent = parent_workbook + self.title = title + self.sheet_state = 'visible' + self._current_row = None + self._worksheet_path = worksheet_path + self._shared_strings = shared_strings + self._get_size() + + + def _get_size(self): + src = self._get_source() + parser = WorkSheetParser(src, []) + dimensions = parser.parse_dimensions() + src.close() + if dimensions is not None: + self._min_column, self._min_row, self._max_column, self._max_row = dimensions + + + def _get_source(self): + """Parse xml source on demand, must close after use""" + return self.parent._archive.open(self._worksheet_path) + + + def _cells_by_row(self, min_col, min_row, max_col, max_row, values_only=False): + """ + The source worksheet file may have columns or rows missing. + Missing cells will be created. + """ + filler = EMPTY_CELL + if values_only: + filler = None + + max_col = max_col or self.max_column + max_row = max_row or self.max_row + empty_row = [] + if max_col is not None: + empty_row = (filler,) * (max_col + 1 - min_col) + + counter = min_row + idx = 1 + src = self._get_source() + parser = WorkSheetParser(src, self._shared_strings, + data_only=self.parent.data_only, epoch=self.parent.epoch, + date_formats=self.parent._date_formats) + for idx, row in parser.parse(): + if max_row is not None and idx > max_row: + break + + # some rows are missing + for _ in range(counter, idx): + counter += 1 + yield empty_row + + # return cells from a row + if counter <= idx: + row = self._get_row(row, min_col, max_col, values_only) + counter += 1 + yield row + + src.close() # make sure source is always closed + + if max_row is not None and max_row < idx: + for _ in range(counter, max_row+1): + yield empty_row + + + def _get_row(self, row, min_col=1, max_col=None, values_only=False): + """ + Make sure a row contains always the same number of cells or values + """ + if not row and not max_col: # in case someone wants to force rows where there aren't any + return () + + max_col = max_col or row[-1]['column'] + row_width = max_col + 1 - min_col + + new_row = [EMPTY_CELL] * row_width + if values_only: + new_row = [None] * row_width + + for cell in row: + counter = cell['column'] + if min_col <= counter <= max_col: + idx = counter - min_col # position in list of cells returned + new_row[idx] = cell['value'] + if not values_only: + new_row[idx] = ReadOnlyCell(self, **cell) + + return tuple(new_row) + + + def _get_cell(self, row, column): + """Cells are returned by a generator which can be empty""" + for row in self._cells_by_row(column, row, column, row): + if row: + return row[0] + return EMPTY_CELL + + + def calculate_dimension(self, force=False): + if not all([self.max_column, self.max_row]): + if force: + self._calculate_dimension() + else: + raise ValueError("Worksheet is unsized, use calculate_dimension(force=True)") + return f"{get_column_letter(self.min_column)}{self.min_row}:{get_column_letter(self.max_column)}{self.max_row}" + + + def _calculate_dimension(self): + """ + Loop through all the cells to get the size of a worksheet. + Do this only if it is explicitly requested. + """ + + max_col = 0 + for r in self.rows: + if not r: + continue + cell = r[-1] + max_col = max(max_col, cell.column) + + self._max_row = cell.row + self._max_column = max_col + + + def reset_dimensions(self): + """ + Remove worksheet dimensions if these are incorrect in the worksheet source. + NB. This probably indicates a bug in the library or application that created + the workbook. + """ + self._max_row = self._max_column = None + + + @property + def min_row(self): + return self._min_row + + + @property + def max_row(self): + return self._max_row + + + @property + def min_column(self): + return self._min_column + + + @property + def max_column(self): + return self._max_column diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_reader.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_reader.py new file mode 100644 index 00000000..c20d8899 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_reader.py @@ -0,0 +1,455 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Reader for a single worksheet.""" +from copy import copy +from warnings import warn + +# compatibility imports +from openpyxl.xml.functions import iterparse + +# package imports +from openpyxl.cell import Cell, MergedCell +from openpyxl.cell.text import Text +from openpyxl.worksheet.dimensions import ( + ColumnDimension, + RowDimension, + SheetFormatProperties, +) + +from openpyxl.xml.constants import ( + SHEET_MAIN_NS, + EXT_TYPES, +) +from openpyxl.formatting.formatting import ConditionalFormatting +from openpyxl.formula.translate import Translator +from openpyxl.utils import ( + get_column_letter, + coordinate_to_tuple, + ) +from openpyxl.utils.datetime import from_excel, from_ISO8601, WINDOWS_EPOCH +from openpyxl.descriptors.excel import ExtensionList + +from .filters import AutoFilter +from .header_footer import HeaderFooter +from .hyperlink import HyperlinkList +from .merge import MergeCells +from .page import PageMargins, PrintOptions, PrintPageSetup +from .pagebreak import RowBreak, ColBreak +from .protection import SheetProtection +from .scenario import ScenarioList +from .views import SheetViewList +from .datavalidation import DataValidationList +from .table import TablePartList +from .properties import WorksheetProperties +from .dimensions import SheetDimension +from .related import Related + + +CELL_TAG = '{%s}c' % SHEET_MAIN_NS +VALUE_TAG = '{%s}v' % SHEET_MAIN_NS +FORMULA_TAG = '{%s}f' % SHEET_MAIN_NS +MERGE_TAG = '{%s}mergeCells' % SHEET_MAIN_NS +INLINE_STRING = "{%s}is" % SHEET_MAIN_NS +COL_TAG = '{%s}col' % SHEET_MAIN_NS +ROW_TAG = '{%s}row' % SHEET_MAIN_NS +CF_TAG = '{%s}conditionalFormatting' % SHEET_MAIN_NS +LEGACY_TAG = '{%s}legacyDrawing' % SHEET_MAIN_NS +PROT_TAG = '{%s}sheetProtection' % SHEET_MAIN_NS +EXT_TAG = "{%s}extLst" % SHEET_MAIN_NS +HYPERLINK_TAG = "{%s}hyperlinks" % SHEET_MAIN_NS +TABLE_TAG = "{%s}tableParts" % SHEET_MAIN_NS +PRINT_TAG = '{%s}printOptions' % SHEET_MAIN_NS +MARGINS_TAG = '{%s}pageMargins' % SHEET_MAIN_NS +PAGE_TAG = '{%s}pageSetup' % SHEET_MAIN_NS +HEADER_TAG = '{%s}headerFooter' % SHEET_MAIN_NS +FILTER_TAG = '{%s}autoFilter' % SHEET_MAIN_NS +VALIDATION_TAG = '{%s}dataValidations' % SHEET_MAIN_NS +PROPERTIES_TAG = '{%s}sheetPr' % SHEET_MAIN_NS +VIEWS_TAG = '{%s}sheetViews' % SHEET_MAIN_NS +FORMAT_TAG = '{%s}sheetFormatPr' % SHEET_MAIN_NS +ROW_BREAK_TAG = '{%s}rowBreaks' % SHEET_MAIN_NS +COL_BREAK_TAG = '{%s}colBreaks' % SHEET_MAIN_NS +SCENARIOS_TAG = '{%s}scenarios' % SHEET_MAIN_NS +DATA_TAG = '{%s}sheetData' % SHEET_MAIN_NS +DIMENSION_TAG = '{%s}dimension' % SHEET_MAIN_NS +CUSTOM_VIEWS_TAG = '{%s}customSheetViews' % SHEET_MAIN_NS + + +def _cast_number(value): + "Convert numbers as string to an int or float" + if "." in value or "E" in value or "e" in value: + return float(value) + return int(value) + + +class WorkSheetParser(object): + + def __init__(self, src, shared_strings, data_only=False, + epoch=WINDOWS_EPOCH, date_formats=set(), + timedelta_formats=set()): + self.min_row = self.min_col = None + self.epoch = epoch + self.source = src + self.shared_strings = shared_strings + self.data_only = data_only + self.shared_formulae = {} + self.array_formulae = {} + self.row_counter = self.col_counter = 0 + self.tables = TablePartList() + self.date_formats = date_formats + self.timedelta_formats = timedelta_formats + self.row_dimensions = {} + self.column_dimensions = {} + self.number_formats = [] + self.keep_vba = False + self.hyperlinks = HyperlinkList() + self.formatting = [] + self.legacy_drawing = None + self.merged_cells = None + self.row_breaks = RowBreak() + self.col_breaks = ColBreak() + + + def parse(self): + dispatcher = { + COL_TAG: self.parse_column_dimensions, + PROT_TAG: self.parse_sheet_protection, + EXT_TAG: self.parse_extensions, + CF_TAG: self.parse_formatting, + LEGACY_TAG: self.parse_legacy, + ROW_BREAK_TAG: self.parse_row_breaks, + COL_BREAK_TAG: self.parse_col_breaks, + CUSTOM_VIEWS_TAG: self.parse_custom_views, + } + + properties = { + PRINT_TAG: ('print_options', PrintOptions), + MARGINS_TAG: ('page_margins', PageMargins), + PAGE_TAG: ('page_setup', PrintPageSetup), + HEADER_TAG: ('HeaderFooter', HeaderFooter), + FILTER_TAG: ('auto_filter', AutoFilter), + VALIDATION_TAG: ('data_validations', DataValidationList), + PROPERTIES_TAG: ('sheet_properties', WorksheetProperties), + VIEWS_TAG: ('views', SheetViewList), + FORMAT_TAG: ('sheet_format', SheetFormatProperties), + SCENARIOS_TAG: ('scenarios', ScenarioList), + TABLE_TAG: ('tables', TablePartList), + HYPERLINK_TAG: ('hyperlinks', HyperlinkList), + MERGE_TAG: ('merged_cells', MergeCells), + + } + + it = iterparse(self.source) # add a finaliser to close the source when this becomes possible + + for _, element in it: + tag_name = element.tag + if tag_name in dispatcher: + dispatcher[tag_name](element) + element.clear() + elif tag_name in properties: + prop = properties[tag_name] + obj = prop[1].from_tree(element) + setattr(self, prop[0], obj) + element.clear() + elif tag_name == ROW_TAG: + row = self.parse_row(element) + element.clear() + yield row + + + def parse_dimensions(self): + """ + Get worksheet dimensions if they are provided. + """ + it = iterparse(self.source) + + for _event, element in it: + if element.tag == DIMENSION_TAG: + dim = SheetDimension.from_tree(element) + return dim.boundaries + + elif element.tag == DATA_TAG: + # Dimensions missing + break + element.clear() + + + def parse_cell(self, element): + data_type = element.get('t', 'n') + coordinate = element.get('r') + style_id = element.get('s', 0) + if style_id: + style_id = int(style_id) + + if data_type == "inlineStr": + value = None + else: + value = element.findtext(VALUE_TAG, None) or None + + if coordinate: + row, column = coordinate_to_tuple(coordinate) + self.col_counter = column + else: + self.col_counter += 1 + row, column = self.row_counter, self.col_counter + + if not self.data_only and element.find(FORMULA_TAG) is not None: + data_type = 'f' + value = self.parse_formula(element) + + elif value is not None: + if data_type == 'n': + value = _cast_number(value) + if style_id in self.date_formats: + data_type = 'd' + try: + value = from_excel( + value, self.epoch, timedelta=style_id in self.timedelta_formats + ) + except (OverflowError, ValueError): + msg = f"""Cell {coordinate} is marked as a date but the serial value {value} is outside the limits for dates. The cell will be treated as an error.""" + warn(msg) + data_type = "e" + value = "#VALUE!" + elif data_type == 's': + value = self.shared_strings[int(value)] + elif data_type == 'b': + value = bool(int(value)) + elif data_type == "str": + data_type = "s" + elif data_type == 'd': + value = from_ISO8601(value) + + elif data_type == 'inlineStr': + child = element.find(INLINE_STRING) + if child is not None: + data_type = 's' + richtext = Text.from_tree(child) + value = richtext.content + + return {'row':row, 'column':column, 'value':value, 'data_type':data_type, 'style_id':style_id} + + + def parse_formula(self, element): + """ + possible formulae types: shared, array, datatable + """ + formula = element.find(FORMULA_TAG) + formula_type = formula.get('t') + coordinate = element.get('r') + value = "=" + if formula.text is not None: + value += formula.text + + if formula_type == "array": + self.array_formulae[coordinate] = dict(formula.attrib) + + elif formula_type == "shared": + idx = formula.get('si') + if idx in self.shared_formulae: + trans = self.shared_formulae[idx] + value = trans.translate_formula(coordinate) + elif value != "=": + self.shared_formulae[idx] = Translator(value, coordinate) + + return value + + + def parse_column_dimensions(self, col): + attrs = dict(col.attrib) + column = get_column_letter(int(attrs['min'])) + attrs['index'] = column + self.column_dimensions[column] = attrs + + + def parse_row(self, row): + attrs = dict(row.attrib) + + if "r" in attrs: + try: + self.row_counter = int(attrs['r']) + except ValueError: + val = float(attrs['r']) + if val.is_integer(): + self.row_counter = int(val) + else: + raise ValueError(f"{attrs['r']} is not a valid row number") + else: + self.row_counter += 1 + self.col_counter = 0 + + keys = {k for k in attrs if not k.startswith('{')} + if keys - {'r', 'spans'}: + # don't create dimension objects unless they have relevant information + self.row_dimensions[str(self.row_counter)] = attrs + + cells = [self.parse_cell(el) for el in row] + return self.row_counter, cells + + + def parse_formatting(self, element): + try: + cf = ConditionalFormatting.from_tree(element) + self.formatting.append(cf) + except TypeError as e: + msg = f"Failed to load a conditional formatting rule. It will be discarded. Cause: {e}" + warn(msg) + + + def parse_sheet_protection(self, element): + protection = SheetProtection.from_tree(element) + password = element.get("password") + if password is not None: + protection.set_password(password, True) + self.protection = protection + + + def parse_extensions(self, element): + extLst = ExtensionList.from_tree(element) + for e in extLst.ext: + ext_type = EXT_TYPES.get(e.uri.upper(), "Unknown") + msg = "{0} extension is not supported and will be removed".format(ext_type) + warn(msg) + + + def parse_legacy(self, element): + obj = Related.from_tree(element) + self.legacy_drawing = obj.id + + + def parse_row_breaks(self, element): + brk = RowBreak.from_tree(element) + self.row_breaks = brk + + + def parse_col_breaks(self, element): + brk = ColBreak.from_tree(element) + self.col_breaks = brk + + + def parse_custom_views(self, element): + # clear page_breaks to avoid duplication which Excel doesn't like + # basically they're ignored in custom views + self.row_breaks = RowBreak() + self.col_breaks = ColBreak() + + +class WorksheetReader(object): + """ + Create a parser and apply it to a workbook + """ + + def __init__(self, ws, xml_source, shared_strings, data_only): + self.ws = ws + self.parser = WorkSheetParser(xml_source, shared_strings, + data_only, ws.parent.epoch, ws.parent._date_formats, + ws.parent._timedelta_formats) + self.tables = [] + + + def bind_cells(self): + for idx, row in self.parser.parse(): + for cell in row: + style = self.ws.parent._cell_styles[cell['style_id']] + c = Cell(self.ws, row=cell['row'], column=cell['column'], style_array=style) + c._value = cell['value'] + c.data_type = cell['data_type'] + self.ws._cells[(cell['row'], cell['column'])] = c + self.ws.formula_attributes = self.parser.array_formulae + if self.ws._cells: + self.ws._current_row = self.ws.max_row # use cells not row dimensions + + + def bind_formatting(self): + for cf in self.parser.formatting: + for rule in cf.rules: + if rule.dxfId is not None: + rule.dxf = self.ws.parent._differential_styles[rule.dxfId] + self.ws.conditional_formatting[cf] = rule + + + def bind_tables(self): + for t in self.parser.tables.tablePart: + rel = self.ws._rels[t.id] + self.tables.append(rel.Target) + + + def bind_merged_cells(self): + from openpyxl.worksheet.cell_range import MultiCellRange + from openpyxl.worksheet.merge import MergedCellRange + if not self.parser.merged_cells: + return + + ranges = [] + for cr in self.parser.merged_cells.mergeCell: + mcr = MergedCellRange(self.ws, cr.ref) + self.ws._clean_merge_range(mcr) + ranges.append(mcr) + self.ws.merged_cells = MultiCellRange(ranges) + + + def bind_hyperlinks(self): + for link in self.parser.hyperlinks.hyperlink: + if link.id: + rel = self.ws._rels[link.id] + link.target = rel.Target + if ":" in link.ref: + # range of cells + for row in self.ws[link.ref]: + for cell in row: + try: + cell.hyperlink = copy(link) + except AttributeError: + pass + else: + cell = self.ws[link.ref] + if isinstance(cell, MergedCell): + cell = self.normalize_merged_cell_link(cell.coordinate) + cell.hyperlink = link + + def normalize_merged_cell_link(self, coord): + """ + Returns the appropriate cell to which a hyperlink, which references a merged cell at the specified coordinates, + should be bound. + """ + for rng in self.ws.merged_cells: + if coord in rng: + return self.ws.cell(*rng.top[0]) + + def bind_col_dimensions(self): + for col, cd in self.parser.column_dimensions.items(): + if 'style' in cd: + key = int(cd['style']) + cd['style'] = self.ws.parent._cell_styles[key] + self.ws.column_dimensions[col] = ColumnDimension(self.ws, **cd) + + + def bind_row_dimensions(self): + for row, rd in self.parser.row_dimensions.items(): + if 's' in rd: + key = int(rd['s']) + rd['s'] = self.ws.parent._cell_styles[key] + self.ws.row_dimensions[int(row)] = RowDimension(self.ws, **rd) + + + def bind_properties(self): + for k in ('print_options', 'page_margins', 'page_setup', + 'HeaderFooter', 'auto_filter', 'data_validations', + 'sheet_properties', 'views', 'sheet_format', + 'row_breaks', 'col_breaks', 'scenarios', 'legacy_drawing', + 'protection', + ): + v = getattr(self.parser, k, None) + if v is not None: + setattr(self.ws, k, v) + + + def bind_all(self): + self.bind_cells() + self.bind_merged_cells() + self.bind_hyperlinks() + self.bind_formatting() + self.bind_col_dimensions() + self.bind_row_dimensions() + self.bind_tables() + self.bind_properties() diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_write_only.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_write_only.py new file mode 100644 index 00000000..06cd1ca8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_write_only.py @@ -0,0 +1,160 @@ +# Copyright (c) 2010-2021 openpyxl + + +"""Write worksheets to xml representations in an optimized way""" + +from inspect import isgenerator + +from openpyxl.cell import Cell, WriteOnlyCell +from openpyxl.workbook.child import _WorkbookChild +from .worksheet import Worksheet +from openpyxl.utils.exceptions import WorkbookAlreadySaved + +from ._writer import WorksheetWriter + + +class WriteOnlyWorksheet(_WorkbookChild): + """ + Streaming worksheet. Optimised to reduce memory by writing rows just in + time. + Cells can be styled and have comments Styles for rows and columns + must be applied before writing cells + """ + + __saved = False + _writer = None + _rows = None + _rel_type = Worksheet._rel_type + _path = Worksheet._path + mime_type = Worksheet.mime_type + + # copy methods from Standard worksheet + _add_row = Worksheet._add_row + _add_column = Worksheet._add_column + add_chart = Worksheet.add_chart + add_image = Worksheet.add_image + add_table = Worksheet.add_table + tables = Worksheet.tables + print_titles = Worksheet.print_titles + print_title_cols = Worksheet.print_title_cols + print_title_rows = Worksheet.print_title_rows + freeze_panes = Worksheet.freeze_panes + print_area = Worksheet.print_area + sheet_view = Worksheet.sheet_view + _setup = Worksheet._setup + + def __init__(self, parent, title): + super(WriteOnlyWorksheet, self).__init__(parent, title) + self._max_col = 0 + self._max_row = 0 + self._setup() + + @property + def closed(self): + return self.__saved + + + def _write_rows(self): + """ + Send rows to the writer's stream + """ + try: + xf = self._writer.xf.send(True) + except StopIteration: + self._already_saved() + + with xf.element("sheetData"): + row_idx = 1 + try: + while True: + row = (yield) + row = self._values_to_row(row, row_idx) + self._writer.write_row(xf, row, row_idx) + row_idx += 1 + except GeneratorExit: + pass + + self._writer.xf.send(None) + + + def _get_writer(self): + if self._writer is None: + self._writer = WorksheetWriter(self) + self._writer.write_top() + + + def close(self): + if self.__saved: + self._already_saved() + + self._get_writer() + + if self._rows is None: + self._writer.write_rows() + else: + self._rows.close() + + self._writer.write_tail() + + self._writer.close() + self.__saved = True + + + def append(self, row): + """ + :param row: iterable containing values to append + :type row: iterable + """ + + if (not isgenerator(row) and + not isinstance(row, (list, tuple, range)) + ): + self._invalid_row(row) + + self._get_writer() + + if self._rows is None: + self._rows = self._write_rows() + next(self._rows) + + self._rows.send(row) + + + def _values_to_row(self, values, row_idx): + """ + Convert whatever has been appended into a form suitable for work_rows + """ + cell = WriteOnlyCell(self) + + for col_idx, value in enumerate(values, 1): + if value is None: + continue + try: + cell.value = value + except ValueError: + if isinstance(value, Cell): + cell = value + else: + raise ValueError + + cell.column = col_idx + cell.row = row_idx + + if cell.hyperlink is not None: + cell.hyperlink.ref = cell.coordinate + + yield cell + + # reset cell if style applied + if cell.has_style or cell.hyperlink: + cell = WriteOnlyCell(self) + + + def _already_saved(self): + raise WorkbookAlreadySaved('Workbook has already been saved and cannot be modified or saved anymore.') + + + def _invalid_row(self, iterable): + raise TypeError('Value must be a list, tuple, range or a generator Supplied value is {0}'.format( + type(iterable)) + ) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_writer.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_writer.py new file mode 100644 index 00000000..38cab8f8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/_writer.py @@ -0,0 +1,390 @@ +# Copyright (c) 2010-2021 openpyxl + +import atexit +from collections import defaultdict +from io import BytesIO +import os +from tempfile import NamedTemporaryFile +from warnings import warn + +from openpyxl.xml.functions import xmlfile +from openpyxl.xml.constants import SHEET_MAIN_NS + +from openpyxl.comments.comment_sheet import CommentRecord +from openpyxl.packaging.relationship import Relationship, RelationshipList +from openpyxl.styles.differential import DifferentialStyle + +from .dimensions import SheetDimension +from .hyperlink import HyperlinkList +from .merge import MergeCell, MergeCells +from .related import Related +from .table import TablePartList + +from openpyxl.cell._writer import write_cell + + +ALL_TEMP_FILES = [] + +@atexit.register +def _openpyxl_shutdown(): + for path in ALL_TEMP_FILES: + if os.path.exists(path): + os.remove(path) + + +def create_temporary_file(suffix=''): + fobj = NamedTemporaryFile(mode='w+', suffix=suffix, + prefix='openpyxl.', delete=False) + filename = fobj.name + fobj.close() + ALL_TEMP_FILES.append(filename) + return filename + + +class WorksheetWriter: + + + def __init__(self, ws, out=None): + self.ws = ws + self.ws._hyperlinks = [] + self.ws._comments = [] + if out is None: + out = create_temporary_file() + self.out = out + self._rels = RelationshipList() + self.xf = self.get_stream() + next(self.xf) # start generator + + + def write_properties(self): + props = self.ws.sheet_properties + self.xf.send(props.to_tree()) + + + def write_dimensions(self): + """ + Write worksheet size if known + """ + ref = getattr(self.ws, 'calculate_dimension', None) + if ref: + dim = SheetDimension(ref()) + self.xf.send(dim.to_tree()) + + + def write_format(self): + self.ws.sheet_format.outlineLevelCol = self.ws.column_dimensions.max_outline + fmt = self.ws.sheet_format + self.xf.send(fmt.to_tree()) + + + def write_views(self): + views = self.ws.views + self.xf.send(views.to_tree()) + + + def write_cols(self): + cols = self.ws.column_dimensions + self.xf.send(cols.to_tree()) + + + def write_top(self): + """ + Write all elements up to rows: + properties + dimensions + views + format + cols + """ + self.write_properties() + self.write_dimensions() + self.write_views() + self.write_format() + self.write_cols() + + + def rows(self): + """Return all rows, and any cells that they contain""" + # order cells by row + rows = defaultdict(list) + for (row, col), cell in sorted(self.ws._cells.items()): + rows[row].append(cell) + + # add empty rows if styling has been applied + for row in self.ws.row_dimensions.keys() - rows.keys(): + rows[row] = [] + + return sorted(rows.items()) + + + def write_rows(self): + xf = self.xf.send(True) + + with xf.element("sheetData"): + for row_idx, row in self.rows(): + self.write_row(xf, row, row_idx) + + self.xf.send(None) # return control to generator + + + def write_row(self, xf, row, row_idx): + attrs = {'r': f"{row_idx}"} + dims = self.ws.row_dimensions + attrs.update(dims.get(row_idx, {})) + + with xf.element("row", attrs): + + for cell in row: + if cell._comment is not None: + comment = CommentRecord.from_cell(cell) + self.ws._comments.append(comment) + if ( + cell._value is None + and not cell.has_style + and not cell._comment + ): + continue + write_cell(xf, self.ws, cell, cell.has_style) + + + def write_protection(self): + prot = self.ws.protection + if prot: + self.xf.send(prot.to_tree()) + + + def write_scenarios(self): + scenarios = self.ws.scenarios + if scenarios: + self.xf.send(scenarios.to_tree()) + + + def write_filter(self): + flt = self.ws.auto_filter + if flt: + self.xf.send(flt.to_tree()) + + + def write_sort(self): + """ + As per discusion with the OOXML Working Group global sort state is not required. + openpyxl never reads it from existing files + """ + pass + + + def write_merged_cells(self): + merged = self.ws.merged_cells + if merged: + cells = [MergeCell(str(ref)) for ref in self.ws.merged_cells] + self.xf.send(MergeCells(mergeCell=cells).to_tree()) + + + def write_formatting(self): + df = DifferentialStyle() + wb = self.ws.parent + for cf in self.ws.conditional_formatting: + for rule in cf.rules: + if rule.dxf and rule.dxf != df: + rule.dxfId = wb._differential_styles.add(rule.dxf) + self.xf.send(cf.to_tree()) + + + def write_validations(self): + dv = self.ws.data_validations + if dv: + self.xf.send(dv.to_tree()) + + + def write_hyperlinks(self): + links = HyperlinkList() + + for link in self.ws._hyperlinks: + if link.target: + rel = Relationship(type="hyperlink", TargetMode="External", Target=link.target) + self._rels.append(rel) + link.id = rel.id + links.hyperlink.append(link) + + if links: + self.xf.send(links.to_tree()) + + + def write_print(self): + print_options = self.ws.print_options + if print_options: + self.xf.send(print_options.to_tree()) + + + def write_margins(self): + margins = self.ws.page_margins + if margins: + self.xf.send(margins.to_tree()) + + + def write_page(self): + setup = self.ws.page_setup + if setup: + self.xf.send(setup.to_tree()) + + + def write_header(self): + hf = self.ws.HeaderFooter + if hf: + self.xf.send(hf.to_tree()) + + + def write_breaks(self): + brks = (self.ws.row_breaks, self.ws.col_breaks) + for brk in brks: + if brk: + self.xf.send(brk.to_tree()) + + + def write_drawings(self): + if self.ws._charts or self.ws._images: + rel = Relationship(type="drawing", Target="") + self._rels.append(rel) + drawing = Related() + drawing.id = rel.id + self.xf.send(drawing.to_tree("drawing")) + + + def write_legacy(self): + """ + Comments & VBA controls use VML and require an additional element + that is no longer in the specification. + """ + if (self.ws.legacy_drawing is not None or self.ws._comments): + legacy = Related(id="anysvml") + self.xf.send(legacy.to_tree("legacyDrawing")) + + + def write_tables(self): + tables = TablePartList() + + for table in self.ws.tables.values(): + if not table.tableColumns: + table._initialise_columns() + if table.headerRowCount: + try: + row = self.ws[table.ref][0] + for cell, col in zip(row, table.tableColumns): + if cell.data_type != "s": + warn("File may not be readable: column headings must be strings.") + col.name = str(cell.value) + except TypeError: + warn("Column headings are missing, file may not be readable") + rel = Relationship(Type=table._rel_type, Target="") + self._rels.append(rel) + table._rel_id = rel.Id + tables.append(Related(id=rel.Id)) + + if tables: + self.xf.send(tables.to_tree()) + + + def get_stream(self): + with xmlfile(self.out) as xf: + with xf.element("worksheet", xmlns=SHEET_MAIN_NS): + try: + while True: + el = (yield) + if el is True: + yield xf + elif el is None: # et_xmlfile chokes + continue + else: + xf.write(el) + except GeneratorExit: + pass + + + def write_tail(self): + """ + Write all elements after the rows + calc properties + protection + protected ranges # + scenarios + filters + sorts # always ignored + data consolidation # + custom views # + merged cells + phonetic properties # + conditional formatting + data validation + hyperlinks + print options + page margins + page setup + header + row breaks + col breaks + custom properties # + cell watches # + ignored errors # + smart tags # + drawing + drawingHF # + background # + OLE objects # + controls # + web publishing # + tables + """ + self.write_protection() + self.write_scenarios() + self.write_filter() + self.write_merged_cells() + self.write_formatting() + self.write_validations() + self.write_hyperlinks() + self.write_print() + self.write_margins() + self.write_page() + self.write_header() + self.write_breaks() + self.write_drawings() + self.write_legacy() + self.write_tables() + + + def write(self): + """ + High level + """ + self.write_top() + self.write_rows() + self.write_tail() + self.close() + + + def close(self): + """ + Close the context manager + """ + if self.xf: + self.xf.close() + + + def read(self): + """ + Close the context manager and return serialised XML + """ + self.close() + if isinstance(self.out, BytesIO): + return self.out.getvalue() + with open(self.out, "rb") as src: + out = src.read() + + return out + + + def cleanup(self): + """ + Remove tempfile + """ + os.remove(self.out) + ALL_TEMP_FILES.remove(self.out) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_range.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_range.py new file mode 100644 index 00000000..7a0c2c3f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_range.py @@ -0,0 +1,501 @@ +# Copyright (c) 2010-2021 openpyxl + +from copy import copy + +from openpyxl.descriptors import Strict +from openpyxl.descriptors import MinMax, Sequence +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.utils import ( + range_boundaries, + range_to_tuple, + get_column_letter, + quote_sheetname, +) + + +class CellRange(Serialisable): + """ + Represents a range in a sheet: title and coordinates. + + This object is used to perform operations on ranges, like: + + - shift, expand or shrink + - union/intersection with another sheet range, + + We can check whether a range is: + + - equal or not equal to another, + - disjoint of another, + - contained in another. + + We can get: + + - the size of a range. + - the range bounds (vertices) + - the coordinates, + - the string representation, + + """ + + min_col = MinMax(min=1, max=18278, expected_type=int) + min_row = MinMax(min=1, max=1048576, expected_type=int) + max_col = MinMax(min=1, max=18278, expected_type=int) + max_row = MinMax(min=1, max=1048576, expected_type=int) + + + def __init__(self, range_string=None, min_col=None, min_row=None, + max_col=None, max_row=None, title=None): + if range_string is not None: + if "!" in range_string: + title, (min_col, min_row, max_col, max_row) = range_to_tuple(range_string) + else: + min_col, min_row, max_col, max_row = range_boundaries(range_string) + + self.min_col = min_col + self.min_row = min_row + self.max_col = max_col + self.max_row = max_row + self.title = title + + if min_col > max_col: + fmt = "{max_col} must be greater than {min_col}" + raise ValueError(fmt.format(min_col=min_col, max_col=max_col)) + if min_row > max_row: + fmt = "{max_row} must be greater than {min_row}" + raise ValueError(fmt.format(min_row=min_row, max_row=max_row)) + + + @property + def bounds(self): + """ + Vertices of the range as a tuple + """ + return self.min_col, self.min_row, self.max_col, self.max_row + + + @property + def coord(self): + """ + Excel-style representation of the range + """ + fmt = "{min_col}{min_row}:{max_col}{max_row}" + if (self.min_col == self.max_col + and self.min_row == self.max_row): + fmt = "{min_col}{min_row}" + + return fmt.format( + min_col=get_column_letter(self.min_col), + min_row=self.min_row, + max_col=get_column_letter(self.max_col), + max_row=self.max_row + ) + + @property + def rows(self): + """ + Return cell coordinates as rows + """ + for row in range(self.min_row, self.max_row+1): + yield [(row, col) for col in range(self.min_col, self.max_col+1)] + + + @property + def cols(self): + """ + Return cell coordinates as columns + """ + for col in range(self.min_col, self.max_col+1): + yield [(row, col) for row in range(self.min_row, self.max_row+1)] + + + @property + def cells(self): + from itertools import product + return product(range(self.min_row, self.max_row+1), range(self.min_col, self.max_col+1)) + + + def _check_title(self, other): + """ + Check whether comparisons between ranges are possible. + Cannot compare ranges from different worksheets + Skip if the range passed in has no title. + """ + if not isinstance(other, CellRange): + raise TypeError(repr(type(other))) + + if other.title and self.title != other.title: + raise ValueError("Cannot work with ranges from different worksheets") + + + def __repr__(self): + fmt = u"<{cls} {coord}>" + if self.title: + fmt = u"<{cls} {title!r}!{coord}>" + return fmt.format(cls=self.__class__.__name__, title=self.title, coord=self.coord) + + + def __str__(self): + fmt = "{coord}" + title = self.title + if title: + fmt = u"{title}!{coord}" + title = quote_sheetname(title) + return fmt.format(title=title, coord=self.coord) + + + def __copy__(self): + return self.__class__(min_col=self.min_col, min_row=self.min_row, + max_col=self.max_col, max_row=self.max_row, + title=self.title) + + + def shift(self, col_shift=0, row_shift=0): + """ + Shift the focus of the range according to the shift values (*col_shift*, *row_shift*). + + :type col_shift: int + :param col_shift: number of columns to be moved by, can be negative + :type row_shift: int + :param row_shift: number of rows to be moved by, can be negative + :raise: :class:`ValueError` if any row or column index < 1 + """ + + if (self.min_col + col_shift <= 0 + or self.min_row + row_shift <= 0): + raise ValueError("Invalid shift value: col_shift={0}, row_shift={1}".format(col_shift, row_shift)) + self.min_col += col_shift + self.min_row += row_shift + self.max_col += col_shift + self.max_row += row_shift + + + def __ne__(self, other): + """ + Test whether the ranges are not equal. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range + :return: ``True`` if *range* != *other*. + """ + try: + self._check_title(other) + except ValueError: + return True + + return ( + other.min_row != self.min_row + or self.max_row != other.max_row + or other.min_col != self.min_col + or self.max_col != other.max_col + ) + + + def __eq__(self, other): + """ + Test whether the ranges are equal. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range + :return: ``True`` if *range* == *other*. + """ + return not self.__ne__(other) + + + def issubset(self, other): + """ + Test whether every cell in this range is also in *other*. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range + :return: ``True`` if *range* <= *other*. + """ + self._check_title(other) + + return other.__superset(self) + + __le__ = issubset + + + def __lt__(self, other): + """ + Test whether *other* contains every cell of this range, and more. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range + :return: ``True`` if *range* < *other*. + """ + return self.__le__(other) and self.__ne__(other) + + + def __superset(self, other): + return ( + (self.min_row <= other.min_row <= other.max_row <= self.max_row) + and + (self.min_col <= other.min_col <= other.max_col <= self.max_col) + ) + + + def issuperset(self, other): + """ + Test whether every cell in *other* is in this range. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range + :return: ``True`` if *range* >= *other* (or *other* in *range*). + """ + self._check_title(other) + + return self.__superset(other) + + __ge__ = issuperset + + + def __contains__(self, coord): + """ + Check whether the range contains a particular cell coordinate + """ + cr = self.__class__(coord) + return self.__superset(cr) + + + def __gt__(self, other): + """ + Test whether this range contains every cell in *other*, and more. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range + :return: ``True`` if *range* > *other*. + """ + return self.__ge__(other) and self.__ne__(other) + + + def isdisjoint(self, other): + """ + Return ``True`` if this range has no cell in common with *other*. + Ranges are disjoint if and only if their intersection is the empty range. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range. + :return: ``True`` if the range has no cells in common with other. + """ + self._check_title(other) + + # Sort by top-left vertex + if self.bounds > other.bounds: + self, other = other, self + + return (self.max_col < other.min_col + or self.max_row < other.min_row + or other.max_row < self.min_row) + + + def intersection(self, other): + """ + Return a new range with cells common to this range and *other* + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range. + :return: the intersecting sheet range. + :raise: :class:`ValueError` if the *other* range doesn't intersect + with this range. + """ + if self.isdisjoint(other): + raise ValueError("Range {0} doesn't intersect {0}".format(self, other)) + + min_row = max(self.min_row, other.min_row) + max_row = min(self.max_row, other.max_row) + min_col = max(self.min_col, other.min_col) + max_col = min(self.max_col, other.max_col) + + return CellRange(min_col=min_col, min_row=min_row, max_col=max_col, + max_row=max_row) + + __and__ = intersection + + + def union(self, other): + """ + Return the minimal superset of this range and *other*. This new range + will contain all cells from this range, *other*, and any additional + cells required to form a rectangular ``CellRange``. + + :type other: openpyxl.worksheet.cell_range.CellRange + :param other: Other sheet range. + :return: a ``CellRange`` that is a superset of this and *other*. + """ + self._check_title(other) + + min_row = min(self.min_row, other.min_row) + max_row = max(self.max_row, other.max_row) + min_col = min(self.min_col, other.min_col) + max_col = max(self.max_col, other.max_col) + return CellRange(min_col=min_col, min_row=min_row, max_col=max_col, + max_row=max_row, title=self.title) + + __or__ = union + + + def __iter__(self): + """ + For use as a dictionary elsewhere in the library. + """ + for x in self.__attrs__: + if x == "title": + continue + v = getattr(self, x) + yield x, v + + + def expand(self, right=0, down=0, left=0, up=0): + """ + Expand the range by the dimensions provided. + + :type right: int + :param right: expand range to the right by this number of cells + :type down: int + :param down: expand range down by this number of cells + :type left: int + :param left: expand range to the left by this number of cells + :type up: int + :param up: expand range up by this number of cells + """ + self.min_col -= left + self.min_row -= up + self.max_col += right + self.max_row += down + + + def shrink(self, right=0, bottom=0, left=0, top=0): + """ + Shrink the range by the dimensions provided. + + :type right: int + :param right: shrink range from the right by this number of cells + :type down: int + :param down: shrink range from the top by this number of cells + :type left: int + :param left: shrink range from the left by this number of cells + :type up: int + :param up: shrink range from the bottown by this number of cells + """ + self.min_col += left + self.min_row += top + self.max_col -= right + self.max_row -= bottom + + + @property + def size(self): + """ Return the size of the range as a dictionary of rows and columns. """ + cols = self.max_col + 1 - self.min_col + rows = self.max_row + 1 - self.min_row + return {'columns':cols, 'rows':rows} + + + @property + def top(self): + """A list of cell coordinates that comprise the top of the range""" + return [(self.min_row, col) for col in range(self.min_col, self.max_col+1)] + + + @property + def bottom(self): + """A list of cell coordinates that comprise the bottom of the range""" + return [(self.max_row, col) for col in range(self.min_col, self.max_col+1)] + + + @property + def left(self): + """A list of cell coordinates that comprise the left-side of the range""" + return [(row, self.min_col) for row in range(self.min_row, self.max_row+1)] + + + @property + def right(self): + """A list of cell coordinates that comprise the right-side of the range""" + return [(row, self.max_col) for row in range(self.min_row, self.max_row+1)] + + +class MultiCellRange(Strict): + + + ranges = Sequence(expected_type=CellRange) + + + def __init__(self, ranges=()): + if isinstance(ranges, str): + ranges = [CellRange(r) for r in ranges.split()] + self.ranges = ranges + + + def __contains__(self, coord): + if isinstance(coord, str): + coord = CellRange(coord) + for r in self.ranges: + if coord <= r: + return True + return False + + + def __repr__(self): + ranges = " ".join([str(r) for r in self.ranges]) + return "<{0} [{1}]>".format(self.__class__.__name__, ranges) + + + def __str__(self): + ranges = u" ".join([str(r) for r in self.ranges]) + return ranges + + __str__ = __str__ + + + def add(self, coord): + """ + Add a cell coordinate or CellRange + """ + cr = coord + if isinstance(coord, str): + cr = CellRange(coord) + elif not isinstance(coord, CellRange): + raise ValueError("You can only add CellRanges") + if cr not in self: + self.ranges.append(cr) + + + def __iadd__(self, coord): + self.add(coord) + return self + + + def __eq__(self, other): + if isinstance(other, str): + other = self.__class__(other) + return self.ranges == other.ranges + + + def __ne__(self, other): + return not self == other + + + def __bool__(self): + return bool(self.ranges) + + + def remove(self, coord): + if not isinstance(coord, CellRange): + coord = CellRange(coord) + self.ranges.remove(coord) + + + def __iter__(self): + for cr in self.ranges: + yield cr + + + def __copy__(self): + n = MultiCellRange() + + for r in self.ranges: + n.ranges.append(copy(r)) + return n diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_watch.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_watch.py new file mode 100644 index 00000000..dea89caf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/cell_watch.py @@ -0,0 +1,34 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + String, +) + +# could be done using a nestedSequence + +class CellWatch(Serialisable): + + tagname = "cellWatch" + + r = String() + + def __init__(self, + r=None, + ): + self.r = r + + +class CellWatches(Serialisable): + + tagname = "cellWatches" + + cellWatch = Sequence(expected_type=CellWatch) + + __elements__ = ('cellWatch',) + + def __init__(self, + cellWatch=(), + ): + self.cellWatch = cellWatch + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/controls.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/controls.py new file mode 100644 index 00000000..3c303e8b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/controls.py @@ -0,0 +1,107 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Integer, + String, + Sequence, +) + +from openpyxl.descriptors.excel import Relation +from .ole import ObjectAnchor + + +class ControlProperty(Serialisable): + + tagname = "controlPr" + + anchor = Typed(expected_type=ObjectAnchor, ) + locked = Bool(allow_none=True) + defaultSize = Bool(allow_none=True) + _print = Bool(allow_none=True) + disabled = Bool(allow_none=True) + recalcAlways = Bool(allow_none=True) + uiObject = Bool(allow_none=True) + autoFill = Bool(allow_none=True) + autoLine = Bool(allow_none=True) + autoPict = Bool(allow_none=True) + macro = String(allow_none=True) + altText = String(allow_none=True) + linkedCell = String(allow_none=True) + listFillRange = String(allow_none=True) + cf = String(allow_none=True) + id = Relation(allow_none=True) + + __elements__ = ('anchor',) + + def __init__(self, + anchor=None, + locked=True, + defaultSize=True, + _print=True, + disabled=False, + recalcAlways=False, + uiObject=False, + autoFill=True, + autoLine=True, + autoPict=True, + macro=None, + altText=None, + linkedCell=None, + listFillRange=None, + cf='pict', + id=None, + ): + self.anchor = anchor + self.locked = locked + self.defaultSize = defaultSize + self._print = _print + self.disabled = disabled + self.recalcAlways = recalcAlways + self.uiObject = uiObject + self.autoFill = autoFill + self.autoLine = autoLine + self.autoPict = autoPict + self.macro = macro + self.altText = altText + self.linkedCell = linkedCell + self.listFillRange = listFillRange + self.cf = cf + self.id = id + + +class Control(Serialisable): + + tagname = "control" + + controlPr = Typed(expected_type=ControlProperty, allow_none=True) + shapeId = Integer() + name = String(allow_none=True) + + __elements__ = ('controlPr',) + + def __init__(self, + controlPr=None, + shapeId=None, + name=None, + ): + self.controlPr = controlPr + self.shapeId = shapeId + self.name = name + + +class Controls(Serialisable): + + tagname = "controls" + + control = Sequence(expected_type=Control) + + __elements__ = ('control',) + + def __init__(self, + control=(), + ): + self.control = control + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/copier.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/copier.py new file mode 100644 index 00000000..e353bdb9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/copier.py @@ -0,0 +1,70 @@ +# Copyright (c) 2010-2021 openpyxl + +#standard lib imports +from copy import copy + +from .worksheet import Worksheet + + +class WorksheetCopy(object): + """ + Copy the values, styles, dimensions, merged cells, margins, and + print/page setup from one worksheet to another within the same + workbook. + """ + + def __init__(self, source_worksheet, target_worksheet): + self.source = source_worksheet + self.target = target_worksheet + self._verify_resources() + + + def _verify_resources(self): + + if (not isinstance(self.source, Worksheet) + and not isinstance(self.target, Worksheet)): + raise TypeError("Can only copy worksheets") + + if self.source is self.target: + raise ValueError("Cannot copy a worksheet to itself") + + if self.source.parent != self.target.parent: + raise ValueError('Cannot copy between worksheets from different workbooks') + + + def copy_worksheet(self): + self._copy_cells() + self._copy_dimensions() + + self.target.sheet_format = copy(self.source.sheet_format) + self.target.sheet_properties = copy(self.source.sheet_properties) + self.target.merged_cells = copy(self.source.merged_cells) + self.target.page_margins = copy(self.source.page_margins) + self.target.page_setup = copy(self.source.page_setup) + self.target.print_options = copy(self.source.print_options) + + + def _copy_cells(self): + for (row, col), source_cell in self.source._cells.items(): + target_cell = self.target.cell(column=col, row=row) + + target_cell._value = source_cell._value + target_cell.data_type = source_cell.data_type + + if source_cell.has_style: + target_cell._style = copy(source_cell._style) + + if source_cell.hyperlink: + target_cell._hyperlink = copy(source_cell.hyperlink) + + if source_cell.comment: + target_cell.comment = copy(source_cell.comment) + + + def _copy_dimensions(self): + for attr in ('row_dimensions', 'column_dimensions'): + src = getattr(self.source, attr) + target = getattr(self.target, attr) + for key, dim in src.items(): + target[key] = copy(dim) + target[key].worksheet = self.target diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/custom.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/custom.py new file mode 100644 index 00000000..b3af5c91 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/custom.py @@ -0,0 +1,35 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + String, + Sequence, +) + +# can be done with a nested sequence + + +class CustomProperty(Serialisable): + + tagname = "customProperty" + + name = String() + + def __init__(self, + name=None, + ): + self.name = name + + +class CustomProperties(Serialisable): + + tagname = "customProperties" + + customPr = Sequence(expected_type=CustomProperty) + + __elements__ = ('customPr',) + + def __init__(self, + customPr=(), + ): + self.customPr = customPr + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/datavalidation.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/datavalidation.py new file mode 100644 index 00000000..37772d7e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/datavalidation.py @@ -0,0 +1,203 @@ +# Copyright (c) 2010-2021 openpyxl + +from collections import defaultdict +from itertools import chain +from operator import itemgetter + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Bool, + NoneSet, + String, + Sequence, + Alias, + Integer, + Convertible, +) +from openpyxl.descriptors.nested import NestedText + +from openpyxl.utils import ( + rows_from_range, + coordinate_to_tuple, + get_column_letter, +) + + +def collapse_cell_addresses(cells, input_ranges=()): + """ Collapse a collection of cell co-ordinates down into an optimal + range or collection of ranges. + + E.g. Cells A1, A2, A3, B1, B2 and B3 should have the data-validation + object applied, attempt to collapse down to a single range, A1:B3. + + Currently only collapsing contiguous vertical ranges (i.e. above + example results in A1:A3 B1:B3). + """ + + ranges = list(input_ranges) + + # convert cell into row, col tuple + raw_coords = (coordinate_to_tuple(cell) for cell in cells) + + # group by column in order + grouped_coords = defaultdict(list) + for row, col in sorted(raw_coords, key=itemgetter(1)): + grouped_coords[col].append(row) + + # create range string from first and last row in column + for col, cells in grouped_coords.items(): + col = get_column_letter(col) + fmt = "{0}{1}:{2}{3}" + if len(cells) == 1: + fmt = "{0}{1}" + r = fmt.format(col, min(cells), col, max(cells)) + ranges.append(r) + + return " ".join(ranges) + + +def expand_cell_ranges(range_string): + """ + Expand cell ranges to a sequence of addresses. + Reverse of collapse_cell_addresses + Eg. converts "A1:A2 B1:B2" to (A1, A2, B1, B2) + """ + # expand ranges to rows and then flatten + rows = (rows_from_range(rs) for rs in range_string.split()) # list of rows + cells = (chain(*row) for row in rows) # flatten rows + return set(chain(*cells)) + + +from .cell_range import MultiCellRange + + +class DataValidation(Serialisable): + + tagname = "dataValidation" + + sqref = Convertible(expected_type=MultiCellRange) + cells = Alias("sqref") + ranges = Alias("sqref") + + showErrorMessage = Bool() + showDropDown = Bool(allow_none=True) + hide_drop_down = Alias('showDropDown') + showInputMessage = Bool() + showErrorMessage = Bool() + allowBlank = Bool() + allow_blank = Alias('allowBlank') + + errorTitle = String(allow_none = True) + error = String(allow_none = True) + promptTitle = String(allow_none = True) + prompt = String(allow_none = True) + formula1 = NestedText(allow_none=True, expected_type=str) + formula2 = NestedText(allow_none=True, expected_type=str) + + type = NoneSet(values=("whole", "decimal", "list", "date", "time", + "textLength", "custom")) + errorStyle = NoneSet(values=("stop", "warning", "information")) + imeMode = NoneSet(values=("noControl", "off", "on", "disabled", + "hiragana", "fullKatakana", "halfKatakana", "fullAlpha","halfAlpha", + "fullHangul", "halfHangul")) + operator = NoneSet(values=("between", "notBetween", "equal", "notEqual", + "lessThan", "lessThanOrEqual", "greaterThan", "greaterThanOrEqual")) + validation_type = Alias('type') + + def __init__(self, + type=None, + formula1=None, + formula2=None, + showErrorMessage=True, + showInputMessage=True, + showDropDown=None, + allowBlank=None, + sqref=(), + promptTitle=None, + errorStyle=None, + error=None, + prompt=None, + errorTitle=None, + imeMode=None, + operator=None, + allow_blank=None, + ): + self.sqref = sqref + self.showDropDown = showDropDown + self.imeMode = imeMode + self.operator = operator + self.formula1 = formula1 + self.formula2 = formula2 + if allow_blank is not None: + allowBlank = allow_blank + self.allowBlank = allowBlank + self.showErrorMessage = showErrorMessage + self.showInputMessage = showInputMessage + self.type = type + self.promptTitle = promptTitle + self.errorStyle = errorStyle + self.error = error + self.prompt = prompt + self.errorTitle = errorTitle + + + def add(self, cell): + """Adds a cell or cell coordinate to this validator""" + if hasattr(cell, "coordinate"): + cell = cell.coordinate + self.sqref += cell + + + def __contains__(self, cell): + if hasattr(cell, "coordinate"): + cell = cell.coordinate + return cell in self.sqref + + +class DataValidationList(Serialisable): + + tagname = "dataValidations" + + disablePrompts = Bool(allow_none=True) + xWindow = Integer(allow_none=True) + yWindow = Integer(allow_none=True) + dataValidation = Sequence(expected_type=DataValidation) + + __elements__ = ('dataValidation',) + __attrs__ = ('disablePrompts', 'xWindow', 'yWindow', 'count') + + def __init__(self, + disablePrompts=None, + xWindow=None, + yWindow=None, + count=None, + dataValidation=(), + ): + self.disablePrompts = disablePrompts + self.xWindow = xWindow + self.yWindow = yWindow + self.dataValidation = dataValidation + + + @property + def count(self): + return len(self) + + + def __len__(self): + return len(self.dataValidation) + + + def append(self, dv): + self.dataValidation.append(dv) + + + def to_tree(self, tagname=None): + """ + Need to skip validations that have no cell ranges + """ + ranges = self.dataValidation # copy + self.dataValidation = [r for r in self.dataValidation if bool(r.sqref)] + xml = super(DataValidationList, self).to_tree(tagname) + self.dataValidation = ranges + return xml diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/dimensions.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/dimensions.py new file mode 100644 index 00000000..0ecfeed7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/dimensions.py @@ -0,0 +1,296 @@ +# Copyright (c) 2010-2021 openpyxl + +from copy import copy + +from openpyxl.compat import safe_string +from openpyxl.utils import ( + get_column_interval, + column_index_from_string, + range_boundaries, +) +from openpyxl.utils.units import DEFAULT_COLUMN_WIDTH +from openpyxl.descriptors import ( + Integer, + Float, + Bool, + Strict, + String, + Alias, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.styles.styleable import StyleableObject +from openpyxl.utils.bound_dictionary import BoundDictionary +from openpyxl.xml.functions import Element + + +class Dimension(Strict, StyleableObject): + """Information about the display properties of a row or column.""" + __fields__ = ('hidden', + 'outlineLevel', + 'collapsed',) + + index = Integer() + hidden = Bool() + outlineLevel = Integer(allow_none=True) + outline_level = Alias('outlineLevel') + collapsed = Bool() + style = Alias('style_id') + + + def __init__(self, index, hidden, outlineLevel, + collapsed, worksheet, visible=True, style=None): + super(Dimension, self).__init__(sheet=worksheet, style_array=style) + self.index = index + self.hidden = hidden + self.outlineLevel = outlineLevel + self.collapsed = collapsed + + + def __iter__(self): + for key in self.__fields__: + value = getattr(self, key, None) + if value: + yield key, safe_string(value) + + + def __copy__(self): + cp = self.__new__(self.__class__) + attrib = self.__dict__ + attrib['worksheet'] = self.parent + cp.__init__(**attrib) + cp._style = copy(self._style) + return cp + + +class RowDimension(Dimension): + """Information about the display properties of a row.""" + + __fields__ = Dimension.__fields__ + ('ht', 'customFormat', 'customHeight', 's', + 'thickBot', 'thickTop') + r = Alias('index') + s = Alias('style_id') + ht = Float(allow_none=True) + height = Alias('ht') + thickBot = Bool() + thickTop = Bool() + + def __init__(self, + worksheet, + index=0, + ht=None, + customHeight=None, # do not write + s=None, + customFormat=None, # do not write + hidden=False, + outlineLevel=0, + outline_level=None, + collapsed=False, + visible=None, + height=None, + r=None, + spans=None, + thickBot=None, + thickTop=None, + **kw + ): + if r is not None: + index = r + if height is not None: + ht = height + self.ht = ht + if visible is not None: + hidden = not visible + if outline_level is not None: + outlineLevel = outline_level + self.thickBot = thickBot + self.thickTop = thickTop + super(RowDimension, self).__init__(index, hidden, outlineLevel, + collapsed, worksheet, style=s) + + @property + def customFormat(self): + """Always true if there is a style for the row""" + return self.has_style + + @property + def customHeight(self): + """Always true if there is a height for the row""" + return self.ht is not None + + +class ColumnDimension(Dimension): + """Information about the display properties of a column.""" + + width = Float() + bestFit = Bool() + auto_size = Alias('bestFit') + index = String() + min = Integer(allow_none=True) + max = Integer(allow_none=True) + collapsed = Bool() + + __fields__ = Dimension.__fields__ + ('width', 'bestFit', 'customWidth', 'style', + 'min', 'max') + + def __init__(self, + worksheet, + index='A', + width=DEFAULT_COLUMN_WIDTH, + bestFit=False, + hidden=False, + outlineLevel=0, + outline_level=None, + collapsed=False, + style=None, + min=None, + max=None, + customWidth=False, # do not write + visible=None, + auto_size=None,): + self.width = width + self.min = min + self.max = max + if visible is not None: + hidden = not visible + if auto_size is not None: + bestFit = auto_size + self.bestFit = bestFit + if outline_level is not None: + outlineLevel = outline_level + self.collapsed = collapsed + super(ColumnDimension, self).__init__(index, hidden, outlineLevel, + collapsed, worksheet, style=style) + + + @property + def customWidth(self): + """Always true if there is a width for the column""" + return bool(self.width) + + + def reindex(self): + """ + Set boundaries for column definition + """ + if not all([self.min, self.max]): + self.min = self.max = column_index_from_string(self.index) + + + def to_tree(self): + attrs = dict(self) + if attrs.keys() != {'min', 'max'}: + return Element("col", **attrs) + + +class DimensionHolder(BoundDictionary): + """ + Allow columns to be grouped + """ + + def __init__(self, worksheet, reference="index", default_factory=None): + self.worksheet = worksheet + self.max_outline = None + self.default_factory = default_factory + super(DimensionHolder, self).__init__(reference, default_factory) + + + def group(self, start, end=None, outline_level=1, hidden=False): + """allow grouping a range of consecutive rows or columns together + + :param start: first row or column to be grouped (mandatory) + :param end: last row or column to be grouped (optional, default to start) + :param outline_level: outline level + :param hidden: should the group be hidden on workbook open or not + """ + if end is None: + end = start + + if isinstance(self.default_factory(), ColumnDimension): + new_dim = self[start] + new_dim.outline_level = outline_level + new_dim.hidden = hidden + work_sequence = get_column_interval(start, end)[1:] + for column_letter in work_sequence: + if column_letter in self: + del self[column_letter] + new_dim.min, new_dim.max = map(column_index_from_string, (start, end)) + elif isinstance(self.default_factory(), RowDimension): + for el in range(start, end + 1): + new_dim = self.worksheet.row_dimensions[el] + new_dim.outline_level = outline_level + new_dim.hidden = hidden + + + def to_tree(self): + + def sorter(value): + value.reindex() + return value.min + + el = Element('cols') + outlines = set() + + for col in sorted(self.values(), key=sorter): + obj = col.to_tree() + if obj is not None: + outlines.add(col.outlineLevel) + el.append(obj) + + if outlines: + self.max_outline = max(outlines) + + if len(el): + return el # must have at least one child + + +class SheetFormatProperties(Serialisable): + + tagname = "sheetFormatPr" + + baseColWidth = Integer(allow_none=True) + defaultColWidth = Float(allow_none=True) + defaultRowHeight = Float() + customHeight = Bool(allow_none=True) + zeroHeight = Bool(allow_none=True) + thickTop = Bool(allow_none=True) + thickBottom = Bool(allow_none=True) + outlineLevelRow = Integer(allow_none=True) + outlineLevelCol = Integer(allow_none=True) + + def __init__(self, + baseColWidth=8, #according to spec + defaultColWidth=None, + defaultRowHeight=15, + customHeight=None, + zeroHeight=None, + thickTop=None, + thickBottom=None, + outlineLevelRow=None, + outlineLevelCol=None, + ): + self.baseColWidth = baseColWidth + self.defaultColWidth = defaultColWidth + self.defaultRowHeight = defaultRowHeight + self.customHeight = customHeight + self.zeroHeight = zeroHeight + self.thickTop = thickTop + self.thickBottom = thickBottom + self.outlineLevelRow = outlineLevelRow + self.outlineLevelCol = outlineLevelCol + + +class SheetDimension(Serialisable): + + tagname = "dimension" + + ref = String() + + def __init__(self, + ref=None, + ): + self.ref = ref + + + @property + def boundaries(self): + return range_boundaries(self.ref) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/drawing.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/drawing.py new file mode 100644 index 00000000..c9f2372d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/drawing.py @@ -0,0 +1,14 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.excel import Relation + + +class Drawing(Serialisable): + + tagname = "drawing" + + id = Relation() + + def __init__(self, id=None): + self.id = id diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/errors.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/errors.py new file mode 100644 index 00000000..1bed3f78 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/errors.py @@ -0,0 +1,93 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Bool, + Sequence, +) +from openpyxl.descriptors.excel import CellRange + + +class Extension(Serialisable): + + tagname = "extension" + + uri = String(allow_none=True) + + def __init__(self, + uri=None, + ): + self.uri = uri + + +class ExtensionList(Serialisable): + + tagname = "extensionList" + + # uses element group EG_ExtensionList + ext = Sequence(expected_type=Extension) + + __elements__ = ('ext',) + + def __init__(self, + ext=(), + ): + self.ext = ext + + +class IgnoredError(Serialisable): + + tagname = "ignoredError" + + sqref = CellRange + evalError = Bool(allow_none=True) + twoDigitTextYear = Bool(allow_none=True) + numberStoredAsText = Bool(allow_none=True) + formula = Bool(allow_none=True) + formulaRange = Bool(allow_none=True) + unlockedFormula = Bool(allow_none=True) + emptyCellReference = Bool(allow_none=True) + listDataValidation = Bool(allow_none=True) + calculatedColumn = Bool(allow_none=True) + + def __init__(self, + sqref=None, + evalError=False, + twoDigitTextYear=False, + numberStoredAsText=False, + formula=False, + formulaRange=False, + unlockedFormula=False, + emptyCellReference=False, + listDataValidation=False, + calculatedColumn=False, + ): + self.sqref = sqref + self.evalError = evalError + self.twoDigitTextYear = twoDigitTextYear + self.numberStoredAsText = numberStoredAsText + self.formula = formula + self.formulaRange = formulaRange + self.unlockedFormula = unlockedFormula + self.emptyCellReference = emptyCellReference + self.listDataValidation = listDataValidation + self.calculatedColumn = calculatedColumn + + +class IgnoredErrors(Serialisable): + + tagname = "ignoredErrors" + + ignoredError = Sequence(expected_type=IgnoredError) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('ignoredError', 'extLst') + + def __init__(self, + ignoredError=(), + extLst=None, + ): + self.ignoredError = ignoredError + self.extLst = extLst + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/filters.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/filters.py new file mode 100644 index 00000000..39b7e705 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/filters.py @@ -0,0 +1,363 @@ +# Copyright (c) 2010-2021 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Set, + Float, + DateTime, + NoneSet, + Bool, + Integer, + String, + Sequence, + MinMax, +) +from openpyxl.descriptors.excel import ExtensionList, CellRange +from openpyxl.descriptors.sequence import ValueSequence + + +class SortCondition(Serialisable): + + tagname = "sortCondition" + + descending = Bool(allow_none=True) + sortBy = NoneSet(values=(['value', 'cellColor', 'fontColor', 'icon'])) + ref = CellRange() + customList = String(allow_none=True) + dxfId = Integer(allow_none=True) + iconSet = NoneSet(values=(['3Arrows', '3ArrowsGray', '3Flags', + '3TrafficLights1', '3TrafficLights2', '3Signs', '3Symbols', '3Symbols2', + '4Arrows', '4ArrowsGray', '4RedToBlack', '4Rating', '4TrafficLights', + '5Arrows', '5ArrowsGray', '5Rating', '5Quarters'])) + iconId = Integer(allow_none=True) + + def __init__(self, + ref=None, + descending=None, + sortBy=None, + customList=None, + dxfId=None, + iconSet=None, + iconId=None, + ): + self.descending = descending + self.sortBy = sortBy + self.ref = ref + self.customList = customList + self.dxfId = dxfId + self.iconSet = iconSet + self.iconId = iconId + + +class SortState(Serialisable): + + tagname = "sortState" + + columnSort = Bool(allow_none=True) + caseSensitive = Bool(allow_none=True) + sortMethod = NoneSet(values=(['stroke', 'pinYin'])) + ref = CellRange() + sortCondition = Sequence(expected_type=SortCondition, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('sortCondition',) + + def __init__(self, + columnSort=None, + caseSensitive=None, + sortMethod=None, + ref=None, + sortCondition=(), + extLst=None, + ): + self.columnSort = columnSort + self.caseSensitive = caseSensitive + self.sortMethod = sortMethod + self.ref = ref + self.sortCondition = sortCondition + + + def __bool__(self): + return self.ref is not None + + + +class IconFilter(Serialisable): + + tagname = "iconFilter" + + iconSet = Set(values=(['3Arrows', '3ArrowsGray', '3Flags', + '3TrafficLights1', '3TrafficLights2', '3Signs', '3Symbols', '3Symbols2', + '4Arrows', '4ArrowsGray', '4RedToBlack', '4Rating', '4TrafficLights', + '5Arrows', '5ArrowsGray', '5Rating', '5Quarters'])) + iconId = Integer(allow_none=True) + + def __init__(self, + iconSet=None, + iconId=None, + ): + self.iconSet = iconSet + self.iconId = iconId + + +class ColorFilter(Serialisable): + + tagname = "colorFilter" + + dxfId = Integer(allow_none=True) + cellColor = Bool(allow_none=True) + + def __init__(self, + dxfId=None, + cellColor=None, + ): + self.dxfId = dxfId + self.cellColor = cellColor + + +class DynamicFilter(Serialisable): + + tagname = "dynamicFilter" + + type = Set(values=(['null', 'aboveAverage', 'belowAverage', 'tomorrow', + 'today', 'yesterday', 'nextWeek', 'thisWeek', 'lastWeek', 'nextMonth', + 'thisMonth', 'lastMonth', 'nextQuarter', 'thisQuarter', 'lastQuarter', + 'nextYear', 'thisYear', 'lastYear', 'yearToDate', 'Q1', 'Q2', 'Q3', 'Q4', + 'M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10', 'M11', + 'M12'])) + val = Float(allow_none=True) + valIso = DateTime(allow_none=True) + maxVal = Float(allow_none=True) + maxValIso = DateTime(allow_none=True) + + def __init__(self, + type=None, + val=None, + valIso=None, + maxVal=None, + maxValIso=None, + ): + self.type = type + self.val = val + self.valIso = valIso + self.maxVal = maxVal + self.maxValIso = maxValIso + + +class CustomFilter(Serialisable): + + tagname = "customFilter" + + operator = NoneSet(values=(['equal', 'lessThan', 'lessThanOrEqual', + 'notEqual', 'greaterThanOrEqual', 'greaterThan'])) + val = String() + + def __init__(self, + operator=None, + val=None, + ): + self.operator = operator + self.val = val + + +class CustomFilters(Serialisable): + + tagname = "customFilters" + + _and = Bool(allow_none=True) + customFilter = Sequence(expected_type=CustomFilter) # min 1, max 2 + + __elements__ = ('customFilter',) + + def __init__(self, + _and=None, + customFilter=(), + ): + self._and = _and + self.customFilter = customFilter + + +class Top10(Serialisable): + + tagname = "top10" + + top = Bool(allow_none=True) + percent = Bool(allow_none=True) + val = Float() + filterVal = Float(allow_none=True) + + def __init__(self, + top=None, + percent=None, + val=None, + filterVal=None, + ): + self.top = top + self.percent = percent + self.val = val + self.filterVal = filterVal + + +class DateGroupItem(Serialisable): + + tagname = "dateGroupItem" + + year = Integer() + month = MinMax(min=1, max=12, allow_none=True) + day = MinMax(min=1, max=31, allow_none=True) + hour = MinMax(min=0, max=23, allow_none=True) + minute = MinMax(min=0, max=59, allow_none=True) + second = Integer(min=0, max=59, allow_none=True) + dateTimeGrouping = Set(values=(['year', 'month', 'day', 'hour', 'minute', + 'second'])) + + def __init__(self, + year=None, + month=None, + day=None, + hour=None, + minute=None, + second=None, + dateTimeGrouping=None, + ): + self.year = year + self.month = month + self.day = day + self.hour = hour + self.minute = minute + self.second = second + self.dateTimeGrouping = dateTimeGrouping + + +class Filters(Serialisable): + + tagname = "filters" + + blank = Bool(allow_none=True) + calendarType = NoneSet(values=["gregorian","gregorianUs", + "gregorianMeFrench","gregorianArabic", "hijri","hebrew", + "taiwan","japan", "thai","korea", + "saka","gregorianXlitEnglish","gregorianXlitFrench"]) + filter = ValueSequence(expected_type=str) + dateGroupItem = Sequence(expected_type=DateGroupItem, allow_none=True) + + __elements__ = ('filter', 'dateGroupItem') + + def __init__(self, + blank=None, + calendarType=None, + filter=(), + dateGroupItem=(), + ): + self.blank = blank + self.calendarType = calendarType + self.filter = filter + self.dateGroupItem = dateGroupItem + + +class FilterColumn(Serialisable): + + tagname = "filterColumn" + + colId = Integer() + col_id = Alias('colId') + hiddenButton = Bool(allow_none=True) + showButton = Bool(allow_none=True) + # some elements are choice + filters = Typed(expected_type=Filters, allow_none=True) + top10 = Typed(expected_type=Top10, allow_none=True) + customFilters = Typed(expected_type=CustomFilters, allow_none=True) + dynamicFilter = Typed(expected_type=DynamicFilter, allow_none=True) + colorFilter = Typed(expected_type=ColorFilter, allow_none=True) + iconFilter = Typed(expected_type=IconFilter, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('filters', 'top10', 'customFilters', 'dynamicFilter', + 'colorFilter', 'iconFilter') + + def __init__(self, + colId=None, + hiddenButton=None, + showButton=None, + filters=None, + top10=None, + customFilters=None, + dynamicFilter=None, + colorFilter=None, + iconFilter=None, + extLst=None, + blank=None, + vals=None, + ): + self.colId = colId + self.hiddenButton = hiddenButton + self.showButton = showButton + self.filters = filters + self.top10 = top10 + self.customFilters = customFilters + self.dynamicFilter = dynamicFilter + self.colorFilter = colorFilter + self.iconFilter = iconFilter + if blank is not None and self.filters: + self.filters.blank = blank + if vals is not None and self.filters: + self.filters.filter = vals + + +class AutoFilter(Serialisable): + + tagname = "autoFilter" + + ref = CellRange() + filterColumn = Sequence(expected_type=FilterColumn, allow_none=True) + sortState = Typed(expected_type=SortState, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('filterColumn', 'sortState') + + def __init__(self, + ref=None, + filterColumn=(), + sortState=None, + extLst=None, + ): + self.ref = ref + self.filterColumn = filterColumn + self.sortState = sortState + + + def __bool__(self): + return self.ref is not None + + + + def add_filter_column(self, col_id, vals, blank=False): + """ + Add row filter for specified column. + + :param col_id: Zero-origin column id. 0 means first column. + :type col_id: int + :param vals: Value list to show. + :type vals: str[] + :param blank: Show rows that have blank cell if True (default=``False``) + :type blank: bool + """ + self.filterColumn.append(FilterColumn(colId=col_id, filters=Filters(blank=blank, filter=vals))) + + + def add_sort_condition(self, ref, descending=False): + """ + Add sort condition for cpecified range of cells. + + :param ref: range of the cells (e.g. 'A2:A150') + :type ref: string + :param descending: Descending sort order (default=``False``) + :type descending: bool + """ + cond = SortCondition(ref, descending) + if self.sortState is None: + self.sortState = SortState(ref=ref) + self.sortState.sortCondition.append(cond) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/header_footer.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/header_footer.py new file mode 100644 index 00000000..c62076e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/header_footer.py @@ -0,0 +1,270 @@ +# Copyright (c) 2010-2021 openpyxl + +# Simplified implementation of headers and footers: let worksheets have separate items + +import re +from warnings import warn + +from openpyxl.descriptors import ( + Alias, + Bool, + Strict, + String, + Integer, + MatchPattern, + Typed, +) +from openpyxl.descriptors.serialisable import Serialisable + + +from openpyxl.xml.functions import Element +from openpyxl.utils.escape import escape, unescape + + +FONT_PATTERN = '&"(?P<font>.+)"' +COLOR_PATTERN = "&K(?P<color>[A-F0-9]{6})" +SIZE_REGEX = r"&(?P<size>\d+\s?)" +FORMAT_REGEX = re.compile("{0}|{1}|{2}".format(FONT_PATTERN, COLOR_PATTERN, + SIZE_REGEX) + ) + +def _split_string(text): + """ + Split the combined (decoded) string into left, center and right parts + + # See http://stackoverflow.com/questions/27711175/regex-with-multiple-optional-groups for discussion + """ + + ITEM_REGEX = re.compile(""" + (&L(?P<left>.+?))? + (&C(?P<center>.+?))? + (&R(?P<right>.+?))? + $""", re.VERBOSE | re.DOTALL) + + m = ITEM_REGEX.match(text) + try: + parts = m.groupdict() + except AttributeError: + warn("""Cannot parse header or footer so it will be ignored""") + parts = {'left':'', 'right':'', 'center':''} + return parts + + +class _HeaderFooterPart(Strict): + + """ + Individual left/center/right header/footer part + + Do not use directly. + + Header & Footer ampersand codes: + + * &A Inserts the worksheet name + * &B Toggles bold + * &D or &[Date] Inserts the current date + * &E Toggles double-underline + * &F or &[File] Inserts the workbook name + * &I Toggles italic + * &N or &[Pages] Inserts the total page count + * &S Toggles strikethrough + * &T Inserts the current time + * &[Tab] Inserts the worksheet name + * &U Toggles underline + * &X Toggles superscript + * &Y Toggles subscript + * &P or &[Page] Inserts the current page number + * &P+n Inserts the page number incremented by n + * &P-n Inserts the page number decremented by n + * &[Path] Inserts the workbook path + * && Escapes the ampersand character + * &"fontname" Selects the named font + * &nn Selects the specified 2-digit font point size + + Colours are in RGB Hex + """ + + text = String(allow_none=True) + font = String(allow_none=True) + size = Integer(allow_none=True) + RGB = ("^[A-Fa-f0-9]{6}$") + color = MatchPattern(allow_none=True, pattern=RGB) + + + def __init__(self, text=None, font=None, size=None, color=None): + self.text = text + self.font = font + self.size = size + self.color = color + + + def __str__(self): + """ + Convert to Excel HeaderFooter miniformat minus position + """ + fmt = [] + if self.font: + fmt.append(u'&"{0}"'.format(self.font)) + if self.size: + fmt.append("&{0} ".format(self.size)) + if self.color: + fmt.append("&K{0}".format(self.color)) + return u"".join(fmt + [self.text]) + + def __bool__(self): + return bool(self.text) + + + + @classmethod + def from_str(cls, text): + """ + Convert from miniformat to object + """ + keys = ('font', 'color', 'size') + kw = dict((k, v) for match in FORMAT_REGEX.findall(text) + for k, v in zip(keys, match) if v) + + kw['text'] = FORMAT_REGEX.sub('', text) + + return cls(**kw) + + +class HeaderFooterItem(Strict): + """ + Header or footer item + + """ + + left = Typed(expected_type=_HeaderFooterPart) + center = Typed(expected_type=_HeaderFooterPart) + centre = Alias("center") + right = Typed(expected_type=_HeaderFooterPart) + + __keys = ('L', 'C', 'R') + + + def __init__(self, left=None, right=None, center=None): + if left is None: + left = _HeaderFooterPart() + self.left = left + if center is None: + center = _HeaderFooterPart() + self.center = center + if right is None: + right = _HeaderFooterPart() + self.right = right + + + def __str__(self): + """ + Pack parts into a single string + """ + TRANSFORM = {'&[Tab]': '&A', '&[Pages]': '&N', '&[Date]': '&D', + '&[Path]': '&Z', '&[Page]': '&P', '&[Time]': '&T', '&[File]': '&F', + '&[Picture]': '&G'} + + # escape keys and create regex + SUBS_REGEX = re.compile("|".join(["({0})".format(re.escape(k)) + for k in TRANSFORM])) + + def replace(match): + """ + Callback for re.sub + Replace expanded control with mini-format equivalent + """ + sub = match.group(0) + return TRANSFORM[sub] + + txt = [] + for key, part in zip( + self.__keys, [self.left, self.center, self.right]): + if part.text is not None: + txt.append(u"&{0}{1}".format(key, str(part))) + txt = "".join(txt) + txt = SUBS_REGEX.sub(replace, txt) + return escape(txt) + + + def __bool__(self): + return any([self.left, self.center, self.right]) + + + + def to_tree(self, tagname): + """ + Return as XML node + """ + el = Element(tagname) + el.text = str(self) + return el + + + @classmethod + def from_tree(cls, node): + if node.text: + text = unescape(node.text) + parts = _split_string(text) + for k, v in parts.items(): + if v is not None: + parts[k] = _HeaderFooterPart.from_str(v) + self = cls(**parts) + return self + + +class HeaderFooter(Serialisable): + + tagname = "headerFooter" + + differentOddEven = Bool(allow_none=True) + differentFirst = Bool(allow_none=True) + scaleWithDoc = Bool(allow_none=True) + alignWithMargins = Bool(allow_none=True) + oddHeader = Typed(expected_type=HeaderFooterItem, allow_none=True) + oddFooter = Typed(expected_type=HeaderFooterItem, allow_none=True) + evenHeader = Typed(expected_type=HeaderFooterItem, allow_none=True) + evenFooter = Typed(expected_type=HeaderFooterItem, allow_none=True) + firstHeader = Typed(expected_type=HeaderFooterItem, allow_none=True) + firstFooter = Typed(expected_type=HeaderFooterItem, allow_none=True) + + __elements__ = ("oddHeader", "oddFooter", "evenHeader", "evenFooter", "firstHeader", "firstFooter") + + def __init__(self, + differentOddEven=None, + differentFirst=None, + scaleWithDoc=None, + alignWithMargins=None, + oddHeader=None, + oddFooter=None, + evenHeader=None, + evenFooter=None, + firstHeader=None, + firstFooter=None, + ): + self.differentOddEven = differentOddEven + self.differentFirst = differentFirst + self.scaleWithDoc = scaleWithDoc + self.alignWithMargins = alignWithMargins + if oddHeader is None: + oddHeader = HeaderFooterItem() + self.oddHeader = oddHeader + if oddFooter is None: + oddFooter = HeaderFooterItem() + self.oddFooter = oddFooter + if evenHeader is None: + evenHeader = HeaderFooterItem() + self.evenHeader = evenHeader + if evenFooter is None: + evenFooter = HeaderFooterItem() + self.evenFooter = evenFooter + if firstHeader is None: + firstHeader = HeaderFooterItem() + self.firstHeader = firstHeader + if firstFooter is None: + firstFooter = HeaderFooterItem() + self.firstFooter = firstFooter + + + def __bool__(self): + parts = [getattr(self, attr) for attr in self.__attrs__ + self.__elements__] + return any(parts) + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/hyperlink.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/hyperlink.py new file mode 100644 index 00000000..1668cee4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/hyperlink.py @@ -0,0 +1,61 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + String, + Sequence, +) +from openpyxl.descriptors.excel import Relation + + +class Hyperlink(Serialisable): + + tagname = "hyperlink" + + ref = String() + location = String(allow_none=True) + tooltip = String(allow_none=True) + display = String(allow_none=True) + id = Relation() + target = String(allow_none=True) + + __attrs__ = ("ref", "location", "tooltip", "display", "id") + + def __init__(self, + ref=None, + location=None, + tooltip=None, + display=None, + id=None, + target=None, + ): + self.ref = ref + self.location = location + self.tooltip = tooltip + self.display = display + self.id = id + self.target = target + + +class HyperlinkList(Serialisable): + + tagname = "hyperlinks" + + hyperlink = Sequence(expected_type=Hyperlink) + + def __init__(self, hyperlink=()): + self.hyperlink = hyperlink + + + def __bool__(self): + return bool(self.hyperlink) + + + def __len__(self): + return len(self.hyperlink) + + + def append(self, value): + values = self.hyperlink[:] + values.append(value) + if not value.id: + value.id = "rId{0}".format(len(values)) + self.hyperlink = values diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/merge.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/merge.py new file mode 100644 index 00000000..1ff70baf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/merge.py @@ -0,0 +1,141 @@ +# Copyright (c) 2010-2021 openpyxl + +import copy + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Integer, + Sequence, +) + +from openpyxl.cell.cell import MergedCell +from openpyxl.styles.borders import Border + +from .cell_range import CellRange + + +class MergeCell(CellRange): + + tagname = "mergeCell" + ref = CellRange.coord + + __attrs__ = ("ref",) + + + def __init__(self, + ref=None, + ): + super(MergeCell, self).__init__(ref) + + + def __copy__(self): + return self.__class__(self.ref) + + +class MergeCells(Serialisable): + + tagname = "mergeCells" + + count = Integer(allow_none=True) + mergeCell = Sequence(expected_type=MergeCell, ) + + __elements__ = ('mergeCell',) + __attrs__ = ('count',) + + def __init__(self, + count=None, + mergeCell=(), + ): + self.mergeCell = mergeCell + + + @property + def count(self): + return len(self.mergeCell) + + +class MergedCellRange(CellRange): + + """ + MergedCellRange stores the border information of a merged cell in the top + left cell of the merged cell. + The remaining cells in the merged cell are stored as MergedCell objects and + get their border information from the upper left cell. + """ + + def __init__(self, worksheet, coord): + self.ws = worksheet + super().__init__(range_string=coord) + self.start_cell = None + self._get_borders() + + + def _get_borders(self): + """ + If the upper left cell of the merged cell does not yet exist, it is + created. + The upper left cell gets the border information of the bottom and right + border from the bottom right cell of the merged cell, if available. + """ + + # Top-left cell. + self.start_cell = self.ws._cells.get((self.min_row, self.min_col)) + if self.start_cell is None: + self.start_cell = self.ws.cell(row=self.min_row, column=self.min_col) + + # Bottom-right cell + end_cell = self.ws._cells.get((self.max_row, self.max_col)) + if end_cell is not None: + self.start_cell.border += Border(right=end_cell.border.right, + bottom=end_cell.border.bottom) + + + def format(self): + """ + Each cell of the merged cell is created as MergedCell if it does not + already exist. + + The MergedCells at the edge of the merged cell gets its borders from + the upper left cell. + + - The top MergedCells get the top border from the top left cell. + - The bottom MergedCells get the bottom border from the top left cell. + - The left MergedCells get the left border from the top left cell. + - The right MergedCells get the right border from the top left cell. + """ + + names = ['top', 'left', 'right', 'bottom'] + + for name in names: + side = getattr(self.start_cell.border, name) + if side and side.style is None: + continue # don't need to do anything if there is no border style + border = Border(**{name:side}) + for coord in getattr(self, name): + cell = self.ws._cells.get(coord) + if cell is None: + row, col = coord + cell = MergedCell(self.ws, row=row, column=col) + self.ws._cells[(cell.row, cell.column)] = cell + cell.border += border + + protected = self.start_cell.protection is not None + if protected: + protection = copy.copy(self.start_cell.protection) + for coord in self.cells: + cell = self.ws._cells.get(coord) + if cell is None: + row, col = coord + cell = MergedCell(self.ws, row=row, column=col) + self.ws._cells[(cell.row, cell.column)] = cell + + if protected: + cell.protection = protection + + + def __contains__(self, coord): + return coord in CellRange(self.coord) + + + def __copy__(self): + return self.__class__(self.ws, self.coord) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/ole.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/ole.py new file mode 100644 index 00000000..00f57a76 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/ole.py @@ -0,0 +1,133 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + String, + Set, + Bool, + Sequence, +) + +from openpyxl.drawing.spreadsheet_drawing import AnchorMarker +from openpyxl.xml.constants import SHEET_DRAWING_NS + + +class ObjectAnchor(Serialisable): + + tagname = "anchor" + + _from = Typed(expected_type=AnchorMarker, namespace=SHEET_DRAWING_NS) + to = Typed(expected_type=AnchorMarker, namespace=SHEET_DRAWING_NS) + moveWithCells = Bool(allow_none=True) + sizeWithCells = Bool(allow_none=True) + z_order = Integer(allow_none=True, hyphenated=True) + + + def __init__(self, + _from=None, + to=None, + moveWithCells=False, + sizeWithCells=False, + z_order=None, + ): + self._from = _from + self.to = to + self.moveWithCells = moveWithCells + self.sizeWithCells = sizeWithCells + self.z_order = z_order + + +class ObjectPr(Serialisable): + + tagname = "objectPr" + + anchor = Typed(expected_type=ObjectAnchor, ) + locked = Bool(allow_none=True) + defaultSize = Bool(allow_none=True) + _print = Bool(allow_none=True) + disabled = Bool(allow_none=True) + uiObject = Bool(allow_none=True) + autoFill = Bool(allow_none=True) + autoLine = Bool(allow_none=True) + autoPict = Bool(allow_none=True) + macro = String() + altText = String(allow_none=True) + dde = Bool(allow_none=True) + + __elements__ = ('anchor',) + + def __init__(self, + anchor=None, + locked=True, + defaultSize=True, + _print=True, + disabled=False, + uiObject=False, + autoFill=True, + autoLine=True, + autoPict=True, + macro=None, + altText=None, + dde=False, + ): + self.anchor = anchor + self.locked = locked + self.defaultSize = defaultSize + self._print = _print + self.disabled = disabled + self.uiObject = uiObject + self.autoFill = autoFill + self.autoLine = autoLine + self.autoPict = autoPict + self.macro = macro + self.altText = altText + self.dde = dde + + +class OleObject(Serialisable): + + tagname = "oleObject" + + objectPr = Typed(expected_type=ObjectPr, allow_none=True) + progId = String(allow_none=True) + dvAspect = Set(values=(['DVASPECT_CONTENT', 'DVASPECT_ICON'])) + link = String(allow_none=True) + oleUpdate = Set(values=(['OLEUPDATE_ALWAYS', 'OLEUPDATE_ONCALL'])) + autoLoad = Bool(allow_none=True) + shapeId = Integer() + + __elements__ = ('objectPr',) + + def __init__(self, + objectPr=None, + progId=None, + dvAspect='DVASPECT_CONTENT', + link=None, + oleUpdate=None, + autoLoad=False, + shapeId=None, + ): + self.objectPr = objectPr + self.progId = progId + self.dvAspect = dvAspect + self.link = link + self.oleUpdate = oleUpdate + self.autoLoad = autoLoad + self.shapeId = shapeId + + +class OleObjects(Serialisable): + + tagname = "oleObjects" + + oleObject = Sequence(expected_type=OleObject) + + __elements__ = ('oleObject',) + + def __init__(self, + oleObject=(), + ): + self.oleObject = oleObject + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/page.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/page.py new file mode 100644 index 00000000..4980b69b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/page.py @@ -0,0 +1,174 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Float, + Bool, + Integer, + NoneSet, + ) +from openpyxl.descriptors.excel import UniversalMeasure, Relation + + +class PrintPageSetup(Serialisable): + """ Worksheet print page setup """ + + tagname = "pageSetup" + + orientation = NoneSet(values=("default", "portrait", "landscape")) + paperSize = Integer(allow_none=True) + scale = Integer(allow_none=True) + fitToHeight = Integer(allow_none=True) + fitToWidth = Integer(allow_none=True) + firstPageNumber = Integer(allow_none=True) + useFirstPageNumber = Bool(allow_none=True) + paperHeight = UniversalMeasure(allow_none=True) + paperWidth = UniversalMeasure(allow_none=True) + pageOrder = NoneSet(values=("downThenOver", "overThenDown")) + usePrinterDefaults = Bool(allow_none=True) + blackAndWhite = Bool(allow_none=True) + draft = Bool(allow_none=True) + cellComments = NoneSet(values=("asDisplayed", "atEnd")) + errors = NoneSet(values=("displayed", "blank", "dash", "NA")) + horizontalDpi = Integer(allow_none=True) + verticalDpi = Integer(allow_none=True) + copies = Integer(allow_none=True) + id = Relation() + + + def __init__(self, + worksheet=None, + orientation=None, + paperSize=None, + scale=None, + fitToHeight=None, + fitToWidth=None, + firstPageNumber=None, + useFirstPageNumber=None, + paperHeight=None, + paperWidth=None, + pageOrder=None, + usePrinterDefaults=None, + blackAndWhite=None, + draft=None, + cellComments=None, + errors=None, + horizontalDpi=None, + verticalDpi=None, + copies=None, + id=None): + self._parent = worksheet + self.orientation = orientation + self.paperSize = paperSize + self.scale = scale + self.fitToHeight = fitToHeight + self.fitToWidth = fitToWidth + self.firstPageNumber = firstPageNumber + self.useFirstPageNumber = useFirstPageNumber + self.paperHeight = paperHeight + self.paperWidth = paperWidth + self.pageOrder = pageOrder + self.usePrinterDefaults = usePrinterDefaults + self.blackAndWhite = blackAndWhite + self.draft = draft + self.cellComments = cellComments + self.errors = errors + self.horizontalDpi = horizontalDpi + self.verticalDpi = verticalDpi + self.copies = copies + self.id = id + + + def __bool__(self): + return bool(dict(self)) + + + + + @property + def sheet_properties(self): + """ + Proxy property + """ + return self._parent.sheet_properties.pageSetUpPr + + + @property + def fitToPage(self): + return self.sheet_properties.fitToPage + + + @fitToPage.setter + def fitToPage(self, value): + self.sheet_properties.fitToPage = value + + + @property + def autoPageBreaks(self): + return self.sheet_properties.autoPageBreaks + + + @autoPageBreaks.setter + def autoPageBreaks(self, value): + self.sheet_properties.autoPageBreaks = value + + + @classmethod + def from_tree(cls, node): + self = super(PrintPageSetup, cls).from_tree(node) + self.id = None # strip link to binary settings + return self + + +class PrintOptions(Serialisable): + """ Worksheet print options """ + + tagname = "printOptions" + horizontalCentered = Bool(allow_none=True) + verticalCentered = Bool(allow_none=True) + headings = Bool(allow_none=True) + gridLines = Bool(allow_none=True) + gridLinesSet = Bool(allow_none=True) + + def __init__(self, horizontalCentered=None, + verticalCentered=None, + headings=None, + gridLines=None, + gridLinesSet=None, + ): + self.horizontalCentered = horizontalCentered + self.verticalCentered = verticalCentered + self.headings = headings + self.gridLines = gridLines + self.gridLinesSet = gridLinesSet + + + def __bool__(self): + return bool(dict(self)) + + +class PageMargins(Serialisable): + """ + Information about page margins for view/print layouts. + Standard values (in inches) + left, right = 0.75 + top, bottom = 1 + header, footer = 0.5 + """ + tagname = "pageMargins" + + left = Float() + right = Float() + top = Float() + bottom = Float() + header = Float() + footer = Float() + + def __init__(self, left=0.75, right=0.75, top=1, bottom=1, header=0.5, + footer=0.5): + self.left = left + self.right = right + self.top = top + self.bottom = bottom + self.header = header + self.footer = footer diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/pagebreak.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/pagebreak.py new file mode 100644 index 00000000..0a5631c3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/pagebreak.py @@ -0,0 +1,94 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Integer, + Bool, + Sequence, +) + + +class Break(Serialisable): + + tagname = "brk" + + id = Integer(allow_none=True) + min = Integer(allow_none=True) + max = Integer(allow_none=True) + man = Bool(allow_none=True) + pt = Bool(allow_none=True) + + def __init__(self, + id=0, + min=0, + max=16383, + man=True, + pt=None, + ): + self.id = id + self.min = min + self.max = max + self.man = man + self.pt = pt + + +class RowBreak(Serialisable): + + tagname = "rowBreaks" + + count = Integer(allow_none=True) + manualBreakCount = Integer(allow_none=True) + brk = Sequence(expected_type=Break, allow_none=True) + + __elements__ = ('brk',) + __attrs__ = ("count", "manualBreakCount",) + + def __init__(self, + count=None, + manualBreakCount=None, + brk=(), + ): + self.brk = brk + + + def __bool__(self): + return len(self.brk) > 0 + + + def __len__(self): + return len(self.brk) + + + @property + def count(self): + return len(self) + + + @property + def manualBreakCount(self): + return len(self) + + + def append(self, brk=None): + """ + Add a page break + """ + vals = list(self.brk) + if not isinstance(brk, Break): + brk = Break(id=self.count+1) + vals.append(brk) + self.brk = vals + + +PageBreak = RowBreak + + +class ColBreak(RowBreak): + + tagname = "colBreaks" + + count = RowBreak.count + manualBreakCount = RowBreak.manualBreakCount + brk = RowBreak.brk + + __attrs__ = RowBreak.__attrs__ diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/picture.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/picture.py new file mode 100644 index 00000000..8fff338a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/picture.py @@ -0,0 +1,8 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable + +# same as related + +class SheetBackgroundPicture(Serialisable): + + tagname = "sheetBackgroundPicture" diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/properties.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/properties.py new file mode 100644 index 00000000..37654dc2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/properties.py @@ -0,0 +1,97 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Worksheet Properties""" + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import String, Bool, Typed +from openpyxl.styles.colors import ColorDescriptor + + +class Outline(Serialisable): + + tagname = "outlinePr" + + applyStyles = Bool(allow_none=True) + summaryBelow = Bool(allow_none=True) + summaryRight = Bool(allow_none=True) + showOutlineSymbols = Bool(allow_none=True) + + + def __init__(self, + applyStyles=None, + summaryBelow=None, + summaryRight=None, + showOutlineSymbols=None + ): + self.applyStyles = applyStyles + self.summaryBelow = summaryBelow + self.summaryRight = summaryRight + self.showOutlineSymbols = showOutlineSymbols + + +class PageSetupProperties(Serialisable): + + tagname = "pageSetUpPr" + + autoPageBreaks = Bool(allow_none=True) + fitToPage = Bool(allow_none=True) + + def __init__(self, autoPageBreaks=None, fitToPage=None): + self.autoPageBreaks = autoPageBreaks + self.fitToPage = fitToPage + + +class WorksheetProperties(Serialisable): + + tagname = "sheetPr" + + codeName = String(allow_none=True) + enableFormatConditionsCalculation = Bool(allow_none=True) + filterMode = Bool(allow_none=True) + published = Bool(allow_none=True) + syncHorizontal = Bool(allow_none=True) + syncRef = String(allow_none=True) + syncVertical = Bool(allow_none=True) + transitionEvaluation = Bool(allow_none=True) + transitionEntry = Bool(allow_none=True) + tabColor = ColorDescriptor(allow_none=True) + outlinePr = Typed(expected_type=Outline, allow_none=True) + pageSetUpPr = Typed(expected_type=PageSetupProperties, allow_none=True) + + __elements__ = ('tabColor', 'outlinePr', 'pageSetUpPr') + + + def __init__(self, + codeName=None, + enableFormatConditionsCalculation=None, + filterMode=None, + published=None, + syncHorizontal=None, + syncRef=None, + syncVertical=None, + transitionEvaluation=None, + transitionEntry=None, + tabColor=None, + outlinePr=None, + pageSetUpPr=None + ): + """ Attributes """ + self.codeName = codeName + self.enableFormatConditionsCalculation = enableFormatConditionsCalculation + self.filterMode = filterMode + self.published = published + self.syncHorizontal = syncHorizontal + self.syncRef = syncRef + self.syncVertical = syncVertical + self.transitionEvaluation = transitionEvaluation + self.transitionEntry = transitionEntry + """ Elements """ + self.tabColor = tabColor + if outlinePr is None: + self.outlinePr = Outline(summaryBelow=True, summaryRight=True) + else: + self.outlinePr = outlinePr + + if pageSetUpPr is None: + pageSetUpPr = PageSetupProperties() + self.pageSetUpPr = pageSetUpPr diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/protection.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/protection.py new file mode 100644 index 00000000..0e02661f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/protection.py @@ -0,0 +1,120 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Bool, + String, + Alias, + Integer, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.excel import ( + Base64Binary, +) +from openpyxl.utils.protection import hash_password + + +class _Protected(object): + _password = None + + def set_password(self, value='', already_hashed=False): + """Set a password on this sheet.""" + if not already_hashed: + value = hash_password(value) + self._password = value + + @property + def password(self): + """Return the password value, regardless of hash.""" + return self._password + + @password.setter + def password(self, value): + """Set a password directly, forcing a hash step.""" + self.set_password(value) + + +class SheetProtection(Serialisable, _Protected): + """ + Information about protection of various aspects of a sheet. True values + mean that protection for the object or action is active This is the + **default** when protection is active, ie. users cannot do something + """ + + tagname = "sheetProtection" + + sheet = Bool() + enabled = Alias('sheet') + objects = Bool() + scenarios = Bool() + formatCells = Bool() + formatColumns = Bool() + formatRows = Bool() + insertColumns = Bool() + insertRows = Bool() + insertHyperlinks = Bool() + deleteColumns = Bool() + deleteRows = Bool() + selectLockedCells = Bool() + selectUnlockedCells = Bool() + sort = Bool() + autoFilter = Bool() + pivotTables = Bool() + saltValue = Base64Binary(allow_none=True) + spinCount = Integer(allow_none=True) + algorithmName = String(allow_none=True) + hashValue = Base64Binary(allow_none=True) + + + __attrs__ = ('selectLockedCells', 'selectUnlockedCells', 'algorithmName', + 'sheet', 'objects', 'insertRows', 'insertHyperlinks', 'autoFilter', + 'scenarios', 'formatColumns', 'deleteColumns', 'insertColumns', + 'pivotTables', 'deleteRows', 'formatCells', 'saltValue', 'formatRows', + 'sort', 'spinCount', 'password', 'hashValue') + + + def __init__(self, sheet=False, objects=False, scenarios=False, + formatCells=True, formatRows=True, formatColumns=True, + insertColumns=True, insertRows=True, insertHyperlinks=True, + deleteColumns=True, deleteRows=True, selectLockedCells=False, + selectUnlockedCells=False, sort=True, autoFilter=True, pivotTables=True, + password=None, algorithmName=None, saltValue=None, spinCount=None, hashValue=None): + self.sheet = sheet + self.objects = objects + self.scenarios = scenarios + self.formatCells = formatCells + self.formatColumns = formatColumns + self.formatRows = formatRows + self.insertColumns = insertColumns + self.insertRows = insertRows + self.insertHyperlinks = insertHyperlinks + self.deleteColumns = deleteColumns + self.deleteRows = deleteRows + self.selectLockedCells = selectLockedCells + self.selectUnlockedCells = selectUnlockedCells + self.sort = sort + self.autoFilter = autoFilter + self.pivotTables = pivotTables + if password is not None: + self.password = password + self.algorithmName = algorithmName + self.saltValue = saltValue + self.spinCount = spinCount + self.hashValue = hashValue + + + def set_password(self, value='', already_hashed=False): + super(SheetProtection, self).set_password(value, already_hashed) + self.enable() + + + def enable(self): + self.sheet = True + + + def disable(self): + self.sheet = False + + + def __bool__(self): + return self.sheet + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/related.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/related.py new file mode 100644 index 00000000..56c63370 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/related.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.excel import Relation + + +class Related(Serialisable): + + id = Relation() + + + def __init__(self, id=None): + self.id = id + + + def to_tree(self, tagname, idx=None): + return super(Related, self).to_tree(tagname) diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/scenario.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/scenario.py new file mode 100644 index 00000000..fd7c53ab --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/scenario.py @@ -0,0 +1,105 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + String, + Integer, + Bool, + Sequence, + Convertible, +) +from .cell_range import MultiCellRange + + +class InputCells(Serialisable): + + tagname = "inputCells" + + r = String() + deleted = Bool(allow_none=True) + undone = Bool(allow_none=True) + val = String() + numFmtId = Integer(allow_none=True) + + def __init__(self, + r=None, + deleted=False, + undone=False, + val=None, + numFmtId=None, + ): + self.r = r + self.deleted = deleted + self.undone = undone + self.val = val + self.numFmtId = numFmtId + + +class Scenario(Serialisable): + + tagname = "scenario" + + inputCells = Sequence(expected_type=InputCells) + name = String() + locked = Bool(allow_none=True) + hidden = Bool(allow_none=True) + user = String(allow_none=True) + comment = String(allow_none=True) + + __elements__ = ('inputCells',) + __attrs__ = ('name', 'locked', 'hidden', 'user', 'comment', 'count') + + def __init__(self, + inputCells=(), + name=None, + locked=False, + hidden=False, + count=None, + user=None, + comment=None, + ): + self.inputCells = inputCells + self.name = name + self.locked = locked + self.hidden = hidden + self.user = user + self.comment = comment + + + @property + def count(self): + return len(self.inputCells) + + +class ScenarioList(Serialisable): + + tagname = "scenarios" + + scenario = Sequence(expected_type=Scenario) + current = Integer(allow_none=True) + show = Integer(allow_none=True) + sqref = Convertible(expected_type=MultiCellRange, allow_none=True) + + __elements__ = ('scenario',) + + def __init__(self, + scenario=(), + current=None, + show=None, + sqref=None, + ): + self.scenario = scenario + self.current = current + self.show = show + self.sqref = sqref + + + def append(self, scenario): + s = self.scenario + s.append(scenario) + self.scenario = s + + + def __bool__(self): + return bool(self.scenario) + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/smart_tag.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/smart_tag.py new file mode 100644 index 00000000..29fe1926 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/smart_tag.py @@ -0,0 +1,78 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Bool, + Integer, + String, + Sequence, +) + + +class CellSmartTagPr(Serialisable): + + tagname = "cellSmartTagPr" + + key = String() + val = String() + + def __init__(self, + key=None, + val=None, + ): + self.key = key + self.val = val + + +class CellSmartTag(Serialisable): + + tagname = "cellSmartTag" + + cellSmartTagPr = Sequence(expected_type=CellSmartTagPr) + type = Integer() + deleted = Bool(allow_none=True) + xmlBased = Bool(allow_none=True) + + __elements__ = ('cellSmartTagPr',) + + def __init__(self, + cellSmartTagPr=(), + type=None, + deleted=False, + xmlBased=False, + ): + self.cellSmartTagPr = cellSmartTagPr + self.type = type + self.deleted = deleted + self.xmlBased = xmlBased + + +class CellSmartTags(Serialisable): + + tagname = "cellSmartTags" + + cellSmartTag = Sequence(expected_type=CellSmartTag) + r = String() + + __elements__ = ('cellSmartTag',) + + def __init__(self, + cellSmartTag=(), + r=None, + ): + self.cellSmartTag = cellSmartTag + self.r = r + + +class SmartTags(Serialisable): + + tagname = "smartTags" + + cellSmartTags = Sequence(expected_type=CellSmartTags) + + __elements__ = ('cellSmartTags',) + + def __init__(self, + cellSmartTags=(), + ): + self.cellSmartTags = cellSmartTags + diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/table.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/table.py new file mode 100644 index 00000000..736eb44f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/table.py @@ -0,0 +1,385 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Descriptor, + Alias, + Typed, + Bool, + Integer, + NoneSet, + String, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList, CellRange +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.xml.constants import SHEET_MAIN_NS, REL_NS +from openpyxl.xml.functions import tostring +from openpyxl.utils import range_boundaries +from openpyxl.utils.escape import escape, unescape + +from .related import Related + +from .filters import ( + AutoFilter, + SortState, +) + +TABLESTYLES = tuple( + ["TableStyleMedium{0}".format(i) for i in range(1, 29)] + + ["TableStyleLight{0}".format(i) for i in range(1, 22)] + + ["TableStyleDark{0}".format(i) for i in range(1, 12)] +) + +PIVOTSTYLES = tuple( + ["PivotStyleMedium{0}".format(i) for i in range(1, 29)] + + ["PivotStyleLight{0}".format(i) for i in range(1, 29)] + + ["PivotStyleDark{0}".format(i) for i in range(1, 29)] +) + + +class TableStyleInfo(Serialisable): + + tagname = "tableStyleInfo" + + name = String(allow_none=True) + showFirstColumn = Bool(allow_none=True) + showLastColumn = Bool(allow_none=True) + showRowStripes = Bool(allow_none=True) + showColumnStripes = Bool(allow_none=True) + + def __init__(self, + name=None, + showFirstColumn=None, + showLastColumn=None, + showRowStripes=None, + showColumnStripes=None, + ): + self.name = name + self.showFirstColumn = showFirstColumn + self.showLastColumn = showLastColumn + self.showRowStripes = showRowStripes + self.showColumnStripes = showColumnStripes + + +class XMLColumnProps(Serialisable): + + tagname = "xmlColumnPr" + + mapId = Integer() + xpath = String() + denormalized = Bool(allow_none=True) + xmlDataType = String() + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + mapId=None, + xpath=None, + denormalized=None, + xmlDataType=None, + extLst=None, + ): + self.mapId = mapId + self.xpath = xpath + self.denormalized = denormalized + self.xmlDataType = xmlDataType + + +class TableFormula(Serialisable): + + tagname = "tableFormula" + + ## Note formula is stored as the text value + + array = Bool(allow_none=True) + attr_text = Descriptor() + text = Alias('attr_text') + + + def __init__(self, + array=None, + attr_text=None, + ): + self.array = array + self.attr_text = attr_text + + +class TableColumn(Serialisable): + + tagname = "tableColumn" + + id = Integer() + uniqueName = String(allow_none=True) + name = String() + totalsRowFunction = NoneSet(values=(['sum', 'min', 'max', 'average', + 'count', 'countNums', 'stdDev', 'var', 'custom'])) + totalsRowLabel = String(allow_none=True) + queryTableFieldId = Integer(allow_none=True) + headerRowDxfId = Integer(allow_none=True) + dataDxfId = Integer(allow_none=True) + totalsRowDxfId = Integer(allow_none=True) + headerRowCellStyle = String(allow_none=True) + dataCellStyle = String(allow_none=True) + totalsRowCellStyle = String(allow_none=True) + calculatedColumnFormula = Typed(expected_type=TableFormula, allow_none=True) + totalsRowFormula = Typed(expected_type=TableFormula, allow_none=True) + xmlColumnPr = Typed(expected_type=XMLColumnProps, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('calculatedColumnFormula', 'totalsRowFormula', + 'xmlColumnPr', 'extLst') + + def __init__(self, + id=None, + uniqueName=None, + name=None, + totalsRowFunction=None, + totalsRowLabel=None, + queryTableFieldId=None, + headerRowDxfId=None, + dataDxfId=None, + totalsRowDxfId=None, + headerRowCellStyle=None, + dataCellStyle=None, + totalsRowCellStyle=None, + calculatedColumnFormula=None, + totalsRowFormula=None, + xmlColumnPr=None, + extLst=None, + ): + self.id = id + self.uniqueName = uniqueName + self.name = name + self.totalsRowFunction = totalsRowFunction + self.totalsRowLabel = totalsRowLabel + self.queryTableFieldId = queryTableFieldId + self.headerRowDxfId = headerRowDxfId + self.dataDxfId = dataDxfId + self.totalsRowDxfId = totalsRowDxfId + self.headerRowCellStyle = headerRowCellStyle + self.dataCellStyle = dataCellStyle + self.totalsRowCellStyle = totalsRowCellStyle + self.calculatedColumnFormula = calculatedColumnFormula + self.totalsRowFormula = totalsRowFormula + self.xmlColumnPr = xmlColumnPr + self.extLst = extLst + + + def __iter__(self): + for k, v in super(TableColumn, self).__iter__(): + if k == 'name': + v = escape(v) + yield k, v + + + @classmethod + def from_tree(cls, node): + self = super(TableColumn, cls).from_tree(node) + self.name = unescape(self.name) + return self + + +class TableNameDescriptor(String): + + """ + Table names cannot have spaces in them + """ + + def __set__(self, instance, value): + if value is not None and " " in value: + raise ValueError("Table names cannot have spaces") + super(TableNameDescriptor, self).__set__(instance, value) + + +class Table(Serialisable): + + _path = "/tables/table{0}.xml" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml" + _rel_type = REL_NS + "/table" + _rel_id = None + + tagname = "table" + + id = Integer() + name = String(allow_none=True) + displayName = TableNameDescriptor() + comment = String(allow_none=True) + ref = CellRange() + tableType = NoneSet(values=(['worksheet', 'xml', 'queryTable'])) + headerRowCount = Integer(allow_none=True) + insertRow = Bool(allow_none=True) + insertRowShift = Bool(allow_none=True) + totalsRowCount = Integer(allow_none=True) + totalsRowShown = Bool(allow_none=True) + published = Bool(allow_none=True) + headerRowDxfId = Integer(allow_none=True) + dataDxfId = Integer(allow_none=True) + totalsRowDxfId = Integer(allow_none=True) + headerRowBorderDxfId = Integer(allow_none=True) + tableBorderDxfId = Integer(allow_none=True) + totalsRowBorderDxfId = Integer(allow_none=True) + headerRowCellStyle = String(allow_none=True) + dataCellStyle = String(allow_none=True) + totalsRowCellStyle = String(allow_none=True) + connectionId = Integer(allow_none=True) + autoFilter = Typed(expected_type=AutoFilter, allow_none=True) + sortState = Typed(expected_type=SortState, allow_none=True) + tableColumns = NestedSequence(expected_type=TableColumn, count=True) + tableStyleInfo = Typed(expected_type=TableStyleInfo, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('autoFilter', 'sortState', 'tableColumns', + 'tableStyleInfo') + + def __init__(self, + id=1, + displayName=None, + ref=None, + name=None, + comment=None, + tableType=None, + headerRowCount=1, + insertRow=None, + insertRowShift=None, + totalsRowCount=None, + totalsRowShown=None, + published=None, + headerRowDxfId=None, + dataDxfId=None, + totalsRowDxfId=None, + headerRowBorderDxfId=None, + tableBorderDxfId=None, + totalsRowBorderDxfId=None, + headerRowCellStyle=None, + dataCellStyle=None, + totalsRowCellStyle=None, + connectionId=None, + autoFilter=None, + sortState=None, + tableColumns=(), + tableStyleInfo=None, + extLst=None, + ): + self.id = id + self.displayName = displayName + if name is None: + name = displayName + self.name = name + self.comment = comment + self.ref = ref + self.tableType = tableType + self.headerRowCount = headerRowCount + self.insertRow = insertRow + self.insertRowShift = insertRowShift + self.totalsRowCount = totalsRowCount + self.totalsRowShown = totalsRowShown + self.published = published + self.headerRowDxfId = headerRowDxfId + self.dataDxfId = dataDxfId + self.totalsRowDxfId = totalsRowDxfId + self.headerRowBorderDxfId = headerRowBorderDxfId + self.tableBorderDxfId = tableBorderDxfId + self.totalsRowBorderDxfId = totalsRowBorderDxfId + self.headerRowCellStyle = headerRowCellStyle + self.dataCellStyle = dataCellStyle + self.totalsRowCellStyle = totalsRowCellStyle + self.connectionId = connectionId + self.autoFilter = autoFilter + self.sortState = sortState + self.tableColumns = tableColumns + self.tableStyleInfo = tableStyleInfo + + + def to_tree(self): + tree = super(Table, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def path(self): + """ + Return path within the archive + """ + return "/xl" + self._path.format(self.id) + + + def _write(self, archive): + """ + Serialise to XML and write to archive + """ + xml = self.to_tree() + archive.writestr(self.path[1:], tostring(xml)) + + + def _initialise_columns(self): + """ + Create a list of table columns from a cell range + Always set a ref if we have headers (the default) + Column headings must be strings and must match cells in the worksheet. + """ + + min_col, min_row, max_col, max_row = range_boundaries(self.ref) + for idx in range(min_col, max_col+1): + col = TableColumn(id=idx, name="Column{0}".format(idx)) + self.tableColumns.append(col) + if self.headerRowCount: + self.autoFilter = AutoFilter(ref=self.ref) + + + @property + def column_names(self): + return [column.name for column in self.tableColumns] + + +class TablePartList(Serialisable): + + tagname = "tableParts" + + count = Integer(allow_none=True) + tablePart = Sequence(expected_type=Related) + + __elements__ = ('tablePart',) + __attrs__ = ('count',) + + def __init__(self, + count=None, + tablePart=(), + ): + self.tablePart = tablePart + + + def append(self, part): + self.tablePart.append(part) + + + @property + def count(self): + return len(self.tablePart) + + + def __bool__(self): + return bool(self.tablePart) + + +class TableList(dict): + + + def add(self, table): + if not isinstance(table, Table): + raise TypeError("You can only add tables") + self[table.name] = table + + + def get(self, name=None, table_range=None): + if name is not None: + return super().get(name) + for table in self.values(): + if table_range == table.ref: + return table + + + def items(self): + return [(name, table.ref) for name, table in super().items()] diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/views.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/views.py new file mode 100644 index 00000000..808a6c93 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/views.py @@ -0,0 +1,149 @@ +# Copyright (c) 2010-2021 openpyxl + +from openpyxl.descriptors import ( + Bool, + Integer, + String, + Set, + Float, + Typed, + NoneSet, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + + +class Pane(Serialisable): + xSplit = Float(allow_none=True) + ySplit = Float(allow_none=True) + topLeftCell = String(allow_none=True) + activePane = Set(values=("bottomRight", "topRight", "bottomLeft", "topLeft")) + state = Set(values=("split", "frozen", "frozenSplit")) + + def __init__(self, + xSplit=None, + ySplit=None, + topLeftCell=None, + activePane="topLeft", + state="split"): + self.xSplit = xSplit + self.ySplit = ySplit + self.topLeftCell = topLeftCell + self.activePane = activePane + self.state = state + + +class Selection(Serialisable): + pane = NoneSet(values=("bottomRight", "topRight", "bottomLeft", "topLeft")) + activeCell = String(allow_none=True) + activeCellId = Integer(allow_none=True) + sqref = String(allow_none=True) + + def __init__(self, + pane=None, + activeCell="A1", + activeCellId=None, + sqref="A1"): + self.pane = pane + self.activeCell = activeCell + self.activeCellId = activeCellId + self.sqref = sqref + + +class SheetView(Serialisable): + + """Information about the visible portions of this sheet.""" + + tagname = "sheetView" + + windowProtection = Bool(allow_none=True) + showFormulas = Bool(allow_none=True) + showGridLines = Bool(allow_none=True) + showRowColHeaders = Bool(allow_none=True) + showZeros = Bool(allow_none=True) + rightToLeft = Bool(allow_none=True) + tabSelected = Bool(allow_none=True) + showRuler = Bool(allow_none=True) + showOutlineSymbols = Bool(allow_none=True) + defaultGridColor = Bool(allow_none=True) + showWhiteSpace = Bool(allow_none=True) + view = NoneSet(values=("normal", "pageBreakPreview", "pageLayout")) + topLeftCell = String(allow_none=True) + colorId = Integer(allow_none=True) + zoomScale = Integer(allow_none=True) + zoomScaleNormal = Integer(allow_none=True) + zoomScaleSheetLayoutView = Integer(allow_none=True) + zoomScalePageLayoutView = Integer(allow_none=True) + zoomToFit = Bool(allow_none=True) # Chart sheets only + workbookViewId = Integer() + selection = Sequence(expected_type=Selection) + pane = Typed(expected_type=Pane, allow_none=True) + + def __init__( + self, + windowProtection=None, + showFormulas=None, + showGridLines=None, + showRowColHeaders=None, + showZeros=None, + rightToLeft=None, + tabSelected=None, + showRuler=None, + showOutlineSymbols=None, + defaultGridColor=None, + showWhiteSpace=None, + view=None, + topLeftCell=None, + colorId=None, + zoomScale=None, + zoomScaleNormal=None, + zoomScaleSheetLayoutView=None, + zoomScalePageLayoutView=None, + zoomToFit=None, + workbookViewId=0, + selection=None, + pane=None, + ): + self.windowProtection = windowProtection + self.showFormulas = showFormulas + self.showGridLines = showGridLines + self.showRowColHeaders = showRowColHeaders + self.showZeros = showZeros + self.rightToLeft = rightToLeft + self.tabSelected = tabSelected + self.showRuler = showRuler + self.showOutlineSymbols = showOutlineSymbols + self.defaultGridColor = defaultGridColor + self.showWhiteSpace = showWhiteSpace + self.view = view + self.topLeftCell = topLeftCell + self.colorId = colorId + self.zoomScale = zoomScale + self.zoomScaleNormal = zoomScaleNormal + self.zoomScaleSheetLayoutView = zoomScaleSheetLayoutView + self.zoomScalePageLayoutView = zoomScalePageLayoutView + self.zoomToFit = zoomToFit + self.workbookViewId = workbookViewId + self.pane = pane + if selection is None: + selection = (Selection(), ) + self.selection = selection + + +class SheetViewList(Serialisable): + + tagname = "sheetViews" + + sheetView = Sequence(expected_type=SheetView, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('sheetView',) + + def __init__(self, + sheetView=None, + extLst=None, + ): + if sheetView is None: + sheetView = [SheetView()] + self.sheetView = sheetView diff --git a/.venv/lib/python3.9/site-packages/openpyxl/worksheet/worksheet.py b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/worksheet.py new file mode 100644 index 00000000..7322aa30 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/worksheet/worksheet.py @@ -0,0 +1,901 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Worksheet is the 2nd-level container in Excel.""" + + +# Python stdlib imports +from itertools import chain +from operator import itemgetter +from inspect import isgenerator +from warnings import warn + +# compatibility imports +from openpyxl.compat import ( + deprecated, +) + +# package imports +from openpyxl.utils import ( + column_index_from_string, + get_column_letter, + range_boundaries, + coordinate_to_tuple, + absolute_coordinate, +) +from openpyxl.cell import Cell, MergedCell +from openpyxl.formatting.formatting import ConditionalFormattingList +from openpyxl.packaging.relationship import RelationshipList +from openpyxl.workbook.child import _WorkbookChild +from openpyxl.workbook.defined_name import COL_RANGE_RE, ROW_RANGE_RE +from openpyxl.formula.translate import Translator + +from .datavalidation import DataValidationList +from .page import ( + PrintPageSetup, + PageMargins, + PrintOptions, +) +from .dimensions import ( + ColumnDimension, + RowDimension, + DimensionHolder, + SheetFormatProperties, +) +from .protection import SheetProtection +from .filters import AutoFilter +from .views import ( + Pane, + Selection, + SheetViewList, +) +from .cell_range import MultiCellRange, CellRange +from .merge import MergedCellRange +from .properties import WorksheetProperties +from .pagebreak import RowBreak, ColBreak +from .scenario import ScenarioList +from .table import TableList + + +class Worksheet(_WorkbookChild): + """Represents a worksheet. + + Do not create worksheets yourself, + use :func:`openpyxl.workbook.Workbook.create_sheet` instead + + """ + + _rel_type = "worksheet" + _path = "/xl/worksheets/sheet{0}.xml" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" + + BREAK_NONE = 0 + BREAK_ROW = 1 + BREAK_COLUMN = 2 + + SHEETSTATE_VISIBLE = 'visible' + SHEETSTATE_HIDDEN = 'hidden' + SHEETSTATE_VERYHIDDEN = 'veryHidden' + + # Paper size + PAPERSIZE_LETTER = '1' + PAPERSIZE_LETTER_SMALL = '2' + PAPERSIZE_TABLOID = '3' + PAPERSIZE_LEDGER = '4' + PAPERSIZE_LEGAL = '5' + PAPERSIZE_STATEMENT = '6' + PAPERSIZE_EXECUTIVE = '7' + PAPERSIZE_A3 = '8' + PAPERSIZE_A4 = '9' + PAPERSIZE_A4_SMALL = '10' + PAPERSIZE_A5 = '11' + + # Page orientation + ORIENTATION_PORTRAIT = 'portrait' + ORIENTATION_LANDSCAPE = 'landscape' + + def __init__(self, parent, title=None): + _WorkbookChild.__init__(self, parent, title) + self._setup() + + def _setup(self): + self.row_dimensions = DimensionHolder(worksheet=self, + default_factory=self._add_row) + self.column_dimensions = DimensionHolder(worksheet=self, + default_factory=self._add_column) + self.row_breaks = RowBreak() + self.col_breaks = ColBreak() + self._cells = {} + self._charts = [] + self._images = [] + self._rels = RelationshipList() + self._drawing = None + self._comments = [] + self.merged_cells = MultiCellRange() + self._tables = TableList() + self._pivots = [] + self.data_validations = DataValidationList() + self._hyperlinks = [] + self.sheet_state = 'visible' + self.page_setup = PrintPageSetup(worksheet=self) + self.print_options = PrintOptions() + self._print_rows = None + self._print_cols = None + self._print_area = None + self.page_margins = PageMargins() + self.views = SheetViewList() + self.protection = SheetProtection() + + self._current_row = 0 + self.auto_filter = AutoFilter() + self.paper_size = None + self.formula_attributes = {} + self.orientation = None + self.conditional_formatting = ConditionalFormattingList() + self.legacy_drawing = None + self.sheet_properties = WorksheetProperties() + self.sheet_format = SheetFormatProperties() + self.scenarios = ScenarioList() + + + @property + def sheet_view(self): + return self.views.sheetView[0] + + + @property + def selected_cell(self): + return self.sheet_view.selection[0].sqref + + + @property + def active_cell(self): + return self.sheet_view.selection[0].activeCell + + + @property + def page_breaks(self): + return (self.row_breaks, self.col_breaks) # legacy, remove at some point + + + @property + def show_gridlines(self): + return self.sheet_view.showGridLines + + + """ To keep compatibility with previous versions""" + @property + def show_summary_below(self): + return self.sheet_properties.outlinePr.summaryBelow + + @property + def show_summary_right(self): + return self.sheet_properties.outlinePr.summaryRight + + + @property + def freeze_panes(self): + if self.sheet_view.pane is not None: + return self.sheet_view.pane.topLeftCell + + @freeze_panes.setter + def freeze_panes(self, topLeftCell=None): + if isinstance(topLeftCell, Cell): + topLeftCell = topLeftCell.coordinate + if topLeftCell == 'A1': + topLeftCell = None + + if not topLeftCell: + self.sheet_view.pane = None + return + + row, column = coordinate_to_tuple(topLeftCell) + + view = self.sheet_view + view.pane = Pane(topLeftCell=topLeftCell, + activePane="topRight", + state="frozen") + view.selection[0].pane = "topRight" + + if column > 1: + view.pane.xSplit = column - 1 + if row > 1: + view.pane.ySplit = row - 1 + view.pane.activePane = 'bottomLeft' + view.selection[0].pane = "bottomLeft" + if column > 1: + view.selection[0].pane = "bottomRight" + view.pane.activePane = 'bottomRight' + + if row > 1 and column > 1: + sel = list(view.selection) + sel.insert(0, Selection(pane="topRight", activeCell=None, sqref=None)) + sel.insert(1, Selection(pane="bottomLeft", activeCell=None, sqref=None)) + view.selection = sel + + + def cell(self, row, column, value=None): + """ + Returns a cell object based on the given coordinates. + + Usage: cell(row=15, column=1, value=5) + + Calling `cell` creates cells in memory when they + are first accessed. + + :param row: row index of the cell (e.g. 4) + :type row: int + + :param column: column index of the cell (e.g. 3) + :type column: int + + :param value: value of the cell (e.g. 5) + :type value: numeric or time or string or bool or none + + :rtype: openpyxl.cell.cell.Cell + """ + + if row < 1 or column < 1: + raise ValueError("Row or column values must be at least 1") + + cell = self._get_cell(row, column) + if value is not None: + cell.value = value + + return cell + + + def _get_cell(self, row, column): + """ + Internal method for getting a cell from a worksheet. + Will create a new cell if one doesn't already exist. + """ + if not 0 < row < 1048577: + raise ValueError("Row numbers must be between 1 and 1048576") + coordinate = (row, column) + if not coordinate in self._cells: + cell = Cell(self, row=row, column=column) + self._add_cell(cell) + return self._cells[coordinate] + + + def _add_cell(self, cell): + """ + Internal method for adding cell objects. + """ + column = cell.col_idx + row = cell.row + self._current_row = max(row, self._current_row) + self._cells[(row, column)] = cell + + + def __getitem__(self, key): + """Convenience access by Excel style coordinates + + The key can be a single cell coordinate 'A1', a range of cells 'A1:D25', + individual rows or columns 'A', 4 or ranges of rows or columns 'A:D', + 4:10. + + Single cells will always be created if they do not exist. + + Returns either a single cell or a tuple of rows or columns. + """ + if isinstance(key, slice): + if not all([key.start, key.stop]): + raise IndexError("{0} is not a valid coordinate or range".format(key)) + key = "{0}:{1}".format(key.start, key.stop) + + if isinstance(key, int): + key = str(key + ) + min_col, min_row, max_col, max_row = range_boundaries(key) + + if not any([min_col, min_row, max_col, max_row]): + raise IndexError("{0} is not a valid coordinate or range".format(key)) + + if min_row is None: + cols = tuple(self.iter_cols(min_col, max_col)) + if min_col == max_col: + cols = cols[0] + return cols + if min_col is None: + rows = tuple(self.iter_rows(min_col=min_col, min_row=min_row, + max_col=self.max_column, max_row=max_row)) + if min_row == max_row: + rows = rows[0] + return rows + if ":" not in key: + return self._get_cell(min_row, min_col) + return tuple(self.iter_rows(min_row=min_row, min_col=min_col, + max_row=max_row, max_col=max_col)) + + + def __setitem__(self, key, value): + self[key].value = value + + + def __iter__(self): + return self.iter_rows() + + + def __delitem__(self, key): + row, column = coordinate_to_tuple(key) + if (row, column) in self._cells: + del self._cells[(row, column)] + + + @property + def min_row(self): + """The minimium row index containing data (1-based) + + :type: int + """ + min_row = 1 + if self._cells: + rows = set(c[0] for c in self._cells) + min_row = min(rows) + return min_row + + + @property + def max_row(self): + """The maximum row index containing data (1-based) + + :type: int + """ + max_row = 1 + if self._cells: + rows = set(c[0] for c in self._cells) + max_row = max(rows) + return max_row + + + @property + def min_column(self): + """The minimum column index containing data (1-based) + + :type: int + """ + min_col = 1 + if self._cells: + cols = set(c[1] for c in self._cells) + min_col = min(cols) + return min_col + + + @property + def max_column(self): + """The maximum column index containing data (1-based) + + :type: int + """ + max_col = 1 + if self._cells: + cols = set(c[1] for c in self._cells) + max_col = max(cols) + return max_col + + + def calculate_dimension(self): + """Return the minimum bounding range for all cells containing data (ex. 'A1:M24') + + :rtype: string + """ + if self._cells: + rows = set() + cols = set() + for row, col in self._cells: + rows.add(row) + cols.add(col) + max_row = max(rows) + max_col = max(cols) + min_col = min(cols) + min_row = min(rows) + else: + return "A1:A1" + + return f"{get_column_letter(min_col)}{min_row}:{get_column_letter(max_col)}{max_row}" + + + @property + def dimensions(self): + """Returns the result of :func:`calculate_dimension`""" + return self.calculate_dimension() + + + def iter_rows(self, min_row=None, max_row=None, min_col=None, max_col=None, values_only=False): + """ + Produces cells from the worksheet, by row. Specify the iteration range + using indices of rows and columns. + + If no indices are specified the range starts at A1. + + If no cells are in the worksheet an empty tuple will be returned. + + :param min_col: smallest column index (1-based index) + :type min_col: int + + :param min_row: smallest row index (1-based index) + :type min_row: int + + :param max_col: largest column index (1-based index) + :type max_col: int + + :param max_row: largest row index (1-based index) + :type max_row: int + + :param values_only: whether only cell values should be returned + :type values_only: bool + + :rtype: generator + """ + + if self._current_row == 0 and not any([min_col, min_row, max_col, max_row ]): + return iter(()) + + + min_col = min_col or 1 + min_row = min_row or 1 + max_col = max_col or self.max_column + max_row = max_row or self.max_row + + return self._cells_by_row(min_col, min_row, max_col, max_row, values_only) + + + def _cells_by_row(self, min_col, min_row, max_col, max_row, values_only=False): + for row in range(min_row, max_row + 1): + cells = (self.cell(row=row, column=column) for column in range(min_col, max_col + 1)) + if values_only: + yield tuple(cell.value for cell in cells) + else: + yield tuple(cells) + + + @property + def rows(self): + """Produces all cells in the worksheet, by row (see :func:`iter_rows`) + + :type: generator + """ + return self.iter_rows() + + + @property + def values(self): + """Produces all cell values in the worksheet, by row + + :type: generator + """ + for row in self.iter_rows(values_only=True): + yield row + + + def iter_cols(self, min_col=None, max_col=None, min_row=None, max_row=None, values_only=False): + """ + Produces cells from the worksheet, by column. Specify the iteration range + using indices of rows and columns. + + If no indices are specified the range starts at A1. + + If no cells are in the worksheet an empty tuple will be returned. + + :param min_col: smallest column index (1-based index) + :type min_col: int + + :param min_row: smallest row index (1-based index) + :type min_row: int + + :param max_col: largest column index (1-based index) + :type max_col: int + + :param max_row: largest row index (1-based index) + :type max_row: int + + :param values_only: whether only cell values should be returned + :type values_only: bool + + :rtype: generator + """ + + if self._current_row == 0 and not any([min_col, min_row, max_col, max_row]): + return iter(()) + + min_col = min_col or 1 + min_row = min_row or 1 + max_col = max_col or self.max_column + max_row = max_row or self.max_row + + return self._cells_by_col(min_col, min_row, max_col, max_row, values_only) + + + def _cells_by_col(self, min_col, min_row, max_col, max_row, values_only=False): + """ + Get cells by column + """ + for column in range(min_col, max_col+1): + cells = (self.cell(row=row, column=column) + for row in range(min_row, max_row+1)) + if values_only: + yield tuple(cell.value for cell in cells) + else: + yield tuple(cells) + + + @property + def columns(self): + """Produces all cells in the worksheet, by column (see :func:`iter_cols`)""" + return self.iter_cols() + + + def set_printer_settings(self, paper_size, orientation): + """Set printer settings """ + + self.page_setup.paperSize = paper_size + self.page_setup.orientation = orientation + + + def add_data_validation(self, data_validation): + """ Add a data-validation object to the sheet. The data-validation + object defines the type of data-validation to be applied and the + cell or range of cells it should apply to. + """ + self.data_validations.append(data_validation) + + + def add_chart(self, chart, anchor=None): + """ + Add a chart to the sheet + Optionally provide a cell for the top-left anchor + """ + if anchor is not None: + chart.anchor = anchor + self._charts.append(chart) + + + def add_image(self, img, anchor=None): + """ + Add an image to the sheet. + Optionally provide a cell for the top-left anchor + """ + if anchor is not None: + img.anchor = anchor + self._images.append(img) + + + def add_table(self, table): + """ + Check for duplicate name in definedNames and other worksheet tables + before adding table. + """ + + if self.parent._duplicate_name(table.name): + raise ValueError("Table with name {0} already exists".format(table.name)) + if not hasattr(self, "_get_cell"): + warn("In write-only mode you must add table columns manually") + self._tables.add(table) + + + @property + def tables(self): + return self._tables + + + def add_pivot(self, pivot): + self._pivots.append(pivot) + + + def merge_cells(self, range_string=None, start_row=None, start_column=None, end_row=None, end_column=None): + """ Set merge on a cell range. Range is a cell range (e.g. A1:E1) """ + if range_string is None: + cr = CellRange(range_string=range_string, min_col=start_column, min_row=start_row, + max_col=end_column, max_row=end_row) + range_string = cr.coord + mcr = MergedCellRange(self, range_string) + self.merged_cells.add(mcr) + self._clean_merge_range(mcr) + + + def _clean_merge_range(self, mcr): + """ + Remove all but the top left-cell from a range of merged cells + and recreate the lost border information. + Borders are then applied + """ + cells = mcr.cells + next(cells) # skip first cell + for row, col in cells: + self._cells[row, col] = MergedCell(self, row, col) + mcr.format() + + + @property + @deprecated("Use ws.merged_cells.ranges") + def merged_cell_ranges(self): + """Return a copy of cell ranges""" + return self.merged_cells.ranges[:] + + + def unmerge_cells(self, range_string=None, start_row=None, start_column=None, end_row=None, end_column=None): + """ Remove merge on a cell range. Range is a cell range (e.g. A1:E1) """ + cr = CellRange(range_string=range_string, min_col=start_column, min_row=start_row, + max_col=end_column, max_row=end_row) + + if cr.coord not in self.merged_cells: + raise ValueError("Cell range {0} is not merged".format(cr.coord)) + + self.merged_cells.remove(cr) + + cells = cr.cells + next(cells) # skip first cell + for row, col in cells: + del self._cells[(row, col)] + + + def append(self, iterable): + """Appends a group of values at the bottom of the current sheet. + + * If it's a list: all values are added in order, starting from the first column + * If it's a dict: values are assigned to the columns indicated by the keys (numbers or letters) + + :param iterable: list, range or generator, or dict containing values to append + :type iterable: list|tuple|range|generator or dict + + Usage: + + * append(['This is A1', 'This is B1', 'This is C1']) + * **or** append({'A' : 'This is A1', 'C' : 'This is C1'}) + * **or** append({1 : 'This is A1', 3 : 'This is C1'}) + + :raise: TypeError when iterable is neither a list/tuple nor a dict + + """ + row_idx = self._current_row + 1 + + if (isinstance(iterable, (list, tuple, range)) + or isgenerator(iterable)): + for col_idx, content in enumerate(iterable, 1): + if isinstance(content, Cell): + # compatible with write-only mode + cell = content + if cell.parent and cell.parent != self: + raise ValueError("Cells cannot be copied from other worksheets") + cell.parent = self + cell.column = col_idx + cell.row = row_idx + else: + cell = Cell(self, row=row_idx, column=col_idx, value=content) + self._cells[(row_idx, col_idx)] = cell + + elif isinstance(iterable, dict): + for col_idx, content in iterable.items(): + if isinstance(col_idx, str): + col_idx = column_index_from_string(col_idx) + cell = Cell(self, row=row_idx, column=col_idx, value=content) + self._cells[(row_idx, col_idx)] = cell + + else: + self._invalid_row(iterable) + + self._current_row = row_idx + + + def _move_cells(self, min_row=None, min_col=None, offset=0, row_or_col="row"): + """ + Move either rows or columns around by the offset + """ + reverse = offset > 0 # start at the end if inserting + row_offset = 0 + col_offset = 0 + + # need to make affected ranges contiguous + if row_or_col == 'row': + cells = self.iter_rows(min_row=min_row) + row_offset = offset + key = 0 + else: + cells = self.iter_cols(min_col=min_col) + col_offset = offset + key = 1 + cells = list(cells) + + for row, column in sorted(self._cells, key=itemgetter(key), reverse=reverse): + if min_row and row < min_row: + continue + elif min_col and column < min_col: + continue + + self._move_cell(row, column, row_offset, col_offset) + + + def insert_rows(self, idx, amount=1): + """ + Insert row or rows before row==idx + """ + self._move_cells(min_row=idx, offset=amount, row_or_col="row") + self._current_row = self.max_row + + + def insert_cols(self, idx, amount=1): + """ + Insert column or columns before col==idx + """ + self._move_cells(min_col=idx, offset=amount, row_or_col="column") + + + def delete_rows(self, idx, amount=1): + """ + Delete row or rows from row==idx + """ + + remainder = _gutter(idx, amount, self.max_row) + + self._move_cells(min_row=idx+amount, offset=-amount, row_or_col="row") + + # calculating min and max col is an expensive operation, do it only once + min_col = self.min_column + max_col = self.max_column + 1 + for row in remainder: + for col in range(min_col, max_col): + if (row, col) in self._cells: + del self._cells[row, col] + self._current_row = self.max_row + if not self._cells: + self._current_row = 0 + + + def delete_cols(self, idx, amount=1): + """ + Delete column or columns from col==idx + """ + + remainder = _gutter(idx, amount, self.max_column) + + self._move_cells(min_col=idx+amount, offset=-amount, row_or_col="column") + + # calculating min and max row is an expensive operation, do it only once + min_row = self.min_row + max_row = self.max_row + 1 + for col in remainder: + for row in range(min_row, max_row): + if (row, col) in self._cells: + del self._cells[row, col] + + + def move_range(self, cell_range, rows=0, cols=0, translate=False): + """ + Move a cell range by the number of rows and/or columns: + down if rows > 0 and up if rows < 0 + right if cols > 0 and left if cols < 0 + Existing cells will be overwritten. + Formulae and references will not be updated. + """ + if isinstance(cell_range, str): + cell_range = CellRange(cell_range) + if not isinstance(cell_range, CellRange): + raise ValueError("Only CellRange objects can be moved") + if not rows and not cols: + return + + down = rows > 0 + right = cols > 0 + + if rows: + cells = sorted(cell_range.rows, reverse=down) + else: + cells = sorted(cell_range.cols, reverse=right) + + for row, col in chain.from_iterable(cells): + self._move_cell(row, col, rows, cols, translate) + + # rebase moved range + cell_range.shift(row_shift=rows, col_shift=cols) + + + def _move_cell(self, row, column, row_offset, col_offset, translate=False): + """ + Move a cell from one place to another. + Delete at old index + Rebase coordinate + """ + cell = self._get_cell(row, column) + new_row = cell.row + row_offset + new_col = cell.column + col_offset + self._cells[new_row, new_col] = cell + del self._cells[(cell.row, cell.column)] + cell.row = new_row + cell.column = new_col + if translate and cell.data_type == "f": + t = Translator(cell.value, cell.coordinate) + cell.value = t.translate_formula(row_delta=row_offset, col_delta=col_offset) + + + def _invalid_row(self, iterable): + raise TypeError('Value must be a list, tuple, range or generator, or a dict. Supplied value is {0}'.format( + type(iterable)) + ) + + + def _add_column(self): + """Dimension factory for column information""" + + return ColumnDimension(self) + + def _add_row(self): + """Dimension factory for row information""" + + return RowDimension(self) + + + @property + def print_title_rows(self): + """Rows to be printed at the top of every page (ex: '1:3')""" + if self._print_rows: + return self._print_rows + + + @print_title_rows.setter + def print_title_rows(self, rows): + """ + Set rows to be printed on the top of every page + format `1:3` + """ + if rows is not None: + if not ROW_RANGE_RE.match(rows): + raise ValueError("Print title rows must be the form 1:3") + self._print_rows = rows + + + @property + def print_title_cols(self): + """Columns to be printed at the left side of every page (ex: 'A:C')""" + if self._print_cols: + return self._print_cols + + + @print_title_cols.setter + def print_title_cols(self, cols): + """ + Set cols to be printed on the left of every page + format ``A:C` + """ + if cols is not None: + if not COL_RANGE_RE.match(cols): + raise ValueError("Print title cols must be the form C:D") + self._print_cols = cols + + + @property + def print_titles(self): + if self.print_title_cols and self.print_title_rows: + return ",".join([self.print_title_rows, self.print_title_cols]) + else: + return self.print_title_rows or self.print_title_cols + + + @property + def print_area(self): + """ + The print area for the worksheet, or None if not set. To set, supply a range + like 'A1:D4' or a list of ranges. + """ + return self._print_area + + + @print_area.setter + def print_area(self, value): + """ + Range of cells in the form A1:D4 or list of ranges + """ + if isinstance(value, str): + value = [value] + + self._print_area = [absolute_coordinate(v) for v in value] + + +def _gutter(idx, offset, max_val): + """ + When deleting rows and columns are deleted we rely on overwriting. + This may not be the case for a large offset on small set of cells: + range(cells_to_delete) > range(cell_to_be_moved) + """ + gutter = range(max(max_val+1-offset, idx), min(idx+offset, max_val)+1) + return gutter diff --git a/.venv/lib/python3.9/site-packages/openpyxl/writer/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/writer/__init__.py new file mode 100644 index 00000000..843d4997 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/writer/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2010-2021 openpyxl diff --git a/.venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32193329f14b1ea997a6cb2ee559061b3b7f5a88 GIT binary patch literal 183 zcmYe~<>g`kg5b@w6P1DVV-N=!FakLaKwQiMBvKfH88jLFRx%WUgb~EA82#MTl*~kZ zkHn%Bm!izFRQ-U;jFS93{ov$`{QMk!y|UE2GX0#)B>jTQl8pR3V?9g#;>?m%-GapA z?8NlcV*UJr)VzYqiX8p&BA|GYetdjpUS>&ryk0@&Ee@O9{FKt1R6CHZpMjVG0O?aN A?*IS* literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/excel.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/excel.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dc6e3b91470150d7109a026080493e6b3c35df3 GIT binary patch literal 8396 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 GhWdXj^fM&@ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/theme.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/writer/__pycache__/theme.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b33c772140817f3e683476e6fc6447d76a5c5e3 GIT binary patch literal 10531 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/writer/excel.py b/.venv/lib/python3.9/site-packages/openpyxl/writer/excel.py new file mode 100644 index 00000000..112f19b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/writer/excel.py @@ -0,0 +1,310 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Write a .xlsx file.""" + +# Python stdlib imports +import re +from tempfile import TemporaryFile +from zipfile import ZipFile, ZIP_DEFLATED + +# package imports +from openpyxl.compat import deprecated +from openpyxl.utils.exceptions import InvalidFileException +from openpyxl.xml.constants import ( + ARC_SHARED_STRINGS, + ARC_CONTENT_TYPES, + ARC_ROOT_RELS, + ARC_WORKBOOK_RELS, + ARC_APP, ARC_CORE, + ARC_THEME, + ARC_STYLE, + ARC_WORKBOOK, + PACKAGE_WORKSHEETS, + PACKAGE_CHARTSHEETS, + PACKAGE_DRAWINGS, + PACKAGE_CHARTS, + PACKAGE_IMAGES, + PACKAGE_XL + ) +from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing +from openpyxl.xml.functions import tostring, fromstring, Element +from openpyxl.packaging.manifest import Manifest +from openpyxl.packaging.relationship import ( + get_rels_path, + RelationshipList, + Relationship, +) +from openpyxl.comments.comment_sheet import CommentSheet +from openpyxl.packaging.extended import ExtendedProperties +from openpyxl.styles.stylesheet import write_stylesheet +from openpyxl.worksheet._writer import WorksheetWriter +from openpyxl.workbook._writer import WorkbookWriter +from .theme import theme_xml + + +class ExcelWriter(object): + """Write a workbook object to an Excel file.""" + + def __init__(self, workbook, archive): + self._archive = archive + self.workbook = workbook + self.manifest = Manifest() + self.vba_modified = set() + self._tables = [] + self._charts = [] + self._images = [] + self._drawings = [] + self._comments = [] + self._pivots = [] + + + def write_data(self): + """Write the various xml files into the zip archive.""" + # cleanup all worksheets + archive = self._archive + + props = ExtendedProperties() + archive.writestr(ARC_APP, tostring(props.to_tree())) + + archive.writestr(ARC_CORE, tostring(self.workbook.properties.to_tree())) + if self.workbook.loaded_theme: + archive.writestr(ARC_THEME, self.workbook.loaded_theme) + else: + archive.writestr(ARC_THEME, theme_xml) + + self._write_worksheets() + self._write_chartsheets() + self._write_images() + self._write_charts() + + #self._archive.writestr(ARC_SHARED_STRINGS, + #write_string_table(self.workbook.shared_strings)) + self._write_external_links() + + stylesheet = write_stylesheet(self.workbook) + archive.writestr(ARC_STYLE, tostring(stylesheet)) + + writer = WorkbookWriter(self.workbook) + archive.writestr(ARC_ROOT_RELS, writer.write_root_rels()) + archive.writestr(ARC_WORKBOOK, writer.write()) + archive.writestr(ARC_WORKBOOK_RELS, writer.write_rels()) + + self._merge_vba() + + self.manifest._write(archive, self.workbook) + + def _merge_vba(self): + """ + If workbook contains macros then extract associated files from cache + of old file and add to archive + """ + ARC_VBA = re.compile("|".join( + ('xl/vba', r'xl/drawings/.*vmlDrawing\d\.vml', + 'xl/ctrlProps', 'customUI', 'xl/activeX', r'xl/media/.*\.emf') + ) + ) + + if self.workbook.vba_archive: + for name in set(self.workbook.vba_archive.namelist()) - self.vba_modified: + if ARC_VBA.match(name): + self._archive.writestr(name, self.workbook.vba_archive.read(name)) + + + def _write_images(self): + # delegate to object + for img in self._images: + self._archive.writestr(img.path[1:], img._data()) + + + def _write_charts(self): + # delegate to object + if len(self._charts) != len(set(self._charts)): + raise InvalidFileException("The same chart cannot be used in more than one worksheet") + for chart in self._charts: + self._archive.writestr(chart.path[1:], tostring(chart._write())) + self.manifest.append(chart) + + + def _write_drawing(self, drawing): + """ + Write a drawing + """ + self._drawings.append(drawing) + drawing._id = len(self._drawings) + for chart in drawing.charts: + self._charts.append(chart) + chart._id = len(self._charts) + for img in drawing.images: + self._images.append(img) + img._id = len(self._images) + rels_path = get_rels_path(drawing.path)[1:] + self._archive.writestr(drawing.path[1:], tostring(drawing._write())) + self._archive.writestr(rels_path, tostring(drawing._write_rels())) + self.manifest.append(drawing) + + + def _write_chartsheets(self): + for idx, sheet in enumerate(self.workbook.chartsheets, 1): + + sheet._id = idx + xml = tostring(sheet.to_tree()) + + self._archive.writestr(sheet.path[1:], xml) + self.manifest.append(sheet) + + if sheet._drawing: + self._write_drawing(sheet._drawing) + + rel = Relationship(type="drawing", Target=sheet._drawing.path) + rels = RelationshipList() + rels.append(rel) + tree = rels.to_tree() + + rels_path = get_rels_path(sheet.path[1:]) + self._archive.writestr(rels_path, tostring(tree)) + + + def _write_comment(self, ws): + + cs = CommentSheet.from_comments(ws._comments) + self._comments.append(cs) + cs._id = len(self._comments) + self._archive.writestr(cs.path[1:], tostring(cs.to_tree())) + self.manifest.append(cs) + + if ws.legacy_drawing is None or self.workbook.vba_archive is None: + ws.legacy_drawing = 'xl/drawings/commentsDrawing{0}.vml'.format(cs._id) + vml = None + else: + vml = fromstring(self.workbook.vba_archive.read(ws.legacy_drawing)) + + vml = cs.write_shapes(vml) + + self._archive.writestr(ws.legacy_drawing, vml) + self.vba_modified.add(ws.legacy_drawing) + + comment_rel = Relationship(Id="comments", type=cs._rel_type, Target=cs.path) + ws._rels.append(comment_rel) + + + def write_worksheet(self, ws): + ws._drawing = SpreadsheetDrawing() + ws._drawing.charts = ws._charts + ws._drawing.images = ws._images + if self.workbook.write_only: + if not ws.closed: + ws.close() + writer = ws._writer + else: + writer = WorksheetWriter(ws) + writer.write() + + ws._rels = writer._rels + self._archive.write(writer.out, ws.path[1:]) + self.manifest.append(ws) + writer.cleanup() + + + def _write_worksheets(self): + + pivot_caches = set() + + for idx, ws in enumerate(self.workbook.worksheets, 1): + + ws._id = idx + self.write_worksheet(ws) + + if ws._drawing: + self._write_drawing(ws._drawing) + + for r in ws._rels.Relationship: + if "drawing" in r.Type: + r.Target = ws._drawing.path + + if ws._comments: + self._write_comment(ws) + + if ws.legacy_drawing is not None: + shape_rel = Relationship(type="vmlDrawing", Id="anysvml", + Target="/" + ws.legacy_drawing) + ws._rels.append(shape_rel) + + for t in ws._tables.values(): + self._tables.append(t) + t.id = len(self._tables) + t._write(self._archive) + self.manifest.append(t) + ws._rels[t._rel_id].Target = t.path + + for p in ws._pivots: + if p.cache not in pivot_caches: + pivot_caches.add(p.cache) + p.cache._id = len(pivot_caches) + + self._pivots.append(p) + p._id = len(self._pivots) + p._write(self._archive, self.manifest) + self.workbook._pivots.append(p) + r = Relationship(Type=p.rel_type, Target=p.path) + ws._rels.append(r) + + if ws._rels: + tree = ws._rels.to_tree() + rels_path = get_rels_path(ws.path)[1:] + self._archive.writestr(rels_path, tostring(tree)) + + + def _write_external_links(self): + # delegate to object + """Write links to external workbooks""" + wb = self.workbook + for idx, link in enumerate(wb._external_links, 1): + link._id = idx + rels_path = get_rels_path(link.path[1:]) + + xml = link.to_tree() + self._archive.writestr(link.path[1:], tostring(xml)) + rels = RelationshipList() + rels.append(link.file_link) + self._archive.writestr(rels_path, tostring(rels.to_tree())) + self.manifest.append(link) + + + def save(self): + """Write data into the archive.""" + self.write_data() + self._archive.close() + + +def save_workbook(workbook, filename): + """Save the given workbook on the filesystem under the name filename. + + :param workbook: the workbook to save + :type workbook: :class:`openpyxl.workbook.Workbook` + + :param filename: the path to which save the workbook + :type filename: string + + :rtype: bool + + """ + archive = ZipFile(filename, 'w', ZIP_DEFLATED, allowZip64=True) + writer = ExcelWriter(workbook, archive) + writer.save() + return True + + +@deprecated("Use a NamedTemporaryFile") +def save_virtual_workbook(workbook): + """Return an in-memory workbook, suitable for a Django response.""" + tmp = TemporaryFile() + archive = ZipFile(tmp, 'w', ZIP_DEFLATED, allowZip64=True) + + writer = ExcelWriter(workbook, archive) + writer.save() + + tmp.seek(0) + virtual_workbook = tmp.read() + tmp.close() + + return virtual_workbook diff --git a/.venv/lib/python3.9/site-packages/openpyxl/writer/theme.py b/.venv/lib/python3.9/site-packages/openpyxl/writer/theme.py new file mode 100644 index 00000000..d54231e8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/writer/theme.py @@ -0,0 +1,291 @@ +# Copyright (c) 2010-2021 openpyxl + +"""Write the theme xml based on a fixed string.""" + + +theme_xml = """<?xml version="1.0"?> +<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"> + <a:themeElements> + <a:clrScheme name="Office"> + <a:dk1> + <a:sysClr val="windowText" lastClr="000000"/> + </a:dk1> + <a:lt1> + <a:sysClr val="window" lastClr="FFFFFF"/> + </a:lt1> + <a:dk2> + <a:srgbClr val="1F497D"/> + </a:dk2> + <a:lt2> + <a:srgbClr val="EEECE1"/> + </a:lt2> + <a:accent1> + <a:srgbClr val="4F81BD"/> + </a:accent1> + <a:accent2> + <a:srgbClr val="C0504D"/> + </a:accent2> + <a:accent3> + <a:srgbClr val="9BBB59"/> + </a:accent3> + <a:accent4> + <a:srgbClr val="8064A2"/> + </a:accent4> + <a:accent5> + <a:srgbClr val="4BACC6"/> + </a:accent5> + <a:accent6> + <a:srgbClr val="F79646"/> + </a:accent6> + <a:hlink> + <a:srgbClr val="0000FF"/> + </a:hlink> + <a:folHlink> + <a:srgbClr val="800080"/> + </a:folHlink> + </a:clrScheme> + <a:fontScheme name="Office"> + <a:majorFont> + <a:latin typeface="Cambria"/> + <a:ea typeface=""/> + <a:cs typeface=""/> + <a:font script="Jpan" typeface="MS Pゴシック"/> + <a:font script="Hang" typeface="맑은 고딕"/> + <a:font script="Hans" typeface="宋体"/> + <a:font script="Hant" typeface="新細明體"/> + <a:font script="Arab" typeface="Times New Roman"/> + <a:font script="Hebr" typeface="Times New Roman"/> + <a:font script="Thai" typeface="Tahoma"/> + <a:font script="Ethi" typeface="Nyala"/> + <a:font script="Beng" typeface="Vrinda"/> + <a:font script="Gujr" typeface="Shruti"/> + <a:font script="Khmr" typeface="MoolBoran"/> + <a:font script="Knda" typeface="Tunga"/> + <a:font script="Guru" typeface="Raavi"/> + <a:font script="Cans" typeface="Euphemia"/> + <a:font script="Cher" typeface="Plantagenet Cherokee"/> + <a:font script="Yiii" typeface="Microsoft Yi Baiti"/> + <a:font script="Tibt" typeface="Microsoft Himalaya"/> + <a:font script="Thaa" typeface="MV Boli"/> + <a:font script="Deva" typeface="Mangal"/> + <a:font script="Telu" typeface="Gautami"/> + <a:font script="Taml" typeface="Latha"/> + <a:font script="Syrc" typeface="Estrangelo Edessa"/> + <a:font script="Orya" typeface="Kalinga"/> + <a:font script="Mlym" typeface="Kartika"/> + <a:font script="Laoo" typeface="DokChampa"/> + <a:font script="Sinh" typeface="Iskoola Pota"/> + <a:font script="Mong" typeface="Mongolian Baiti"/> + <a:font script="Viet" typeface="Times New Roman"/> + <a:font script="Uigh" typeface="Microsoft Uighur"/> + </a:majorFont> + <a:minorFont> + <a:latin typeface="Calibri"/> + <a:ea typeface=""/> + <a:cs typeface=""/> + <a:font script="Jpan" typeface="MS Pゴシック"/> + <a:font script="Hang" typeface="맑은 고딕"/> + <a:font script="Hans" typeface="宋体"/> + <a:font script="Hant" typeface="新細明體"/> + <a:font script="Arab" typeface="Arial"/> + <a:font script="Hebr" typeface="Arial"/> + <a:font script="Thai" typeface="Tahoma"/> + <a:font script="Ethi" typeface="Nyala"/> + <a:font script="Beng" typeface="Vrinda"/> + <a:font script="Gujr" typeface="Shruti"/> + <a:font script="Khmr" typeface="DaunPenh"/> + <a:font script="Knda" typeface="Tunga"/> + <a:font script="Guru" typeface="Raavi"/> + <a:font script="Cans" typeface="Euphemia"/> + <a:font script="Cher" typeface="Plantagenet Cherokee"/> + <a:font script="Yiii" typeface="Microsoft Yi Baiti"/> + <a:font script="Tibt" typeface="Microsoft Himalaya"/> + <a:font script="Thaa" typeface="MV Boli"/> + <a:font script="Deva" typeface="Mangal"/> + <a:font script="Telu" typeface="Gautami"/> + <a:font script="Taml" typeface="Latha"/> + <a:font script="Syrc" typeface="Estrangelo Edessa"/> + <a:font script="Orya" typeface="Kalinga"/> + <a:font script="Mlym" typeface="Kartika"/> + <a:font script="Laoo" typeface="DokChampa"/> + <a:font script="Sinh" typeface="Iskoola Pota"/> + <a:font script="Mong" typeface="Mongolian Baiti"/> + <a:font script="Viet" typeface="Arial"/> + <a:font script="Uigh" typeface="Microsoft Uighur"/> + </a:minorFont> + </a:fontScheme> + <a:fmtScheme name="Office"> + <a:fillStyleLst> + <a:solidFill> + <a:schemeClr val="phClr"/> + </a:solidFill> + <a:gradFill rotWithShape="1"> + <a:gsLst> + <a:gs pos="0"> + <a:schemeClr val="phClr"> + <a:tint val="50000"/> + <a:satMod val="300000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="35000"> + <a:schemeClr val="phClr"> + <a:tint val="37000"/> + <a:satMod val="300000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="100000"> + <a:schemeClr val="phClr"> + <a:tint val="15000"/> + <a:satMod val="350000"/> + </a:schemeClr> + </a:gs> + </a:gsLst> + <a:lin ang="16200000" scaled="1"/> + </a:gradFill> + <a:gradFill rotWithShape="1"> + <a:gsLst> + <a:gs pos="0"> + <a:schemeClr val="phClr"> + <a:shade val="51000"/> + <a:satMod val="130000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="80000"> + <a:schemeClr val="phClr"> + <a:shade val="93000"/> + <a:satMod val="130000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="100000"> + <a:schemeClr val="phClr"> + <a:shade val="94000"/> + <a:satMod val="135000"/> + </a:schemeClr> + </a:gs> + </a:gsLst> + <a:lin ang="16200000" scaled="0"/> + </a:gradFill> + </a:fillStyleLst> + <a:lnStyleLst> + <a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"> + <a:solidFill> + <a:schemeClr val="phClr"> + <a:shade val="95000"/> + <a:satMod val="105000"/> + </a:schemeClr> + </a:solidFill> + <a:prstDash val="solid"/> + </a:ln> + <a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"> + <a:solidFill> + <a:schemeClr val="phClr"/> + </a:solidFill> + <a:prstDash val="solid"/> + </a:ln> + <a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"> + <a:solidFill> + <a:schemeClr val="phClr"/> + </a:solidFill> + <a:prstDash val="solid"/> + </a:ln> + </a:lnStyleLst> + <a:effectStyleLst> + <a:effectStyle> + <a:effectLst> + <a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"> + <a:srgbClr val="000000"> + <a:alpha val="38000"/> + </a:srgbClr> + </a:outerShdw> + </a:effectLst> + </a:effectStyle> + <a:effectStyle> + <a:effectLst> + <a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"> + <a:srgbClr val="000000"> + <a:alpha val="35000"/> + </a:srgbClr> + </a:outerShdw> + </a:effectLst> + </a:effectStyle> + <a:effectStyle> + <a:effectLst> + <a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"> + <a:srgbClr val="000000"> + <a:alpha val="35000"/> + </a:srgbClr> + </a:outerShdw> + </a:effectLst> + <a:scene3d> + <a:camera prst="orthographicFront"> + <a:rot lat="0" lon="0" rev="0"/> + </a:camera> + <a:lightRig rig="threePt" dir="t"> + <a:rot lat="0" lon="0" rev="1200000"/> + </a:lightRig> + </a:scene3d> + <a:sp3d> + <a:bevelT w="63500" h="25400"/> + </a:sp3d> + </a:effectStyle> + </a:effectStyleLst> + <a:bgFillStyleLst> + <a:solidFill> + <a:schemeClr val="phClr"/> + </a:solidFill> + <a:gradFill rotWithShape="1"> + <a:gsLst> + <a:gs pos="0"> + <a:schemeClr val="phClr"> + <a:tint val="40000"/> + <a:satMod val="350000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="40000"> + <a:schemeClr val="phClr"> + <a:tint val="45000"/> + <a:shade val="99000"/> + <a:satMod val="350000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="100000"> + <a:schemeClr val="phClr"> + <a:shade val="20000"/> + <a:satMod val="255000"/> + </a:schemeClr> + </a:gs> + </a:gsLst> + <a:path path="circle"> + <a:fillToRect l="50000" t="-80000" r="50000" b="180000"/> + </a:path> + </a:gradFill> + <a:gradFill rotWithShape="1"> + <a:gsLst> + <a:gs pos="0"> + <a:schemeClr val="phClr"> + <a:tint val="80000"/> + <a:satMod val="300000"/> + </a:schemeClr> + </a:gs> + <a:gs pos="100000"> + <a:schemeClr val="phClr"> + <a:shade val="30000"/> + <a:satMod val="200000"/> + </a:schemeClr> + </a:gs> + </a:gsLst> + <a:path path="circle"> + <a:fillToRect l="50000" t="50000" r="50000" b="50000"/> + </a:path> + </a:gradFill> + </a:bgFillStyleLst> + </a:fmtScheme> + </a:themeElements> + <a:objectDefaults/> + <a:extraClrSchemeLst/> +</a:theme> +""" + +def write_theme(): + """Write the theme xml.""" + return theme_xml diff --git a/.venv/lib/python3.9/site-packages/openpyxl/xml/__init__.py b/.venv/lib/python3.9/site-packages/openpyxl/xml/__init__.py new file mode 100644 index 00000000..bb89eb81 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/xml/__init__.py @@ -0,0 +1,42 @@ +# Copyright (c) 2010-2021 openpyxl + + +"""Collection of XML resources compatible across different Python versions""" +import os + + +def lxml_available(): + try: + from lxml.etree import LXML_VERSION + LXML = LXML_VERSION >= (3, 3, 1, 0) + if not LXML: + import warnings + warnings.warn("The installed version of lxml is too old to be used with openpyxl") + return False # we have it, but too old + else: + return True # we have it, and recent enough + except ImportError: + return False # we don't even have it + + +def lxml_env_set(): + return os.environ.get("OPENPYXL_LXML", "True") == "True" + + +LXML = lxml_available() and lxml_env_set() + + +def defusedxml_available(): + try: + import defusedxml # noqa + except ImportError: + return False + else: + return True + + +def defusedxml_env_set(): + return os.environ.get("OPENPYXL_DEFUSEDXML", "True") == "True" + + +DEFUSEDXML = defusedxml_available() and defusedxml_env_set() diff --git a/.venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8a9e1f1920ef4c6d7e7c79985ce7ed5df3bd91f GIT binary patch literal 1197 zcma)5K~LK-6t<nT4W&gIpxq|rgw#oErtP$8nzlh1NUT(Aw?Qvah+XQD#*v*CTG12x z7rVfb^Zre)JMj;uo%UW>p-dcR$<KMupY88`?<J^K%LGRI$M@iuMaWNd<_81k4NS8I zhLbMgl(UF-DYv-&jdU3=@FJ|1U}r?F3G0k>ZC>II#0q?Ymtif60_?pLMc6BG)+V*Z z$@_H`MIub4ie1%n_de{nS{OCdp)hW!21$_0gGjhRsFg7;m%X0QB2L}bIPJrkk<bRt znk%41njzZRfHa5$a~-DXfSJ6^Ns|Gc5=u@9w~h)^dP>bRh?9s-*(p1RbS@|*UkER> z(H7Cht2RmPag)M1O7`=;ryv8QP-<!lch_iE*#Y+c=f=nO_HMIgU*Y>2%@qQ2)Z}fa zFI*X$G=Qx6j6G%^oeUyZ8aGwSRS}2PJrM5D0P0w#eODzSPR1wEW~Wx@mJwYSsTM+G zWisbD(6NjUO=ja)wzxe=lujF3D_yg$F*fqk1%O`?y#UMaeQzMR47{yC^9?OW!fTED zsfxXJ*jFm@>LU@4yht8+Nv`(u`U}s<R6I$7@F+MG#+$M7fRE?<GM1_D*OPI!gbe*) z6v!xm`e7Z+Di}f;tx$*BFw3;a%HZimh=;g2KWuRF(k;VBO1{!_hUL3poE>5BSj}#A zx7BF2_V;#tOf9oJdMGZfj46|qfVtEv&a6X_3A3Fu*y>p&X9zIgi1Z;u3@S;eegO6k z8!X>VBRvLZQpp>JOlPe@$L>vQ05@n2t9{)b_H}ze)mT>MqK5?qWpoLysZou&{Yz~1 z7IrJ(BLJHG7tI9(37KAj*d3zalmC!y<wmxR#^$GXV*~r50%vs<&3$4m&vE)I*&1MH zl3`_QPFC`Lu0r3}OLz}fH5XH(csW~*z;1c&+4~-IUJVo<M&dQ*V(>2L7Ry<3?26?q GIQAdY!wB&J literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/constants.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/constants.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2fffdbd0ee07159e1b382a9b72e031a700733f39 GIT binary patch literal 4195 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/functions.cpython-39.pyc b/.venv/lib/python3.9/site-packages/openpyxl/xml/__pycache__/functions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..400d3ec9c54a8b0025a2a4381253f0d5b1411858 GIT binary patch literal 1818 zcmZWpOLH4V5Z>9<W3`rK#j)kLt-K%$L|YUbfJ)_2wi2gE6d@T1b5X5##`cn(-L2V? zv7O`;E*$a;xY$)(ICJ5rP;=$PU*JUdN*1PAX?woup6;3Xx_kXv%`xB^{Pwf|Q`s>7 z#L4BS0FwvsX1@@_U<Nf9VP-@qfzgUgVNr{qt`HSOkrqXXmWaVitT-~{G3ZNCS=iJT z6<QGvb;Jyv5mj0hHCoI4%3!b4x|pT2;8$ZdtGpw$!7X0r4d%QvX+3@bab}_`(>Rm$ zc`S&T$Eikh7#Cs`^SyrupIUSkW9Rld_|Mb%sr?VI*TJ6a0MAAXqDh-#kuHiQx+IqA zvRI)jVwJ9nHM%C&>AKjU8)B1giWY4_9EWf6jkhKu)?imAXpXN>&{f`?pn1MHK?{6k zf|`7Ff)@GO1TFEU30mgM6STrxIkNaHTV-p&$1Epj*Vy_wq1V|4+x*d>H&}~ZgYhOa z2Znq7blrLJ>}e}VMCvPlFN{KU(%K)zfeMp2bN<5gc3q;&sV`ONN3N-z?argG2c2#3 z(}kxXcioaMcOou$th6&2?OotG$q&LzaXE~A!L!s4xGsLx1+2@*BH9llt}76r=gW*s z481QCktrF*2ijK27(IgR8<J;9bi{|SMW{lafvyBcLbsAE!Xae|#SDrn3L6C!so@e? z84qX#ze)%v%6;7#ZEx*7?C%b{1GrZI;kQq^j}bQpJDtw%@UmvS?vkc9Ui0kXlP<cH z`kkjZR=(N&uGbkLtZqH;cY6Kjy?o|uZ|!#a&vIP9z`cIw(UTXt1SEh5wOJq_0VzLS zJ5(zD!t>IRjM|Aj@K^x<vC-3JkZKZW;%ky=BB^<#sCCRFwE`x!1>Bw%M{57hXK-Dc z$3en?(bM;Dzq)<DcXyhLd+iV0H|~AZMM>aCXh2;6-8+3uKo-1P@MbqbDC4#9J$XxH zosof|%rgt;=j07}P0k9;JO{07b#)nPDzAvjG`L&THtZAmdysfb`H<x9VA$_G?!3@N z;j7?K7Y}4IN}&`Aal*JJ;#Wg{2)q7GJ;7P%dpo{l+cG@jUhm{kC9yXM4wEGE+DAM- z@}h9hOHb5c5`WVE+{+--ccAKC`UgDol9b2k$#LWXJ>Im$+v$nC0ULaY_rszwh(ijb zN^<Rgs6blq6E~#-E{<dvXXM;?Y01`W<IH44Sxp0IfM#b#PQ_{E)vfH!xcu!DDw)e8 z?iO@G@nfZnSmmj<GOpwbM2C8HVaRVi79YV>J3k!4ZSo*}m}htpgEWZz7)JM&E)Rz+ z35G*$N@%M5dO%AiF@My)xVywuO7x{p1U$51Q_e3n5O&PCcd%(Be((QnDms(-`+PX= zJH0UVZ%-P27h+L|t07phxoMN}{ZYmluDrBH&f)=@7l&%2Ie^cN@?(|B2JU$U#T<&O zDCSWtplG64M6rZo83odo*INpk%J)!(Db1<5#3R;`%*nLxiiC|Keh=x$?tv&2ZBjKG grcGLQ!*od9YS~qSy0QGU?m%7q?`Ks_$F$4;0Am`^yZ`_I literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/openpyxl/xml/constants.py b/.venv/lib/python3.9/site-packages/openpyxl/xml/constants.py new file mode 100644 index 00000000..65746be8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/xml/constants.py @@ -0,0 +1,122 @@ +# Copyright (c) 2010-2021 openpyxl + + +"""Constants for fixed paths in a file and xml namespace urls.""" + +MIN_ROW = 0 +MIN_COLUMN = 0 +MAX_COLUMN = 16384 +MAX_ROW = 1048576 + +# constants +PACKAGE_PROPS = 'docProps' +PACKAGE_XL = 'xl' +PACKAGE_RELS = '_rels' +PACKAGE_THEME = PACKAGE_XL + '/' + 'theme' +PACKAGE_WORKSHEETS = PACKAGE_XL + '/' + 'worksheets' +PACKAGE_CHARTSHEETS = PACKAGE_XL + '/' + 'chartsheets' +PACKAGE_DRAWINGS = PACKAGE_XL + '/' + 'drawings' +PACKAGE_CHARTS = PACKAGE_XL + '/' + 'charts' +PACKAGE_IMAGES = PACKAGE_XL + '/' + 'media' +PACKAGE_WORKSHEET_RELS = PACKAGE_WORKSHEETS + '/' + '_rels' +PACKAGE_CHARTSHEETS_RELS = PACKAGE_CHARTSHEETS + '/' + '_rels' +PACKAGE_PIVOT_TABLE = PACKAGE_XL + '/' + 'pivotTables' +PACKAGE_PIVOT_CACHE = PACKAGE_XL + '/' + 'pivotCache' + +ARC_CONTENT_TYPES = '[Content_Types].xml' +ARC_ROOT_RELS = PACKAGE_RELS + '/.rels' +ARC_WORKBOOK_RELS = PACKAGE_XL + '/' + PACKAGE_RELS + '/workbook.xml.rels' +ARC_CORE = PACKAGE_PROPS + '/core.xml' +ARC_APP = PACKAGE_PROPS + '/app.xml' +ARC_WORKBOOK = PACKAGE_XL + '/workbook.xml' +ARC_STYLE = PACKAGE_XL + '/styles.xml' +ARC_THEME = PACKAGE_THEME + '/theme1.xml' +ARC_SHARED_STRINGS = PACKAGE_XL + '/sharedStrings.xml' +ARC_CUSTOM_UI = 'customUI/customUI.xml' + +## namespaces +# XML +XML_NS = "http://www.w3.org/XML/1998/namespace" +# Dublin Core +DCORE_NS = 'http://purl.org/dc/elements/1.1/' +DCTERMS_NS = 'http://purl.org/dc/terms/' +DCTERMS_PREFIX = 'dcterms' + +# Document +DOC_NS = "http://schemas.openxmlformats.org/officeDocument/2006/" +REL_NS = DOC_NS + "relationships" +COMMENTS_NS = REL_NS + "/comments" +IMAGE_NS = REL_NS + "/image" +VML_NS = REL_NS + "/vmlDrawing" +VTYPES_NS = DOC_NS + 'docPropsVTypes' +XPROPS_NS = DOC_NS + 'extended-properties' +EXTERNAL_LINK_NS = REL_NS + "/externalLink" + +# Package +PKG_NS = "http://schemas.openxmlformats.org/package/2006/" +PKG_REL_NS = PKG_NS + "relationships" +COREPROPS_NS = PKG_NS + 'metadata/core-properties' +CONTYPES_NS = PKG_NS + 'content-types' + +XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance' +XML_NS = 'http://www.w3.org/XML/1998/namespace' +SHEET_MAIN_NS = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' + +# Drawing +CHART_NS = "http://schemas.openxmlformats.org/drawingml/2006/chart" +DRAWING_NS = "http://schemas.openxmlformats.org/drawingml/2006/main" +SHEET_DRAWING_NS = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" +CHART_DRAWING_NS = "http://schemas.openxmlformats.org/drawingml/2006/chartDrawing" + +CUSTOMUI_NS = 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility' + + +NAMESPACES = { + 'cp': COREPROPS_NS, + 'dc': DCORE_NS, + DCTERMS_PREFIX: DCTERMS_NS, + 'dcmitype': 'http://purl.org/dc/dcmitype/', + 'xsi': XSI_NS, + 'vt': VTYPES_NS, + 'xml': XML_NS, + 'main': SHEET_MAIN_NS +} + +## Mime types +WORKBOOK_MACRO = "application/vnd.ms-excel.%s.macroEnabled.main+xml" +WORKBOOK = "application/vnd.openxmlformats-officedocument.spreadsheetml.%s.main+xml" +SPREADSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.%s+xml" +SHARED_STRINGS = SPREADSHEET % "sharedStrings" +EXTERNAL_LINK = SPREADSHEET % "externalLink" +WORKSHEET_TYPE = SPREADSHEET % "worksheet" +COMMENTS_TYPE = SPREADSHEET % "comments" +STYLES_TYPE = SPREADSHEET % "styles" +CHARTSHEET_TYPE = SPREADSHEET % "chartsheet" +DRAWING_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml" +CHART_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" +CHARTSHAPE_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml" +THEME_TYPE = "application/vnd.openxmlformats-officedocument.theme+xml" +XLTM = WORKBOOK_MACRO % 'template' +XLSM = WORKBOOK_MACRO % 'sheet' +XLTX = WORKBOOK % 'template' +XLSX = WORKBOOK % 'sheet' + + +# Extensions to the specification + +EXT_TYPES = { + '{78C0D931-6437-407D-A8EE-F0AAD7539E65}': 'Conditional Formatting', + '{CCE6A557-97BC-4B89-ADB6-D9C93CAAB3DF}': 'Data Validation', + '{05C60535-1F16-4FD2-B633-F4F36F0B64E0}': 'Sparkline Group', + '{A8765BA9-456A-4DAB-B4F3-ACF838C121DE}': 'Slicer List', + '{FC87AEE6-9EDD-4A0A-B7FB-166176984837}': 'Protected Range', + '{01252117-D84E-4E92-8308-4BE1C098FCBB}': 'Ignored Error', + '{F7C9EE02-42E1-4005-9D12-6889AFFD525C}': 'Web Extension', + '{3A4CF648-6AED-40f4-86FF-DC5316D8AED3}': 'Slicer List', + '{7E03D99C-DC04-49d9-9315-930204A7B6E9}': 'Timeline Ref', +} + +# Objects related to macros that we preserve +CTRL = "application/vnd.ms-excel.controlproperties+xml" +ACTIVEX = "application/vnd.ms-office.activeX+xml" +VBA = "application/vnd.ms-office.vbaProject" diff --git a/.venv/lib/python3.9/site-packages/openpyxl/xml/functions.py b/.venv/lib/python3.9/site-packages/openpyxl/xml/functions.py new file mode 100644 index 00000000..19a95f78 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/openpyxl/xml/functions.py @@ -0,0 +1,84 @@ +# Copyright (c) 2010-2021 openpyxl + +""" +XML compatability functions +""" + +# Python stdlib imports +import re +from functools import partial + +from openpyxl import DEFUSEDXML, LXML + +if LXML is True: + from lxml.etree import ( + Element, + SubElement, + register_namespace, + QName, + xmlfile, + XMLParser, + ) + from lxml.etree import fromstring, tostring + # do not resolve entities + safe_parser = XMLParser(resolve_entities=False) + fromstring = partial(fromstring, parser=safe_parser) + +else: + from xml.etree.ElementTree import ( + Element, + SubElement, + fromstring, + tostring, + QName, + register_namespace + ) + from et_xmlfile import xmlfile + if DEFUSEDXML is True: + from defusedxml.ElementTree import fromstring + +from xml.etree.ElementTree import iterparse +if DEFUSEDXML is True: + from defusedxml.ElementTree import iterparse + +from openpyxl.xml.constants import ( + CHART_NS, + DRAWING_NS, + SHEET_DRAWING_NS, + CHART_DRAWING_NS, + SHEET_MAIN_NS, + REL_NS, + VTYPES_NS, + COREPROPS_NS, + DCTERMS_NS, + DCTERMS_PREFIX, + XML_NS +) + +register_namespace(DCTERMS_PREFIX, DCTERMS_NS) +register_namespace('dcmitype', 'http://purl.org/dc/dcmitype/') +register_namespace('cp', COREPROPS_NS) +register_namespace('c', CHART_NS) +register_namespace('a', DRAWING_NS) +register_namespace('s', SHEET_MAIN_NS) +register_namespace('r', REL_NS) +register_namespace('vt', VTYPES_NS) +register_namespace('xdr', SHEET_DRAWING_NS) +register_namespace('cdr', CHART_DRAWING_NS) +register_namespace('xml', XML_NS) + + +tostring = partial(tostring, encoding="utf-8") + +NS_REGEX = re.compile("({(?P<namespace>.*)})?(?P<localname>.*)") + +def localname(node): + if callable(node.tag): + return "comment" + m = NS_REGEX.match(node.tag) + return m.group('localname') + + +def whitespace(node): + if node.text != node.text.strip(): + node.set("{%s}space" % XML_NS, "preserve") diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/LICENSE b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/LICENSE new file mode 100644 index 00000000..54acf3ca --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/LICENSE @@ -0,0 +1,76 @@ +There are two licenses associated with xlrd. This one relates to the bulk of +the work done on the library:: + + Portions copyright © 2005-2009, Stephen John Machin, Lingfo Pty Ltd + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +This one covers some earlier work:: + + /*- + * Copyright (c) 2001 David Giffin. + * All rights reserved. + * + * Based on the the Java version: Andrew Khan Copyright (c) 2000. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * David Giffin <david@giffin.org>." + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * David Giffin <david@giffin.org>." + * + * THIS SOFTWARE IS PROVIDED BY DAVID GIFFIN ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID GIFFIN OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/METADATA b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/METADATA new file mode 100644 index 00000000..1561871e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/METADATA @@ -0,0 +1,93 @@ +Metadata-Version: 2.1 +Name: xlrd +Version: 2.0.1 +Summary: Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files +Home-page: http://www.python-excel.org/ +Author: Chris Withers +Author-email: chris@withers.org +License: BSD +Keywords: xls,excel,spreadsheet,workbook +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Operating System :: OS Independent +Classifier: Topic :: Database +Classifier: Topic :: Office/Business +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.* +Provides-Extra: build +Requires-Dist: wheel ; extra == 'build' +Requires-Dist: twine ; extra == 'build' +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Provides-Extra: test +Requires-Dist: pytest ; extra == 'test' +Requires-Dist: pytest-cov ; extra == 'test' + +xlrd +==== + +|Build Status|_ |Coverage Status|_ |Documentation|_ |PyPI version|_ + +.. |Build Status| image:: https://circleci.com/gh/python-excel/xlrd/tree/master.svg?style=shield +.. _Build Status: https://circleci.com/gh/python-excel/xlrd/tree/master + +.. |Coverage Status| image:: https://codecov.io/gh/python-excel/xlrd/branch/master/graph/badge.svg?token=lNSqwBBbvk +.. _Coverage Status: https://codecov.io/gh/python-excel/xlrd + +.. |Documentation| image:: https://readthedocs.org/projects/xlrd/badge/?version=latest +.. _Documentation: http://xlrd.readthedocs.io/en/latest/?badge=latest + +.. |PyPI version| image:: https://badge.fury.io/py/xlrd.svg +.. _PyPI version: https://badge.fury.io/py/xlrd + + +xlrd is a library for reading data and formatting information from Excel +files in the historical ``.xls`` format. + +.. warning:: + + This library will no longer read anything other than ``.xls`` files. For + alternatives that read newer file formats, please see http://www.python-excel.org/. + +The following are also not supported but will safely and reliably be ignored: + +* Charts, Macros, Pictures, any other embedded object, **including** embedded worksheets. +* VBA modules +* Formulas, but results of formula calculations are extracted. +* Comments +* Hyperlinks +* Autofilters, advanced filters, pivot tables, conditional formatting, data validation + +Password-protected files are not supported and cannot be read by this library. + +Quick start: + +.. code-block:: python + + import xlrd + book = xlrd.open_workbook("myfile.xls") + print("The number of worksheets is {0}".format(book.nsheets)) + print("Worksheet name(s): {0}".format(book.sheet_names())) + sh = book.sheet_by_index(0) + print("{0} {1} {2}".format(sh.name, sh.nrows, sh.ncols)) + print("Cell D30 is {0}".format(sh.cell_value(rowx=29, colx=3))) + for rx in range(sh.nrows): + print(sh.row(rx)) + +From the command line, this will show the first, second and last rows of each sheet in each file: + +.. code-block:: bash + + python PYDIR/scripts/runxlrd.py 3rows *blah*.xls + + diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/RECORD b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/RECORD new file mode 100644 index 00000000..8e45a551 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/RECORD @@ -0,0 +1,29 @@ +../../../bin/__pycache__/runxlrd.cpython-39.pyc,, +../../../bin/runxlrd.py,sha256=R6q512qkPgJJDA5XFRdr8boK-t1BP--T6wLYae5nwUQ,16060 +xlrd-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +xlrd-2.0.1.dist-info/LICENSE,sha256=taXbzmAmXjBagVpsuD7QfyRRnYumRPKjB5lEiLztiBU,3771 +xlrd-2.0.1.dist-info/METADATA,sha256=QJVZePpw66Bmob0e1NE8VEGl6_LdOu1A5h8tTjJ94d8,3443 +xlrd-2.0.1.dist-info/RECORD,, +xlrd-2.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +xlrd-2.0.1.dist-info/WHEEL,sha256=yeYGyEAjAVN0fp2OvAcpF9SZtr2vIzJc053UqC_mhts,110 +xlrd-2.0.1.dist-info/top_level.txt,sha256=nZ-t3Sc_CqNsctS3L-V36qXwC5DuAFvkB1inJApF_vM,5 +xlrd/__init__.py,sha256=kmuIEz8cutatGjjW3EmaApTEgcHmcsYo0CcWH_th2P0,7320 +xlrd/__pycache__/__init__.cpython-39.pyc,, +xlrd/__pycache__/biffh.cpython-39.pyc,, +xlrd/__pycache__/book.cpython-39.pyc,, +xlrd/__pycache__/compdoc.cpython-39.pyc,, +xlrd/__pycache__/formatting.cpython-39.pyc,, +xlrd/__pycache__/formula.cpython-39.pyc,, +xlrd/__pycache__/info.cpython-39.pyc,, +xlrd/__pycache__/sheet.cpython-39.pyc,, +xlrd/__pycache__/timemachine.cpython-39.pyc,, +xlrd/__pycache__/xldate.cpython-39.pyc,, +xlrd/biffh.py,sha256=GjtBoI9PNIEdbZmGTvfDQ8HuhZiLJd2ofdo2qmunzmA,16651 +xlrd/book.py,sha256=1LHxyWMb50ZBIwUlZC4UtIFem5y8o8M3zADcdPCF_84,57527 +xlrd/compdoc.py,sha256=3xkfuJbLRGRMdPiZVBsWAgGktw7Jna-w3p_lcTCT6YQ,21091 +xlrd/formatting.py,sha256=6Mh6GOM7FZTj0cJ4FHSl0CVo-8InYDv2KIZTMycEw6Q,45573 +xlrd/formula.py,sha256=VskzjScNbPKqDxOa_wOmGZ2u_5xac3lqFgDM0dTGJ30,94455 +xlrd/info.py,sha256=Z0BLafyBeEMKR6QL-80x0djZPtK0dMQYbknGRjZP1OU,36 +xlrd/sheet.py,sha256=mhwEqEteUIQY9jK5CyX87stuk2MmXURONf86kZQMhY0,106806 +xlrd/timemachine.py,sha256=7CtU7FY6mQW7HSP5FjM1K9pIfNexVCt_PJXG2M3FEAc,1757 +xlrd/xldate.py,sha256=S-uUaLX8kauGy2SkX_M-S-FBqqQ6H9EfTAVpv5FMX0I,7934 diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/WHEEL new file mode 100644 index 00000000..0774feba --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/top_level.txt new file mode 100644 index 00000000..1a6410a5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +xlrd diff --git a/.venv/lib/python3.9/site-packages/xlrd/__init__.py b/.venv/lib/python3.9/site-packages/xlrd/__init__.py new file mode 100644 index 00000000..84d5f269 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/__init__.py @@ -0,0 +1,213 @@ +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +import os +import pprint +import sys +import zipfile + +from . import timemachine +from .biffh import ( + XL_CELL_BLANK, XL_CELL_BOOLEAN, XL_CELL_DATE, XL_CELL_EMPTY, XL_CELL_ERROR, + XL_CELL_NUMBER, XL_CELL_TEXT, XLRDError, biff_text_from_num, + error_text_from_code, +) +from .book import Book, colname, open_workbook_xls +from .compdoc import SIGNATURE as XLS_SIGNATURE +from .formula import * # is constrained by __all__ +from .info import __VERSION__, __version__ +from .sheet import empty_cell +from .xldate import XLDateError, xldate_as_datetime, xldate_as_tuple + + +#: descriptions of the file types :mod:`xlrd` can :func:`inspect <inspect_format>`. +FILE_FORMAT_DESCRIPTIONS = { + 'xls': 'Excel xls', + 'xlsb': 'Excel 2007 xlsb file', + 'xlsx': 'Excel xlsx file', + 'ods': 'Openoffice.org ODS file', + 'zip': 'Unknown ZIP file', + None: 'Unknown file type', +} + +ZIP_SIGNATURE = b"PK\x03\x04" + +PEEK_SIZE = max(len(XLS_SIGNATURE), len(ZIP_SIGNATURE)) + + +def inspect_format(path=None, content=None): + """ + Inspect the content at the supplied path or the :class:`bytes` content provided + and return the file's type as a :class:`str`, or ``None`` if it cannot + be determined. + + :param path: + A :class:`string <str>` path containing the content to inspect. + ``~`` will be expanded. + + :param content: + The :class:`bytes` content to inspect. + + :returns: + A :class:`str`, or ``None`` if the format cannot be determined. + The return value can always be looked up in :data:`FILE_FORMAT_DESCRIPTIONS` + to return a human-readable description of the format found. + """ + if content: + peek = content[:PEEK_SIZE] + else: + path = os.path.expanduser(path) + with open(path, "rb") as f: + peek = f.read(PEEK_SIZE) + + if peek.startswith(XLS_SIGNATURE): + return 'xls' + + if peek.startswith(ZIP_SIGNATURE): + zf = zipfile.ZipFile(timemachine.BYTES_IO(content) if content else path) + + # Workaround for some third party files that use forward slashes and + # lower case names. We map the expected name in lowercase to the + # actual filename in the zip container. + component_names = {name.replace('\\', '/').lower(): name + for name in zf.namelist()} + + if 'xl/workbook.xml' in component_names: + return 'xlsx' + if 'xl/workbook.bin' in component_names: + return 'xlsb' + if 'content.xml' in component_names: + return 'ods' + return 'zip' + + +def open_workbook(filename=None, + logfile=sys.stdout, + verbosity=0, + use_mmap=True, + file_contents=None, + encoding_override=None, + formatting_info=False, + on_demand=False, + ragged_rows=False, + ignore_workbook_corruption=False + ): + """ + Open a spreadsheet file for data extraction. + + :param filename: The path to the spreadsheet file to be opened. + + :param logfile: An open file to which messages and diagnostics are written. + + :param verbosity: Increases the volume of trace material written to the + logfile. + + :param use_mmap: + + Whether to use the mmap module is determined heuristically. + Use this arg to override the result. + + Current heuristic: mmap is used if it exists. + + :param file_contents: + + A string or an :class:`mmap.mmap` object or some other behave-alike + object. If ``file_contents`` is supplied, ``filename`` will not be used, + except (possibly) in messages. + + :param encoding_override: + + Used to overcome missing or bad codepage information + in older-version files. See :doc:`unicode`. + + :param formatting_info: + + The default is ``False``, which saves memory. + In this case, "Blank" cells, which are those with their own formatting + information but no data, are treated as empty by ignoring the file's + ``BLANK`` and ``MULBLANK`` records. + This cuts off any bottom or right "margin" of rows of empty or blank + cells. + Only :meth:`~xlrd.sheet.Sheet.cell_value` and + :meth:`~xlrd.sheet.Sheet.cell_type` are available. + + When ``True``, formatting information will be read from the spreadsheet + file. This provides all cells, including empty and blank cells. + Formatting information is available for each cell. + + Note that this will raise a NotImplementedError when used with an + xlsx file. + + :param on_demand: + + Governs whether sheets are all loaded initially or when demanded + by the caller. See :doc:`on_demand`. + + :param ragged_rows: + + The default of ``False`` means all rows are padded out with empty cells so + that all rows have the same size as found in + :attr:`~xlrd.sheet.Sheet.ncols`. + + ``True`` means that there are no empty cells at the ends of rows. + This can result in substantial memory savings if rows are of widely + varying sizes. See also the :meth:`~xlrd.sheet.Sheet.row_len` method. + + + :param ignore_workbook_corruption: + + This option allows to read corrupted workbooks. + When ``False`` you may face CompDocError: Workbook corruption. + When ``True`` that exception will be ignored. + + :returns: An instance of the :class:`~xlrd.book.Book` class. + """ + + file_format = inspect_format(filename, file_contents) + # We have to let unknown file formats pass through here, as some ancient + # files that xlrd can parse don't start with the expected signature. + if file_format and file_format != 'xls': + raise XLRDError(FILE_FORMAT_DESCRIPTIONS[file_format]+'; not supported') + + bk = open_workbook_xls( + filename=filename, + logfile=logfile, + verbosity=verbosity, + use_mmap=use_mmap, + file_contents=file_contents, + encoding_override=encoding_override, + formatting_info=formatting_info, + on_demand=on_demand, + ragged_rows=ragged_rows, + ignore_workbook_corruption=ignore_workbook_corruption, + ) + + return bk + + +def dump(filename, outfile=sys.stdout, unnumbered=False): + """ + For debugging: dump an XLS file's BIFF records in char & hex. + + :param filename: The path to the file to be dumped. + :param outfile: An open file, to which the dump is written. + :param unnumbered: If true, omit offsets (for meaningful diffs). + """ + from .biffh import biff_dump + bk = Book() + bk.biff2_8_load(filename=filename, logfile=outfile, ) + biff_dump(bk.mem, bk.base, bk.stream_len, 0, outfile, unnumbered) + + +def count_records(filename, outfile=sys.stdout): + """ + For debugging and analysis: summarise the file's BIFF records. + ie: produce a sorted file of ``(record_name, count)``. + + :param filename: The path to the file to be summarised. + :param outfile: An open file, to which the summary is written. + """ + from .biffh import biff_count_records + bk = Book() + bk.biff2_8_load(filename=filename, logfile=outfile, ) + biff_count_records(bk.mem, bk.base, bk.stream_len, outfile) diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4cf1469b7c74f12c51b18b5118ee0143068ac5dc GIT binary patch literal 6558 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 I?#$2r2MN=Vi2wiq literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/biffh.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/biffh.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..396bee313ebd7d8c2f1c0ce6bb3b7d5348b0d75e GIT binary patch literal 12963 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/book.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/book.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a56d3d9c6270026d72046a2a40bd37213219917d GIT binary patch literal 32519 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 R4r2gE@9%JCD3{CR{}0XoPV@i( literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/compdoc.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/compdoc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac4a7d80617c610f40c9a1e1ed599b450267a4b8 GIT binary patch literal 12345 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 OI*C;Fs~Z38l>NW-d;?wp literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/formatting.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/formatting.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e589999d1868688ac9de417d0fd7e850fdf77ad5 GIT binary patch literal 23492 zcmch9dvF{_df(3M>=TOzK@b2Rl7<v1arpp9>J3rin<8}-B$L#MH<WI<Ko5Wg_5t<` zNN}_8DaW&YaeOY>u`hmH5=y=}CHw3ob}G44oa8F0B(6%zf21l-JmuI|w(A^JC61k> zDi_&z%J28}>;n*amUE6l?l;|Ef8Ep1ufIq4l>7Vh3H;6e@=upjzm!P)E+f5v`w%&a zU-0Q<BB2sqLRreLS~c6VEryfTq?g3gsXDckmjW)O($#b=<7H}DFI&rbxmw=K*ZRD^ zTEQ#S`n~?zfHzPZ^ag7~-cW6ex1~1h4cE4MTWcfUNNt<9tv2e7*2cUsDJxSQzl!&G z+x?v1@Au)C#V_L*{Jh^^NqIX|*4ydtR7sWlK6`t@+okf}L;gcPaQ?3LKh^i8toQJI zLKWV#yhl{O8u(nobJZO+sD?ggdAnaqs4Z&va|t!<@41`w9z}es8bN%--{L=d*YWnM zZE6&G_Ng&7j^|^b*sgYfVn<BzxZ0_9sfX0V>JjCt-D;0|RP9y!)MM&#wO<w00d-Iv zQis)qnp8*BQFTl`p`KJvsi)O5>RI)ino`fJ<LU+VqB`-Vz21KGsO4wplIYEoeh$x5 z>a?H1^Nczx&vWX$;9pV~Bz{p{l=#c)Wj_zPPpC_Re??6T{#EswJikX>CVsx)6)Oqv zfL}bDc<aEMN$;R{$UE#$_y_$%Dm9k4YXd*vP5OuZ1ODXk#5>l_Kfv$TuOGaA==$O7 zll~EP<*wx&MH$EZqyEu*PrRELOGut4)m8tP|0MF*H}_nB@;Ygr0{!cEtvEg9KNY8+ z27cFF3ptLcPsYzT_^c$or-6G6;S`=P#Lu1a^IH7;RQ!CA&q~^R#!udRR(bDQi_dve z>biOpWA(g0IA^K1)Tc4h$5lz)0PY1<Rx^0MsAiSI^Mvx%9G)lDyjsBXl&YvjJWs17 zRmJm+s;N4jXEE*#wTy9Jj>r8RFgKM3M#q@*zyvA;CX6vJ0kfiR0dp(HTma^_S_Nj+ z-+~!;Q6;Y=ip}QU{3{K$QuW=rhUTwU4#P@)-mTP4q|&HQ<kz`Ai&iwStSj}fG`CWp z4UsSnRC=YpT%KK8w@9%iN}fM^72u_F0MoAnT)hVH`gBydcBypc+@(vUv!||}iw4@U z>DOO5eeQBM_3F86SEC$K&%b*4l~Y%vd?$D;%AYO={;M;K{%lB+%V*DN-Oy2HPHHt5 z^_!}cf)KTwj|SrCO1(1MP=1tISzel{gh7;z1D8;J>p=0@_mi5n?}GP}>mL%ay$wJi z;SO0lO9$IV2nDh2EWH$+u$0!$B3P4U{cXPSBX8N=#<sHv1+hR#VlPD}EJ^Jw9WpfT zYb<Yn8*C_pWT5y-CLSa(6_fD#6F{ub2}QSW*8iPB9BEUxvrG_S5io?eNJD$-(M{CM z3<@cuThOD-{4l{o1dkAKchD}uV+6YZ+D+_cJZi9A1Vil_NHy3tAf$;MvNlBr3xp(g zdZL(I=YFM+w*e>)Gc1jb`$`a!FMe&OJZW+JEkeQmX(NQJ7t!fKP)Jw+$uLA;<ZI8h z0Vt%aJCy-K!cr*nZJ>zXA`}8*giv=7`jE-jB{T0fC3mpvLm(?YtH)TGae^HLI|(>G zX7pqnWe8p3_7KPb?`24aRYvr2MvDXo2v`L@Nx(tX#|StN^^*io5q!*PqMs(Ql=2Ki z&k{UGFh%e@Krx=AGBnZ`FEHsvf)fNM2~H85COAWImf$?WO9U4PE)u*<Aoc$QLze)G z+4a}(SSR^9+p^AHT<2O`X9ulsBNzcF=A-m9*>Tm&U2fdI<lpkEB`<fTQH_JCGmToU z>}AiaXzkZ$?|7Nld_C*e!zPH3;$A=UR<qDfWz5T6xU=l*YNfu^96sNuRvWi{<#uqh zoCEDY2Z0oRY+>*vfJ&m3C>dzA5{q^)suEWcAp|kmWQW(R&}muQLA97@S;rGM9}ZIp zr?)58>@d@^?^<igFxg59_2`<_vb)sE8cQI@C1rA>OvZa-%I;!RvYn<JhOmvLrq{Em zqh_twzlY$%&#<$j{A{DzSka|gc{$n|hbwjEulhl0CGb@=*xm9<-14IIsxH^({b<}@ zokfo;f3Cbz4NJ@Asvm}aY3A8za1$;n%v9#)O1FF+U{8#aRli<zB4?)2Sc<HQ=P2a$ zQguGwPiTeOeQ;&6=BrA1@<Lgwv$}H2pM34kLfEKJUYT8JG^&#mxBU98$!cY0a`{fU z(5OE#@!Vuk3H`&|YRmI}Fu7XQYO-7RiRC-dwo1JcR?5{%;Fr41uHbzES7E|R%Rk31 zSVMN+%31|$BspLY+j(2hA$>MUs`!s0dKy0&|922j7(ZpNIjzLK#Jd!`yf^K&)LOcg zR>>7C=bJ-F&4gCVYNbV#+lBP(TCSC8Wm~!Ngn=W8XA*0fn@506E+zC(E3=ktrBG@{ z*-N$_vJy9|EJ^=R3%}4-m{Tphv*loBCFh*c#9hlmjAP#X#k`BZm;Ab~%T;%IMK3o3 z$T6{SGU2`+_<?(FWwu&Te!1?dN)VRov;Kr+yV7vy%G%{5a2xgN9g%;QwSV~Z#q;N% zb#L5w<Kpz$SKl~x<A$sK*@jjo+u4R6)E^Jsh4L-m4IAzZURVyo?~sV4X;Lu~NwXnN zH>c`;S^GiQ9w?cUQiXlmtpqOM;THj)#|uDJ^C8mAN`fjTqZFn>Er^oM%5szv;is6_ z9tO!v>iL-{rTm$dd9SZ0t5=YSnHH;FrX9%6mg}m7OkUc&BeHKD#qXHRQv-Yy9F5$s z!habl!Crtw#<3i02y@EDbJ!Y1h?p#9976*irfppV-7JZF|7=7~;up98e!{a<!n2`# zVXmnp#9v1_DupLhJdAlJ%ALD8?Vk!m9W;MR#_{R`x~o=Mt<>GwYB>m8e-+~#xIv@p zV^o8N8!nVXcS;hcZk!hh_r{?fL_I_l-5|xa>8_o>;Wk9n3|#aP$9c9<TgIR%cjk_J zt6W|2-NqaW_bb|k%&aRjE1@5lp`K;4+J6l5Crhb-fMJn~i6#3=;(crSeJjeAN~$s& zmP+s2krVjUxhT~@$$IlBq)R3LW~p=!g#_{nt2xx^kO`CcG2ad8UpG*WUMA24WKBIo zj+fQ_Qt4-az3<DII9*J$^7V2Jktkm()kK*<xKJwHTq#%Mlnff!m@Sp`3g2<7^P-Kf z)O95M6yHbIgNc*MCI$E4X~W%g<Fvt)u`yk2FO47AikHPNhhHAQKEI%_1$q4l58yY5 z-;i>=EqD&&w^gMu&C-!`zJZPOf6J6ORd>&EFL3wF0l5eBpdOYh^?+mVDj(r$z^PDg zgocyc=7htntjqRYy(6U{R{%c5`BTP1uZM?ROc=LZ#S9Iwxs!#-n3GQ}SIc)kGN~?d z(c|sd?t|q<U6K7@a{$Z3+-F@(EnmO*c;K38P9Ac`ucCSgutjcIo|(u?eI_Q{c*iO$ zsG?I-?vaV7Cyq**h>}ra{T)aI)5Q^e8=b6I3GNUy0l2?aRn$MbP}b$y(ATBF52Hfw z78~XLV79#M*C3Nb>A7;PQoR%9*>vo**n*=p*3SF_(kdZ#t=VYs_34XeUOjuRbm`*N zbC*wDiX6_KC=Y#ZWp=65SP7}qp${R!_@xlCOqB1$Rg}JMipzkI6j3@@D67Wpo_Q_P zjr>giC4isBPxuaITFx1FlK9u>IPy$plTH?4i3#;b)>RVO&#(w`K(6T^FCr1_C@t%* ztza7h7Hn*LqLdUd#hL!VK7K)Zcl|^B6&?L1@@ke8<)mk@dx!dM#EZ6`A=**p5K;ny zJxjB&0De{VOFr{pLo^G9JG^7*9|i4(%4UGb;U|^-CGd@IWf8K1aqQ{#y9o=@-11&v z9Azz~)@+1QklE6BW+1&eEi5eT(F<YrZelGJ=D3hrsZt*MbE?!ws(Xc2>Rs8Nm4kiW zF_ia7hmmP1^^T=q4Et5OWvPtH(v)`Belv@*1|U_WN`v?f;kO09Vf?n@Hv(zHIhjCc z8$#B}gmO+WHj0>ym?g0>#Bzw`B{q&&A7Zw|wj)+RtY2a~T1hn^HF+%DiFyoTw;B2Z zB$86TJ`(OC2JM2W5m>e||Bh9^j67Q;&;LU0ho$yg<JvQ4yY_$1HI>?I-B=qnqPET3 zt<>T+Of~eD$^fFj7RT@$me^KEQ0TY%mr&PHsjD5=75P|KMcP61%AdJkrTpyzcS!9x zV|x1P<JYcbzhmoPM7<wE2@k7rg!)Fs-1&thOtO^lcTTT!!&`R%n)$OUwb~uwXd?1! z?gGu+@eBMrefqRnVf{erH<*d*OTWU9OF+FwPct+C@V@1xA+qDYzJh4eVKM96fb<$b z<yY|DW)evKDJDO3`0!y!f3p0z`lf`LU*D(Ra*@*93b6SPn>tus8H@{Tu+adsEIuL& zKgvRJYbYA4pj1!~D0L92v*%8~{!)|!lb&fn_P?WVuu5eD%HzV5^!GFL0|a*gqI7wA z*{>^MC@>HL{Wig0C7`gT{|dop37{V(^!E~cAHfe2e4gNk2);nDm0%bEOlYOH9A&DF z`MFBfFJ^=_=^v4#nQD0<vS)5Z7Sx?Nuh3YgX|Oa`Ezbv=`Bq;~$EC@(SQW#zC@2nq zfl)CbOhY|kh|hd#=ud2<7Ayyf%RuK}-Ws;YLAwjTE_TEoLM{<Kk;<}68q4aYF=j+m z`&;_wP>WesrT35X?IeBywe!s~hM)4%enw=8Y%HSYVi7fMHs9u(vX}0eUa8IaZPV(W zUc0J0ryDi53|+$*J9|`(kGDnZ7HT%jCDHz4nDst`UoZ*)^$BV_l<h>xY9#?}E+>)v zph#dhrop$g<ll)Lv!TWq>_XU`0+gV?0ub4lfWOF?>HyITApBA7ES2VaccHY^95lTE zq12>LLq)ld93iD^?oRp_2xLObi~e5cMH^qKe;x_1@@>>nv2|y#X(zL`;Jr+&4o23I zrgLQC$dP91(L;|udZd|^2N7HLO0ZOXe!mG7o1-1n^~=6`tAigic;xC*BT{Vc>P_g) zBdPY0ea#eKzOR|(<J>{1hkIi3L^JcKdjiiB&2(*2P2RcF%&Nn+TJ11GDG4FLMDq(% zwVHeCmC4s$Y4$Tb1?|Q}9Y&j(7}@MIMY)0<?6gh-#pXyGVd@!Ux5*QgW6Rp%Vsl43 z2y_p<H0$xw{sXS0BavZ~&xH3J-XqwPrDF3yY5(3{N<_M(i1gA<b_z9h>6XiP%4<M= z;zXzLW8K2rIXdOXS<EYX@}#8mnz`!>Z<&5<W&|_AdGRQQ2qW}?b+WkE7*V;3^j{<R zBEgRl`~<;I1FTb=GW!ar?K)@SdLIFGJN@GXKgsAIL$d2^VU&z?U0CYtOt{J@SLpga zg2xCR2hf)pDiRzZI7n~^K#N^#f>D}7*N+e!WkTnb`d5gf-F1C06aFc|za(H@{jUiA zHNn3j;HJ4gML^T%I*pv`<Qw``hF)Um8i1F&Q&S6pbU-t^_wd2V{lzKw1MATbtP@yn z=cXyDdzpD-hH2*Byng-DZ^6O$)&?1rU6sa97}NJ-^}?P65gFDW3$~=sLAr$259#u? zmJ=q(RuMj+oHhIAEy1MjTDM?!SLqnT*m<NSTa+}D^~aFkL7v5){I~dydI2eEq(}^+ zx$VFVvt;QX1l9@)bvTt@PJGt7S%B<etISeL{}Qa4Sy(esi&4tTDV6&Is6xn?z_HHB zFoXC1s>)w6?dV%e;a!kO9ay*4vaKZA^dcqZFbld5TA46Q3zpcvz7A~e7TlY9xRnia zF+O_}_*M$_{5o2lMl1u0BdzQ^R&YcWuE2hV_(@`elYFnF&#=zGe{YzVdiIO8&AJPx zczstZ4chPBL@@x>1Tq!rmS{yAoHkRQJIM#u9ucRS$u~;}%>3unZyu}!C1@djsk=8o zWSN3pOs&j9pEYQ)P6aS#IFu32e_%a-wfW2&r!G%loPNnYaNq#adrO&e%e9%x{7Pdb zaMcU@lv^j5tA#J@)A{CPZ^pQM*u<7K%nP%y>QS49F)u_N0+?s>#Xi|NqJdzkvRtB4 zDh3t(9_p_v0G@OC+-sNh-$pRng13}rptdggp|488%1ks=hse^?#vmGySf`|@zrIrI zDVR&Tm@_I`66HlH)F<>KD~PPwC>QuJ6_vw=#=>ZX(2bX2eqa~QHdgAPmy^O6+`OM| z+t^F7FZ0*9-EkXC<RP=zSx7ep$S?fC<Q5xq5w;zXg+?Ls(ClXZk(#m<<se8!F#kG~ zq$F<|2D|jP(@7&Q*_QonC#ioM@0m^VZSfz)WO6-OyziOC3t3_jDqhPDxm0(BHq}Ec z4j5|}lfeO~lnzx&m0U|9#lf<g4pS-x6$B?Zj0&RQE?7&~hmj)<m5=JBrEg#%!ExXK zr_#?rm?<%Z%kr~uBOou{k*N=3K|%TCyHr|bzu>IFpuBt!@tn#*dCj)6`d4I8^eHY4 zn5pqEa~DD<YF21v-nG_pVShLP^(lv1<f$YsIan~qrM6g}!SQhL-cTzYZh6;U>udFe z155TAnM4i??l>4pS{Mlz@(#3*&*OCm3-94pp_Mx&akM@e*P<ZrM>^EU<PsOH7pL^M z^2}js7Y>6ik8pCWUzYsmTm6ev`}))X+o96qi8-((HAt4!>IV)=@o}=LZTCi7{Zzk2 zVH%1T_7#kqS<B@2`iBW*f175A#x~jHnnfdnPGM<bTE=S;a^VX{R`(rwa@F0xPZiyO zi*x;Fkg0j0cZHs5)aPKq31KD;Z~K1T4Z;pvKp8GpHZ&#|&i6L6Fg|)%xbw|p;hiZ^ zJl|muvxvg--ChZSiE_~*7x>OT-P{2ktE+`khIQL2Skcmd0~IWe=^Jbu=Wdj&RqFb0 zF?x@HbK4t$*$M_q6e}(6YCDE$aDfZEkmaDccj**Bv<+=*b3&dg5JMD<@$<0JbC(l2 zRDYSIZSk7kr@okZih+kcSR63o^^YLzW#D6={H@<*34e#EG#U$shL=VS(T83J=NQ<2 z{j*HXb$Unt-I!XTCSCk2bj?}_6ZU2f)8Em-*lqpL76K0?Bw~(0&5OJvYmM6`M6OY= zhrvC_Gi)ml9+3wJ8F32WL=y&UJKu5~=C!RX(hX;#**|aT-$&^igaP(#M;NGspJ4WO ztY^!kh}p&jg^d_)LWNB>Hd@dLMb0A}1?$!ct>A*t!=R*RC=M(o^^9>n!nDLqoNICz z?xZlrf>Uq{RM@6RVaQUsR&oSy7y~aNhk>apqjKON;1{u|@D+r!V-OmGe@1a3%z~#N zb<U<c>a!Nyn&fMVR@&4OPA_nYdD!=Z-=b)Nbq-MiBEe_DXL7AP>J5HEqZIf_pDLV4 zNVp$in587lwT_flek=j%LGXwE*1%kHZHOg<e+`M5jj@;t9KhD!XbrUn(f)71*fRhw zGT0iLLmNSrHvHn|H`=^Gq+2or*cnAe6>qq(2y%z!bJMS0Jty1txz%ONO>Fa6#KycE z(BS;qsY~asg2Xt-4q;ux6bqVs^O227?(Isr(33geOdnre-KQ!dS2vGrq@IHRCVpQ~ zY5K?q-;69&H;$WQB(&>D#hd;6jw$j4<1o|zo^2X1c2!wSA_8#58-CDT*Ja{{0K9>= zch@vVzr_?T3eANo@<VY;*X9wgz<TVw3%ucWol6~@j#zMiCxMms`b<ko63K<|P{G$c z>$lx;fu7~-{xd+7gm4w5<~5ipOzm(sc^TN-#Z#B5`?nd`ys!u2g&jBM6j%0Hlx6HI z1J(#u@Tja%2RlC3vhyKj3EEj4YX{tSSmRi^M!G17#6U=1p#Lk%nZ-)&{Bf3aWP-nm z2xeIVb9@1=eG6tlN(G}gVccD`VM>OmkN9JV(@2;UQC`+@%ZB5hCgzk*L%?>h0+9~G za$+&7QZ)ZTL}r*JvE)3C5<t8)d9ahF^iPE}K0Egc?_xF5U=5R`ne{9I>s97_cG~E1 zG~yX4ciR<CFgR|^tfp_I>QBi8Y96D&dLHII_fbsy{rfajo|w$IYU5oIUY?cuQoV7z zo^Ni?pOM1`SRHb}Kzz$nd2DgTeq*w`%Xhy`Ad~AlLmdg#nD!{3YMG-yfDl|54UM%I zIoJUL7-VLMbZB?<?Ag`Gq5FFCJQ!-vW`DT?i`|Ovb0)aRnE;2*Q{uyjf{%;*NLOuM z`1F9K{|Gc2I5m}9Zfa-n3vPV}PR&t6-$RBIejQ7AZzk6qIV1pn4Q@yIjVjI@MiV1` z%R&h1lFEV$XTf#TYmnX`5&jNju>8q{{xVbHfJlJ{=4fb+`SA~9c3Jv8k+z0fIm{W1 zIZlvTP_T0KFQcq>nQ2H{ePXQN)fAbLr(9Lx3(2+I8;SZQ2ea!P>(00ZNfk3KuOM?l zHA=P8JZgYCLh2ILFRuo}0%{K~FFc;}lWCZrpG|x!QMZp^j|uxpfs_F_GzySOb1-aM z`a%ou2ri&>!>^@8zGseWA=S!}cY{ucbT;&<E$uqAa(6TF>>ZvK8IHzjn765wt$zsn zLcmqXKmADp#*8|*9id9%ZA3d_Ui0ZIS3h;>+!Wl76&z@(h`zNtXMAy#_uBYUMf9;M zPaFYn7DQf`Z^Q8o5BJnYt?m@3xju|DF>tvl2!3~PmUN*UxMjEL>&9W&)#t(g>B(!J z_<yT`WTxa<9NNs+Z?MHl0B=CvY&_AFw?;b6=_Wu*g9Ig#UNro=ITm^rhdY!nCrO3T z8x_&f{<0R^w-Fn;B4NPf@)NVSYyn12E6~zif_Ph6om<7Bi@0~ui&0AYFv_-j*~_+5 zjm=)MH~~IFQQNC06~4no%fV~2@3b|Jr8*48ZT?{q+ajiQa9r9(Ov`_Wa~N3EFpNWR zBNL}-oeN$6AC$O3xg!(rFmgHsLKuw~EqVmWPQ{inhqF9b$P{#N-pkg<U>$+S39J|k z;O214xd(^)yHL)+j^V4rG){xXe-geYcm-Q2uz2{%7PE_3+*k(42k{Bkj?N-WbCKN~ z7Dk<!7U9|ON$xm?TR%pQygDcR7~6d?u`h66aNV|c5_93A+5ZdrzcJ%Yf_Z=_gBi&~ z8qv<#1s}d-!^_Rw$5F6Ky#z^_mJ2vfI=nf`l}d7K1m_mSbWKBo%#YXu5+#w#%Qtjo z-V_z3c>F4i9KOqQ>P${GIQKU(Y5m72>dPGBT?iyFJO$Cusikumxg(oF6v_=dFSQW< zgeRXxAWYn|<Ve=j+<VdGHaKgR!w9}<hsk?5L<M~}3Hg`D?jZfrG(|c&sF!888;?L* z{|?I2e?ahi0MT%dvV)}qvrnLZm#H+u7a?(z$XYczNG4X%^fyScX>yOlDpX}h({7{S z(T;Vf;P0~+XgW<uA7rsd6)cT;wQ(of0|HuErPbrm9>T;jwQ+FV*jWw3T$qGw+6kSz zI3iZUlx#VRsS?e0eBvyKJYjT`beso4JYVXIpN05|vmt^X2nU63DBL2?VbR=E`tx|} z*5LEuNDB)@^m$k}z`t*)6qd*~(FI^~j5QXx$KjrKN@Htj?0>AsIFH4x<=6Vw3gLEO zpz$l{``CXFLpa+GV{d>a(i4N;!B{J&`c5R)`sd|#$=X0`0CLxXwL$D&gJTIb08M5n z+<~%&TK#J9WJ2;FbP0KIqhbKAZnV3F{isuR9=Zo^1)vU08$xWjlQz^UsI82>ldS)Q z=>xcHKdDA~TDvW7En3)Z?b;T$^5eF#wS^A-wPCbs^gXLJ441a_7VzIqYyTu}QC^L8 z+Q+t!ciP9cZ<qGj^{=9RJ209_eMs%>Y2U8<v~Md$ab#`VpW42y>LHfi+NvJL!GlLI zavyEucQc~)&96%xhFT-a?P=pSwOd+cL7qygJ+L`B+E$N#$5xG^O=CT+8ts{xqiU~| z&N-WEjj4U@7{ZS+{9Vn<<jtS`POVUVG8=Zu+Qb^dL5u=g^*CCU(ssB_?dO@BH=+NR zM%TvIwztMr5eG%E?&9$oR|nelMeVn%gN&&|SUE3abK9}Dv$ego<75JTq|{+xcC9^B z&#DO%;%dT**{LSS60KcQP8)Ls7#ZJpoSPRwHMI6HN<S(keMKE>JzRRIP7Z{pdIIoa z^(5dUY)8r6+^P*L`r-SnXf?>U<6U2`7sB1@DQY@mgV+PE^eFTur?i)$d;40B*a&Dz z-Kqw~lDZXgW=da1Egu8T<LYUI_QIBk`(Tu0n>&Er58N2`jYq;_>ygC+2)U4y#~~>n zd?$71bFrj+Xw5ZJZ_7oV!z>3<Y+`W&<^Lw07M{Nb>}33O)pJWphSk*C?(hgG9&hbt zF8o|tXu_iqD6lIn${fRU3^e<1S?{Fvh<bi)x0IRI1Do=Wk&^nL^yioe5XYyD+fHN$ zv1m|?YWDG52M&jvzF4`S>4GcTIPTvOFrq#j@vD}q4R{4`#@5Kvw30*vwA`&!%cX@1 zPRiAbN!+9m^9jZ9C<{ZJ2yDQ95eV@NdPCJlxDu3(9($sMGsSb2Riu)<@`oRCSLHcU z9D_5Gsi=!ms#NBeS!!;fp-Ye@=Ig!1MupH{4NJE*k9n|?q6jQALmuZuLX)GIM2Zyk zRi+eJK3*oWXq-nZCsBW}fE|6Q6gEn8l@Lik4SKm36b;CWV55QQ2d6oty*L($<0~{8 z>i;fvl=_*|eKR;9jpHr(88}tuHBCWL9?qK3WRI*;l#&x6(yhhph6)Z={W(;RXZoZ= z;ySMXAzv~C*=k`^Ds4ZN1k@i?v$4D}=RcA(WR14WG{Ue^>q&k;DeZ<7_ZcM1TYKd| z+^HFp+=;hOM8oF2z3Hge6_cJg8Rc+%e;#LAZ(}~JKLT_$3BW%a(xxP?EEk=)q3fSP zLNvtMZYq(Zz5ezIq;8GtyNQ6V<VxaekD$g<>ZatYlAQRug%d=qQBOBosM!uAw`;bV z#AFaDY#HLIuj@q{Jt7ljKVUQxqc>C$6s)2?K#H;M@NOV#X3g5VSdk8fokB{b61q-l zdi@^&pev35t&ag9Ors-4;sh@D5W%%b?=?_gv-BfEv(5tAwQu3<{6V7As1lwDQ?G}G z(yYAyLhMe1;8d=B4PpI|L9hyy#J7Pc66+G|A)vn<4LE%o%;CaC%?X6M^f9R)0Aa2& z<2T3sg!tkd%kM0-b&Ox!4zq|VUxP-akDBcN5Q*!FZvycI5n26=!IN9818r)A_n3Lj zoN*(VQ$NRd?lxKCPRrmfoI(5(P@v1c8TU<cecT`tnRdH>6^M2pE~5|kvN+CTX)~I9 z;UY}HJdCBYEYt33x}|f(bB;Ar$4{@q^zf|Ept9_M^^*V^+?%~5(_}XhV8^&DD&=4W z*wU`Z;d+aW%~48L7h-=M^idM)0^#Gx=B3Rlh;tcsVVm(rGAybaCZ5;VGvUom$bZZl zaIodI%^l!E^SWGv1_s;2%G})Hm1USfZo#n8+}`6vTAiDMcG>nJePFrzODyAm05qq2 zU0v7C3lR)|<=EJ9?Yx}i{wPm}2P@W356R*1BdbT|=H~Lv=k8NptiQVZVP`3}on&;K z!S8k`E~S_+9y1Q2_gJ?Z0Fiy|eAIXC{3&h~WW{<Lx=C*#7$(>X5M{5OKP?Pej}rGd z!9IdR1cw1|0r&jr4)4~u2rsDDIxJknMg}+4>YQ<wL0T|&7ddxmj)-MJT$bHpgf+|H zKseQ4y^WQXmBW=Ti$Io!&NDj1)UsrNnMAx}vL=3p_%9QPNz`bgCGOGToe~eum7s0n zP}(mrYxWcV9b>|wiWrT<6k`-}?rp18anbP(m@yy0s21B;{}|N8JzYOs*Q^uSt8*Ss zm-E6E-{cjRm*`+y_Hh+VdwIrW|2x_fKIUbKr%~4HV+c0-nWg!oy^&+Rktb;9pZD_Y zCv1K=-|yu*VGWaH!W(2curP27HwBxB^t3nBsYgs>YJ-=So_I12L)AvOpOmKl7$*~z z2AKB5-bp{4_3{e_k8tGF%E6N7$jybf(OF);yTRbl=e)Q%qO=(nxLJew&3b<>_DZbI z@gQw5fi@+Qv_{b$b)SFs09+DzT>apYPZ~qA_JC8!4x|QBpw7}=F@)bhejq)N9EP!= zO$n<QDTR(Vk$PxT1I}>vev~Y0Bjs@ZBXkZ*O>U$b$g>{#)G+Ed;LvG;nxfvk`6NeJ z;W{<{kT0&uuwB5>3)<qk2T#)}TQD^H@EyBg_Mw}`I9>1#IB4``blEJst^G%%e8&{> zMf56-&ZPwI-oc~;n;jgI%exTffeM`CM-0a?;#2)exCxKPj!+z>fZ#0mt6(${*XLP$ zLgB5`Jb(e?5Y6ssQ3~J;UF0!|3|yI+#Xf|xPzpw&6cq0KP^=R4uVrJEAdB1s$T27? z!BFtt%}a=F(LaY9o;ZfFI2@btzt*yr62b4$rP@mB-|Dz$k(2bK6ww}ea07s9OcwUV zORXe~EytOP-=G|&7?1Pjh>?8Ql<HK3U};>0DPeJo9Ow|sB%QZiDk)8oidY^2(JC&j zZv$PYESUAviz6uS$6C4g$r}9_O=_@f=2U?S4QRH3=BuDFPtts~L(?DAjDqGjL1Uhz z`OOZ^Kuk0CjvYX`UNp}nj4xD`%)16-nsLxv1C4o-<{D~{111N>3$eIOIM5<(XUZT` z(MR#<E^e1FaOkh?cOeacc1LgtRD?<Ne0p&wVmsktz7&_oQDTo1&+>Nihz)P@;%ph5 z77nJWIbSjcmfUto;YMAx3TMH@#6*6UB<(+jc`$(TNiYFGBPI-%HlA|!4sSkS<~ioP zwTRh_Us7fX-)K)a_lx6t_wL<pyi=QfSvGB$j1{ix;u2OJN5-58hZfBiMf1MdXSnwi z9T@6yd$&~9^IjVD<})Af?+6QH-puFn#;_c~){9es=5TVt9>HSgiHYVTJ<Xpu4lNTG zzh0c^<`EI6IrUM=U<y)%(Tj|aCUie4XdoqTWV*>st)IFNHN_ISc1CCYiyt!s&*zAC z=F;6E;tck8$TEM1{J57zcdJMuC-O#8p`>A?5(*g2CpQ(x1A$9u-l4c1GK>bN;aPPj z<pj~hM{+gl;p#`S86@0DyP_?W5ZcobY8<Z+a}Hc!b>3x2M6B+-kRt)V$AqsCTqGSu z5B)MjG+*maFeI|en~aK-AtKHVM593?E>K1jO*k4V^=MP<R9u-fYIHljt3Vq}p?&O# z)N&dhH!kT5Hmdbqyaqn101q$21BR<}fjO#>;vJ2n`YI~uCE=ygmsr&Zg!(mvqO_S( zo2PVNPak~~>TU2j<Vav^GdC)5iqOtxuyxsH6XPaWKq~#qHk?)Fv{2#Oj>8;j+4y)m zD4pc}40BG%tPgWyXb4;1<{#;}-537ENpZ85__y_2sP%>|5w+|3sZS#Uh9TB@*&3}7 zn_%dKsSqc8LfHSq{9KN{5?KF6utD<t0FDVS_Qwvb{ldO%bAk?sN#;<Xew}^xa}50` z!S56NHUW=Di052<YqDKS1@%kJ@hzs_27rn*gGZFXMeBuz3L@UPMqqQV5B6N+Cl@+J zZn6tG6A&fR=<7}Y{<qVI^BFhTBP8nm11@nAzkp!#4Hn#Xec&xwq`@1~b4!-r3Uc%r z6j*Mcuk$YcPVq!U)mV7qtI)2Qz=yS8#bH+*`h#3+?nRRT*QjsYz{To~H|oF3Ypq7H z>ABd7>$*5~Q!Up;LYAAkdyLV<9L&HKoWPyOU8_3c?i8$ZQ#axh6F1_!u~!$cMYyoh zxpSa%De}h5uiK#KT^!B}DmXG?E`x)o@=ET4FMWfH=#ANm^g#{Z9-!Zh<xNeQ%eU}b z!g)E}kfIMs4%a;j6mGNE+wo(Fzk<(1tkl|=CxAIy4i<dX#ym+3>|9;UQ>K*Jr8dda zLb8I2wlU8DbD><(?fA2Zn-{riL+Q_%vhe8ypbx7J?>WjA^yJ!{+-^-L?`DE-^BzdT z_FlnAaw_qI87s98DNx<P>KUQHeU|s4k3QkjMq0+l6zZ<|q61H*2kw4zN&2~GWVkp> zby03@n~VGB@e!<L9AsU=x$^d$?hb!&2*=$ZKJX2%zS?kcr5zs{cwz#5v48p4v7!v* zo=coAS9lIWPP@ttY}ZUvyq}eM!hB?+xM$+{<noIj^FH=8PCt;3Y`TbT%vlG?YG@=f z_8rmdX&Mtdk(iUj9wfTHQT@dTpjciP@Dze9^2_8ov>n0^?K-x(5xo%#-Gey}8P)?i zj^QR}$xSQ(>9+fFla58=FOs9YhD8qRDDL-nF3bNp@RUFGzWp=~72I!pHyKU3qa`63 zGWO(;bI7u0$nqR2P3(gQ=+Ji>pv@mnFavfP1vdZ114m1&29$4PX$2MB_s9C{a?Jm4 zTfnKd@vCRVK1O%<-M707Xr!@Loo8r(V52#$XTfxbfPM2mhk*7htePx_fIs=Cnfl<N zpw0HaYsGZ?Ib<#?fCu(`S5W*5T_+vmpy_wY1k-T&$hRmu?6Fdxt*$78+(SDN<bSi7 z?s4%oKJw>~pKzIh&j@>IV@_Jxoi!8O6LI30hmPgj7qj8>@&?I_<Cpw^4+_DDIDCy` z{@7~W+Y6Q1g}B5($^*j&;m7^-1-@!>-?`yn5X8ED-zmUx`nQ|{)L=0M3XY?Rd+@+t zdeFc>;NvA5#IhtlUXsMeOOp6_NfI9~F_YyKUReI;c05`9Yay<Kql2)o!Ed+u!;Z8w zyBrg4Y>nH15R_JGV11NjMBT#&ZKMOwmC>V+;rN}Ar$q<Ekoow67&<<fg^-8Mj%oNX zPDSCidS@Wr0LQs2bNG4@{esKF1Ugb-T=+$NyN5I5+W8<BkPbsqq?Yr-3r8R>QiL+a zcjraBGDdZY#j{wvgrp^vc4c-Pa`*E~EAo-2ifKVlF_iM8(yYGri_e?(b}Ml7J5~Vm zvinkbUAIj}U;Z;R3Lk=r$0#s$dS06Gr`E*{78)y6oZ!T#VCM1FnT~7(E{LzvQ0(O5 zbY+B2y>^lJ=x*U-G#EmBa>m^QpTiz@z6?8r<=QenWuyc(;bL$&un?87z+kxvI&x%t z%y8TjQZl}^B826c24a;udK2Gu!SQwsAFk2i@(S*{FE3Xsy|w$!pP-p8ZoTh1?5frt zSLAhqPZC@sc!S_m1fq=J#Ue9GJ1q?2+AyXE@r4Kn?*iFQPl4{YxR715kasw#HYdl^ zNJF@C9`klm!`lUzD86jIjf3wX&8<*|#KA!<MR;j6zedB)*o5Y^YKotXTaXJy=4uhV zesI7M<Opcu)wJ;%(?UN(0|fAT$37FWO$;--m4H4MakY4TVWsBRuwEgD8e0af72bnB zX!q0uQ;z9Rv(OU3|0EDSxQ0-aZp`pAtnzUt^Cb#VibXyCd6HUu<sd9I@YmsvlzdY! zkkf^cU2R16LPgFDnyVHt0Gpft?MsrlzZvNN#22MjAjWIYF3sxyz;b_s;6E}2-!QA; zfV+<~y(=^N*NGQ(Tom(7IywD3I?1=tm~OOlYRn?Uil8K0w6JT<0lgoCkFv+j*R5XU zU<I69j$DB%*q2f`i<@kp%ys@4TmKfY|KUxl`Zv?tvi;fd{Pv;3@bzpedvSR8CW_6< z{l|^}M*6UL8t82>|KN>~e}Ci*<NIsyOu*9t9KX`W&uH)psCYH3?6;guCzqA|z{wRd k*%O5)vwO26*@LKWU-mONF!5scVs^{$nc<P)J;Pi7FHV;eC;$Ke literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/formula.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/formula.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae95931bd7d6a5be34bf042c9ae43663bb6d1663 GIT binary patch literal 56656 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 a`@j8MP$!D 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/info.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/info.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b581d64caf99cef93d3ccd465ed942f56cbbb749 GIT binary patch literal 206 zcmYe~<>g`kg5=G!6IFopV-N=!FabFZKwQiNBvKey7@`=W7*iO788n%ySdH`y^bGwp znQn2%$CsrR6=&w>#m7ehIbp6r!Jhtp@$oAeia;iSiC@0@xv43ciTWOiMJX;tnPsW^ z0hJjg`FZ-m$r<_iIr@5Ksd;7kIhjfN1(hWk`FX~Amion+C8@dviOJcC>8Zu~6*)yI d`k8rY`FaJFw>WHa^HWN5QtcRlHhczR1_0VsHJShb literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/sheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/sheet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b842a3a49e174f98778b82d89e49d5d987e767b2 GIT binary patch literal 49682 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 Dzd_l~ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/timemachine.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/timemachine.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79346d433b3a2fcbdef570b9c004335200685e9f GIT binary patch literal 1774 zcmbVMOK&4Z5bo}Ic<lI*HzW(o9(E;sAhQTAAhd!_3`B~YC^jPLi#48Z$HUIU>Y12$ zWjMice*qVA%$YyIZ)k}Vr<}R%1*+Qd5)uN^_Nc3?t81$3@l^-4T7|#}fB7l+%_Zb_ z+*}_XY(9mVYaoIM8k2-_N|9$V<4m$|EWw2JBikU{mbS2;IqG}i2=@oV9Z@o?D?GE7 z;IDG*C1qX#3l*^dIt#KYYqCDDctbAoCArL3MAc}oikey1<T`&NJrZ@Zb40_eu6&cD zy~TJV*);Z+X7-+Y@*Zz|P0~%9$hRiHQi31cvN~YAaZFP7n8>%`rWL*-SNWP?9nxIB zg!o94>iS4UY0(>;reP6fY5S6)oLyS5C<-J*G9Sp}$u`U!GdU$cLz2#Ehg`6>cH^Lk z()YovE7LF&@`7r5c0m-XzU=ym6j9)R87Q%%qOtT3Cc`32{Z2T{ve@4m%XI9=QQsd; zieZ+1u=SCjM}>TE6ok)$6Pf#Gu@Zg}B{B)ZVU)_P(L{Tn#6i**!INdU;}4ibRe{`c z8(lYU+rUf-I858dFNHOqap<O6K!PS#kf{Ore_N`Ky7)W9ZucjJ{5ONEbAzB%dJ0PU z?tX~*o)1i|1M>@*`R72U6w@o%Is1+RPla_()#HLqDImc{3{KfPL?fIRjFNO2Pf#LU zkd<Coh+C+(*M80(DVTw#mCHY2Kw<1b6w8<N!3EQnOoc-4nwBmpIf{c&Y7d0@w^1?F zj={)<rRhLhgQVcjI8Z0~jSzKdVDtv-=shNt&?>FaO{VSx|Bpa1=vz3N8XTEaf_tuz zh@d9Zw2uykH?A`!dI#*hPKoGE=Z&=ouVd{__&mJR+O5cE9i*`I%7k<mBp=Of0)o6w z0j+!TUl95j)MpR^*gPx%LimhbgN=0s8#pnru?>{wuyNaJ2`|LXQl=Z$ki+^mnc6^p z6UjX!rg#2sAT0U2Fmrfi0SE|#=6zl1!Anz|D%tBP#DKQ)Nv^A7sd9LHdQm#a;6*as z-@6j>+SB81tJB-t?Y0g-+vAO|+PmBPJFQoO^6Y&7a7VK!Q;T4hdo$VYzJVUxQYh_U zTjsiqOQ2LR;ft-K?bcqe+dAs@y2l5tj&_1Pj3RCywhj)pqk{BAYWHlmmQK?M5y|T^ z%`t?ly|%)k(k|{`wK64iIh@x<AqsS*ALJ5zfO6?_J4j?psZ41bT1{O`+^g)tqn2di nG?q_Llz#}M$~;zw=~4Wu)N?D;h7WhHh15o=!^~+N&#b=y-Z^9? literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/__pycache__/xldate.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlrd/__pycache__/xldate.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ab8ad76e584f0b03dd0827da7c93b47a7e023c8 GIT binary patch literal 7225 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 SwfC33nw`t0Ei=2E9s4hWbr356 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlrd/biffh.py b/.venv/lib/python3.9/site-packages/xlrd/biffh.py new file mode 100644 index 00000000..07ac6291 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/biffh.py @@ -0,0 +1,643 @@ +# -*- coding: utf-8 -*- +# Portions copyright © 2005-2010 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +from __future__ import print_function + +import sys +from struct import unpack + +from .timemachine import * + +DEBUG = 0 + + + +class XLRDError(Exception): + """ + An exception indicating problems reading data from an Excel file. + """ + + +class BaseObject(object): + """ + Parent of almost all other classes in the package. Defines a common + :meth:`dump` method for debugging. + """ + + _repr_these = [] + + + def dump(self, f=None, header=None, footer=None, indent=0): + """ + :param f: open file object, to which the dump is written + :param header: text to write before the dump + :param footer: text to write after the dump + :param indent: number of leading spaces (for recursive calls) + """ + if f is None: + f = sys.stderr + if hasattr(self, "__slots__"): + alist = [] + for attr in self.__slots__: + alist.append((attr, getattr(self, attr))) + else: + alist = self.__dict__.items() + alist = sorted(alist) + pad = " " * indent + if header is not None: print(header, file=f) + list_type = type([]) + dict_type = type({}) + for attr, value in alist: + if getattr(value, 'dump', None) and attr != 'book': + value.dump(f, + header="%s%s (%s object):" % (pad, attr, value.__class__.__name__), + indent=indent+4) + elif (attr not in self._repr_these and + (isinstance(value, list_type) or isinstance(value, dict_type))): + print("%s%s: %s, len = %d" % (pad, attr, type(value), len(value)), file=f) + else: + fprintf(f, "%s%s: %r\n", pad, attr, value) + if footer is not None: print(footer, file=f) + +FUN, FDT, FNU, FGE, FTX = range(5) # unknown, date, number, general, text +DATEFORMAT = FDT +NUMBERFORMAT = FNU + +( + XL_CELL_EMPTY, + XL_CELL_TEXT, + XL_CELL_NUMBER, + XL_CELL_DATE, + XL_CELL_BOOLEAN, + XL_CELL_ERROR, + XL_CELL_BLANK, # for use in debugging, gathering stats, etc +) = range(7) + +biff_text_from_num = { + 0: "(not BIFF)", + 20: "2.0", + 21: "2.1", + 30: "3", + 40: "4S", + 45: "4W", + 50: "5", + 70: "7", + 80: "8", + 85: "8X", +} + +#: This dictionary can be used to produce a text version of the internal codes +#: that Excel uses for error cells. +error_text_from_code = { + 0x00: '#NULL!', # Intersection of two cell ranges is empty + 0x07: '#DIV/0!', # Division by zero + 0x0F: '#VALUE!', # Wrong type of operand + 0x17: '#REF!', # Illegal or deleted cell reference + 0x1D: '#NAME?', # Wrong function or range name + 0x24: '#NUM!', # Value range overflow + 0x2A: '#N/A', # Argument or function not available +} + +BIFF_FIRST_UNICODE = 80 + +XL_WORKBOOK_GLOBALS = WBKBLOBAL = 0x5 +XL_WORKBOOK_GLOBALS_4W = 0x100 +XL_WORKSHEET = WRKSHEET = 0x10 + +XL_BOUNDSHEET_WORKSHEET = 0x00 +XL_BOUNDSHEET_CHART = 0x02 +XL_BOUNDSHEET_VB_MODULE = 0x06 + +# XL_RK2 = 0x7e +XL_ARRAY = 0x0221 +XL_ARRAY2 = 0x0021 +XL_BLANK = 0x0201 +XL_BLANK_B2 = 0x01 +XL_BOF = 0x809 +XL_BOOLERR = 0x205 +XL_BOOLERR_B2 = 0x5 +XL_BOUNDSHEET = 0x85 +XL_BUILTINFMTCOUNT = 0x56 +XL_CF = 0x01B1 +XL_CODEPAGE = 0x42 +XL_COLINFO = 0x7D +XL_COLUMNDEFAULT = 0x20 # BIFF2 only +XL_COLWIDTH = 0x24 # BIFF2 only +XL_CONDFMT = 0x01B0 +XL_CONTINUE = 0x3c +XL_COUNTRY = 0x8C +XL_DATEMODE = 0x22 +XL_DEFAULTROWHEIGHT = 0x0225 +XL_DEFCOLWIDTH = 0x55 +XL_DIMENSION = 0x200 +XL_DIMENSION2 = 0x0 +XL_EFONT = 0x45 +XL_EOF = 0x0a +XL_EXTERNNAME = 0x23 +XL_EXTERNSHEET = 0x17 +XL_EXTSST = 0xff +XL_FEAT11 = 0x872 +XL_FILEPASS = 0x2f +XL_FONT = 0x31 +XL_FONT_B3B4 = 0x231 +XL_FORMAT = 0x41e +XL_FORMAT2 = 0x1E # BIFF2, BIFF3 +XL_FORMULA = 0x6 +XL_FORMULA3 = 0x206 +XL_FORMULA4 = 0x406 +XL_GCW = 0xab +XL_HLINK = 0x01B8 +XL_QUICKTIP = 0x0800 +XL_HORIZONTALPAGEBREAKS = 0x1b +XL_INDEX = 0x20b +XL_INTEGER = 0x2 # BIFF2 only +XL_IXFE = 0x44 # BIFF2 only +XL_LABEL = 0x204 +XL_LABEL_B2 = 0x04 +XL_LABELRANGES = 0x15f +XL_LABELSST = 0xfd +XL_LEFTMARGIN = 0x26 +XL_TOPMARGIN = 0x28 +XL_RIGHTMARGIN = 0x27 +XL_BOTTOMMARGIN = 0x29 +XL_HEADER = 0x14 +XL_FOOTER = 0x15 +XL_HCENTER = 0x83 +XL_VCENTER = 0x84 +XL_MERGEDCELLS = 0xE5 +XL_MSO_DRAWING = 0x00EC +XL_MSO_DRAWING_GROUP = 0x00EB +XL_MSO_DRAWING_SELECTION = 0x00ED +XL_MULRK = 0xbd +XL_MULBLANK = 0xbe +XL_NAME = 0x18 +XL_NOTE = 0x1c +XL_NUMBER = 0x203 +XL_NUMBER_B2 = 0x3 +XL_OBJ = 0x5D +XL_PAGESETUP = 0xA1 +XL_PALETTE = 0x92 +XL_PANE = 0x41 +XL_PRINTGRIDLINES = 0x2B +XL_PRINTHEADERS = 0x2A +XL_RK = 0x27e +XL_ROW = 0x208 +XL_ROW_B2 = 0x08 +XL_RSTRING = 0xd6 +XL_SCL = 0x00A0 +XL_SHEETHDR = 0x8F # BIFF4W only +XL_SHEETPR = 0x81 +XL_SHEETSOFFSET = 0x8E # BIFF4W only +XL_SHRFMLA = 0x04bc +XL_SST = 0xfc +XL_STANDARDWIDTH = 0x99 +XL_STRING = 0x207 +XL_STRING_B2 = 0x7 +XL_STYLE = 0x293 +XL_SUPBOOK = 0x1AE # aka EXTERNALBOOK in OOo docs +XL_TABLEOP = 0x236 +XL_TABLEOP2 = 0x37 +XL_TABLEOP_B2 = 0x36 +XL_TXO = 0x1b6 +XL_UNCALCED = 0x5e +XL_UNKNOWN = 0xffff +XL_VERTICALPAGEBREAKS = 0x1a +XL_WINDOW2 = 0x023E +XL_WINDOW2_B2 = 0x003E +XL_WRITEACCESS = 0x5C +XL_WSBOOL = XL_SHEETPR +XL_XF = 0xe0 +XL_XF2 = 0x0043 # BIFF2 version of XF record +XL_XF3 = 0x0243 # BIFF3 version of XF record +XL_XF4 = 0x0443 # BIFF4 version of XF record + +boflen = {0x0809: 8, 0x0409: 6, 0x0209: 6, 0x0009: 4} +bofcodes = (0x0809, 0x0409, 0x0209, 0x0009) + +XL_FORMULA_OPCODES = (0x0006, 0x0406, 0x0206) + +_cell_opcode_list = [ + XL_BOOLERR, + XL_FORMULA, + XL_FORMULA3, + XL_FORMULA4, + XL_LABEL, + XL_LABELSST, + XL_MULRK, + XL_NUMBER, + XL_RK, + XL_RSTRING, +] +_cell_opcode_dict = {} +for _cell_opcode in _cell_opcode_list: + _cell_opcode_dict[_cell_opcode] = 1 + +def is_cell_opcode(c): + return c in _cell_opcode_dict + +def upkbits(tgt_obj, src, manifest, local_setattr=setattr): + for n, mask, attr in manifest: + local_setattr(tgt_obj, attr, (src & mask) >> n) + +def upkbitsL(tgt_obj, src, manifest, local_setattr=setattr, local_int=int): + for n, mask, attr in manifest: + local_setattr(tgt_obj, attr, local_int((src & mask) >> n)) + +def unpack_string(data, pos, encoding, lenlen=1): + nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] + pos += lenlen + return unicode(data[pos:pos+nchars], encoding) + +def unpack_string_update_pos(data, pos, encoding, lenlen=1, known_len=None): + if known_len is not None: + # On a NAME record, the length byte is detached from the front of the string. + nchars = known_len + else: + nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] + pos += lenlen + newpos = pos + nchars + return (unicode(data[pos:newpos], encoding), newpos) + +def unpack_unicode(data, pos, lenlen=2): + "Return unicode_strg" + nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] + if not nchars: + # Ambiguous whether 0-length string should have an "options" byte. + # Avoid crash if missing. + return UNICODE_LITERAL("") + pos += lenlen + options = BYTES_ORD(data[pos]) + pos += 1 + # phonetic = options & 0x04 + # richtext = options & 0x08 + if options & 0x08: + # rt = unpack('<H', data[pos:pos+2])[0] # unused + pos += 2 + if options & 0x04: + # sz = unpack('<i', data[pos:pos+4])[0] # unused + pos += 4 + if options & 0x01: + # Uncompressed UTF-16-LE + rawstrg = data[pos:pos+2*nchars] + # if DEBUG: print "nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) + strg = unicode(rawstrg, 'utf_16_le') + # pos += 2*nchars + else: + # Note: this is COMPRESSED (not ASCII!) encoding!!! + # Merely returning the raw bytes would work OK 99.99% of the time + # if the local codepage was cp1252 -- however this would rapidly go pear-shaped + # for other codepages so we grit our Anglocentric teeth and return Unicode :-) + + strg = unicode(data[pos:pos+nchars], "latin_1") + # pos += nchars + # if richtext: + # pos += 4 * rt + # if phonetic: + # pos += sz + # return (strg, pos) + return strg + +def unpack_unicode_update_pos(data, pos, lenlen=2, known_len=None): + "Return (unicode_strg, updated value of pos)" + if known_len is not None: + # On a NAME record, the length byte is detached from the front of the string. + nchars = known_len + else: + nchars = unpack('<' + 'BH'[lenlen-1], data[pos:pos+lenlen])[0] + pos += lenlen + if not nchars and not data[pos:]: + # Zero-length string with no options byte + return (UNICODE_LITERAL(""), pos) + options = BYTES_ORD(data[pos]) + pos += 1 + phonetic = options & 0x04 + richtext = options & 0x08 + if richtext: + rt = unpack('<H', data[pos:pos+2])[0] + pos += 2 + if phonetic: + sz = unpack('<i', data[pos:pos+4])[0] + pos += 4 + if options & 0x01: + # Uncompressed UTF-16-LE + strg = unicode(data[pos:pos+2*nchars], 'utf_16_le') + pos += 2*nchars + else: + # Note: this is COMPRESSED (not ASCII!) encoding!!! + strg = unicode(data[pos:pos+nchars], "latin_1") + pos += nchars + if richtext: + pos += 4 * rt + if phonetic: + pos += sz + return (strg, pos) + +def unpack_cell_range_address_list_update_pos(output_list, data, pos, biff_version, addr_size=6): + # output_list is updated in situ + assert addr_size in (6, 8) + # Used to assert size == 6 if not BIFF8, but pyWLWriter writes + # BIFF8-only MERGEDCELLS records in a BIFF5 file! + n, = unpack("<H", data[pos:pos+2]) + pos += 2 + if n: + if addr_size == 6: + fmt = "<HHBB" + else: + fmt = "<HHHH" + for _unused in xrange(n): + ra, rb, ca, cb = unpack(fmt, data[pos:pos+addr_size]) + output_list.append((ra, rb+1, ca, cb+1)) + pos += addr_size + return pos + +_brecstrg = """\ +0000 DIMENSIONS_B2 +0001 BLANK_B2 +0002 INTEGER_B2_ONLY +0003 NUMBER_B2 +0004 LABEL_B2 +0005 BOOLERR_B2 +0006 FORMULA +0007 STRING_B2 +0008 ROW_B2 +0009 BOF_B2 +000A EOF +000B INDEX_B2_ONLY +000C CALCCOUNT +000D CALCMODE +000E PRECISION +000F REFMODE +0010 DELTA +0011 ITERATION +0012 PROTECT +0013 PASSWORD +0014 HEADER +0015 FOOTER +0016 EXTERNCOUNT +0017 EXTERNSHEET +0018 NAME_B2,5+ +0019 WINDOWPROTECT +001A VERTICALPAGEBREAKS +001B HORIZONTALPAGEBREAKS +001C NOTE +001D SELECTION +001E FORMAT_B2-3 +001F BUILTINFMTCOUNT_B2 +0020 COLUMNDEFAULT_B2_ONLY +0021 ARRAY_B2_ONLY +0022 DATEMODE +0023 EXTERNNAME +0024 COLWIDTH_B2_ONLY +0025 DEFAULTROWHEIGHT_B2_ONLY +0026 LEFTMARGIN +0027 RIGHTMARGIN +0028 TOPMARGIN +0029 BOTTOMMARGIN +002A PRINTHEADERS +002B PRINTGRIDLINES +002F FILEPASS +0031 FONT +0032 FONT2_B2_ONLY +0036 TABLEOP_B2 +0037 TABLEOP2_B2 +003C CONTINUE +003D WINDOW1 +003E WINDOW2_B2 +0040 BACKUP +0041 PANE +0042 CODEPAGE +0043 XF_B2 +0044 IXFE_B2_ONLY +0045 EFONT_B2_ONLY +004D PLS +0051 DCONREF +0055 DEFCOLWIDTH +0056 BUILTINFMTCOUNT_B3-4 +0059 XCT +005A CRN +005B FILESHARING +005C WRITEACCESS +005D OBJECT +005E UNCALCED +005F SAVERECALC +0063 OBJECTPROTECT +007D COLINFO +007E RK2_mythical_? +0080 GUTS +0081 WSBOOL +0082 GRIDSET +0083 HCENTER +0084 VCENTER +0085 BOUNDSHEET +0086 WRITEPROT +008C COUNTRY +008D HIDEOBJ +008E SHEETSOFFSET +008F SHEETHDR +0090 SORT +0092 PALETTE +0099 STANDARDWIDTH +009B FILTERMODE +009C FNGROUPCOUNT +009D AUTOFILTERINFO +009E AUTOFILTER +00A0 SCL +00A1 SETUP +00AB GCW +00BD MULRK +00BE MULBLANK +00C1 MMS +00D6 RSTRING +00D7 DBCELL +00DA BOOKBOOL +00DD SCENPROTECT +00E0 XF +00E1 INTERFACEHDR +00E2 INTERFACEEND +00E5 MERGEDCELLS +00E9 BITMAP +00EB MSO_DRAWING_GROUP +00EC MSO_DRAWING +00ED MSO_DRAWING_SELECTION +00EF PHONETIC +00FC SST +00FD LABELSST +00FF EXTSST +013D TABID +015F LABELRANGES +0160 USESELFS +0161 DSF +01AE SUPBOOK +01AF PROTECTIONREV4 +01B0 CONDFMT +01B1 CF +01B2 DVAL +01B6 TXO +01B7 REFRESHALL +01B8 HLINK +01BC PASSWORDREV4 +01BE DV +01C0 XL9FILE +01C1 RECALCID +0200 DIMENSIONS +0201 BLANK +0203 NUMBER +0204 LABEL +0205 BOOLERR +0206 FORMULA_B3 +0207 STRING +0208 ROW +0209 BOF +020B INDEX_B3+ +0218 NAME +0221 ARRAY +0223 EXTERNNAME_B3-4 +0225 DEFAULTROWHEIGHT +0231 FONT_B3B4 +0236 TABLEOP +023E WINDOW2 +0243 XF_B3 +027E RK +0293 STYLE +0406 FORMULA_B4 +0409 BOF +041E FORMAT +0443 XF_B4 +04BC SHRFMLA +0800 QUICKTIP +0809 BOF +0862 SHEETLAYOUT +0867 SHEETPROTECTION +0868 RANGEPROTECTION +""" + +biff_rec_name_dict = {} +for _buff in _brecstrg.splitlines(): + _numh, _name = _buff.split() + biff_rec_name_dict[int(_numh, 16)] = _name +del _buff, _name, _brecstrg + +def hex_char_dump(strg, ofs, dlen, base=0, fout=sys.stdout, unnumbered=False): + endpos = min(ofs + dlen, len(strg)) + pos = ofs + numbered = not unnumbered + num_prefix = '' + while pos < endpos: + endsub = min(pos + 16, endpos) + substrg = strg[pos:endsub] + lensub = endsub - pos + if lensub <= 0 or lensub != len(substrg): + fprintf( + sys.stdout, + '??? hex_char_dump: ofs=%d dlen=%d base=%d -> endpos=%d pos=%d endsub=%d substrg=%r\n', + ofs, dlen, base, endpos, pos, endsub, substrg) + break + hexd = ''.join("%02x " % BYTES_ORD(c) for c in substrg) + + chard = '' + for c in substrg: + c = chr(BYTES_ORD(c)) + if c == '\0': + c = '~' + elif not (' ' <= c <= '~'): + c = '?' + chard += c + if numbered: + num_prefix = "%5d: " % (base+pos-ofs) + + fprintf(fout, "%s %-48s %s\n", num_prefix, hexd, chard) + pos = endsub + +def biff_dump(mem, stream_offset, stream_len, base=0, fout=sys.stdout, unnumbered=False): + pos = stream_offset + stream_end = stream_offset + stream_len + adj = base - stream_offset + dummies = 0 + numbered = not unnumbered + num_prefix = '' + while stream_end - pos >= 4: + rc, length = unpack('<HH', mem[pos:pos+4]) + if rc == 0 and length == 0: + if mem[pos:] == b'\0' * (stream_end - pos): + dummies = stream_end - pos + savpos = pos + pos = stream_end + break + if dummies: + dummies += 4 + else: + savpos = pos + dummies = 4 + pos += 4 + else: + if dummies: + if numbered: + num_prefix = "%5d: " % (adj + savpos) + fprintf(fout, "%s---- %d zero bytes skipped ----\n", num_prefix, dummies) + dummies = 0 + recname = biff_rec_name_dict.get(rc, '<UNKNOWN>') + if numbered: + num_prefix = "%5d: " % (adj + pos) + fprintf(fout, "%s%04x %s len = %04x (%d)\n", num_prefix, rc, recname, length, length) + pos += 4 + hex_char_dump(mem, pos, length, adj+pos, fout, unnumbered) + pos += length + if dummies: + if numbered: + num_prefix = "%5d: " % (adj + savpos) + fprintf(fout, "%s---- %d zero bytes skipped ----\n", num_prefix, dummies) + if pos < stream_end: + if numbered: + num_prefix = "%5d: " % (adj + pos) + fprintf(fout, "%s---- Misc bytes at end ----\n", num_prefix) + hex_char_dump(mem, pos, stream_end-pos, adj + pos, fout, unnumbered) + elif pos > stream_end: + fprintf(fout, "Last dumped record has length (%d) that is too large\n", length) + +def biff_count_records(mem, stream_offset, stream_len, fout=sys.stdout): + pos = stream_offset + stream_end = stream_offset + stream_len + tally = {} + while stream_end - pos >= 4: + rc, length = unpack('<HH', mem[pos:pos+4]) + if rc == 0 and length == 0: + if mem[pos:] == b'\0' * (stream_end - pos): + break + recname = "<Dummy (zero)>" + else: + recname = biff_rec_name_dict.get(rc, None) + if recname is None: + recname = "Unknown_0x%04X" % rc + if recname in tally: + tally[recname] += 1 + else: + tally[recname] = 1 + pos += length + 4 + slist = sorted(tally.items()) + for recname, count in slist: + print("%8d %s" % (count, recname), file=fout) + +encoding_from_codepage = { + 1200 : 'utf_16_le', + 10000: 'mac_roman', + 10006: 'mac_greek', # guess + 10007: 'mac_cyrillic', # guess + 10029: 'mac_latin2', # guess + 10079: 'mac_iceland', # guess + 10081: 'mac_turkish', # guess + 32768: 'mac_roman', + 32769: 'cp1252', +} +# some more guessing, for Indic scripts +# codepage 57000 range: +# 2 Devanagari [0] +# 3 Bengali [1] +# 4 Tamil [5] +# 5 Telegu [6] +# 6 Assamese [1] c.f. Bengali +# 7 Oriya [4] +# 8 Kannada [7] +# 9 Malayalam [8] +# 10 Gujarati [3] +# 11 Gurmukhi [2] diff --git a/.venv/lib/python3.9/site-packages/xlrd/book.py b/.venv/lib/python3.9/site-packages/xlrd/book.py new file mode 100644 index 00000000..6876a3e8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/book.py @@ -0,0 +1,1474 @@ +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. + +from __future__ import print_function + +import struct + +from . import compdoc, formatting, sheet +from .biffh import * +from .formula import * +from .timemachine import * + +try: + from time import perf_counter +except ImportError: + # Python 2.7 + from time import clock as perf_counter + +from struct import unpack + +empty_cell = sheet.empty_cell # for exposure to the world ... + +DEBUG = 0 + +import mmap + +MY_EOF = 0xF00BAAA # not a 16-bit number + +SUPBOOK_UNK, SUPBOOK_INTERNAL, SUPBOOK_EXTERNAL, SUPBOOK_ADDIN, SUPBOOK_DDEOLE = range(5) + +SUPPORTED_VERSIONS = (80, 70, 50, 45, 40, 30, 21, 20) + +_code_from_builtin_name = { + "Consolidate_Area": "\x00", + "Auto_Open": "\x01", + "Auto_Close": "\x02", + "Extract": "\x03", + "Database": "\x04", + "Criteria": "\x05", + "Print_Area": "\x06", + "Print_Titles": "\x07", + "Recorder": "\x08", + "Data_Form": "\x09", + "Auto_Activate": "\x0A", + "Auto_Deactivate": "\x0B", + "Sheet_Title": "\x0C", + "_FilterDatabase": "\x0D", +} +builtin_name_from_code = {} +code_from_builtin_name = {} +for _bin, _bic in _code_from_builtin_name.items(): + _bin = UNICODE_LITERAL(_bin) + _bic = UNICODE_LITERAL(_bic) + code_from_builtin_name[_bin] = _bic + builtin_name_from_code[_bic] = _bin +del _bin, _bic, _code_from_builtin_name + +def open_workbook_xls(filename=None, + logfile=sys.stdout, verbosity=0, use_mmap=True, + file_contents=None, + encoding_override=None, + formatting_info=False, on_demand=False, ragged_rows=False, + ignore_workbook_corruption=False): + t0 = perf_counter() + bk = Book() + try: + bk.biff2_8_load( + filename=filename, file_contents=file_contents, + logfile=logfile, verbosity=verbosity, use_mmap=use_mmap, + encoding_override=encoding_override, + formatting_info=formatting_info, + on_demand=on_demand, + ragged_rows=ragged_rows, + ignore_workbook_corruption=ignore_workbook_corruption + ) + t1 = perf_counter() + bk.load_time_stage_1 = t1 - t0 + biff_version = bk.getbof(XL_WORKBOOK_GLOBALS) + if not biff_version: + raise XLRDError("Can't determine file's BIFF version") + if biff_version not in SUPPORTED_VERSIONS: + raise XLRDError( + "BIFF version %s is not supported" + % biff_text_from_num[biff_version] + ) + bk.biff_version = biff_version + if biff_version <= 40: + # no workbook globals, only 1 worksheet + if on_demand: + fprintf(bk.logfile, + "*** WARNING: on_demand is not supported for this Excel version.\n" + "*** Setting on_demand to False.\n") + bk.on_demand = on_demand = False + bk.fake_globals_get_sheet() + elif biff_version == 45: + # worksheet(s) embedded in global stream + bk.parse_globals() + if on_demand: + fprintf(bk.logfile, "*** WARNING: on_demand is not supported for this Excel version.\n" + "*** Setting on_demand to False.\n") + bk.on_demand = on_demand = False + else: + bk.parse_globals() + bk._sheet_list = [None for sh in bk._sheet_names] + if not on_demand: + bk.get_sheets() + bk.nsheets = len(bk._sheet_list) + if biff_version == 45 and bk.nsheets > 1: + fprintf( + bk.logfile, + "*** WARNING: Excel 4.0 workbook (.XLW) file contains %d worksheets.\n" + "*** Book-level data will be that of the last worksheet.\n", + bk.nsheets + ) + t2 = perf_counter() + bk.load_time_stage_2 = t2 - t1 + except: + bk.release_resources() + raise + # normal exit + if not on_demand: + bk.release_resources() + return bk + + +class Name(BaseObject): + """ + Information relating to a named reference, formula, macro, etc. + + .. note:: + + Name information is **not** extracted from files older than + Excel 5.0 (``Book.biff_version < 50``) + """ + _repr_these = ['stack'] + book = None # parent + + #: 0 = Visible; 1 = Hidden + hidden = 0 + + #: 0 = Command macro; 1 = Function macro. Relevant only if macro == 1 + func = 0 + + #: 0 = Sheet macro; 1 = VisualBasic macro. Relevant only if macro == 1 + vbasic = 0 + + #: 0 = Standard name; 1 = Macro name + macro = 0 + + #: 0 = Simple formula; 1 = Complex formula (array formula or user defined). + #: + #: .. note:: No examples have been sighted. + complex = 0 + + #: 0 = User-defined name; 1 = Built-in name + #: + #: Common examples: ``Print_Area``, ``Print_Titles``; see OOo docs for + #: full list + builtin = 0 + + #: Function group. Relevant only if macro == 1; see OOo docs for values. + funcgroup = 0 + + #: 0 = Formula definition; 1 = Binary data + #: + #: .. note:: No examples have been sighted. + binary = 0 + + #: The index of this object in book.name_obj_list + name_index = 0 + + # A Unicode string. If builtin, decoded as per OOo docs. + name = UNICODE_LITERAL("") + + #: An 8-bit string. + raw_formula = b'' + + #: ``-1``: + #: The name is global (visible in all calculation sheets). + #: ``-2``: + #: The name belongs to a macro sheet or VBA sheet. + #: ``-3``: + #: The name is invalid. + #: ``0 <= scope < book.nsheets``: + #: The name is local to the sheet whose index is scope. + scope = -1 + + #: The result of evaluating the formula, if any. + #: If no formula, or evaluation of the formula encountered problems, + #: the result is ``None``. Otherwise the result is a single instance of the + #: :class:`~xlrd.formula.Operand` class. + # + result = None + + def cell(self): + """ + This is a convenience method for the frequent use case where the name + refers to a single cell. + + :returns: An instance of the :class:`~xlrd.sheet.Cell` class. + + :raises xlrd.biffh.XLRDError: + The name is not a constant absolute reference + to a single cell. + """ + res = self.result + if res: + # result should be an instance of the Operand class + kind = res.kind + value = res.value + if kind == oREF and len(value) == 1: + ref3d = value[0] + if (0 <= ref3d.shtxlo == ref3d.shtxhi - 1 and + ref3d.rowxlo == ref3d.rowxhi - 1 and + ref3d.colxlo == ref3d.colxhi - 1): + sh = self.book.sheet_by_index(ref3d.shtxlo) + return sh.cell(ref3d.rowxlo, ref3d.colxlo) + self.dump( + self.book.logfile, + header="=== Dump of Name object ===", + footer="======= End of dump =======", + ) + raise XLRDError("Not a constant absolute reference to a single cell") + + def area2d(self, clipped=True): + """ + This is a convenience method for the use case where the name + refers to one rectangular area in one worksheet. + + :param clipped: + If ``True``, the default, the returned rectangle is clipped + to fit in ``(0, sheet.nrows, 0, sheet.ncols)``. + it is guaranteed that ``0 <= rowxlo <= rowxhi <= sheet.nrows`` and + that the number of usable rows in the area (which may be zero) is + ``rowxhi - rowxlo``; likewise for columns. + + :returns: a tuple ``(sheet_object, rowxlo, rowxhi, colxlo, colxhi)``. + + :raises xlrd.biffh.XLRDError: + The name is not a constant absolute reference + to a single area in a single sheet. + """ + res = self.result + if res: + # result should be an instance of the Operand class + kind = res.kind + value = res.value + if kind == oREF and len(value) == 1: # only 1 reference + ref3d = value[0] + if 0 <= ref3d.shtxlo == ref3d.shtxhi - 1: # only 1 usable sheet + sh = self.book.sheet_by_index(ref3d.shtxlo) + if not clipped: + return sh, ref3d.rowxlo, ref3d.rowxhi, ref3d.colxlo, ref3d.colxhi + rowxlo = min(ref3d.rowxlo, sh.nrows) + rowxhi = max(rowxlo, min(ref3d.rowxhi, sh.nrows)) + colxlo = min(ref3d.colxlo, sh.ncols) + colxhi = max(colxlo, min(ref3d.colxhi, sh.ncols)) + assert 0 <= rowxlo <= rowxhi <= sh.nrows + assert 0 <= colxlo <= colxhi <= sh.ncols + return sh, rowxlo, rowxhi, colxlo, colxhi + self.dump( + self.book.logfile, + header="=== Dump of Name object ===", + footer="======= End of dump =======", + ) + raise XLRDError("Not a constant absolute reference to a single area in a single sheet") + + +class Book(BaseObject): + """ + Contents of a "workbook". + + .. warning:: + + You should not instantiate this class yourself. You use the :class:`Book` + object that was returned when you called :func:`~xlrd.open_workbook`. + """ + + #: The number of worksheets present in the workbook file. + #: This information is available even when no sheets have yet been loaded. + nsheets = 0 + + #: Which date system was in force when this file was last saved. + #: + #: 0: + #: 1900 system (the Excel for Windows default). + #: + #: 1: + #: 1904 system (the Excel for Macintosh default). + #: + #: Defaults to 0 in case it's not specified in the file. + datemode = 0 + + #: Version of BIFF (Binary Interchange File Format) used to create the file. + #: Latest is 8.0 (represented here as 80), introduced with Excel 97. + #: Earliest supported by this module: 2.0 (represented as 20). + biff_version = 0 + + #: List containing a :class:`Name` object for each ``NAME`` record in the + #: workbook. + #: + #: .. versionadded:: 0.6.0 + name_obj_list = [] + + #: An integer denoting the character set used for strings in this file. + #: For BIFF 8 and later, this will be 1200, meaning Unicode; + #: more precisely, UTF_16_LE. + #: For earlier versions, this is used to derive the appropriate Python + #: encoding to be used to convert to Unicode. + #: Examples: ``1252 -> 'cp1252'``, ``10000 -> 'mac_roman'`` + codepage = None + + #: The encoding that was derived from the codepage. + encoding = None + + #: A tuple containing the telephone country code for: + #: + #: ``[0]``: + #: the user-interface setting when the file was created. + #: + #: ``[1]``: + #: the regional settings. + #: + #: Example: ``(1, 61)`` meaning ``(USA, Australia)``. + #: + #: This information may give a clue to the correct encoding for an + #: unknown codepage. For a long list of observed values, refer to the + #: OpenOffice.org documentation for the ``COUNTRY`` record. + countries = (0, 0) + + #: What (if anything) is recorded as the name of the last user to + #: save the file. + user_name = UNICODE_LITERAL('') + + #: A list of :class:`~xlrd.formatting.Font` class instances, + #: each corresponding to a FONT record. + #: + #: .. versionadded:: 0.6.1 + font_list = [] + + #: A list of :class:`~xlrd.formatting.XF` class instances, + #: each corresponding to an ``XF`` record. + #: + #: .. versionadded:: 0.6.1 + xf_list = [] + + #: A list of :class:`~xlrd.formatting.Format` objects, each corresponding to + #: a ``FORMAT`` record, in the order that they appear in the input file. + #: It does *not* contain builtin formats. + #: + #: If you are creating an output file using (for example) :mod:`xlwt`, + #: use this list. + #: + #: The collection to be used for all visual rendering purposes is + #: :attr:`format_map`. + #: + #: .. versionadded:: 0.6.1 + format_list = [] + + ## + #: The mapping from :attr:`~xlrd.formatting.XF.format_key` to + #: :class:`~xlrd.formatting.Format` object. + #: + #: .. versionadded:: 0.6.1 + format_map = {} + + #: This provides access via name to the extended format information for + #: both built-in styles and user-defined styles. + #: + #: It maps ``name`` to ``(built_in, xf_index)``, where + #: ``name`` is either the name of a user-defined style, + #: or the name of one of the built-in styles. Known built-in names are + #: Normal, RowLevel_1 to RowLevel_7, + #: ColLevel_1 to ColLevel_7, Comma, Currency, Percent, "Comma [0]", + #: "Currency [0]", Hyperlink, and "Followed Hyperlink". + #: + #: ``built_in`` has the following meanings + #: + #: 1: + #: built-in style + #: + #: 0: + #: user-defined + #: + #: ``xf_index`` is an index into :attr:`Book.xf_list`. + #: + #: References: OOo docs s6.99 (``STYLE`` record); Excel UI Format/Style + #: + #: .. versionadded:: 0.6.1 + #: + #: Extracted only if ``open_workbook(..., formatting_info=True)`` + #: + #: .. versionadded:: 0.7.4 + style_name_map = {} + + #: This provides definitions for colour indexes. Please refer to + #: :ref:`palette` for an explanation + #: of how colours are represented in Excel. + #: + #: Colour indexes into the palette map into ``(red, green, blue)`` tuples. + #: "Magic" indexes e.g. ``0x7FFF`` map to ``None``. + #: + #: :attr:`colour_map` is what you need if you want to render cells on screen + #: or in a PDF file. If you are writing an output XLS file, use + #: :attr:`palette_record`. + #: + #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)`` + #: + #: .. versionadded:: 0.6.1 + colour_map = {} + + #: If the user has changed any of the colours in the standard palette, the + #: XLS file will contain a ``PALETTE`` record with 56 (16 for Excel 4.0 and + #: earlier) RGB values in it, and this list will be e.g. + #: ``[(r0, b0, g0), ..., (r55, b55, g55)]``. + #: Otherwise this list will be empty. This is what you need if you are + #: writing an output XLS file. If you want to render cells on screen or in a + #: PDF file, use :attr:`colour_map`. + #: + #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)`` + #: + #: .. versionadded:: 0.6.1 + palette_record = [] + + #: Time in seconds to extract the XLS image as a contiguous string + #: (or mmap equivalent). + load_time_stage_1 = -1.0 + + #: Time in seconds to parse the data from the contiguous string + #: (or mmap equivalent). + load_time_stage_2 = -1.0 + + def sheets(self): + """ + :returns: A list of all sheets in the book. + + All sheets not already loaded will be loaded. + """ + for sheetx in xrange(self.nsheets): + if not self._sheet_list[sheetx]: + self.get_sheet(sheetx) + return self._sheet_list[:] + + def sheet_by_index(self, sheetx): + """ + :param sheetx: Sheet index in ``range(nsheets)`` + :returns: A :class:`~xlrd.sheet.Sheet`. + """ + return self._sheet_list[sheetx] or self.get_sheet(sheetx) + + def __iter__(self): + """ + Makes iteration through sheets of a book a little more straightforward. + Don't free resources after use since it can be called like `list(book)` + """ + for i in range(self.nsheets): + yield self.sheet_by_index(i) + + def sheet_by_name(self, sheet_name): + """ + :param sheet_name: Name of the sheet required. + :returns: A :class:`~xlrd.sheet.Sheet`. + """ + try: + sheetx = self._sheet_names.index(sheet_name) + except ValueError: + raise XLRDError('No sheet named <%r>' % sheet_name) + return self.sheet_by_index(sheetx) + + def __getitem__(self, item): + """ + Allow indexing with sheet name or index. + :param item: Name or index of sheet enquired upon + :return: :class:`~xlrd.sheet.Sheet`. + """ + if isinstance(item, int): + return self.sheet_by_index(item) + else: + return self.sheet_by_name(item) + + def sheet_names(self): + """ + :returns: + A list of the names of all the worksheets in the workbook file. + This information is available even when no sheets have yet been + loaded. + """ + return self._sheet_names[:] + + def sheet_loaded(self, sheet_name_or_index): + """ + :param sheet_name_or_index: Name or index of sheet enquired upon + :returns: ``True`` if sheet is loaded, ``False`` otherwise. + + .. versionadded:: 0.7.1 + """ + if isinstance(sheet_name_or_index, int): + sheetx = sheet_name_or_index + else: + try: + sheetx = self._sheet_names.index(sheet_name_or_index) + except ValueError: + raise XLRDError('No sheet named <%r>' % sheet_name_or_index) + return bool(self._sheet_list[sheetx]) + + def unload_sheet(self, sheet_name_or_index): + """ + :param sheet_name_or_index: Name or index of sheet to be unloaded. + + .. versionadded:: 0.7.1 + """ + if isinstance(sheet_name_or_index, int): + sheetx = sheet_name_or_index + else: + try: + sheetx = self._sheet_names.index(sheet_name_or_index) + except ValueError: + raise XLRDError('No sheet named <%r>' % sheet_name_or_index) + self._sheet_list[sheetx] = None + + def release_resources(self): + """ + This method has a dual purpose. You can call it to release + memory-consuming objects and (possibly) a memory-mapped file + (:class:`mmap.mmap` object) when you have finished loading sheets in + ``on_demand`` mode, but still require the :class:`Book` object to + examine the loaded sheets. It is also called automatically (a) when + :func:`~xlrd.open_workbook` + raises an exception and (b) if you are using a ``with`` statement, when + the ``with`` block is exited. Calling this method multiple times on the + same object has no ill effect. + """ + self._resources_released = 1 + if hasattr(self.mem, "close"): + # must be a mmap.mmap object + self.mem.close() + self.mem = None + if hasattr(self.filestr, "close"): + self.filestr.close() + self.filestr = None + self._sharedstrings = None + self._rich_text_runlist_map = None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + self.release_resources() + # return false + + #: A mapping from ``(lower_case_name, scope)`` to a single :class:`Name` + #: object. + #: + #: .. versionadded:: 0.6.0 + name_and_scope_map = {} + + #: A mapping from `lower_case_name` to a list of :class:`Name` objects. + #: The list is sorted in scope order. Typically there will be one item + #: (of global scope) in the list. + #: + #: .. versionadded:: 0.6.0 + name_map = {} + + def __init__(self): + self._sheet_list = [] + self._sheet_names = [] + self._sheet_visibility = [] # from BOUNDSHEET record + self.nsheets = 0 + self._sh_abs_posn = [] # sheet's absolute position in the stream + self._sharedstrings = [] + self._rich_text_runlist_map = {} + self.raw_user_name = False + self._sheethdr_count = 0 # BIFF 4W only + self.builtinfmtcount = -1 # unknown as yet. BIFF 3, 4S, 4W + self.initialise_format_info() + self._all_sheets_count = 0 # includes macro & VBA sheets + self._supbook_count = 0 + self._supbook_locals_inx = None + self._supbook_addins_inx = None + self._all_sheets_map = [] # maps an all_sheets index to a calc-sheets index (or -1) + self._externsheet_info = [] + self._externsheet_type_b57 = [] + self._extnsht_name_from_num = {} + self._sheet_num_from_name = {} + self._extnsht_count = 0 + self._supbook_types = [] + self._resources_released = 0 + self.addin_func_names = [] + self.name_obj_list = [] + self.colour_map = {} + self.palette_record = [] + self.xf_list = [] + self.style_name_map = {} + self.mem = b'' + self.filestr = b'' + + def biff2_8_load(self, filename=None, file_contents=None, + logfile=sys.stdout, verbosity=0, use_mmap=True, + encoding_override=None, + formatting_info=False, + on_demand=False, + ragged_rows=False, + ignore_workbook_corruption=False + ): + # DEBUG = 0 + self.logfile = logfile + self.verbosity = verbosity + self.use_mmap = use_mmap + self.encoding_override = encoding_override + self.formatting_info = formatting_info + self.on_demand = on_demand + self.ragged_rows = ragged_rows + + if not file_contents: + with open(filename, "rb") as f: + f.seek(0, 2) # EOF + size = f.tell() + f.seek(0, 0) # BOF + if size == 0: + raise XLRDError("File size is 0 bytes") + if self.use_mmap: + self.filestr = mmap.mmap(f.fileno(), size, access=mmap.ACCESS_READ) + self.stream_len = size + else: + self.filestr = f.read() + self.stream_len = len(self.filestr) + else: + self.filestr = file_contents + self.stream_len = len(file_contents) + + self.base = 0 + if self.filestr[:8] != compdoc.SIGNATURE: + # got this one at the antique store + self.mem = self.filestr + else: + cd = compdoc.CompDoc(self.filestr, logfile=self.logfile, + ignore_workbook_corruption=ignore_workbook_corruption) + for qname in ['Workbook', 'Book']: + self.mem, self.base, self.stream_len = \ + cd.locate_named_stream(UNICODE_LITERAL(qname)) + if self.mem: + break + else: + raise XLRDError("Can't find workbook in OLE2 compound document") + del cd + if self.mem is not self.filestr: + if hasattr(self.filestr, "close"): + self.filestr.close() + self.filestr = b'' + self._position = self.base + if DEBUG: + print("mem: %s, base: %d, len: %d" % (type(self.mem), self.base, self.stream_len), file=self.logfile) + + def initialise_format_info(self): + # needs to be done once per sheet for BIFF 4W :-( + self.format_map = {} + self.format_list = [] + self.xfcount = 0 + self.actualfmtcount = 0 # number of FORMAT records seen so far + self._xf_index_to_xl_type_map = {0: XL_CELL_NUMBER} + self._xf_epilogue_done = 0 + self.xf_list = [] + self.font_list = [] + + def get2bytes(self): + pos = self._position + buff_two = self.mem[pos:pos+2] + lenbuff = len(buff_two) + self._position += lenbuff + if lenbuff < 2: + return MY_EOF + lo, hi = buff_two + return (BYTES_ORD(hi) << 8) | BYTES_ORD(lo) + + def get_record_parts(self): + pos = self._position + mem = self.mem + code, length = unpack('<HH', mem[pos:pos+4]) + pos += 4 + data = mem[pos:pos+length] + self._position = pos + length + return (code, length, data) + + def get_record_parts_conditional(self, reqd_record): + pos = self._position + mem = self.mem + code, length = unpack('<HH', mem[pos:pos+4]) + if code != reqd_record: + return (None, 0, b'') + pos += 4 + data = mem[pos:pos+length] + self._position = pos + length + return (code, length, data) + + def get_sheet(self, sh_number, update_pos=True): + if self._resources_released: + raise XLRDError("Can't load sheets after releasing resources.") + if update_pos: + self._position = self._sh_abs_posn[sh_number] + self.getbof(XL_WORKSHEET) + # assert biff_version == self.biff_version ### FAILS + # Have an example where book is v7 but sheet reports v8!!! + # It appears to work OK if the sheet version is ignored. + # Confirmed by Daniel Rentz: happens when Excel does "save as" + # creating an old version file; ignore version details on sheet BOF. + sh = sheet.Sheet( + self, + self._position, + self._sheet_names[sh_number], + sh_number, + ) + sh.read(self) + self._sheet_list[sh_number] = sh + return sh + + def get_sheets(self): + # DEBUG = 0 + if DEBUG: print("GET_SHEETS:", self._sheet_names, self._sh_abs_posn, file=self.logfile) + for sheetno in xrange(len(self._sheet_names)): + if DEBUG: print("GET_SHEETS: sheetno =", sheetno, self._sheet_names, self._sh_abs_posn, file=self.logfile) + self.get_sheet(sheetno) + + def fake_globals_get_sheet(self): # for BIFF 4.0 and earlier + formatting.initialise_book(self) + fake_sheet_name = UNICODE_LITERAL('Sheet 1') + self._sheet_names = [fake_sheet_name] + self._sh_abs_posn = [0] + self._sheet_visibility = [0] # one sheet, visible + self._sheet_list.append(None) # get_sheet updates _sheet_list but needs a None beforehand + self.get_sheets() + + def handle_boundsheet(self, data): + # DEBUG = 1 + bv = self.biff_version + self.derive_encoding() + if DEBUG: + fprintf(self.logfile, "BOUNDSHEET: bv=%d data %r\n", bv, data) + if bv == 45: # BIFF4W + #### Not documented in OOo docs ... + # In fact, the *only* data is the name of the sheet. + sheet_name = unpack_string(data, 0, self.encoding, lenlen=1) + visibility = 0 + sheet_type = XL_BOUNDSHEET_WORKSHEET # guess, patch later + if len(self._sh_abs_posn) == 0: + abs_posn = self._sheetsoffset + self.base + # Note (a) this won't be used + # (b) it's the position of the SHEETHDR record + # (c) add 11 to get to the worksheet BOF record + else: + abs_posn = -1 # unknown + else: + offset, visibility, sheet_type = unpack('<iBB', data[0:6]) + abs_posn = offset + self.base # because global BOF is always at posn 0 in the stream + if bv < BIFF_FIRST_UNICODE: + sheet_name = unpack_string(data, 6, self.encoding, lenlen=1) + else: + sheet_name = unpack_unicode(data, 6, lenlen=1) + + if DEBUG or self.verbosity >= 2: + fprintf(self.logfile, + "BOUNDSHEET: inx=%d vis=%r sheet_name=%r abs_posn=%d sheet_type=0x%02x\n", + self._all_sheets_count, visibility, sheet_name, abs_posn, sheet_type) + self._all_sheets_count += 1 + if sheet_type != XL_BOUNDSHEET_WORKSHEET: + self._all_sheets_map.append(-1) + descr = { + 1: 'Macro sheet', + 2: 'Chart', + 6: 'Visual Basic module', + }.get(sheet_type, 'UNKNOWN') + + if DEBUG or self.verbosity >= 1: + fprintf(self.logfile, + "NOTE *** Ignoring non-worksheet data named %r (type 0x%02x = %s)\n", + sheet_name, sheet_type, descr) + else: + snum = len(self._sheet_names) + self._all_sheets_map.append(snum) + self._sheet_names.append(sheet_name) + self._sh_abs_posn.append(abs_posn) + self._sheet_visibility.append(visibility) + self._sheet_num_from_name[sheet_name] = snum + + def handle_builtinfmtcount(self, data): + ### N.B. This count appears to be utterly useless. + # DEBUG = 1 + builtinfmtcount = unpack('<H', data[0:2])[0] + if DEBUG: fprintf(self.logfile, "BUILTINFMTCOUNT: %r\n", builtinfmtcount) + self.builtinfmtcount = builtinfmtcount + + def derive_encoding(self): + if self.encoding_override: + self.encoding = self.encoding_override + elif self.codepage is None: + if self.biff_version < 80: + fprintf(self.logfile, + "*** No CODEPAGE record, no encoding_override: will use 'iso-8859-1'\n") + self.encoding = 'iso-8859-1' + else: + self.codepage = 1200 # utf16le + if self.verbosity >= 2: + fprintf(self.logfile, "*** No CODEPAGE record; assuming 1200 (utf_16_le)\n") + else: + codepage = self.codepage + if codepage in encoding_from_codepage: + encoding = encoding_from_codepage[codepage] + elif 300 <= codepage <= 1999: + encoding = 'cp' + str(codepage) + elif self.biff_version >= 80: + self.codepage = 1200 + encoding = 'utf_16_le' + else: + encoding = 'unknown_codepage_' + str(codepage) + if DEBUG or (self.verbosity and encoding != self.encoding) : + fprintf(self.logfile, "CODEPAGE: codepage %r -> encoding %r\n", codepage, encoding) + self.encoding = encoding + if self.codepage != 1200: # utf_16_le + # If we don't have a codec that can decode ASCII into Unicode, + # we're well & truly stuffed -- let the punter know ASAP. + try: + unicode(b'trial', self.encoding) + except BaseException as e: + fprintf(self.logfile, + "ERROR *** codepage %r -> encoding %r -> %s: %s\n", + self.codepage, self.encoding, type(e).__name__.split(".")[-1], e) + raise + if self.raw_user_name: + strg = unpack_string(self.user_name, 0, self.encoding, lenlen=1) + strg = strg.rstrip() + # if DEBUG: + # print "CODEPAGE: user name decoded from %r to %r" % (self.user_name, strg) + self.user_name = strg + self.raw_user_name = False + return self.encoding + + def handle_codepage(self, data): + # DEBUG = 0 + codepage = unpack('<H', data[0:2])[0] + self.codepage = codepage + self.derive_encoding() + + def handle_country(self, data): + countries = unpack('<HH', data[0:4]) + if self.verbosity: print("Countries:", countries, file=self.logfile) + # Note: in BIFF7 and earlier, country record was put (redundantly?) in each worksheet. + assert self.countries == (0, 0) or self.countries == countries + self.countries = countries + + def handle_datemode(self, data): + datemode = unpack('<H', data[0:2])[0] + if DEBUG or self.verbosity: + fprintf(self.logfile, "DATEMODE: datemode %r\n", datemode) + assert datemode in (0, 1) + self.datemode = datemode + + def handle_externname(self, data): + blah = DEBUG or self.verbosity >= 2 + if self.biff_version >= 80: + option_flags, other_info =unpack("<HI", data[:6]) + pos = 6 + name, pos = unpack_unicode_update_pos(data, pos, lenlen=1) + extra = data[pos:] + if self._supbook_types[-1] == SUPBOOK_ADDIN: + self.addin_func_names.append(name) + if blah: + fprintf(self.logfile, + "EXTERNNAME: sbktype=%d oflags=0x%04x oinfo=0x%08x name=%r extra=%r\n", + self._supbook_types[-1], option_flags, other_info, name, extra) + + def handle_externsheet(self, data): + self.derive_encoding() # in case CODEPAGE record missing/out of order/wrong + self._extnsht_count += 1 # for use as a 1-based index + blah1 = DEBUG or self.verbosity >= 1 + blah2 = DEBUG or self.verbosity >= 2 + if self.biff_version >= 80: + num_refs = unpack("<H", data[0:2])[0] + bytes_reqd = num_refs * 6 + 2 + while len(data) < bytes_reqd: + if blah1: + fprintf( + self.logfile, + "INFO: EXTERNSHEET needs %d bytes, have %d\n", + bytes_reqd, len(data), + ) + code2, length2, data2 = self.get_record_parts() + if code2 != XL_CONTINUE: + raise XLRDError("Missing CONTINUE after EXTERNSHEET record") + data += data2 + pos = 2 + for k in xrange(num_refs): + info = unpack("<HHH", data[pos:pos+6]) + ref_recordx, ref_first_sheetx, ref_last_sheetx = info + self._externsheet_info.append(info) + pos += 6 + if blah2: + fprintf( + self.logfile, + "EXTERNSHEET(b8): k = %2d, record = %2d, first_sheet = %5d, last sheet = %5d\n", + k, ref_recordx, ref_first_sheetx, ref_last_sheetx, + ) + else: + nc, ty = unpack("<BB", data[:2]) + if blah2: + print("EXTERNSHEET(b7-):", file=self.logfile) + hex_char_dump(data, 0, len(data), fout=self.logfile) + msg = { + 1: "Encoded URL", + 2: "Current sheet!!", + 3: "Specific sheet in own doc't", + 4: "Nonspecific sheet in own doc't!!", + }.get(ty, "Not encoded") + print(" %3d chars, type is %d (%s)" % (nc, ty, msg), file=self.logfile) + if ty == 3: + sheet_name = unicode(data[2:nc+2], self.encoding) + self._extnsht_name_from_num[self._extnsht_count] = sheet_name + if blah2: print(self._extnsht_name_from_num, file=self.logfile) + if not (1 <= ty <= 4): + ty = 0 + self._externsheet_type_b57.append(ty) + + def handle_filepass(self, data): + if self.verbosity >= 2: + logf = self.logfile + fprintf(logf, "FILEPASS:\n") + hex_char_dump(data, 0, len(data), base=0, fout=logf) + if self.biff_version >= 80: + kind1, = unpack('<H', data[:2]) + if kind1 == 0: # weak XOR encryption + key, hash_value = unpack('<HH', data[2:]) + fprintf(logf, + 'weak XOR: key=0x%04x hash=0x%04x\n', + key, hash_value) + elif kind1 == 1: + kind2, = unpack('<H', data[4:6]) + if kind2 == 1: # BIFF8 standard encryption + caption = "BIFF8 std" + elif kind2 == 2: + caption = "BIFF8 strong" + else: + caption = "** UNKNOWN ENCRYPTION METHOD **" + fprintf(logf, "%s\n", caption) + raise XLRDError("Workbook is encrypted") + + def handle_name(self, data): + blah = DEBUG or self.verbosity >= 2 + bv = self.biff_version + if bv < 50: + return + self.derive_encoding() + # print + # hex_char_dump(data, 0, len(data), fout=self.logfile) + ( + option_flags, kb_shortcut, name_len, fmla_len, extsht_index, sheet_index, + menu_text_len, description_text_len, help_topic_text_len, status_bar_text_len, + ) = unpack("<HBBHHH4B", data[0:14]) + nobj = Name() + nobj.book = self ### CIRCULAR ### + name_index = len(self.name_obj_list) + nobj.name_index = name_index + self.name_obj_list.append(nobj) + nobj.option_flags = option_flags + attrs = [ + ('hidden', 1, 0), + ('func', 2, 1), + ('vbasic', 4, 2), + ('macro', 8, 3), + ('complex', 0x10, 4), + ('builtin', 0x20, 5), + ('funcgroup', 0xFC0, 6), + ('binary', 0x1000, 12), + ] + for attr, mask, nshift in attrs: + setattr(nobj, attr, (option_flags & mask) >> nshift) + + macro_flag = " M"[nobj.macro] + if bv < 80: + internal_name, pos = unpack_string_update_pos(data, 14, self.encoding, known_len=name_len) + else: + internal_name, pos = unpack_unicode_update_pos(data, 14, known_len=name_len) + nobj.extn_sheet_num = extsht_index + nobj.excel_sheet_index = sheet_index + nobj.scope = None # patched up in the names_epilogue() method + if blah: + fprintf( + self.logfile, + "NAME[%d]:%s oflags=%d, name_len=%d, fmla_len=%d, extsht_index=%d, sheet_index=%d, name=%r\n", + name_index, macro_flag, option_flags, name_len, + fmla_len, extsht_index, sheet_index, internal_name) + name = internal_name + if nobj.builtin: + name = builtin_name_from_code.get(name, "??Unknown??") + if blah: print(" builtin: %s" % name, file=self.logfile) + nobj.name = name + nobj.raw_formula = data[pos:] + nobj.basic_formula_len = fmla_len + nobj.evaluated = 0 + if blah: + nobj.dump( + self.logfile, + header="--- handle_name: name[%d] ---" % name_index, + footer="-------------------", + ) + + def names_epilogue(self): + blah = self.verbosity >= 2 + f = self.logfile + if blah: + print("+++++ names_epilogue +++++", file=f) + print("_all_sheets_map", REPR(self._all_sheets_map), file=f) + print("_extnsht_name_from_num", REPR(self._extnsht_name_from_num), file=f) + print("_sheet_num_from_name", REPR(self._sheet_num_from_name), file=f) + num_names = len(self.name_obj_list) + for namex in range(num_names): + nobj = self.name_obj_list[namex] + # Convert from excel_sheet_index to scope. + # This is done here because in BIFF7 and earlier, the + # BOUNDSHEET records (from which _all_sheets_map is derived) + # come after the NAME records. + if self.biff_version >= 80: + sheet_index = nobj.excel_sheet_index + if sheet_index == 0: + intl_sheet_index = -1 # global + elif 1 <= sheet_index <= len(self._all_sheets_map): + intl_sheet_index = self._all_sheets_map[sheet_index-1] + if intl_sheet_index == -1: # maps to a macro or VBA sheet + intl_sheet_index = -2 # valid sheet reference but not useful + else: + # huh? + intl_sheet_index = -3 # invalid + elif 50 <= self.biff_version <= 70: + sheet_index = nobj.extn_sheet_num + if sheet_index == 0: + intl_sheet_index = -1 # global + else: + sheet_name = self._extnsht_name_from_num[sheet_index] + intl_sheet_index = self._sheet_num_from_name.get(sheet_name, -2) + nobj.scope = intl_sheet_index + + for namex in range(num_names): + nobj = self.name_obj_list[namex] + # Parse the formula ... + if nobj.macro or nobj.binary: continue + if nobj.evaluated: continue + evaluate_name_formula(self, nobj, namex, blah=blah) + + if self.verbosity >= 2: + print("---------- name object dump ----------", file=f) + for namex in range(num_names): + nobj = self.name_obj_list[namex] + nobj.dump(f, header="--- name[%d] ---" % namex) + print("--------------------------------------", file=f) + # + # Build some dicts for access to the name objects + # + name_and_scope_map = {} # (name.lower(), scope): Name_object + name_map = {} # name.lower() : list of Name_objects (sorted in scope order) + for namex in range(num_names): + nobj = self.name_obj_list[namex] + name_lcase = nobj.name.lower() + key = (name_lcase, nobj.scope) + if key in name_and_scope_map and self.verbosity: + fprintf(f, 'Duplicate entry %r in name_and_scope_map\n', key) + name_and_scope_map[key] = nobj + sort_data = (nobj.scope, namex, nobj) + # namex (a temp unique ID) ensures the Name objects will not + # be compared (fatal in py3) + if name_lcase in name_map: + name_map[name_lcase].append(sort_data) + else: + name_map[name_lcase] = [sort_data] + for key in name_map.keys(): + alist = name_map[key] + alist.sort() + name_map[key] = [x[2] for x in alist] + self.name_and_scope_map = name_and_scope_map + self.name_map = name_map + + def handle_obj(self, data): + # Not doing much handling at all. + # Worrying about embedded (BOF ... EOF) substreams is done elsewhere. + # DEBUG = 1 + obj_type, obj_id = unpack('<HI', data[4:10]) + # if DEBUG: print "---> handle_obj type=%d id=0x%08x" % (obj_type, obj_id) + + def handle_supbook(self, data): + # aka EXTERNALBOOK in OOo docs + self._supbook_types.append(None) + blah = DEBUG or self.verbosity >= 2 + if blah: + print("SUPBOOK:", file=self.logfile) + hex_char_dump(data, 0, len(data), fout=self.logfile) + num_sheets = unpack("<H", data[0:2])[0] + if blah: print("num_sheets = %d" % num_sheets, file=self.logfile) + sbn = self._supbook_count + self._supbook_count += 1 + if data[2:4] == b"\x01\x04": + self._supbook_types[-1] = SUPBOOK_INTERNAL + self._supbook_locals_inx = self._supbook_count - 1 + if blah: + print("SUPBOOK[%d]: internal 3D refs; %d sheets" % (sbn, num_sheets), file=self.logfile) + print(" _all_sheets_map", self._all_sheets_map, file=self.logfile) + return + if data[0:4] == b"\x01\x00\x01\x3A": + self._supbook_types[-1] = SUPBOOK_ADDIN + self._supbook_addins_inx = self._supbook_count - 1 + if blah: print("SUPBOOK[%d]: add-in functions" % sbn, file=self.logfile) + return + url, pos = unpack_unicode_update_pos(data, 2, lenlen=2) + if num_sheets == 0: + self._supbook_types[-1] = SUPBOOK_DDEOLE + if blah: fprintf(self.logfile, "SUPBOOK[%d]: DDE/OLE document = %r\n", sbn, url) + return + self._supbook_types[-1] = SUPBOOK_EXTERNAL + if blah: fprintf(self.logfile, "SUPBOOK[%d]: url = %r\n", sbn, url) + sheet_names = [] + for x in range(num_sheets): + try: + shname, pos = unpack_unicode_update_pos(data, pos, lenlen=2) + except struct.error: + # #### FIX ME #### + # Should implement handling of CONTINUE record(s) ... + if self.verbosity: + print( + "*** WARNING: unpack failure in sheet %d of %d in SUPBOOK record for file %r" + % (x, num_sheets, url), + file=self.logfile, + ) + break + sheet_names.append(shname) + if blah: fprintf(self.logfile, " sheetx=%d namelen=%d name=%r (next pos=%d)\n", x, len(shname), shname, pos) + + def handle_sheethdr(self, data): + # This a BIFF 4W special. + # The SHEETHDR record is followed by a (BOF ... EOF) substream containing + # a worksheet. + # DEBUG = 1 + self.derive_encoding() + sheet_len = unpack('<i', data[:4])[0] + sheet_name = unpack_string(data, 4, self.encoding, lenlen=1) + sheetno = self._sheethdr_count + assert sheet_name == self._sheet_names[sheetno] + self._sheethdr_count += 1 + BOF_posn = self._position + posn = BOF_posn - 4 - len(data) + if DEBUG: fprintf(self.logfile, 'SHEETHDR %d at posn %d: len=%d name=%r\n', sheetno, posn, sheet_len, sheet_name) + self.initialise_format_info() + if DEBUG: print('SHEETHDR: xf epilogue flag is %d' % self._xf_epilogue_done, file=self.logfile) + self._sheet_list.append(None) # get_sheet updates _sheet_list but needs a None beforehand + self.get_sheet(sheetno, update_pos=False) + if DEBUG: print('SHEETHDR: posn after get_sheet() =', self._position, file=self.logfile) + self._position = BOF_posn + sheet_len + + def handle_sheetsoffset(self, data): + # DEBUG = 0 + posn = unpack('<i', data)[0] + if DEBUG: print('SHEETSOFFSET:', posn, file=self.logfile) + self._sheetsoffset = posn + + def handle_sst(self, data): + # DEBUG = 1 + if DEBUG: + print("SST Processing", file=self.logfile) + t0 = perf_counter() + nbt = len(data) + strlist = [data] + uniquestrings = unpack('<i', data[4:8])[0] + if DEBUG or self.verbosity >= 2: + fprintf(self.logfile, "SST: unique strings: %d\n", uniquestrings) + while 1: + code, nb, data = self.get_record_parts_conditional(XL_CONTINUE) + if code is None: + break + nbt += nb + if DEBUG >= 2: + fprintf(self.logfile, "CONTINUE: adding %d bytes to SST -> %d\n", nb, nbt) + strlist.append(data) + self._sharedstrings, rt_runlist = unpack_SST_table(strlist, uniquestrings) + if self.formatting_info: + self._rich_text_runlist_map = rt_runlist + if DEBUG: + t1 = perf_counter() + print("SST processing took %.2f seconds" % (t1 - t0, ), file=self.logfile) + + def handle_writeaccess(self, data): + DEBUG = 0 + if self.biff_version < 80: + if not self.encoding: + self.raw_user_name = True + self.user_name = data + return + strg = unpack_string(data, 0, self.encoding, lenlen=1) + else: + try: + strg = unpack_unicode(data, 0, lenlen=2) + except UnicodeDecodeError: + # may have invalid trailing characters + strg = unpack_unicode(data.strip(), 0, lenlen=2) + if DEBUG: fprintf(self.logfile, "WRITEACCESS: %d bytes; raw=%s %r\n", len(data), self.raw_user_name, strg) + strg = strg.rstrip() + self.user_name = strg + + def parse_globals(self): + # DEBUG = 0 + # no need to position, just start reading (after the BOF) + formatting.initialise_book(self) + while 1: + rc, length, data = self.get_record_parts() + if DEBUG: print("parse_globals: record code is 0x%04x" % rc, file=self.logfile) + if rc == XL_SST: + self.handle_sst(data) + elif rc == XL_FONT or rc == XL_FONT_B3B4: + self.handle_font(data) + elif rc == XL_FORMAT: # XL_FORMAT2 is BIFF <= 3.0, can't appear in globals + self.handle_format(data) + elif rc == XL_XF: + self.handle_xf(data) + elif rc == XL_BOUNDSHEET: + self.handle_boundsheet(data) + elif rc == XL_DATEMODE: + self.handle_datemode(data) + elif rc == XL_CODEPAGE: + self.handle_codepage(data) + elif rc == XL_COUNTRY: + self.handle_country(data) + elif rc == XL_EXTERNNAME: + self.handle_externname(data) + elif rc == XL_EXTERNSHEET: + self.handle_externsheet(data) + elif rc == XL_FILEPASS: + self.handle_filepass(data) + elif rc == XL_WRITEACCESS: + self.handle_writeaccess(data) + elif rc == XL_SHEETSOFFSET: + self.handle_sheetsoffset(data) + elif rc == XL_SHEETHDR: + self.handle_sheethdr(data) + elif rc == XL_SUPBOOK: + self.handle_supbook(data) + elif rc == XL_NAME: + self.handle_name(data) + elif rc == XL_PALETTE: + self.handle_palette(data) + elif rc == XL_STYLE: + self.handle_style(data) + elif rc & 0xff == 9 and self.verbosity: + fprintf(self.logfile, "*** Unexpected BOF at posn %d: 0x%04x len=%d data=%r\n", + self._position - length - 4, rc, length, data) + elif rc == XL_EOF: + self.xf_epilogue() + self.names_epilogue() + self.palette_epilogue() + if not self.encoding: + self.derive_encoding() + if self.biff_version == 45: + # DEBUG = 0 + if DEBUG: print("global EOF: position", self._position, file=self.logfile) + # if DEBUG: + # pos = self._position - 4 + # print repr(self.mem[pos:pos+40]) + return + else: + # if DEBUG: + # print >> self.logfile, "parse_globals: ignoring record code 0x%04x" % rc + pass + + def read(self, pos, length): + data = self.mem[pos:pos+length] + self._position = pos + len(data) + return data + + def getbof(self, rqd_stream): + # DEBUG = 1 + # if DEBUG: print >> self.logfile, "getbof(): position", self._position + if DEBUG: print("reqd: 0x%04x" % rqd_stream, file=self.logfile) + + def bof_error(msg): + raise XLRDError('Unsupported format, or corrupt file: ' + msg) + savpos = self._position + opcode = self.get2bytes() + if opcode == MY_EOF: + bof_error('Expected BOF record; met end of file') + if opcode not in bofcodes: + bof_error('Expected BOF record; found %r' % self.mem[savpos:savpos+8]) + length = self.get2bytes() + if length == MY_EOF: + bof_error('Incomplete BOF record[1]; met end of file') + if not (4 <= length <= 20): + bof_error( + 'Invalid length (%d) for BOF record type 0x%04x' + % (length, opcode)) + padding = b'\0' * max(0, boflen[opcode] - length) + data = self.read(self._position, length) + if DEBUG: fprintf(self.logfile, "\ngetbof(): data=%r\n", data) + if len(data) < length: + bof_error('Incomplete BOF record[2]; met end of file') + data += padding + version1 = opcode >> 8 + version2, streamtype = unpack('<HH', data[0:4]) + if DEBUG: + print("getbof(): op=0x%04x version2=0x%04x streamtype=0x%04x" + % (opcode, version2, streamtype), file=self.logfile) + bof_offset = self._position - 4 - length + if DEBUG: + print("getbof(): BOF found at offset %d; savpos=%d" + % (bof_offset, savpos), file=self.logfile) + version = build = year = 0 + if version1 == 0x08: + build, year = unpack('<HH', data[4:8]) + if version2 == 0x0600: + version = 80 + elif version2 == 0x0500: + if year < 1994 or build in (2412, 3218, 3321): + version = 50 + else: + version = 70 + else: + # dodgy one, created by a 3rd-party tool + version = { + 0x0000: 21, + 0x0007: 21, + 0x0200: 21, + 0x0300: 30, + 0x0400: 40, + }.get(version2, 0) + elif version1 in (0x04, 0x02, 0x00): + version = {0x04: 40, 0x02: 30, 0x00: 21}[version1] + + if version == 40 and streamtype == XL_WORKBOOK_GLOBALS_4W: + version = 45 # i.e. 4W + + if DEBUG or self.verbosity >= 2: + print("BOF: op=0x%04x vers=0x%04x stream=0x%04x buildid=%d buildyr=%d -> BIFF%d" + % (opcode, version2, streamtype, build, year, version), file=self.logfile) + got_globals = streamtype == XL_WORKBOOK_GLOBALS or ( + version == 45 and streamtype == XL_WORKBOOK_GLOBALS_4W) + if (rqd_stream == XL_WORKBOOK_GLOBALS and got_globals) or streamtype == rqd_stream: + return version + if version < 50 and streamtype == XL_WORKSHEET: + return version + if version >= 50 and streamtype == 0x0100: + bof_error("Workspace file -- no spreadsheet data") + bof_error( + 'BOF not workbook/worksheet: op=0x%04x vers=0x%04x strm=0x%04x build=%d year=%d -> BIFF%d' + % (opcode, version2, streamtype, build, year, version) + ) + +# === helper functions + +def expand_cell_address(inrow, incol): + # Ref : OOo docs, "4.3.4 Cell Addresses in BIFF8" + outrow = inrow + if incol & 0x8000: + if outrow >= 32768: + outrow -= 65536 + relrow = 1 + else: + relrow = 0 + outcol = incol & 0xFF + if incol & 0x4000: + if outcol >= 128: + outcol -= 256 + relcol = 1 + else: + relcol = 0 + return outrow, outcol, relrow, relcol + +def colname(colx, _A2Z="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): + assert colx >= 0 + name = UNICODE_LITERAL('') + while 1: + quot, rem = divmod(colx, 26) + name = _A2Z[rem] + name + if not quot: + return name + colx = quot - 1 + +def display_cell_address(rowx, colx, relrow, relcol): + if relrow: + rowpart = "(*%s%d)" % ("+-"[rowx < 0], abs(rowx)) + else: + rowpart = "$%d" % (rowx+1,) + if relcol: + colpart = "(*%s%d)" % ("+-"[colx < 0], abs(colx)) + else: + colpart = "$" + colname(colx) + return colpart + rowpart + +def unpack_SST_table(datatab, nstrings): + "Return list of strings" + datainx = 0 + ndatas = len(datatab) + data = datatab[0] + datalen = len(data) + pos = 8 + strings = [] + strappend = strings.append + richtext_runs = {} + local_unpack = unpack + local_min = min + local_BYTES_ORD = BYTES_ORD + latin_1 = "latin_1" + for _unused_i in xrange(nstrings): + nchars = local_unpack('<H', data[pos:pos+2])[0] + pos += 2 + options = local_BYTES_ORD(data[pos]) + pos += 1 + rtcount = 0 + phosz = 0 + if options & 0x08: # richtext + rtcount = local_unpack('<H', data[pos:pos+2])[0] + pos += 2 + if options & 0x04: # phonetic + phosz = local_unpack('<i', data[pos:pos+4])[0] + pos += 4 + accstrg = UNICODE_LITERAL('') + charsgot = 0 + while 1: + charsneed = nchars - charsgot + if options & 0x01: + # Uncompressed UTF-16 + charsavail = local_min((datalen - pos) >> 1, charsneed) + rawstrg = data[pos:pos+2*charsavail] + # if DEBUG: print "SST U16: nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) + try: + accstrg += unicode(rawstrg, "utf_16_le") + except: + # print "SST U16: nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) + # Probable cause: dodgy data e.g. unfinished surrogate pair. + # E.g. file unicode2.xls in pyExcelerator's examples has cells containing + # unichr(i) for i in range(0x100000) + # so this will include 0xD800 etc + raise + pos += 2*charsavail + else: + # Note: this is COMPRESSED (not ASCII!) encoding!!! + charsavail = local_min(datalen - pos, charsneed) + rawstrg = data[pos:pos+charsavail] + # if DEBUG: print "SST CMPRSD: nchars=%d pos=%d rawstrg=%r" % (nchars, pos, rawstrg) + accstrg += unicode(rawstrg, latin_1) + pos += charsavail + charsgot += charsavail + if charsgot == nchars: + break + datainx += 1 + data = datatab[datainx] + datalen = len(data) + options = local_BYTES_ORD(data[0]) + pos = 1 + + if rtcount: + runs = [] + for runindex in xrange(rtcount): + if pos == datalen: + pos = 0 + datainx += 1 + data = datatab[datainx] + datalen = len(data) + runs.append(local_unpack("<HH", data[pos:pos+4])) + pos += 4 + richtext_runs[len(strings)] = runs + + pos += phosz # size of the phonetic stuff to skip + if pos >= datalen: + # adjust to correct position in next record + pos = pos - datalen + datainx += 1 + if datainx < ndatas: + data = datatab[datainx] + datalen = len(data) + else: + assert _unused_i == nstrings - 1 + strappend(accstrg) + return strings, richtext_runs diff --git a/.venv/lib/python3.9/site-packages/xlrd/compdoc.py b/.venv/lib/python3.9/site-packages/xlrd/compdoc.py new file mode 100644 index 00000000..412a89e7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/compdoc.py @@ -0,0 +1,485 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +# No part of the content of this file was derived from the works of +# David Giffin. +""" +Implements the minimal functionality required +to extract a "Workbook" or "Book" stream (as one big string) +from an OLE2 Compound Document file. +""" +from __future__ import print_function + +import array +import sys +from struct import unpack + +from .timemachine import * + +#: Magic cookie that should appear in the first 8 bytes of the file. +SIGNATURE = b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" + +EOCSID = -2 +FREESID = -1 +SATSID = -3 +MSATSID = -4 +EVILSID = -5 + +class CompDocError(Exception): + pass + +class DirNode(object): + + def __init__(self, DID, dent, DEBUG=0, logfile=sys.stdout): + # dent is the 128-byte directory entry + self.DID = DID + self.logfile = logfile + (cbufsize, self.etype, self.colour, self.left_DID, self.right_DID, + self.root_DID) = \ + unpack('<HBBiii', dent[64:80]) + (self.first_SID, self.tot_size) = \ + unpack('<ii', dent[116:124]) + if cbufsize == 0: + self.name = UNICODE_LITERAL('') + else: + self.name = unicode(dent[0:cbufsize-2], 'utf_16_le') # omit the trailing U+0000 + self.children = [] # filled in later + self.parent = -1 # indicates orphan; fixed up later + self.tsinfo = unpack('<IIII', dent[100:116]) + if DEBUG: + self.dump(DEBUG) + + def dump(self, DEBUG=1): + fprintf( + self.logfile, + "DID=%d name=%r etype=%d DIDs(left=%d right=%d root=%d parent=%d kids=%r) first_SID=%d tot_size=%d\n", + self.DID, self.name, self.etype, self.left_DID, + self.right_DID, self.root_DID, self.parent, self.children, self.first_SID, self.tot_size + ) + if DEBUG == 2: + # cre_lo, cre_hi, mod_lo, mod_hi = tsinfo + print("timestamp info", self.tsinfo, file=self.logfile) + +def _build_family_tree(dirlist, parent_DID, child_DID): + if child_DID < 0: return + _build_family_tree(dirlist, parent_DID, dirlist[child_DID].left_DID) + dirlist[parent_DID].children.append(child_DID) + dirlist[child_DID].parent = parent_DID + _build_family_tree(dirlist, parent_DID, dirlist[child_DID].right_DID) + if dirlist[child_DID].etype == 1: # storage + _build_family_tree(dirlist, child_DID, dirlist[child_DID].root_DID) + + +class CompDoc(object): + """ + Compound document handler. + + :param mem: + The raw contents of the file, as a string, or as an :class:`mmap.mmap` + object. The only operation it needs to support is slicing. + """ + + + def __init__(self, mem, logfile=sys.stdout, DEBUG=0, ignore_workbook_corruption=False): + self.logfile = logfile + self.ignore_workbook_corruption = ignore_workbook_corruption + self.DEBUG = DEBUG + if mem[0:8] != SIGNATURE: + raise CompDocError('Not an OLE2 compound document') + if mem[28:30] != b'\xFE\xFF': + raise CompDocError('Expected "little-endian" marker, found %r' % mem[28:30]) + revision, version = unpack('<HH', mem[24:28]) + if DEBUG: + print("\nCompDoc format: version=0x%04x revision=0x%04x" % (version, revision), file=logfile) + self.mem = mem + ssz, sssz = unpack('<HH', mem[30:34]) + if ssz > 20: # allows for 2**20 bytes i.e. 1MB + print("WARNING: sector size (2**%d) is preposterous; assuming 512 and continuing ..." + % ssz, file=logfile) + ssz = 9 + if sssz > ssz: + print("WARNING: short stream sector size (2**%d) is preposterous; assuming 64 and continuing ..." + % sssz, file=logfile) + sssz = 6 + self.sec_size = sec_size = 1 << ssz + self.short_sec_size = 1 << sssz + if self.sec_size != 512 or self.short_sec_size != 64: + print("@@@@ sec_size=%d short_sec_size=%d" % (self.sec_size, self.short_sec_size), file=logfile) + ( + SAT_tot_secs, self.dir_first_sec_sid, _unused, self.min_size_std_stream, + SSAT_first_sec_sid, SSAT_tot_secs, + MSATX_first_sec_sid, MSATX_tot_secs, + ) = unpack('<iiiiiiii', mem[44:76]) + mem_data_len = len(mem) - 512 + mem_data_secs, left_over = divmod(mem_data_len, sec_size) + if left_over: + #### raise CompDocError("Not a whole number of sectors") + mem_data_secs += 1 + print("WARNING *** file size (%d) not 512 + multiple of sector size (%d)" + % (len(mem), sec_size), file=logfile) + self.mem_data_secs = mem_data_secs # use for checking later + self.mem_data_len = mem_data_len + seen = self.seen = array.array('B', [0]) * mem_data_secs + + if DEBUG: + print('sec sizes', ssz, sssz, sec_size, self.short_sec_size, file=logfile) + print("mem data: %d bytes == %d sectors" % (mem_data_len, mem_data_secs), file=logfile) + print("SAT_tot_secs=%d, dir_first_sec_sid=%d, min_size_std_stream=%d" + % (SAT_tot_secs, self.dir_first_sec_sid, self.min_size_std_stream,), file=logfile) + print("SSAT_first_sec_sid=%d, SSAT_tot_secs=%d" % (SSAT_first_sec_sid, SSAT_tot_secs,), file=logfile) + print("MSATX_first_sec_sid=%d, MSATX_tot_secs=%d" % (MSATX_first_sec_sid, MSATX_tot_secs,), file=logfile) + nent = sec_size // 4 # number of SID entries in a sector + fmt = "<%di" % nent + trunc_warned = 0 + # + # === build the MSAT === + # + MSAT = list(unpack('<109i', mem[76:512])) + SAT_sectors_reqd = (mem_data_secs + nent - 1) // nent + expected_MSATX_sectors = max(0, (SAT_sectors_reqd - 109 + nent - 2) // (nent - 1)) + actual_MSATX_sectors = 0 + if MSATX_tot_secs == 0 and MSATX_first_sec_sid in (EOCSID, FREESID, 0): + # Strictly, if there is no MSAT extension, then MSATX_first_sec_sid + # should be set to EOCSID ... FREESID and 0 have been met in the wild. + pass # Presuming no extension + else: + sid = MSATX_first_sec_sid + while sid not in (EOCSID, FREESID, MSATSID): + # Above should be only EOCSID according to MS & OOo docs + # but Excel doesn't complain about FREESID. Zero is a valid + # sector number, not a sentinel. + if DEBUG > 1: + print('MSATX: sid=%d (0x%08X)' % (sid, sid), file=logfile) + if sid >= mem_data_secs: + msg = "MSAT extension: accessing sector %d but only %d in file" % (sid, mem_data_secs) + if DEBUG > 1: + print(msg, file=logfile) + break + raise CompDocError(msg) + elif sid < 0: + raise CompDocError("MSAT extension: invalid sector id: %d" % sid) + if seen[sid]: + raise CompDocError("MSAT corruption: seen[%d] == %d" % (sid, seen[sid])) + seen[sid] = 1 + actual_MSATX_sectors += 1 + if DEBUG and actual_MSATX_sectors > expected_MSATX_sectors: + print("[1]===>>>", mem_data_secs, nent, SAT_sectors_reqd, expected_MSATX_sectors, actual_MSATX_sectors, file=logfile) + offset = 512 + sec_size * sid + MSAT.extend(unpack(fmt, mem[offset:offset+sec_size])) + sid = MSAT.pop() # last sector id is sid of next sector in the chain + + if DEBUG and actual_MSATX_sectors != expected_MSATX_sectors: + print("[2]===>>>", mem_data_secs, nent, SAT_sectors_reqd, expected_MSATX_sectors, actual_MSATX_sectors, file=logfile) + if DEBUG: + print("MSAT: len =", len(MSAT), file=logfile) + dump_list(MSAT, 10, logfile) + # + # === build the SAT === + # + self.SAT = [] + actual_SAT_sectors = 0 + dump_again = 0 + for msidx in xrange(len(MSAT)): + msid = MSAT[msidx] + if msid in (FREESID, EOCSID): + # Specification: the MSAT array may be padded with trailing FREESID entries. + # Toleration: a FREESID or EOCSID entry anywhere in the MSAT array will be ignored. + continue + if msid >= mem_data_secs: + if not trunc_warned: + print("WARNING *** File is truncated, or OLE2 MSAT is corrupt!!", file=logfile) + print("INFO: Trying to access sector %d but only %d available" + % (msid, mem_data_secs), file=logfile) + trunc_warned = 1 + MSAT[msidx] = EVILSID + dump_again = 1 + continue + elif msid < -2: + raise CompDocError("MSAT: invalid sector id: %d" % msid) + if seen[msid]: + raise CompDocError("MSAT extension corruption: seen[%d] == %d" % (msid, seen[msid])) + seen[msid] = 2 + actual_SAT_sectors += 1 + if DEBUG and actual_SAT_sectors > SAT_sectors_reqd: + print("[3]===>>>", mem_data_secs, nent, SAT_sectors_reqd, expected_MSATX_sectors, actual_MSATX_sectors, actual_SAT_sectors, msid, file=logfile) + offset = 512 + sec_size * msid + self.SAT.extend(unpack(fmt, mem[offset:offset+sec_size])) + + if DEBUG: + print("SAT: len =", len(self.SAT), file=logfile) + dump_list(self.SAT, 10, logfile) + # print >> logfile, "SAT ", + # for i, s in enumerate(self.SAT): + # print >> logfile, "entry: %4d offset: %6d, next entry: %4d" % (i, 512 + sec_size * i, s) + # print >> logfile, "%d:%d " % (i, s), + print(file=logfile) + if DEBUG and dump_again: + print("MSAT: len =", len(MSAT), file=logfile) + dump_list(MSAT, 10, logfile) + for satx in xrange(mem_data_secs, len(self.SAT)): + self.SAT[satx] = EVILSID + print("SAT: len =", len(self.SAT), file=logfile) + dump_list(self.SAT, 10, logfile) + # + # === build the directory === + # + dbytes = self._get_stream( + self.mem, 512, self.SAT, self.sec_size, self.dir_first_sec_sid, + name="directory", seen_id=3) + dirlist = [] + did = -1 + for pos in xrange(0, len(dbytes), 128): + did += 1 + dirlist.append(DirNode(did, dbytes[pos:pos+128], 0, logfile)) + self.dirlist = dirlist + _build_family_tree(dirlist, 0, dirlist[0].root_DID) # and stand well back ... + if DEBUG: + for d in dirlist: + d.dump(DEBUG) + # + # === get the SSCS === + # + sscs_dir = self.dirlist[0] + assert sscs_dir.etype == 5 # root entry + if sscs_dir.first_SID < 0 or sscs_dir.tot_size == 0: + # Problem reported by Frank Hoffsuemmer: some software was + # writing -1 instead of -2 (EOCSID) for the first_SID + # when the SCCS was empty. Not having EOCSID caused assertion + # failure in _get_stream. + # Solution: avoid calling _get_stream in any case when the + # SCSS appears to be empty. + self.SSCS = "" + else: + self.SSCS = self._get_stream( + self.mem, 512, self.SAT, sec_size, sscs_dir.first_SID, + sscs_dir.tot_size, name="SSCS", seen_id=4) + # if DEBUG: print >> logfile, "SSCS", repr(self.SSCS) + # + # === build the SSAT === + # + self.SSAT = [] + if SSAT_tot_secs > 0 and sscs_dir.tot_size == 0: + print("WARNING *** OLE2 inconsistency: SSCS size is 0 but SSAT size is non-zero", file=logfile) + if sscs_dir.tot_size > 0: + sid = SSAT_first_sec_sid + nsecs = SSAT_tot_secs + while sid >= 0 and nsecs > 0: + if seen[sid]: + raise CompDocError("SSAT corruption: seen[%d] == %d" % (sid, seen[sid])) + seen[sid] = 5 + nsecs -= 1 + start_pos = 512 + sid * sec_size + news = list(unpack(fmt, mem[start_pos:start_pos+sec_size])) + self.SSAT.extend(news) + sid = self.SAT[sid] + if DEBUG: print("SSAT last sid %d; remaining sectors %d" % (sid, nsecs), file=logfile) + assert nsecs == 0 and sid == EOCSID + if DEBUG: + print("SSAT", file=logfile) + dump_list(self.SSAT, 10, logfile) + if DEBUG: + print("seen", file=logfile) + dump_list(seen, 20, logfile) + + def _get_stream(self, mem, base, sat, sec_size, start_sid, size=None, name='', seen_id=None): + # print >> self.logfile, "_get_stream", base, sec_size, start_sid, size + sectors = [] + s = start_sid + if size is None: + # nothing to check against + while s >= 0: + if seen_id is not None: + if self.seen[s]: + raise CompDocError("%s corruption: seen[%d] == %d" % (name, s, self.seen[s])) + self.seen[s] = seen_id + start_pos = base + s * sec_size + sectors.append(mem[start_pos:start_pos+sec_size]) + try: + s = sat[s] + except IndexError: + raise CompDocError( + "OLE2 stream %r: sector allocation table invalid entry (%d)" % + (name, s) + ) + assert s == EOCSID + else: + todo = size + while s >= 0: + if seen_id is not None: + if self.seen[s]: + raise CompDocError("%s corruption: seen[%d] == %d" % (name, s, self.seen[s])) + self.seen[s] = seen_id + start_pos = base + s * sec_size + grab = sec_size + if grab > todo: + grab = todo + todo -= grab + sectors.append(mem[start_pos:start_pos+grab]) + try: + s = sat[s] + except IndexError: + raise CompDocError( + "OLE2 stream %r: sector allocation table invalid entry (%d)" % + (name, s) + ) + assert s == EOCSID + if todo != 0: + fprintf(self.logfile, + "WARNING *** OLE2 stream %r: expected size %d, actual size %d\n", + name, size, size - todo) + + return b''.join(sectors) + + def _dir_search(self, path, storage_DID=0): + # Return matching DirNode instance, or None + head = path[0] + tail = path[1:] + dl = self.dirlist + for child in dl[storage_DID].children: + if dl[child].name.lower() == head.lower(): + et = dl[child].etype + if et == 2: + return dl[child] + if et == 1: + if not tail: + raise CompDocError("Requested component is a 'storage'") + return self._dir_search(tail, child) + dl[child].dump(1) + raise CompDocError("Requested stream is not a 'user stream'") + return None + + + def get_named_stream(self, qname): + """ + Interrogate the compound document's directory; return the stream as a + string if found, otherwise return ``None``. + + :param qname: + Name of the desired stream e.g. ``'Workbook'``. + Should be in Unicode or convertible thereto. + """ + d = self._dir_search(qname.split("/")) + if d is None: + return None + if d.tot_size >= self.min_size_std_stream: + return self._get_stream( + self.mem, 512, self.SAT, self.sec_size, d.first_SID, + d.tot_size, name=qname, seen_id=d.DID+6) + else: + return self._get_stream( + self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID, + d.tot_size, name=qname + " (from SSCS)", seen_id=None) + + def locate_named_stream(self, qname): + """ + Interrogate the compound document's directory. + + If the named stream is not found, ``(None, 0, 0)`` will be returned. + + If the named stream is found and is contiguous within the original + byte sequence (``mem``) used when the document was opened, + then ``(mem, offset_to_start_of_stream, length_of_stream)`` is returned. + + Otherwise a new string is built from the fragments and + ``(new_string, 0, length_of_stream)`` is returned. + + :param qname: + Name of the desired stream e.g. ``'Workbook'``. + Should be in Unicode or convertible thereto. + """ + d = self._dir_search(qname.split("/")) + if d is None: + return (None, 0, 0) + if d.tot_size > self.mem_data_len: + raise CompDocError("%r stream length (%d bytes) > file data size (%d bytes)" + % (qname, d.tot_size, self.mem_data_len)) + if d.tot_size >= self.min_size_std_stream: + result = self._locate_stream( + self.mem, 512, self.SAT, self.sec_size, d.first_SID, + d.tot_size, qname, d.DID+6) + if self.DEBUG: + print("\nseen", file=self.logfile) + dump_list(self.seen, 20, self.logfile) + return result + else: + return ( + self._get_stream( + self.SSCS, 0, self.SSAT, self.short_sec_size, d.first_SID, + d.tot_size, qname + " (from SSCS)", None), + 0, + d.tot_size, + ) + + def _locate_stream(self, mem, base, sat, sec_size, start_sid, expected_stream_size, qname, seen_id): + # print >> self.logfile, "_locate_stream", base, sec_size, start_sid, expected_stream_size + s = start_sid + if s < 0: + raise CompDocError("_locate_stream: start_sid (%d) is -ve" % start_sid) + p = -99 # dummy previous SID + start_pos = -9999 + end_pos = -8888 + slices = [] + tot_found = 0 + found_limit = (expected_stream_size + sec_size - 1) // sec_size + while s >= 0: + if self.seen[s]: + if not self.ignore_workbook_corruption: + print("_locate_stream(%s): seen" % qname, file=self.logfile); dump_list(self.seen, 20, self.logfile) + raise CompDocError("%s corruption: seen[%d] == %d" % (qname, s, self.seen[s])) + self.seen[s] = seen_id + tot_found += 1 + if tot_found > found_limit: + # Note: expected size rounded up to higher sector + raise CompDocError( + "%s: size exceeds expected %d bytes; corrupt?" + % (qname, found_limit * sec_size) + ) + if s == p+1: + # contiguous sectors + end_pos += sec_size + else: + # start new slice + if p >= 0: + # not first time + slices.append((start_pos, end_pos)) + start_pos = base + s * sec_size + end_pos = start_pos + sec_size + p = s + s = sat[s] + assert s == EOCSID + assert tot_found == found_limit + # print >> self.logfile, "_locate_stream(%s): seen" % qname; dump_list(self.seen, 20, self.logfile) + if not slices: + # The stream is contiguous ... just what we like! + return (mem, start_pos, expected_stream_size) + slices.append((start_pos, end_pos)) + # print >> self.logfile, "+++>>> %d fragments" % len(slices) + return (b''.join(mem[start_pos:end_pos] for start_pos, end_pos in slices), 0, expected_stream_size) + +# ========================================================================================== +def x_dump_line(alist, stride, f, dpos, equal=0): + print("%5d%s" % (dpos, " ="[equal]), end=' ', file=f) + for value in alist[dpos:dpos + stride]: + print(str(value), end=' ', file=f) + print(file=f) + +def dump_list(alist, stride, f=sys.stdout): + def _dump_line(dpos, equal=0): + print("%5d%s" % (dpos, " ="[equal]), end=' ', file=f) + for value in alist[dpos:dpos + stride]: + print(str(value), end=' ', file=f) + print(file=f) + pos = None + oldpos = None + for pos in xrange(0, len(alist), stride): + if oldpos is None: + _dump_line(pos) + oldpos = pos + elif alist[pos:pos+stride] != alist[oldpos:oldpos+stride]: + if pos - oldpos > stride: + _dump_line(pos - stride, equal=1) + _dump_line(pos) + oldpos = pos + if oldpos is not None and pos is not None and pos != oldpos: + _dump_line(pos, equal=1) diff --git a/.venv/lib/python3.9/site-packages/xlrd/formatting.py b/.venv/lib/python3.9/site-packages/xlrd/formatting.py new file mode 100644 index 00000000..ca637b81 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/formatting.py @@ -0,0 +1,1324 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +# No part of the content of this file was derived from the works of +# David Giffin. +""" +Module for formatting information. +""" + +from __future__ import print_function + +import re +from struct import unpack + +from .biffh import ( + FDT, FGE, FNU, FTX, FUN, XL_CELL_DATE, XL_CELL_NUMBER, XL_CELL_TEXT, + XL_FORMAT, XL_FORMAT2, BaseObject, XLRDError, fprintf, unpack_string, + unpack_unicode, upkbits, upkbitsL, +) +from .timemachine import * + +DEBUG = 0 + +_cellty_from_fmtty = { + FNU: XL_CELL_NUMBER, + FUN: XL_CELL_NUMBER, + FGE: XL_CELL_NUMBER, + FDT: XL_CELL_DATE, + FTX: XL_CELL_NUMBER, # Yes, a number can be formatted as text. +} + +excel_default_palette_b5 = ( + ( 0, 0, 0), (255, 255, 255), (255, 0, 0), ( 0, 255, 0), + ( 0, 0, 255), (255, 255, 0), (255, 0, 255), ( 0, 255, 255), + (128, 0, 0), ( 0, 128, 0), ( 0, 0, 128), (128, 128, 0), + (128, 0, 128), ( 0, 128, 128), (192, 192, 192), (128, 128, 128), + (153, 153, 255), (153, 51, 102), (255, 255, 204), (204, 255, 255), + (102, 0, 102), (255, 128, 128), ( 0, 102, 204), (204, 204, 255), + ( 0, 0, 128), (255, 0, 255), (255, 255, 0), ( 0, 255, 255), + (128, 0, 128), (128, 0, 0), ( 0, 128, 128), ( 0, 0, 255), + ( 0, 204, 255), (204, 255, 255), (204, 255, 204), (255, 255, 153), + (153, 204, 255), (255, 153, 204), (204, 153, 255), (227, 227, 227), + ( 51, 102, 255), ( 51, 204, 204), (153, 204, 0), (255, 204, 0), + (255, 153, 0), (255, 102, 0), (102, 102, 153), (150, 150, 150), + ( 0, 51, 102), ( 51, 153, 102), ( 0, 51, 0), ( 51, 51, 0), + (153, 51, 0), (153, 51, 102), ( 51, 51, 153), ( 51, 51, 51), +) + +excel_default_palette_b2 = excel_default_palette_b5[:16] + +# Following table borrowed from Gnumeric 1.4 source. +# Checked against OOo docs and MS docs. +excel_default_palette_b8 = ( # (red, green, blue) + ( 0, 0, 0), (255,255,255), (255, 0, 0), ( 0,255, 0), # 0 + ( 0, 0,255), (255,255, 0), (255, 0,255), ( 0,255,255), # 4 + (128, 0, 0), ( 0,128, 0), ( 0, 0,128), (128,128, 0), # 8 + (128, 0,128), ( 0,128,128), (192,192,192), (128,128,128), # 12 + (153,153,255), (153, 51,102), (255,255,204), (204,255,255), # 16 + (102, 0,102), (255,128,128), ( 0,102,204), (204,204,255), # 20 + ( 0, 0,128), (255, 0,255), (255,255, 0), ( 0,255,255), # 24 + (128, 0,128), (128, 0, 0), ( 0,128,128), ( 0, 0,255), # 28 + ( 0,204,255), (204,255,255), (204,255,204), (255,255,153), # 32 + (153,204,255), (255,153,204), (204,153,255), (255,204,153), # 36 + ( 51,102,255), ( 51,204,204), (153,204, 0), (255,204, 0), # 40 + (255,153, 0), (255,102, 0), (102,102,153), (150,150,150), # 44 + ( 0, 51,102), ( 51,153,102), ( 0, 51, 0), ( 51, 51, 0), # 48 + (153, 51, 0), (153, 51,102), ( 51, 51,153), ( 51, 51, 51), # 52 +) + +default_palette = { + 80: excel_default_palette_b8, + 70: excel_default_palette_b5, + 50: excel_default_palette_b5, + 45: excel_default_palette_b2, + 40: excel_default_palette_b2, + 30: excel_default_palette_b2, + 21: excel_default_palette_b2, + 20: excel_default_palette_b2, +} + +# 00H = Normal +# 01H = RowLevel_lv (see next field) +# 02H = ColLevel_lv (see next field) +# 03H = Comma +# 04H = Currency +# 05H = Percent +# 06H = Comma [0] (BIFF4-BIFF8) +# 07H = Currency [0] (BIFF4-BIFF8) +# 08H = Hyperlink (BIFF8) +# 09H = Followed Hyperlink (BIFF8) +built_in_style_names = [ + "Normal", + "RowLevel_", + "ColLevel_", + "Comma", + "Currency", + "Percent", + "Comma [0]", + "Currency [0]", + "Hyperlink", + "Followed Hyperlink", +] + +def initialise_colour_map(book): + book.colour_map = {} + book.colour_indexes_used = {} + if not book.formatting_info: + return + # Add the 8 invariant colours + for i in xrange(8): + book.colour_map[i] = excel_default_palette_b8[i] + # Add the default palette depending on the version + dpal = default_palette[book.biff_version] + ndpal = len(dpal) + for i in xrange(ndpal): + book.colour_map[i+8] = dpal[i] + # Add the specials -- None means the RGB value is not known + # System window text colour for border lines + book.colour_map[ndpal+8] = None + # System window background colour for pattern background + book.colour_map[ndpal+8+1] = None + # System ToolTip text colour (used in note objects) + book.colour_map[0x51] = None + # 32767, system window text colour for fonts + book.colour_map[0x7FFF] = None + + +def nearest_colour_index(colour_map, rgb, debug=0): + """ + General purpose function. Uses Euclidean distance. + So far used only for pre-BIFF8 ``WINDOW2`` record. + Doesn't have to be fast. + Doesn't have to be fancy. + """ + best_metric = 3 * 256 * 256 + best_colourx = 0 + for colourx, cand_rgb in colour_map.items(): + if cand_rgb is None: + continue + metric = 0 + for v1, v2 in zip(rgb, cand_rgb): + metric += (v1 - v2) * (v1 - v2) + if metric < best_metric: + best_metric = metric + best_colourx = colourx + if metric == 0: + break + if 0 and debug: + print("nearest_colour_index for %r is %r -> %r; best_metric is %d" + % (rgb, best_colourx, colour_map[best_colourx], best_metric)) + return best_colourx + +class EqNeAttrs(object): + """ + This mixin class exists solely so that :class:`Format`, :class:`Font`, and + :class:`XF` objects can be compared by value of their attributes. + """ + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + return self.__dict__ != other.__dict__ + +class Font(BaseObject, EqNeAttrs): + """ + An Excel "font" contains the details of not only what is normally + considered a font, but also several other display attributes. + Items correspond to those in the Excel UI's Format -> Cells -> Font tab. + + .. versionadded:: 0.6.1 + """ + + #: 1 = Characters are bold. Redundant; see "weight" attribute. + bold = 0 + + #: Values: + #: :: + #: + #: 0 = ANSI Latin + #: 1 = System default + #: 2 = Symbol, + #: 77 = Apple Roman, + #: 128 = ANSI Japanese Shift-JIS, + #: 129 = ANSI Korean (Hangul), + #: 130 = ANSI Korean (Johab), + #: 134 = ANSI Chinese Simplified GBK, + #: 136 = ANSI Chinese Traditional BIG5, + #: 161 = ANSI Greek, + #: 162 = ANSI Turkish, + #: 163 = ANSI Vietnamese, + #: 177 = ANSI Hebrew, + #: 178 = ANSI Arabic, + #: 186 = ANSI Baltic, + #: 204 = ANSI Cyrillic, + #: 222 = ANSI Thai, + #: 238 = ANSI Latin II (Central European), + #: 255 = OEM Latin I + character_set = 0 + + #: An explanation of "colour index" is given in :ref:`palette`. + colour_index = 0 + + #: 1 = Superscript, 2 = Subscript. + escapement = 0 + + #: Values: + #: :: + #: + #: 0 = None (unknown or don't care) + #: 1 = Roman (variable width, serifed) + #: 2 = Swiss (variable width, sans-serifed) + #: 3 = Modern (fixed width, serifed or sans-serifed) + #: 4 = Script (cursive) + #: 5 = Decorative (specialised, for example Old English, Fraktur) + family = 0 + + #: The 0-based index used to refer to this Font() instance. + #: Note that index 4 is never used; xlrd supplies a dummy place-holder. + font_index = 0 + + #: Height of the font (in twips). A twip = 1/20 of a point. + height = 0 + + #: 1 = Characters are italic. + italic = 0 + + #: The name of the font. Example: ``"Arial"``. + name = UNICODE_LITERAL("") + + #: 1 = Characters are struck out. + struck_out = 0 + + #: Values: + #: :: + #: + #: 0 = None + #: 1 = Single; 0x21 (33) = Single accounting + #: 2 = Double; 0x22 (34) = Double accounting + underline_type = 0 + + #: 1 = Characters are underlined. Redundant; see + #: :attr:`underline_type` attribute. + underlined = 0 + + #: Font weight (100-1000). Standard values are 400 for normal text + #: and 700 for bold text. + weight = 400 + + #: 1 = Font is outline style (Macintosh only) + outline = 0 + + #: 1 = Font is shadow style (Macintosh only) + shadow = 0 + +def handle_efont(book, data): # BIFF2 only + if not book.formatting_info: + return + book.font_list[-1].colour_index = unpack('<H', data)[0] + +def handle_font(book, data): + if not book.formatting_info: + return + if not book.encoding: + book.derive_encoding() + blah = DEBUG or book.verbosity >= 2 + bv = book.biff_version + k = len(book.font_list) + if k == 4: + f = Font() + f.name = UNICODE_LITERAL('Dummy Font') + f.font_index = k + book.font_list.append(f) + k += 1 + f = Font() + f.font_index = k + book.font_list.append(f) + if bv >= 50: + ( + f.height, option_flags, f.colour_index, f.weight, + f.escapement, f.underline_type, f.family, + f.character_set, + ) = unpack('<HHHHHBBB', data[0:13]) + f.bold = option_flags & 1 + f.italic = (option_flags & 2) >> 1 + f.underlined = (option_flags & 4) >> 2 + f.struck_out = (option_flags & 8) >> 3 + f.outline = (option_flags & 16) >> 4 + f.shadow = (option_flags & 32) >> 5 + if bv >= 80: + f.name = unpack_unicode(data, 14, lenlen=1) + else: + f.name = unpack_string(data, 14, book.encoding, lenlen=1) + elif bv >= 30: + f.height, option_flags, f.colour_index = unpack('<HHH', data[0:6]) + f.bold = option_flags & 1 + f.italic = (option_flags & 2) >> 1 + f.underlined = (option_flags & 4) >> 2 + f.struck_out = (option_flags & 8) >> 3 + f.outline = (option_flags & 16) >> 4 + f.shadow = (option_flags & 32) >> 5 + f.name = unpack_string(data, 6, book.encoding, lenlen=1) + # Now cook up the remaining attributes ... + f.weight = [400, 700][f.bold] + f.escapement = 0 # None + f.underline_type = f.underlined # None or Single + f.family = 0 # Unknown / don't care + f.character_set = 1 # System default (0 means "ANSI Latin") + else: # BIFF2 + f.height, option_flags = unpack('<HH', data[0:4]) + f.colour_index = 0x7FFF # "system window text colour" + f.bold = option_flags & 1 + f.italic = (option_flags & 2) >> 1 + f.underlined = (option_flags & 4) >> 2 + f.struck_out = (option_flags & 8) >> 3 + f.outline = 0 + f.shadow = 0 + f.name = unpack_string(data, 4, book.encoding, lenlen=1) + # Now cook up the remaining attributes ... + f.weight = [400, 700][f.bold] + f.escapement = 0 # None + f.underline_type = f.underlined # None or Single + f.family = 0 # Unknown / don't care + f.character_set = 1 # System default (0 means "ANSI Latin") + if blah: + f.dump( + book.logfile, + header="--- handle_font: font[%d] ---" % f.font_index, + footer="-------------------", + ) + +# === "Number formats" === + +class Format(BaseObject, EqNeAttrs): + """ + "Number format" information from a ``FORMAT`` record. + + .. versionadded:: 0.6.1 + """ + + #: The key into :attr:`~xlrd.book.Book.format_map` + format_key = 0 + + #: A classification that has been inferred from the format string. + #: Currently, this is used only to distinguish between numbers and dates. + #: Values:: + #: + #: FUN = 0 # unknown + #: FDT = 1 # date + #: FNU = 2 # number + #: FGE = 3 # general + #: FTX = 4 # text + type = FUN + + #: The format string + format_str = UNICODE_LITERAL('') + + def __init__(self, format_key, ty, format_str): + self.format_key = format_key + self.type = ty + self.format_str = format_str + +std_format_strings = { + # "std" == "standard for US English locale" + # #### TODO ... a lot of work to tailor these to the user's locale. + # See e.g. gnumeric-1.x.y/src/formats.c + 0x00: "General", + 0x01: "0", + 0x02: "0.00", + 0x03: "#,##0", + 0x04: "#,##0.00", + 0x05: "$#,##0_);($#,##0)", + 0x06: "$#,##0_);[Red]($#,##0)", + 0x07: "$#,##0.00_);($#,##0.00)", + 0x08: "$#,##0.00_);[Red]($#,##0.00)", + 0x09: "0%", + 0x0a: "0.00%", + 0x0b: "0.00E+00", + 0x0c: "# ?/?", + 0x0d: "# ??/??", + 0x0e: "m/d/yy", + 0x0f: "d-mmm-yy", + 0x10: "d-mmm", + 0x11: "mmm-yy", + 0x12: "h:mm AM/PM", + 0x13: "h:mm:ss AM/PM", + 0x14: "h:mm", + 0x15: "h:mm:ss", + 0x16: "m/d/yy h:mm", + 0x25: "#,##0_);(#,##0)", + 0x26: "#,##0_);[Red](#,##0)", + 0x27: "#,##0.00_);(#,##0.00)", + 0x28: "#,##0.00_);[Red](#,##0.00)", + 0x29: "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", + 0x2a: "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)", + 0x2b: "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", + 0x2c: "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)", + 0x2d: "mm:ss", + 0x2e: "[h]:mm:ss", + 0x2f: "mm:ss.0", + 0x30: "##0.0E+0", + 0x31: "@", +} + +fmt_code_ranges = [ # both-inclusive ranges of "standard" format codes + # Source: the openoffice.org doc't + # and the OOXML spec Part 4, section 3.8.30 + ( 0, 0, FGE), + ( 1, 13, FNU), + (14, 22, FDT), + (27, 36, FDT), # CJK date formats + (37, 44, FNU), + (45, 47, FDT), + (48, 48, FNU), + (49, 49, FTX), + # Gnumeric assumes (or assumed) that built-in formats finish at 49, not at 163 + (50, 58, FDT), # CJK date formats + (59, 62, FNU), # Thai number (currency?) formats + (67, 70, FNU), # Thai number (currency?) formats + (71, 81, FDT), # Thai date formats +] + +std_format_code_types = {} +for lo, hi, ty in fmt_code_ranges: + for x in xrange(lo, hi+1): + std_format_code_types[x] = ty +del lo, hi, ty, x + +date_chars = UNICODE_LITERAL('ymdhs') # year, month/minute, day, hour, second +date_char_dict = {} +for _c in date_chars + date_chars.upper(): + date_char_dict[_c] = 5 +del _c, date_chars + +skip_char_dict = {} +for _c in UNICODE_LITERAL('$-+/(): '): + skip_char_dict[_c] = 1 + +num_char_dict = { + UNICODE_LITERAL('0'): 5, + UNICODE_LITERAL('#'): 5, + UNICODE_LITERAL('?'): 5, +} + +non_date_formats = { + UNICODE_LITERAL('0.00E+00'):1, + UNICODE_LITERAL('##0.0E+0'):1, + UNICODE_LITERAL('General') :1, + UNICODE_LITERAL('GENERAL') :1, # OOo Calc 1.1.4 does this. + UNICODE_LITERAL('general') :1, # pyExcelerator 0.6.3 does this. + UNICODE_LITERAL('@') :1, +} + +fmt_bracketed_sub = re.compile(r'\[[^]]*\]').sub + +# Boolean format strings (actual cases) +# '"Yes";"Yes";"No"' +# '"True";"True";"False"' +# '"On";"On";"Off"' + +def is_date_format_string(book, fmt): + # Heuristics: + # Ignore "text" and [stuff in square brackets (aarrgghh -- see below)]. + # Handle backslashed-escaped chars properly. + # E.g. hh\hmm\mss\s should produce a display like 23h59m59s + # Date formats have one or more of ymdhs (caseless) in them. + # Numeric formats have # and 0. + # N.B. 'General"."' hence get rid of "text" first. + # TODO: Find where formats are interpreted in Gnumeric + # TODO: '[h]\\ \\h\\o\\u\\r\\s' ([h] means don't care about hours > 23) + state = 0 + s = '' + + for c in fmt: + if state == 0: + if c == UNICODE_LITERAL('"'): + state = 1 + elif c in UNICODE_LITERAL(r"\_*"): + state = 2 + elif c in skip_char_dict: + pass + else: + s += c + elif state == 1: + if c == UNICODE_LITERAL('"'): + state = 0 + elif state == 2: + # Ignore char after backslash, underscore or asterisk + state = 0 + assert 0 <= state <= 2 + if book.verbosity >= 4: + print("is_date_format_string: reduced format is %s" % REPR(s), file=book.logfile) + s = fmt_bracketed_sub('', s) + if s in non_date_formats: + return False + state = 0 + separator = ";" + got_sep = 0 + date_count = num_count = 0 + for c in s: + if c in date_char_dict: + date_count += date_char_dict[c] + elif c in num_char_dict: + num_count += num_char_dict[c] + elif c == separator: + got_sep = 1 + # print num_count, date_count, repr(fmt) + if date_count and not num_count: + return True + if num_count and not date_count: + return False + if date_count: + if book.verbosity: + fprintf(book.logfile, + 'WARNING *** is_date_format: ambiguous d=%d n=%d fmt=%r\n', + date_count, num_count, fmt) + elif not got_sep: + if book.verbosity: + fprintf(book.logfile, + "WARNING *** format %r produces constant result\n", + fmt) + return date_count > num_count + +def handle_format(self, data, rectype=XL_FORMAT): + DEBUG = 0 + bv = self.biff_version + if rectype == XL_FORMAT2: + bv = min(bv, 30) + if not self.encoding: + self.derive_encoding() + strpos = 2 + if bv >= 50: + fmtkey = unpack('<H', data[0:2])[0] + else: + fmtkey = self.actualfmtcount + if bv <= 30: + strpos = 0 + self.actualfmtcount += 1 + if bv >= 80: + unistrg = unpack_unicode(data, 2) + else: + unistrg = unpack_string(data, strpos, self.encoding, lenlen=1) + blah = DEBUG or self.verbosity >= 3 + if blah: + fprintf(self.logfile, + "FORMAT: count=%d fmtkey=0x%04x (%d) s=%r\n", + self.actualfmtcount, fmtkey, fmtkey, unistrg) + is_date_s = self.is_date_format_string(unistrg) + ty = [FGE, FDT][is_date_s] + if not(fmtkey > 163 or bv < 50): + # user_defined if fmtkey > 163 + # N.B. Gnumeric incorrectly starts these at 50 instead of 164 :-( + # if earlier than BIFF 5, standard info is useless + std_ty = std_format_code_types.get(fmtkey, FUN) + # print "std ty", std_ty + is_date_c = std_ty == FDT + if self.verbosity and 0 < fmtkey < 50 and (is_date_c ^ is_date_s): + DEBUG = 2 + fprintf(self.logfile, + "WARNING *** Conflict between " + "std format key %d and its format string %r\n", + fmtkey, unistrg) + if DEBUG == 2: + fprintf(self.logfile, + "ty: %d; is_date_c: %r; is_date_s: %r; fmt_strg: %r", + ty, is_date_c, is_date_s, unistrg) + fmtobj = Format(fmtkey, ty, unistrg) + if blah: + fmtobj.dump(self.logfile, + header="--- handle_format [%d] ---" % (self.actualfmtcount-1, )) + self.format_map[fmtkey] = fmtobj + self.format_list.append(fmtobj) + +# ============================================================================= + +def handle_palette(book, data): + if not book.formatting_info: + return + blah = DEBUG or book.verbosity >= 2 + n_colours, = unpack('<H', data[:2]) + expected_n_colours = (16, 56)[book.biff_version >= 50] + if (DEBUG or book.verbosity >= 1) and n_colours != expected_n_colours: + fprintf(book.logfile, + "NOTE *** Expected %d colours in PALETTE record, found %d\n", + expected_n_colours, n_colours) + elif blah: + fprintf(book.logfile, + "PALETTE record with %d colours\n", n_colours) + fmt = '<xx%di' % n_colours # use i to avoid long integers + expected_size = 4 * n_colours + 2 + actual_size = len(data) + tolerance = 4 + if not expected_size <= actual_size <= expected_size + tolerance: + raise XLRDError('PALETTE record: expected size %d, actual size %d' % (expected_size, actual_size)) + colours = unpack(fmt, data[:expected_size]) + assert book.palette_record == [] # There should be only 1 PALETTE record + # a colour will be 0xbbggrr + # IOW, red is at the little end + for i in xrange(n_colours): + c = colours[i] + red = c & 0xff + green = (c >> 8) & 0xff + blue = (c >> 16) & 0xff + old_rgb = book.colour_map[8+i] + new_rgb = (red, green, blue) + book.palette_record.append(new_rgb) + book.colour_map[8+i] = new_rgb + if blah: + if new_rgb != old_rgb: + print("%2d: %r -> %r" % (i, old_rgb, new_rgb), file=book.logfile) + +def palette_epilogue(book): + # Check colour indexes in fonts etc. + # This must be done here as FONT records + # come *before* the PALETTE record :-( + for font in book.font_list: + if font.font_index == 4: # the missing font record + continue + cx = font.colour_index + if cx == 0x7fff: # system window text colour + continue + if cx in book.colour_map: + book.colour_indexes_used[cx] = 1 + elif book.verbosity: + print("Size of colour table:", len(book.colour_map), file=book.logfile) + fprintf(book.logfile, "*** Font #%d (%r): colour index 0x%04x is unknown\n", + font.font_index, font.name, cx) + if book.verbosity >= 1: + used = sorted(book.colour_indexes_used.keys()) + print("\nColour indexes used:\n%r\n" % used, file=book.logfile) + +def handle_style(book, data): + if not book.formatting_info: + return + blah = DEBUG or book.verbosity >= 2 + bv = book.biff_version + flag_and_xfx, built_in_id, level = unpack('<HBB', data[:4]) + xf_index = flag_and_xfx & 0x0fff + if data == b"\0\0\0\0" and "Normal" not in book.style_name_map: + # Erroneous record (doesn't have built-in bit set). + # Example file supplied by Jeff Bell. + built_in = 1 + built_in_id = 0 + xf_index = 0 + name = "Normal" + level = 255 + elif flag_and_xfx & 0x8000: + # built-in style + built_in = 1 + name = built_in_style_names[built_in_id] + if 1 <= built_in_id <= 2: + name += str(level + 1) + else: + # user-defined style + built_in = 0 + built_in_id = 0 + level = 0 + if bv >= 80: + try: + name = unpack_unicode(data, 2, lenlen=2) + except UnicodeDecodeError: + print("STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d" + % (built_in, xf_index, built_in_id, level), file=book.logfile) + print("raw bytes:", repr(data[2:]), file=book.logfile) + raise + else: + name = unpack_string(data, 2, book.encoding, lenlen=1) + if blah and not name: + print("WARNING *** A user-defined style has a zero-length name", file=book.logfile) + book.style_name_map[name] = (built_in, xf_index) + if blah: + fprintf(book.logfile, "STYLE: built_in=%d xf_index=%d built_in_id=%d level=%d name=%r\n", + built_in, xf_index, built_in_id, level, name) + +def check_colour_indexes_in_obj(book, obj, orig_index): + alist = sorted(obj.__dict__.items()) + for attr, nobj in alist: + if hasattr(nobj, 'dump'): + check_colour_indexes_in_obj(book, nobj, orig_index) + elif attr.find('colour_index') >= 0: + if nobj in book.colour_map: + book.colour_indexes_used[nobj] = 1 + continue + oname = obj.__class__.__name__ + print("*** xf #%d : %s.%s = 0x%04x (unknown)" + % (orig_index, oname, attr, nobj), file=book.logfile) + +def fill_in_standard_formats(book): + for x in std_format_code_types.keys(): + if x not in book.format_map: + ty = std_format_code_types[x] + # Note: many standard format codes (mostly CJK date formats) have + # format strings that vary by locale; xlrd does not (yet) + # handle those; the type (date or numeric) is recorded but the fmt_str will be None. + fmt_str = std_format_strings.get(x) + fmtobj = Format(x, ty, fmt_str) + book.format_map[x] = fmtobj + +def handle_xf(self, data): + # self is a Book instance + # DEBUG = 0 + blah = DEBUG or self.verbosity >= 3 + bv = self.biff_version + xf = XF() + xf.alignment = XFAlignment() + xf.alignment.indent_level = 0 + xf.alignment.shrink_to_fit = 0 + xf.alignment.text_direction = 0 + xf.border = XFBorder() + xf.border.diag_up = 0 + xf.border.diag_down = 0 + xf.border.diag_colour_index = 0 + xf.border.diag_line_style = 0 # no line + xf.background = XFBackground() + xf.protection = XFProtection() + # fill in the known standard formats + if bv >= 50 and not self.xfcount: + # i.e. do this once before we process the first XF record + fill_in_standard_formats(self) + if bv >= 80: + unpack_fmt = '<HHHBBBBIiH' + ( + xf.font_index, xf.format_key, pkd_type_par, + pkd_align1, xf.alignment.rotation, pkd_align2, + pkd_used, pkd_brdbkg1, pkd_brdbkg2, pkd_brdbkg3, + ) = unpack(unpack_fmt, data[0:20]) + upkbits(xf.protection, pkd_type_par, ( + (0, 0x01, 'cell_locked'), + (1, 0x02, 'formula_hidden'), + )) + upkbits(xf, pkd_type_par, ( + (2, 0x0004, 'is_style'), + # Following is not in OOo docs, but is mentioned + # in Gnumeric source and also in (deep breath) + # org.apache.poi.hssf.record.ExtendedFormatRecord.java + (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known. + (4, 0xFFF0, 'parent_style_index'), + )) + upkbits(xf.alignment, pkd_align1, ( + (0, 0x07, 'hor_align'), + (3, 0x08, 'text_wrapped'), + (4, 0x70, 'vert_align'), + )) + upkbits(xf.alignment, pkd_align2, ( + (0, 0x0f, 'indent_level'), + (4, 0x10, 'shrink_to_fit'), + (6, 0xC0, 'text_direction'), + )) + reg = pkd_used >> 2 + attr_stems = [ + 'format', + 'font', + 'alignment', + 'border', + 'background', + 'protection', + ] + for attr_stem in attr_stems: + attr = "_" + attr_stem + "_flag" + setattr(xf, attr, reg & 1) + reg >>= 1 + upkbitsL(xf.border, pkd_brdbkg1, ( + (0, 0x0000000f, 'left_line_style'), + (4, 0x000000f0, 'right_line_style'), + (8, 0x00000f00, 'top_line_style'), + (12, 0x0000f000, 'bottom_line_style'), + (16, 0x007f0000, 'left_colour_index'), + (23, 0x3f800000, 'right_colour_index'), + (30, 0x40000000, 'diag_down'), + (31, 0x80000000, 'diag_up'), + )) + upkbits(xf.border, pkd_brdbkg2, ( + (0, 0x0000007F, 'top_colour_index'), + (7, 0x00003F80, 'bottom_colour_index'), + (14, 0x001FC000, 'diag_colour_index'), + (21, 0x01E00000, 'diag_line_style'), + )) + upkbitsL(xf.background, pkd_brdbkg2, ( + (26, 0xFC000000, 'fill_pattern'), + )) + upkbits(xf.background, pkd_brdbkg3, ( + (0, 0x007F, 'pattern_colour_index'), + (7, 0x3F80, 'background_colour_index'), + )) + elif bv >= 50: + unpack_fmt = '<HHHBBIi' + ( + xf.font_index, xf.format_key, pkd_type_par, + pkd_align1, pkd_orient_used, + pkd_brdbkg1, pkd_brdbkg2, + ) = unpack(unpack_fmt, data[0:16]) + upkbits(xf.protection, pkd_type_par, ( + (0, 0x01, 'cell_locked'), + (1, 0x02, 'formula_hidden'), + )) + upkbits(xf, pkd_type_par, ( + (2, 0x0004, 'is_style'), + (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known. + (4, 0xFFF0, 'parent_style_index'), + )) + upkbits(xf.alignment, pkd_align1, ( + (0, 0x07, 'hor_align'), + (3, 0x08, 'text_wrapped'), + (4, 0x70, 'vert_align'), + )) + orientation = pkd_orient_used & 0x03 + xf.alignment.rotation = [0, 255, 90, 180][orientation] + reg = pkd_orient_used >> 2 + attr_stems = [ + 'format', + 'font', + 'alignment', + 'border', + 'background', + 'protection', + ] + for attr_stem in attr_stems: + attr = "_" + attr_stem + "_flag" + setattr(xf, attr, reg & 1) + reg >>= 1 + upkbitsL(xf.background, pkd_brdbkg1, ( + ( 0, 0x0000007F, 'pattern_colour_index'), + ( 7, 0x00003F80, 'background_colour_index'), + (16, 0x003F0000, 'fill_pattern'), + )) + upkbitsL(xf.border, pkd_brdbkg1, ( + (22, 0x01C00000, 'bottom_line_style'), + (25, 0xFE000000, 'bottom_colour_index'), + )) + upkbits(xf.border, pkd_brdbkg2, ( + ( 0, 0x00000007, 'top_line_style'), + ( 3, 0x00000038, 'left_line_style'), + ( 6, 0x000001C0, 'right_line_style'), + ( 9, 0x0000FE00, 'top_colour_index'), + (16, 0x007F0000, 'left_colour_index'), + (23, 0x3F800000, 'right_colour_index'), + )) + elif bv >= 40: + unpack_fmt = '<BBHBBHI' + ( + xf.font_index, xf.format_key, pkd_type_par, + pkd_align_orient, pkd_used, + pkd_bkg_34, pkd_brd_34, + ) = unpack(unpack_fmt, data[0:12]) + upkbits(xf.protection, pkd_type_par, ( + (0, 0x01, 'cell_locked'), + (1, 0x02, 'formula_hidden'), + )) + upkbits(xf, pkd_type_par, ( + (2, 0x0004, 'is_style'), + (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known. + (4, 0xFFF0, 'parent_style_index'), + )) + upkbits(xf.alignment, pkd_align_orient, ( + (0, 0x07, 'hor_align'), + (3, 0x08, 'text_wrapped'), + (4, 0x30, 'vert_align'), + )) + orientation = (pkd_align_orient & 0xC0) >> 6 + xf.alignment.rotation = [0, 255, 90, 180][orientation] + reg = pkd_used >> 2 + attr_stems = [ + 'format', + 'font', + 'alignment', + 'border', + 'background', + 'protection', + ] + for attr_stem in attr_stems: + attr = "_" + attr_stem + "_flag" + setattr(xf, attr, reg & 1) + reg >>= 1 + upkbits(xf.background, pkd_bkg_34, ( + ( 0, 0x003F, 'fill_pattern'), + ( 6, 0x07C0, 'pattern_colour_index'), + (11, 0xF800, 'background_colour_index'), + )) + upkbitsL(xf.border, pkd_brd_34, ( + ( 0, 0x00000007, 'top_line_style'), + ( 3, 0x000000F8, 'top_colour_index'), + ( 8, 0x00000700, 'left_line_style'), + (11, 0x0000F800, 'left_colour_index'), + (16, 0x00070000, 'bottom_line_style'), + (19, 0x00F80000, 'bottom_colour_index'), + (24, 0x07000000, 'right_line_style'), + (27, 0xF8000000, 'right_colour_index'), + )) + elif bv == 30: + unpack_fmt = '<BBBBHHI' + ( + xf.font_index, xf.format_key, pkd_type_prot, + pkd_used, pkd_align_par, + pkd_bkg_34, pkd_brd_34, + ) = unpack(unpack_fmt, data[0:12]) + upkbits(xf.protection, pkd_type_prot, ( + (0, 0x01, 'cell_locked'), + (1, 0x02, 'formula_hidden'), + )) + upkbits(xf, pkd_type_prot, ( + (2, 0x0004, 'is_style'), + (3, 0x0008, 'lotus_123_prefix'), # Meaning is not known. + )) + upkbits(xf.alignment, pkd_align_par, ( + (0, 0x07, 'hor_align'), + (3, 0x08, 'text_wrapped'), + )) + upkbits(xf, pkd_align_par, ( + (4, 0xFFF0, 'parent_style_index'), + )) + reg = pkd_used >> 2 + attr_stems = [ + 'format', + 'font', + 'alignment', + 'border', + 'background', + 'protection', + ] + for attr_stem in attr_stems: + attr = "_" + attr_stem + "_flag" + setattr(xf, attr, reg & 1) + reg >>= 1 + upkbits(xf.background, pkd_bkg_34, ( + ( 0, 0x003F, 'fill_pattern'), + ( 6, 0x07C0, 'pattern_colour_index'), + (11, 0xF800, 'background_colour_index'), + )) + upkbitsL(xf.border, pkd_brd_34, ( + ( 0, 0x00000007, 'top_line_style'), + ( 3, 0x000000F8, 'top_colour_index'), + ( 8, 0x00000700, 'left_line_style'), + (11, 0x0000F800, 'left_colour_index'), + (16, 0x00070000, 'bottom_line_style'), + (19, 0x00F80000, 'bottom_colour_index'), + (24, 0x07000000, 'right_line_style'), + (27, 0xF8000000, 'right_colour_index'), + )) + xf.alignment.vert_align = 2 # bottom + xf.alignment.rotation = 0 + elif bv == 21: + ## Warning: incomplete treatment; formatting_info not fully supported. + ## Probably need to offset incoming BIFF2 XF[n] to BIFF8-like XF[n+16], + ## and create XF[0:16] like the standard ones in BIFF8 *AND* add 16 to + ## all XF references in cell records :-( + (xf.font_index, format_etc, halign_etc) = unpack('<BxBB', data) + xf.format_key = format_etc & 0x3F + upkbits(xf.protection, format_etc, ( + (6, 0x40, 'cell_locked'), + (7, 0x80, 'formula_hidden'), + )) + upkbits(xf.alignment, halign_etc, ( + (0, 0x07, 'hor_align'), + )) + for mask, side in ((0x08, 'left'), (0x10, 'right'), (0x20, 'top'), (0x40, 'bottom')): + if halign_etc & mask: + colour_index, line_style = 8, 1 # black, thin + else: + colour_index, line_style = 0, 0 # none, none + setattr(xf.border, side + '_colour_index', colour_index) + setattr(xf.border, side + '_line_style', line_style) + bg = xf.background + if halign_etc & 0x80: + bg.fill_pattern = 17 + else: + bg.fill_pattern = 0 + bg.background_colour_index = 9 # white + bg.pattern_colour_index = 8 # black + xf.parent_style_index = 0 # ??????????? + xf.alignment.vert_align = 2 # bottom + xf.alignment.rotation = 0 + attr_stems = [ + 'format', + 'font', + 'alignment', + 'border', + 'background', + 'protection', + ] + for attr_stem in attr_stems: + attr = "_" + attr_stem + "_flag" + setattr(xf, attr, 1) + else: + raise XLRDError('programmer stuff-up: bv=%d' % bv) + + xf.xf_index = len(self.xf_list) + self.xf_list.append(xf) + self.xfcount += 1 + if blah: + xf.dump( + self.logfile, + header="--- handle_xf: xf[%d] ---" % xf.xf_index, + footer=" ", + ) + try: + fmt = self.format_map[xf.format_key] + cellty = _cellty_from_fmtty[fmt.type] + except KeyError: + cellty = XL_CELL_NUMBER + self._xf_index_to_xl_type_map[xf.xf_index] = cellty + + # Now for some assertions ... + if self.formatting_info: + if self.verbosity and xf.is_style and xf.parent_style_index != 0x0FFF: + msg = "WARNING *** XF[%d] is a style XF but parent_style_index is 0x%04x, not 0x0fff\n" + fprintf(self.logfile, msg, xf.xf_index, xf.parent_style_index) + check_colour_indexes_in_obj(self, xf, xf.xf_index) + if xf.format_key not in self.format_map: + msg = "WARNING *** XF[%d] unknown (raw) format key (%d, 0x%04x)\n" + if self.verbosity: + fprintf(self.logfile, msg, + xf.xf_index, xf.format_key, xf.format_key) + xf.format_key = 0 + +def xf_epilogue(self): + # self is a Book instance. + self._xf_epilogue_done = 1 + num_xfs = len(self.xf_list) + blah = DEBUG or self.verbosity >= 3 + blah1 = DEBUG or self.verbosity >= 1 + if blah: + fprintf(self.logfile, "xf_epilogue called ...\n") + + def check_same(book_arg, xf_arg, parent_arg, attr): + # the _arg caper is to avoid a Warning msg from Python 2.1 :-( + if getattr(xf_arg, attr) != getattr(parent_arg, attr): + fprintf(book_arg.logfile, + "NOTE !!! XF[%d] parent[%d] %s different\n", + xf_arg.xf_index, parent_arg.xf_index, attr) + + for xfx in xrange(num_xfs): + xf = self.xf_list[xfx] + + try: + fmt = self.format_map[xf.format_key] + cellty = _cellty_from_fmtty[fmt.type] + except KeyError: + cellty = XL_CELL_TEXT + self._xf_index_to_xl_type_map[xf.xf_index] = cellty + # Now for some assertions etc + if not self.formatting_info: + continue + if xf.is_style: + continue + if not(0 <= xf.parent_style_index < num_xfs): + if blah1: + fprintf(self.logfile, + "WARNING *** XF[%d]: is_style=%d but parent_style_index=%d\n", + xf.xf_index, xf.is_style, xf.parent_style_index) + # make it conform + xf.parent_style_index = 0 + if self.biff_version >= 30: + if blah1: + if xf.parent_style_index == xf.xf_index: + fprintf(self.logfile, + "NOTE !!! XF[%d]: parent_style_index is also %d\n", + xf.xf_index, xf.parent_style_index) + elif not self.xf_list[xf.parent_style_index].is_style: + fprintf(self.logfile, + "NOTE !!! XF[%d]: parent_style_index is %d; style flag not set\n", + xf.xf_index, xf.parent_style_index) + if blah1 and xf.parent_style_index > xf.xf_index: + fprintf(self.logfile, + "NOTE !!! XF[%d]: parent_style_index is %d; out of order?\n", + xf.xf_index, xf.parent_style_index) + parent = self.xf_list[xf.parent_style_index] + if not xf._alignment_flag and not parent._alignment_flag: + if blah1: check_same(self, xf, parent, 'alignment') + if not xf._background_flag and not parent._background_flag: + if blah1: check_same(self, xf, parent, 'background') + if not xf._border_flag and not parent._border_flag: + if blah1: check_same(self, xf, parent, 'border') + if not xf._protection_flag and not parent._protection_flag: + if blah1: check_same(self, xf, parent, 'protection') + if not xf._format_flag and not parent._format_flag: + if blah1 and xf.format_key != parent.format_key: + fprintf(self.logfile, + "NOTE !!! XF[%d] fmtk=%d, parent[%d] fmtk=%r\n%r / %r\n", + xf.xf_index, xf.format_key, parent.xf_index, parent.format_key, + self.format_map[xf.format_key].format_str, + self.format_map[parent.format_key].format_str) + if not xf._font_flag and not parent._font_flag: + if blah1 and xf.font_index != parent.font_index: + fprintf(self.logfile, + "NOTE !!! XF[%d] fontx=%d, parent[%d] fontx=%r\n", + xf.xf_index, xf.font_index, parent.xf_index, parent.font_index) + +def initialise_book(book): + initialise_colour_map(book) + book._xf_epilogue_done = 0 + methods = ( + handle_font, + handle_efont, + handle_format, + is_date_format_string, + handle_palette, + palette_epilogue, + handle_style, + handle_xf, + xf_epilogue, + ) + for method in methods: + setattr(book.__class__, method.__name__, method) + +class XFBorder(BaseObject, EqNeAttrs): + """ + A collection of the border-related attributes of an ``XF`` record. + Items correspond to those in the Excel UI's Format -> Cells -> Border tab. + + An explanations of "colour index" is given in :ref:`palette`. + + There are five line style attributes; possible values and the + associated meanings are:: + + 0 = No line, + 1 = Thin, + 2 = Medium, + 3 = Dashed, + 4 = Dotted, + 5 = Thick, + 6 = Double, + 7 = Hair, + 8 = Medium dashed, + 9 = Thin dash-dotted, + 10 = Medium dash-dotted, + 11 = Thin dash-dot-dotted, + 12 = Medium dash-dot-dotted, + 13 = Slanted medium dash-dotted. + + The line styles 8 to 13 appear in BIFF8 files (Excel 97 and later) only. + For pictures of the line styles, refer to OOo docs s3.10 (p22) + "Line Styles for Cell Borders (BIFF3-BIFF8)".</p> + + .. versionadded:: 0.6.1 + """ + + #: The colour index for the cell's top line + top_colour_index = 0 + #: The colour index for the cell's bottom line + bottom_colour_index = 0 + + #: The colour index for the cell's left line + left_colour_index = 0 + + #: The colour index for the cell's right line + right_colour_index = 0 + + #: The colour index for the cell's diagonal lines, if any + diag_colour_index = 0 + + #: The line style for the cell's top line + top_line_style = 0 + + #: The line style for the cell's bottom line + bottom_line_style = 0 + + #: The line style for the cell's left line + left_line_style = 0 + + #: The line style for the cell's right line + right_line_style = 0 + + #: The line style for the cell's diagonal lines, if any + diag_line_style = 0 + + #: 1 = draw a diagonal from top left to bottom right + diag_down = 0 + + #: 1 = draw a diagonal from bottom left to top right + diag_up = 0 + +class XFBackground(BaseObject, EqNeAttrs): + """ + A collection of the background-related attributes of an ``XF`` record. + Items correspond to those in the Excel UI's Format -> Cells -> Patterns tab. + + An explanations of "colour index" is given in :ref:`palette`. + + .. versionadded:: 0.6.1 + """ + + #: See section 3.11 of the OOo docs. + fill_pattern = 0 + + #: See section 3.11 of the OOo docs. + background_colour_index = 0 + + #: See section 3.11 of the OOo docs. + pattern_colour_index = 0 + + +class XFAlignment(BaseObject, EqNeAttrs): + """ + A collection of the alignment and similar attributes of an ``XF`` record. + Items correspond to those in the Excel UI's Format -> Cells -> Alignment tab. + + .. versionadded:: 0.6.1 + """ + + #: Values: section 6.115 (p 214) of OOo docs + hor_align = 0 + + #: Values: section 6.115 (p 215) of OOo docs + vert_align = 0 + + #: Values: section 6.115 (p 215) of OOo docs. + #: + #: .. note:: + #: file versions BIFF7 and earlier use the documented + #: :attr:`orientation` attribute; this will be mapped (without loss) + #: into :attr:`rotation`. + rotation = 0 + + #: 1 = text is wrapped at right margin + text_wrapped = 0 + + #: A number in ``range(15)``. + indent_level = 0 + + #: 1 = shrink font size to fit text into cell. + shrink_to_fit = 0 + + #: 0 = according to context; 1 = left-to-right; 2 = right-to-left + text_direction = 0 + +class XFProtection(BaseObject, EqNeAttrs): + """ + A collection of the protection-related attributes of an ``XF`` record. + Items correspond to those in the Excel UI's Format -> Cells -> Protection tab. + Note the OOo docs include the "cell or style" bit in this bundle of + attributes. This is incorrect; the bit is used in determining which bundles + to use. + + .. versionadded:: 0.6.1 + """ + + #: 1 = Cell is prevented from being changed, moved, resized, or deleted + #: (only if the sheet is protected). + cell_locked = 0 + + #: 1 = Hide formula so that it doesn't appear in the formula bar when + #: the cell is selected (only if the sheet is protected). + formula_hidden = 0 + +class XF(BaseObject): + """ + eXtended Formatting information for cells, rows, columns and styles. + + Each of the 6 flags below describes the validity of + a specific group of attributes. + + In cell XFs: + + - ``flag==0`` means the attributes of the parent style ``XF`` are + used, (but only if the attributes are valid there); + + - ``flag==1`` means the attributes of this ``XF`` are used. + + In style XFs: + + - ``flag==0`` means the attribute setting is valid; + - ``flag==1`` means the attribute should be ignored. + + .. note:: + the API provides both "raw" XFs and "computed" XFs. In the latter case, + cell XFs have had the above inheritance mechanism applied. + + .. versionadded:: 0.6.1 + """ + + #: 0 = cell XF, 1 = style XF + is_style = 0 + + #: cell XF: Index into Book.xf_list of this XF's style XF + #: + #: style XF: 0xFFF + parent_style_index = 0 + + # + _format_flag = 0 + + # + _font_flag = 0 + + # + _alignment_flag = 0 + + # + _border_flag = 0 + + # + _background_flag = 0 + + _protection_flag = 0 + + #: Index into :attr:`~xlrd.book.Book.xf_list` + xf_index = 0 + + #: Index into :attr:`~xlrd.book.Book.font_list` + font_index = 0 + + #: Key into :attr:`~xlrd.book.Book.format_map` + #: + #: .. warning:: + #: OOo docs on the XF record call this "Index to FORMAT record". + #: It is not an index in the Python sense. It is a key to a map. + #: It is true *only* for Excel 4.0 and earlier files + #: that the key into format_map from an XF instance + #: is the same as the index into format_list, and *only* + #: if the index is less than 164. + format_key = 0 + + #: An instance of an :class:`XFProtection` object. + protection = None + + #: An instance of an :class:`XFBackground` object. + background = None + + #: An instance of an :class:`XFAlignment` object. + alignment = None + + #: An instance of an :class:`XFBorder` object. + border = None diff --git a/.venv/lib/python3.9/site-packages/xlrd/formula.py b/.venv/lib/python3.9/site-packages/xlrd/formula.py new file mode 100644 index 00000000..e26639b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/formula.py @@ -0,0 +1,2190 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +# No part of the content of this file was derived from the works of +# David Giffin. +""" +Module for parsing/evaluating Microsoft Excel formulas. +""" + +from __future__ import print_function + +import copy +import operator as opr +from struct import unpack + +from .biffh import ( + BaseObject, XLRDError, error_text_from_code, hex_char_dump, + unpack_string_update_pos, unpack_unicode_update_pos, +) +from .timemachine import * + +__all__ = [ + 'oBOOL', 'oERR', 'oNUM', 'oREF', 'oREL', 'oSTRG', 'oUNK', + 'decompile_formula', + 'dump_formula', + 'evaluate_name_formula', + 'okind_dict', + 'rangename3d', 'rangename3drel', 'cellname', 'cellnameabs', 'colname', + 'FMLA_TYPE_CELL', + 'FMLA_TYPE_SHARED', + 'FMLA_TYPE_ARRAY', + 'FMLA_TYPE_COND_FMT', + 'FMLA_TYPE_DATA_VAL', + 'FMLA_TYPE_NAME', + 'Operand', 'Ref3D', +] + +FMLA_TYPE_CELL = 1 +FMLA_TYPE_SHARED = 2 +FMLA_TYPE_ARRAY = 4 +FMLA_TYPE_COND_FMT = 8 +FMLA_TYPE_DATA_VAL = 16 +FMLA_TYPE_NAME = 32 +ALL_FMLA_TYPES = 63 + + +FMLA_TYPEDESCR_MAP = { + 1 : 'CELL', + 2 : 'SHARED', + 4 : 'ARRAY', + 8 : 'COND-FMT', + 16: 'DATA-VAL', + 32: 'NAME', +} + +_TOKEN_NOT_ALLOWED = { + 0x01: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tExp + 0x02: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tTbl + 0x0F: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tIsect + 0x10: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tUnion/List + 0x11: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tRange + 0x20: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tArray + 0x23: FMLA_TYPE_SHARED, # tName + 0x39: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tNameX + 0x3A: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tRef3d + 0x3B: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tArea3d + 0x2C: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tRefN + 0x2D: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tAreaN + # plus weird stuff like tMem* +}.get + +oBOOL = 3 +oERR = 4 +oMSNG = 5 # tMissArg +oNUM = 2 +oREF = -1 +oREL = -2 +oSTRG = 1 +oUNK = 0 + +okind_dict = { + -2: "oREL", + -1: "oREF", + 0 : "oUNK", + 1 : "oSTRG", + 2 : "oNUM", + 3 : "oBOOL", + 4 : "oERR", + 5 : "oMSNG", +} + +listsep = ',' #### probably should depend on locale + + +# sztabN[opcode] -> the number of bytes to consume. +# -1 means variable +# -2 means this opcode not implemented in this version. +# Which N to use? Depends on biff_version; see szdict. +sztab0 = [-2, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 8, 4, 2, 2, 3, 9, 8, 2, 3, 8, 4, 7, 5, 5, 5, 2, 4, 7, 4, 7, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2] +sztab1 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 11, 5, 2, 2, 3, 9, 9, 2, 3, 11, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2] +sztab2 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 11, 5, 2, 2, 3, 9, 9, 3, 4, 11, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2] +sztab3 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, -2, -2, 2, 2, 3, 9, 9, 3, 4, 15, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, 25, 18, 21, 18, 21, -2, -2] +sztab4 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -2, -2, 2, 2, 3, 9, 9, 3, 4, 5, 5, 9, 7, 7, 7, 3, 5, 9, 5, 9, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, 7, 7, 11, 7, 11, -2, -2] + +szdict = { + 20 : sztab0, + 21 : sztab0, + 30 : sztab1, + 40 : sztab2, + 45 : sztab2, + 50 : sztab3, + 70 : sztab3, + 80 : sztab4, +} + +# For debugging purposes ... the name for each opcode +# (without the prefix "t" used on OOo docs) +onames = ['Unk00', 'Exp', 'Tbl', 'Add', 'Sub', 'Mul', 'Div', 'Power', 'Concat', 'LT', 'LE', 'EQ', 'GE', 'GT', 'NE', 'Isect', 'List', 'Range', 'Uplus', 'Uminus', 'Percent', 'Paren', 'MissArg', 'Str', 'Extended', 'Attr', 'Sheet', 'EndSheet', 'Err', 'Bool', 'Int', 'Num', 'Array', 'Func', 'FuncVar', 'Name', 'Ref', 'Area', 'MemArea', 'MemErr', 'MemNoMem', 'MemFunc', 'RefErr', 'AreaErr', 'RefN', 'AreaN', 'MemAreaN', 'MemNoMemN', '', '', '', '', '', '', '', '', 'FuncCE', 'NameX', 'Ref3d', 'Area3d', 'RefErr3d', 'AreaErr3d', '', ''] + +func_defs = { + # index: (name, min#args, max#args, flags, #known_args, return_type, kargs) + 0 : ('COUNT', 0, 30, 0x04, 1, 'V', 'R'), + 1 : ('IF', 2, 3, 0x04, 3, 'V', 'VRR'), + 2 : ('ISNA', 1, 1, 0x02, 1, 'V', 'V'), + 3 : ('ISERROR', 1, 1, 0x02, 1, 'V', 'V'), + 4 : ('SUM', 0, 30, 0x04, 1, 'V', 'R'), + 5 : ('AVERAGE', 1, 30, 0x04, 1, 'V', 'R'), + 6 : ('MIN', 1, 30, 0x04, 1, 'V', 'R'), + 7 : ('MAX', 1, 30, 0x04, 1, 'V', 'R'), + 8 : ('ROW', 0, 1, 0x04, 1, 'V', 'R'), + 9 : ('COLUMN', 0, 1, 0x04, 1, 'V', 'R'), + 10 : ('NA', 0, 0, 0x02, 0, 'V', ''), + 11 : ('NPV', 2, 30, 0x04, 2, 'V', 'VR'), + 12 : ('STDEV', 1, 30, 0x04, 1, 'V', 'R'), + 13 : ('DOLLAR', 1, 2, 0x04, 1, 'V', 'V'), + 14 : ('FIXED', 2, 3, 0x04, 3, 'V', 'VVV'), + 15 : ('SIN', 1, 1, 0x02, 1, 'V', 'V'), + 16 : ('COS', 1, 1, 0x02, 1, 'V', 'V'), + 17 : ('TAN', 1, 1, 0x02, 1, 'V', 'V'), + 18 : ('ATAN', 1, 1, 0x02, 1, 'V', 'V'), + 19 : ('PI', 0, 0, 0x02, 0, 'V', ''), + 20 : ('SQRT', 1, 1, 0x02, 1, 'V', 'V'), + 21 : ('EXP', 1, 1, 0x02, 1, 'V', 'V'), + 22 : ('LN', 1, 1, 0x02, 1, 'V', 'V'), + 23 : ('LOG10', 1, 1, 0x02, 1, 'V', 'V'), + 24 : ('ABS', 1, 1, 0x02, 1, 'V', 'V'), + 25 : ('INT', 1, 1, 0x02, 1, 'V', 'V'), + 26 : ('SIGN', 1, 1, 0x02, 1, 'V', 'V'), + 27 : ('ROUND', 2, 2, 0x02, 2, 'V', 'VV'), + 28 : ('LOOKUP', 2, 3, 0x04, 2, 'V', 'VR'), + 29 : ('INDEX', 2, 4, 0x0c, 4, 'R', 'RVVV'), + 30 : ('REPT', 2, 2, 0x02, 2, 'V', 'VV'), + 31 : ('MID', 3, 3, 0x02, 3, 'V', 'VVV'), + 32 : ('LEN', 1, 1, 0x02, 1, 'V', 'V'), + 33 : ('VALUE', 1, 1, 0x02, 1, 'V', 'V'), + 34 : ('TRUE', 0, 0, 0x02, 0, 'V', ''), + 35 : ('FALSE', 0, 0, 0x02, 0, 'V', ''), + 36 : ('AND', 1, 30, 0x04, 1, 'V', 'R'), + 37 : ('OR', 1, 30, 0x04, 1, 'V', 'R'), + 38 : ('NOT', 1, 1, 0x02, 1, 'V', 'V'), + 39 : ('MOD', 2, 2, 0x02, 2, 'V', 'VV'), + 40 : ('DCOUNT', 3, 3, 0x02, 3, 'V', 'RRR'), + 41 : ('DSUM', 3, 3, 0x02, 3, 'V', 'RRR'), + 42 : ('DAVERAGE', 3, 3, 0x02, 3, 'V', 'RRR'), + 43 : ('DMIN', 3, 3, 0x02, 3, 'V', 'RRR'), + 44 : ('DMAX', 3, 3, 0x02, 3, 'V', 'RRR'), + 45 : ('DSTDEV', 3, 3, 0x02, 3, 'V', 'RRR'), + 46 : ('VAR', 1, 30, 0x04, 1, 'V', 'R'), + 47 : ('DVAR', 3, 3, 0x02, 3, 'V', 'RRR'), + 48 : ('TEXT', 2, 2, 0x02, 2, 'V', 'VV'), + 49 : ('LINEST', 1, 4, 0x04, 4, 'A', 'RRVV'), + 50 : ('TREND', 1, 4, 0x04, 4, 'A', 'RRRV'), + 51 : ('LOGEST', 1, 4, 0x04, 4, 'A', 'RRVV'), + 52 : ('GROWTH', 1, 4, 0x04, 4, 'A', 'RRRV'), + 56 : ('PV', 3, 5, 0x04, 5, 'V', 'VVVVV'), + 57 : ('FV', 3, 5, 0x04, 5, 'V', 'VVVVV'), + 58 : ('NPER', 3, 5, 0x04, 5, 'V', 'VVVVV'), + 59 : ('PMT', 3, 5, 0x04, 5, 'V', 'VVVVV'), + 60 : ('RATE', 3, 6, 0x04, 6, 'V', 'VVVVVV'), + 61 : ('MIRR', 3, 3, 0x02, 3, 'V', 'RVV'), + 62 : ('IRR', 1, 2, 0x04, 2, 'V', 'RV'), + 63 : ('RAND', 0, 0, 0x0a, 0, 'V', ''), + 64 : ('MATCH', 2, 3, 0x04, 3, 'V', 'VRR'), + 65 : ('DATE', 3, 3, 0x02, 3, 'V', 'VVV'), + 66 : ('TIME', 3, 3, 0x02, 3, 'V', 'VVV'), + 67 : ('DAY', 1, 1, 0x02, 1, 'V', 'V'), + 68 : ('MONTH', 1, 1, 0x02, 1, 'V', 'V'), + 69 : ('YEAR', 1, 1, 0x02, 1, 'V', 'V'), + 70 : ('WEEKDAY', 1, 2, 0x04, 2, 'V', 'VV'), + 71 : ('HOUR', 1, 1, 0x02, 1, 'V', 'V'), + 72 : ('MINUTE', 1, 1, 0x02, 1, 'V', 'V'), + 73 : ('SECOND', 1, 1, 0x02, 1, 'V', 'V'), + 74 : ('NOW', 0, 0, 0x0a, 0, 'V', ''), + 75 : ('AREAS', 1, 1, 0x02, 1, 'V', 'R'), + 76 : ('ROWS', 1, 1, 0x02, 1, 'V', 'R'), + 77 : ('COLUMNS', 1, 1, 0x02, 1, 'V', 'R'), + 78 : ('OFFSET', 3, 5, 0x04, 5, 'R', 'RVVVV'), + 82 : ('SEARCH', 2, 3, 0x04, 3, 'V', 'VVV'), + 83 : ('TRANSPOSE', 1, 1, 0x02, 1, 'A', 'A'), + 86 : ('TYPE', 1, 1, 0x02, 1, 'V', 'V'), + 92 : ('SERIESSUM', 4, 4, 0x02, 4, 'V', 'VVVA'), + 97 : ('ATAN2', 2, 2, 0x02, 2, 'V', 'VV'), + 98 : ('ASIN', 1, 1, 0x02, 1, 'V', 'V'), + 99 : ('ACOS', 1, 1, 0x02, 1, 'V', 'V'), + 100: ('CHOOSE', 2, 30, 0x04, 2, 'V', 'VR'), + 101: ('HLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'), + 102: ('VLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'), + 105: ('ISREF', 1, 1, 0x02, 1, 'V', 'R'), + 109: ('LOG', 1, 2, 0x04, 2, 'V', 'VV'), + 111: ('CHAR', 1, 1, 0x02, 1, 'V', 'V'), + 112: ('LOWER', 1, 1, 0x02, 1, 'V', 'V'), + 113: ('UPPER', 1, 1, 0x02, 1, 'V', 'V'), + 114: ('PROPER', 1, 1, 0x02, 1, 'V', 'V'), + 115: ('LEFT', 1, 2, 0x04, 2, 'V', 'VV'), + 116: ('RIGHT', 1, 2, 0x04, 2, 'V', 'VV'), + 117: ('EXACT', 2, 2, 0x02, 2, 'V', 'VV'), + 118: ('TRIM', 1, 1, 0x02, 1, 'V', 'V'), + 119: ('REPLACE', 4, 4, 0x02, 4, 'V', 'VVVV'), + 120: ('SUBSTITUTE', 3, 4, 0x04, 4, 'V', 'VVVV'), + 121: ('CODE', 1, 1, 0x02, 1, 'V', 'V'), + 124: ('FIND', 2, 3, 0x04, 3, 'V', 'VVV'), + 125: ('CELL', 1, 2, 0x0c, 2, 'V', 'VR'), + 126: ('ISERR', 1, 1, 0x02, 1, 'V', 'V'), + 127: ('ISTEXT', 1, 1, 0x02, 1, 'V', 'V'), + 128: ('ISNUMBER', 1, 1, 0x02, 1, 'V', 'V'), + 129: ('ISBLANK', 1, 1, 0x02, 1, 'V', 'V'), + 130: ('T', 1, 1, 0x02, 1, 'V', 'R'), + 131: ('N', 1, 1, 0x02, 1, 'V', 'R'), + 140: ('DATEVALUE', 1, 1, 0x02, 1, 'V', 'V'), + 141: ('TIMEVALUE', 1, 1, 0x02, 1, 'V', 'V'), + 142: ('SLN', 3, 3, 0x02, 3, 'V', 'VVV'), + 143: ('SYD', 4, 4, 0x02, 4, 'V', 'VVVV'), + 144: ('DDB', 4, 5, 0x04, 5, 'V', 'VVVVV'), + 148: ('INDIRECT', 1, 2, 0x0c, 2, 'R', 'VV'), + 162: ('CLEAN', 1, 1, 0x02, 1, 'V', 'V'), + 163: ('MDETERM', 1, 1, 0x02, 1, 'V', 'A'), + 164: ('MINVERSE', 1, 1, 0x02, 1, 'A', 'A'), + 165: ('MMULT', 2, 2, 0x02, 2, 'A', 'AA'), + 167: ('IPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'), + 168: ('PPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'), + 169: ('COUNTA', 0, 30, 0x04, 1, 'V', 'R'), + 183: ('PRODUCT', 0, 30, 0x04, 1, 'V', 'R'), + 184: ('FACT', 1, 1, 0x02, 1, 'V', 'V'), + 189: ('DPRODUCT', 3, 3, 0x02, 3, 'V', 'RRR'), + 190: ('ISNONTEXT', 1, 1, 0x02, 1, 'V', 'V'), + 193: ('STDEVP', 1, 30, 0x04, 1, 'V', 'R'), + 194: ('VARP', 1, 30, 0x04, 1, 'V', 'R'), + 195: ('DSTDEVP', 3, 3, 0x02, 3, 'V', 'RRR'), + 196: ('DVARP', 3, 3, 0x02, 3, 'V', 'RRR'), + 197: ('TRUNC', 1, 2, 0x04, 2, 'V', 'VV'), + 198: ('ISLOGICAL', 1, 1, 0x02, 1, 'V', 'V'), + 199: ('DCOUNTA', 3, 3, 0x02, 3, 'V', 'RRR'), + 204: ('USDOLLAR', 1, 2, 0x04, 2, 'V', 'VV'), + 205: ('FINDB', 2, 3, 0x04, 3, 'V', 'VVV'), + 206: ('SEARCHB', 2, 3, 0x04, 3, 'V', 'VVV'), + 207: ('REPLACEB', 4, 4, 0x02, 4, 'V', 'VVVV'), + 208: ('LEFTB', 1, 2, 0x04, 2, 'V', 'VV'), + 209: ('RIGHTB', 1, 2, 0x04, 2, 'V', 'VV'), + 210: ('MIDB', 3, 3, 0x02, 3, 'V', 'VVV'), + 211: ('LENB', 1, 1, 0x02, 1, 'V', 'V'), + 212: ('ROUNDUP', 2, 2, 0x02, 2, 'V', 'VV'), + 213: ('ROUNDDOWN', 2, 2, 0x02, 2, 'V', 'VV'), + 214: ('ASC', 1, 1, 0x02, 1, 'V', 'V'), + 215: ('DBCS', 1, 1, 0x02, 1, 'V', 'V'), + 216: ('RANK', 2, 3, 0x04, 3, 'V', 'VRV'), + 219: ('ADDRESS', 2, 5, 0x04, 5, 'V', 'VVVVV'), + 220: ('DAYS360', 2, 3, 0x04, 3, 'V', 'VVV'), + 221: ('TODAY', 0, 0, 0x0a, 0, 'V', ''), + 222: ('VDB', 5, 7, 0x04, 7, 'V', 'VVVVVVV'), + 227: ('MEDIAN', 1, 30, 0x04, 1, 'V', 'R'), + 228: ('SUMPRODUCT', 1, 30, 0x04, 1, 'V', 'A'), + 229: ('SINH', 1, 1, 0x02, 1, 'V', 'V'), + 230: ('COSH', 1, 1, 0x02, 1, 'V', 'V'), + 231: ('TANH', 1, 1, 0x02, 1, 'V', 'V'), + 232: ('ASINH', 1, 1, 0x02, 1, 'V', 'V'), + 233: ('ACOSH', 1, 1, 0x02, 1, 'V', 'V'), + 234: ('ATANH', 1, 1, 0x02, 1, 'V', 'V'), + 235: ('DGET', 3, 3, 0x02, 3, 'V', 'RRR'), + 244: ('INFO', 1, 1, 0x02, 1, 'V', 'V'), + 247: ('DB', 4, 5, 0x04, 5, 'V', 'VVVVV'), + 252: ('FREQUENCY', 2, 2, 0x02, 2, 'A', 'RR'), + 261: ('ERROR.TYPE', 1, 1, 0x02, 1, 'V', 'V'), + 269: ('AVEDEV', 1, 30, 0x04, 1, 'V', 'R'), + 270: ('BETADIST', 3, 5, 0x04, 1, 'V', 'V'), + 271: ('GAMMALN', 1, 1, 0x02, 1, 'V', 'V'), + 272: ('BETAINV', 3, 5, 0x04, 1, 'V', 'V'), + 273: ('BINOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), + 274: ('CHIDIST', 2, 2, 0x02, 2, 'V', 'VV'), + 275: ('CHIINV', 2, 2, 0x02, 2, 'V', 'VV'), + 276: ('COMBIN', 2, 2, 0x02, 2, 'V', 'VV'), + 277: ('CONFIDENCE', 3, 3, 0x02, 3, 'V', 'VVV'), + 278: ('CRITBINOM', 3, 3, 0x02, 3, 'V', 'VVV'), + 279: ('EVEN', 1, 1, 0x02, 1, 'V', 'V'), + 280: ('EXPONDIST', 3, 3, 0x02, 3, 'V', 'VVV'), + 281: ('FDIST', 3, 3, 0x02, 3, 'V', 'VVV'), + 282: ('FINV', 3, 3, 0x02, 3, 'V', 'VVV'), + 283: ('FISHER', 1, 1, 0x02, 1, 'V', 'V'), + 284: ('FISHERINV', 1, 1, 0x02, 1, 'V', 'V'), + 285: ('FLOOR', 2, 2, 0x02, 2, 'V', 'VV'), + 286: ('GAMMADIST', 4, 4, 0x02, 4, 'V', 'VVVV'), + 287: ('GAMMAINV', 3, 3, 0x02, 3, 'V', 'VVV'), + 288: ('CEILING', 2, 2, 0x02, 2, 'V', 'VV'), + 289: ('HYPGEOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), + 290: ('LOGNORMDIST', 3, 3, 0x02, 3, 'V', 'VVV'), + 291: ('LOGINV', 3, 3, 0x02, 3, 'V', 'VVV'), + 292: ('NEGBINOMDIST', 3, 3, 0x02, 3, 'V', 'VVV'), + 293: ('NORMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), + 294: ('NORMSDIST', 1, 1, 0x02, 1, 'V', 'V'), + 295: ('NORMINV', 3, 3, 0x02, 3, 'V', 'VVV'), + 296: ('NORMSINV', 1, 1, 0x02, 1, 'V', 'V'), + 297: ('STANDARDIZE', 3, 3, 0x02, 3, 'V', 'VVV'), + 298: ('ODD', 1, 1, 0x02, 1, 'V', 'V'), + 299: ('PERMUT', 2, 2, 0x02, 2, 'V', 'VV'), + 300: ('POISSON', 3, 3, 0x02, 3, 'V', 'VVV'), + 301: ('TDIST', 3, 3, 0x02, 3, 'V', 'VVV'), + 302: ('WEIBULL', 4, 4, 0x02, 4, 'V', 'VVVV'), + 303: ('SUMXMY2', 2, 2, 0x02, 2, 'V', 'AA'), + 304: ('SUMX2MY2', 2, 2, 0x02, 2, 'V', 'AA'), + 305: ('SUMX2PY2', 2, 2, 0x02, 2, 'V', 'AA'), + 306: ('CHITEST', 2, 2, 0x02, 2, 'V', 'AA'), + 307: ('CORREL', 2, 2, 0x02, 2, 'V', 'AA'), + 308: ('COVAR', 2, 2, 0x02, 2, 'V', 'AA'), + 309: ('FORECAST', 3, 3, 0x02, 3, 'V', 'VAA'), + 310: ('FTEST', 2, 2, 0x02, 2, 'V', 'AA'), + 311: ('INTERCEPT', 2, 2, 0x02, 2, 'V', 'AA'), + 312: ('PEARSON', 2, 2, 0x02, 2, 'V', 'AA'), + 313: ('RSQ', 2, 2, 0x02, 2, 'V', 'AA'), + 314: ('STEYX', 2, 2, 0x02, 2, 'V', 'AA'), + 315: ('SLOPE', 2, 2, 0x02, 2, 'V', 'AA'), + 316: ('TTEST', 4, 4, 0x02, 4, 'V', 'AAVV'), + 317: ('PROB', 3, 4, 0x04, 3, 'V', 'AAV'), + 318: ('DEVSQ', 1, 30, 0x04, 1, 'V', 'R'), + 319: ('GEOMEAN', 1, 30, 0x04, 1, 'V', 'R'), + 320: ('HARMEAN', 1, 30, 0x04, 1, 'V', 'R'), + 321: ('SUMSQ', 0, 30, 0x04, 1, 'V', 'R'), + 322: ('KURT', 1, 30, 0x04, 1, 'V', 'R'), + 323: ('SKEW', 1, 30, 0x04, 1, 'V', 'R'), + 324: ('ZTEST', 2, 3, 0x04, 2, 'V', 'RV'), + 325: ('LARGE', 2, 2, 0x02, 2, 'V', 'RV'), + 326: ('SMALL', 2, 2, 0x02, 2, 'V', 'RV'), + 327: ('QUARTILE', 2, 2, 0x02, 2, 'V', 'RV'), + 328: ('PERCENTILE', 2, 2, 0x02, 2, 'V', 'RV'), + 329: ('PERCENTRANK', 2, 3, 0x04, 2, 'V', 'RV'), + 330: ('MODE', 1, 30, 0x04, 1, 'V', 'A'), + 331: ('TRIMMEAN', 2, 2, 0x02, 2, 'V', 'RV'), + 332: ('TINV', 2, 2, 0x02, 2, 'V', 'VV'), + 336: ('CONCATENATE', 0, 30, 0x04, 1, 'V', 'V'), + 337: ('POWER', 2, 2, 0x02, 2, 'V', 'VV'), + 342: ('RADIANS', 1, 1, 0x02, 1, 'V', 'V'), + 343: ('DEGREES', 1, 1, 0x02, 1, 'V', 'V'), + 344: ('SUBTOTAL', 2, 30, 0x04, 2, 'V', 'VR'), + 345: ('SUMIF', 2, 3, 0x04, 3, 'V', 'RVR'), + 346: ('COUNTIF', 2, 2, 0x02, 2, 'V', 'RV'), + 347: ('COUNTBLANK', 1, 1, 0x02, 1, 'V', 'R'), + 350: ('ISPMT', 4, 4, 0x02, 4, 'V', 'VVVV'), + 351: ('DATEDIF', 3, 3, 0x02, 3, 'V', 'VVV'), + 352: ('DATESTRING', 1, 1, 0x02, 1, 'V', 'V'), + 353: ('NUMBERSTRING', 2, 2, 0x02, 2, 'V', 'VV'), + 354: ('ROMAN', 1, 2, 0x04, 2, 'V', 'VV'), + 358: ('GETPIVOTDATA', 2, 2, 0x02, 2, 'V', 'RV'), + 359: ('HYPERLINK', 1, 2, 0x04, 2, 'V', 'VV'), + 360: ('PHONETIC', 1, 1, 0x02, 1, 'V', 'V'), + 361: ('AVERAGEA', 1, 30, 0x04, 1, 'V', 'R'), + 362: ('MAXA', 1, 30, 0x04, 1, 'V', 'R'), + 363: ('MINA', 1, 30, 0x04, 1, 'V', 'R'), + 364: ('STDEVPA', 1, 30, 0x04, 1, 'V', 'R'), + 365: ('VARPA', 1, 30, 0x04, 1, 'V', 'R'), + 366: ('STDEVA', 1, 30, 0x04, 1, 'V', 'R'), + 367: ('VARA', 1, 30, 0x04, 1, 'V', 'R'), + 368: ('BAHTTEXT', 1, 1, 0x02, 1, 'V', 'V'), + 369: ('THAIDAYOFWEEK', 1, 1, 0x02, 1, 'V', 'V'), + 370: ('THAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'), + 371: ('THAIMONTHOFYEAR', 1, 1, 0x02, 1, 'V', 'V'), + 372: ('THAINUMSOUND', 1, 1, 0x02, 1, 'V', 'V'), + 373: ('THAINUMSTRING', 1, 1, 0x02, 1, 'V', 'V'), + 374: ('THAISTRINGLENGTH', 1, 1, 0x02, 1, 'V', 'V'), + 375: ('ISTHAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'), + 376: ('ROUNDBAHTDOWN', 1, 1, 0x02, 1, 'V', 'V'), + 377: ('ROUNDBAHTUP', 1, 1, 0x02, 1, 'V', 'V'), + 378: ('THAIYEAR', 1, 1, 0x02, 1, 'V', 'V'), + 379: ('RTD', 2, 5, 0x04, 1, 'V', 'V'), +} + +tAttrNames = { + 0x00: "Skip??", # seen in SAMPLES.XLS which shipped with Excel 5.0 + 0x01: "Volatile", + 0x02: "If", + 0x04: "Choose", + 0x08: "Skip", + 0x10: "Sum", + 0x20: "Assign", + 0x40: "Space", + 0x41: "SpaceVolatile", +} + +error_opcodes = set([0x07, 0x08, 0x0A, 0x0B, 0x1C, 0x1D, 0x2F]) + +tRangeFuncs = (min, max, min, max, min, max) +tIsectFuncs = (max, min, max, min, max, min) + +def do_box_funcs(box_funcs, boxa, boxb): + return tuple( + func(numa, numb) + for func, numa, numb in zip(box_funcs, boxa.coords, boxb.coords) + ) + +def adjust_cell_addr_biff8(rowval, colval, reldelta, browx=None, bcolx=None): + row_rel = (colval >> 15) & 1 + col_rel = (colval >> 14) & 1 + rowx = rowval + colx = colval & 0xff + if reldelta: + if row_rel and rowx >= 32768: + rowx -= 65536 + if col_rel and colx >= 128: + colx -= 256 + else: + if row_rel: + rowx -= browx + if col_rel: + colx -= bcolx + return rowx, colx, row_rel, col_rel + +def adjust_cell_addr_biff_le7( + rowval, colval, reldelta, browx=None, bcolx=None): + row_rel = (rowval >> 15) & 1 + col_rel = (rowval >> 14) & 1 + rowx = rowval & 0x3fff + colx = colval + if reldelta: + if row_rel and rowx >= 8192: + rowx -= 16384 + if col_rel and colx >= 128: + colx -= 256 + else: + if row_rel: + rowx -= browx + if col_rel: + colx -= bcolx + return rowx, colx, row_rel, col_rel + +def get_cell_addr(data, pos, bv, reldelta, browx=None, bcolx=None): + if bv >= 80: + rowval, colval = unpack("<HH", data[pos:pos+4]) + # print " rv=%04xh cv=%04xh" % (rowval, colval) + return adjust_cell_addr_biff8(rowval, colval, reldelta, browx, bcolx) + else: + rowval, colval = unpack("<HB", data[pos:pos+3]) + # print " rv=%04xh cv=%04xh" % (rowval, colval) + return adjust_cell_addr_biff_le7( + rowval, colval, reldelta, browx, bcolx) + +def get_cell_range_addr(data, pos, bv, reldelta, browx=None, bcolx=None): + if bv >= 80: + row1val, row2val, col1val, col2val = unpack("<HHHH", data[pos:pos+8]) + # print " rv=%04xh cv=%04xh" % (row1val, col1val) + # print " rv=%04xh cv=%04xh" % (row2val, col2val) + res1 = adjust_cell_addr_biff8(row1val, col1val, reldelta, browx, bcolx) + res2 = adjust_cell_addr_biff8(row2val, col2val, reldelta, browx, bcolx) + return res1, res2 + else: + row1val, row2val, col1val, col2val = unpack("<HHBB", data[pos:pos+6]) + # print " rv=%04xh cv=%04xh" % (row1val, col1val) + # print " rv=%04xh cv=%04xh" % (row2val, col2val) + res1 = adjust_cell_addr_biff_le7( + row1val, col1val, reldelta, browx, bcolx) + res2 = adjust_cell_addr_biff_le7( + row2val, col2val, reldelta, browx, bcolx) + return res1, res2 + +def get_externsheet_local_range(bk, refx, blah=0): + try: + info = bk._externsheet_info[refx] + except IndexError: + print("!!! get_externsheet_local_range: refx=%d, not in range(%d)" + % (refx, len(bk._externsheet_info)), file=bk.logfile) + return (-101, -101) + ref_recordx, ref_first_sheetx, ref_last_sheetx = info + if ref_recordx == bk._supbook_addins_inx: + if blah: + print("/// get_externsheet_local_range(refx=%d) -> addins %r" % (refx, info), file=bk.logfile) + assert ref_first_sheetx == 0xFFFE == ref_last_sheetx + return (-5, -5) + if ref_recordx != bk._supbook_locals_inx: + if blah: + print("/// get_externsheet_local_range(refx=%d) -> external %r" % (refx, info), file=bk.logfile) + return (-4, -4) # external reference + if ref_first_sheetx == 0xFFFE == ref_last_sheetx: + if blah: + print("/// get_externsheet_local_range(refx=%d) -> unspecified sheet %r" % (refx, info), file=bk.logfile) + return (-1, -1) # internal reference, any sheet + if ref_first_sheetx == 0xFFFF == ref_last_sheetx: + if blah: + print("/// get_externsheet_local_range(refx=%d) -> deleted sheet(s)" % (refx, ), file=bk.logfile) + return (-2, -2) # internal reference, deleted sheet(s) + nsheets = len(bk._all_sheets_map) + if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): + if blah: + print("/// get_externsheet_local_range(refx=%d) -> %r" % (refx, info), file=bk.logfile) + print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile) + return (-102, -102) # stuffed up somewhere :-( + xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] + xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] + if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2): + return (-3, -3) # internal reference, but to a macro sheet + return xlrd_sheetx1, xlrd_sheetx2 + +def get_externsheet_local_range_b57( + bk, raw_extshtx, ref_first_sheetx, ref_last_sheetx, blah=0): + if raw_extshtx > 0: + if blah: + print("/// get_externsheet_local_range_b57(raw_extshtx=%d) -> external" % raw_extshtx, file=bk.logfile) + return (-4, -4) # external reference + if ref_first_sheetx == -1 and ref_last_sheetx == -1: + return (-2, -2) # internal reference, deleted sheet(s) + nsheets = len(bk._all_sheets_map) + if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): + if blah: + print("/// get_externsheet_local_range_b57(%d, %d, %d) -> ???" + % (raw_extshtx, ref_first_sheetx, ref_last_sheetx), file=bk.logfile) + print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile) + return (-103, -103) # stuffed up somewhere :-( + xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] + xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] + if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2): + return (-3, -3) # internal reference, but to a macro sheet + return xlrd_sheetx1, xlrd_sheetx2 + +class FormulaError(Exception): + pass + + +class Operand(object): + """ + Used in evaluating formulas. + The following table describes the kinds and how their values + are represented. + + .. raw:: html + + <table border="1" cellpadding="7"> + <tr> + <th>Kind symbol</th> + <th>Kind number</th> + <th>Value representation</th> + </tr> + <tr> + <td>oBOOL</td> + <td align="center">3</td> + <td>integer: 0 => False; 1 => True</td> + </tr> + <tr> + <td>oERR</td> + <td align="center">4</td> + <td>None, or an int error code (same as XL_CELL_ERROR in the Cell class). + </td> + </tr> + <tr> + <td>oMSNG</td> + <td align="center">5</td> + <td>Used by Excel as a placeholder for a missing (not supplied) function + argument. Should *not* appear as a final formula result. Value is None.</td> + </tr> + <tr> + <td>oNUM</td> + <td align="center">2</td> + <td>A float. Note that there is no way of distinguishing dates.</td> + </tr> + <tr> + <td>oREF</td> + <td align="center">-1</td> + <td>The value is either None or a non-empty list of + absolute Ref3D instances.<br> + </td> + </tr> + <tr> + <td>oREL</td> + <td align="center">-2</td> + <td>The value is None or a non-empty list of + fully or partially relative Ref3D instances. + </td> + </tr> + <tr> + <td>oSTRG</td> + <td align="center">1</td> + <td>A Unicode string.</td> + </tr> + <tr> + <td>oUNK</td> + <td align="center">0</td> + <td>The kind is unknown or ambiguous. The value is None</td> + </tr> + </table> + """ + + #: None means that the actual value of the operand is a variable + #: (depends on cell data), not a constant. + value = None + + #: oUNK means that the kind of operand is not known unambiguously. + kind = oUNK + + #: The reconstituted text of the original formula. Function names will be + #: in English irrespective of the original language, which doesn't seem + #: to be recorded anywhere. The separator is ",", not ";" or whatever else + #: might be more appropriate for the end-user's locale; patches welcome. + text = '?' + + def __init__(self, akind=None, avalue=None, arank=0, atext='?'): + if akind is not None: + self.kind = akind + if avalue is not None: + self.value = avalue + self.rank = arank + # rank is an internal gizmo (operator precedence); + # it's used in reconstructing formula text. + self.text = atext + + def __repr__(self): + kind_text = okind_dict.get(self.kind, "?Unknown kind?") + return "Operand(kind=%s, value=%r, text=%r)" \ + % (kind_text, self.value, self.text) + + +class Ref3D(tuple): + """ + Represents an absolute or relative 3-dimensional reference to a box + of one or more cells. + + The ``coords`` attribute is a tuple of the form:: + + (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi) + + where ``0 <= thingxlo <= thingx < thingxhi``. + + .. note:: + It is quite possible to have ``thingx > nthings``; for example + ``Print_Titles`` could have ``colxhi == 256`` and/or ``rowxhi == 65536`` + irrespective of how many columns/rows are actually used in the worksheet. + The caller will need to decide how to handle this situation. + Keyword: :class:`IndexError` :-) + + The components of the coords attribute are also available as individual + attributes: ``shtxlo``, ``shtxhi``, ``rowxlo``, ``rowxhi``, ``colxlo``, and + ``colxhi``. + + The ``relflags`` attribute is a 6-tuple of flags which indicate whether + the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0). + + .. note:: + There is necessarily no information available as to what cell(s) + the reference could possibly be relative to. The caller must decide what + if any use to make of ``oREL`` operands. + + .. note: + A partially relative reference may well be a typo. + For example, define name ``A1Z10`` as ``$a$1:$z10`` (missing ``$`` after + ``z``) while the cursor is on cell ``Sheet3!A27``. + + The resulting :class:`Ref3D` instance will have + ``coords = (2, 3, 0, -16, 0, 26)`` + and ``relflags = (0, 0, 0, 1, 0, 0).<br> + + So far, only one possibility of a sheet-relative component in + a reference has been noticed: a 2D reference located in the + "current sheet". + + This will appear as ``coords = (0, 1, ...)`` and + ``relflags = (1, 1, ...)``. + + .. versionadded:: 0.6.0 + """ + + def __init__(self, atuple): + self.coords = atuple[0:6] + self.relflags = atuple[6:12] + if not self.relflags: + self.relflags = (0, 0, 0, 0, 0, 0) + (self.shtxlo, self.shtxhi, + self.rowxlo, self.rowxhi, + self.colxlo, self.colxhi) = self.coords + + def __repr__(self): + if not self.relflags or self.relflags == (0, 0, 0, 0, 0, 0): + return "Ref3D(coords=%r)" % (self.coords, ) + else: + return "Ref3D(coords=%r, relflags=%r)" \ + % (self.coords, self.relflags) + +tAdd = 0x03 +tSub = 0x04 +tMul = 0x05 +tDiv = 0x06 +tPower = 0x07 +tConcat = 0x08 +tLT, tLE, tEQ, tGE, tGT, tNE = range(0x09, 0x0F) + + +def nop(x): + return x + +def _opr_pow(x, y): return x ** y + +def _opr_lt(x, y): return x < y +def _opr_le(x, y): return x <= y +def _opr_eq(x, y): return x == y +def _opr_ge(x, y): return x >= y +def _opr_gt(x, y): return x > y +def _opr_ne(x, y): return x != y + +def num2strg(num): + """ + Attempt to emulate Excel's default conversion from number to string. + """ + s = str(num) + if s.endswith(".0"): + s = s[:-2] + return s + +_arith_argdict = {oNUM: nop, oSTRG: float} +_cmp_argdict = {oNUM: nop, oSTRG: nop} +# Seems no conversions done on relops; in Excel, "1" > 9 produces TRUE. +_strg_argdict = {oNUM:num2strg, oSTRG:nop} +binop_rules = { + tAdd: (_arith_argdict, oNUM, opr.add, 30, '+'), + tSub: (_arith_argdict, oNUM, opr.sub, 30, '-'), + tMul: (_arith_argdict, oNUM, opr.mul, 40, '*'), + tDiv: (_arith_argdict, oNUM, opr.truediv, 40, '/'), + tPower: (_arith_argdict, oNUM, _opr_pow, 50, '^',), + tConcat:(_strg_argdict, oSTRG, opr.add, 20, '&'), + tLT: (_cmp_argdict, oBOOL, _opr_lt, 10, '<'), + tLE: (_cmp_argdict, oBOOL, _opr_le, 10, '<='), + tEQ: (_cmp_argdict, oBOOL, _opr_eq, 10, '='), + tGE: (_cmp_argdict, oBOOL, _opr_ge, 10, '>='), + tGT: (_cmp_argdict, oBOOL, _opr_gt, 10, '>'), + tNE: (_cmp_argdict, oBOOL, _opr_ne, 10, '<>'), +} + +unop_rules = { + 0x13: (lambda x: -x, 70, '-', ''), # unary minus + 0x12: (lambda x: x, 70, '+', ''), # unary plus + 0x14: (lambda x: x / 100.0, 60, '', '%'),# percent +} + +LEAF_RANK = 90 +FUNC_RANK = 90 + +STACK_ALARM_LEVEL = 5 +STACK_PANIC_LEVEL = 10 + +def evaluate_name_formula(bk, nobj, namex, blah=0, level=0): + if level > STACK_ALARM_LEVEL: + blah = 1 + data = nobj.raw_formula + fmlalen = nobj.basic_formula_len + bv = bk.biff_version + reldelta = 1 # All defined name formulas use "Method B" [OOo docs] + if blah: + print("::: evaluate_name_formula %r %r %d %d %r level=%d" + % (namex, nobj.name, fmlalen, bv, data, level), file=bk.logfile) + hex_char_dump(data, 0, fmlalen, fout=bk.logfile) + if level > STACK_PANIC_LEVEL: + raise XLRDError("Excessive indirect references in NAME formula") + sztab = szdict[bv] + pos = 0 + stack = [] + any_rel = 0 + any_err = 0 + any_external = 0 + unk_opnd = Operand(oUNK, None) + error_opnd = Operand(oERR, None) + spush = stack.append + + def do_binop(opcd, stk): + assert len(stk) >= 2 + bop = stk.pop() + aop = stk.pop() + argdict, result_kind, func, rank, sym = binop_rules[opcd] + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + resop = Operand(result_kind, None, rank, otext) + try: + bconv = argdict[bop.kind] + aconv = argdict[aop.kind] + except KeyError: + stk.append(resop) + return + if bop.value is None or aop.value is None: + stk.append(resop) + return + bval = bconv(bop.value) + aval = aconv(aop.value) + result = func(aval, bval) + if result_kind == oBOOL: + result = 1 if result else 0 + resop.value = result + stk.append(resop) + + def do_unaryop(opcode, result_kind, stk): + assert len(stk) >= 1 + aop = stk.pop() + val = aop.value + func, rank, sym1, sym2 = unop_rules[opcode] + otext = ''.join([ + sym1, + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym2, + ]) + if val is not None: + val = func(val) + stk.append(Operand(result_kind, val, rank, otext)) + + def not_in_name_formula(op_arg, oname_arg): + msg = "ERROR *** Token 0x%02x (%s) found in NAME formula" \ + % (op_arg, oname_arg) + raise FormulaError(msg) + + if fmlalen == 0: + stack = [unk_opnd] + + while 0 <= pos < fmlalen: + op = BYTES_ORD(data[pos]) + opcode = op & 0x1f + optype = (op & 0x60) >> 5 + if optype: + opx = opcode + 32 + else: + opx = opcode + oname = onames[opx] # + [" RVA"][optype] + sz = sztab[opx] + if blah: + print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" + % (pos, op, oname, sz, opcode, optype), file=bk.logfile) + print("Stack =", stack, file=bk.logfile) + if sz == -2: + msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \ + % (op, oname, bv) + raise FormulaError(msg) + if not optype: + if 0x00 <= opcode <= 0x02: # unk_opnd, tExp, tTbl + not_in_name_formula(op, oname) + elif 0x03 <= opcode <= 0x0E: + # Add, Sub, Mul, Div, Power + # tConcat + # tLT, ..., tNE + do_binop(opcode, stack) + elif opcode == 0x0F: # tIsect + if blah: print("tIsect pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + sym = ' ' + rank = 80 ########## check ####### + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + res = Operand(oREF) + res.text = otext + if bop.kind == oERR or aop.kind == oERR: + res.kind = oERR + elif bop.kind == oUNK or aop.kind == oUNK: + # This can happen with undefined + # (go search in the current sheet) labels. + # For example =Bob Sales + # Each label gets a NAME record with an empty formula (!) + # Evaluation of the tName token classifies it as oUNK + # res.kind = oREF + pass + elif bop.kind == oREF == aop.kind: + if aop.value is not None and bop.value is not None: + assert len(aop.value) == 1 + assert len(bop.value) == 1 + coords = do_box_funcs( + tIsectFuncs, aop.value[0], bop.value[0]) + res.value = [Ref3D(coords)] + elif bop.kind == oREL == aop.kind: + res.kind = oREL + if aop.value is not None and bop.value is not None: + assert len(aop.value) == 1 + assert len(bop.value) == 1 + coords = do_box_funcs( + tIsectFuncs, aop.value[0], bop.value[0]) + relfa = aop.value[0].relflags + relfb = bop.value[0].relflags + if relfa == relfb: + res.value = [Ref3D(coords + relfa)] + else: + pass + spush(res) + if blah: print("tIsect post", stack, file=bk.logfile) + elif opcode == 0x10: # tList + if blah: print("tList pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + sym = ',' + rank = 80 ########## check ####### + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + res = Operand(oREF, None, rank, otext) + if bop.kind == oERR or aop.kind == oERR: + res.kind = oERR + elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL): + res.kind = oREF + if aop.kind == oREL or bop.kind == oREL: + res.kind = oREL + if aop.value is not None and bop.value is not None: + assert len(aop.value) >= 1 + assert len(bop.value) == 1 + res.value = aop.value + bop.value + else: + pass + spush(res) + if blah: print("tList post", stack, file=bk.logfile) + elif opcode == 0x11: # tRange + if blah: print("tRange pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + sym = ':' + rank = 80 ########## check ####### + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + res = Operand(oREF, None, rank, otext) + if bop.kind == oERR or aop.kind == oERR: + res = oERR + elif bop.kind == oREF == aop.kind: + if aop.value is not None and bop.value is not None: + assert len(aop.value) == 1 + assert len(bop.value) == 1 + coords = do_box_funcs( + tRangeFuncs, aop.value[0], bop.value[0]) + res.value = [Ref3D(coords)] + elif bop.kind == oREL == aop.kind: + res.kind = oREL + if aop.value is not None and bop.value is not None: + assert len(aop.value) == 1 + assert len(bop.value) == 1 + coords = do_box_funcs( + tRangeFuncs, aop.value[0], bop.value[0]) + relfa = aop.value[0].relflags + relfb = bop.value[0].relflags + if relfa == relfb: + res.value = [Ref3D(coords + relfa)] + else: + pass + spush(res) + if blah: print("tRange post", stack, file=bk.logfile) + elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent + do_unaryop(opcode, oNUM, stack) + elif opcode == 0x15: # tParen + # source cosmetics + pass + elif opcode == 0x16: # tMissArg + spush(Operand(oMSNG, None, LEAF_RANK, '')) + elif opcode == 0x17: # tStr + if bv <= 70: + strg, newpos = unpack_string_update_pos( + data, pos+1, bk.encoding, lenlen=1) + else: + strg, newpos = unpack_unicode_update_pos( + data, pos+1, lenlen=1) + sz = newpos - pos + if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile) + text = '"' + strg.replace('"', '""') + '"' + spush(Operand(oSTRG, strg, LEAF_RANK, text)) + elif opcode == 0x18: # tExtended + # new with BIFF 8 + assert bv >= 80 + # not in OOo docs + raise FormulaError("tExtended token not implemented") + elif opcode == 0x19: # tAttr + subop, nc = unpack("<BH", data[pos+1:pos+4]) + subname = tAttrNames.get(subop, "??Unknown??") + if subop == 0x04: # Choose + sz = nc * 2 + 6 + elif subop == 0x10: # Sum (single arg) + sz = 4 + if blah: print("tAttrSum", stack, file=bk.logfile) + assert len(stack) >= 1 + aop = stack[-1] + otext = 'SUM(%s)' % aop.text + stack[-1] = Operand(oNUM, None, FUNC_RANK, otext) + else: + sz = 4 + if blah: + print(" subop=%02xh subname=t%s sz=%d nc=%02xh" + % (subop, subname, sz, nc), file=bk.logfile) + elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet + assert bv < 50 + raise FormulaError("tSheet & tEndsheet tokens not implemented") + elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum + inx = opcode - 0x1C + nb = [1, 1, 2, 8][inx] + kind = [oERR, oBOOL, oNUM, oNUM][inx] + value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb]) + if inx == 2: # tInt + value = float(value) + text = str(value) + elif inx == 3: # tNum + text = str(value) + elif inx == 1: # tBool + text = ('FALSE', 'TRUE')[value] + else: + text = '"' +error_text_from_code[value] + '"' + spush(Operand(kind, value, LEAF_RANK, text)) + else: + raise FormulaError("Unhandled opcode: 0x%02x" % opcode) + if sz <= 0: + raise FormulaError("Size not set for opcode 0x%02x" % opcode) + pos += sz + continue + if opcode == 0x00: # tArray + spush(unk_opnd) + elif opcode == 0x01: # tFunc + nb = 1 + int(bv >= 40) + funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0] + func_attrs = func_defs.get(funcx, None) + if not func_attrs: + print("*** formula/tFunc unknown FuncID:%d" + % funcx, file=bk.logfile) + spush(unk_opnd) + else: + func_name, nargs = func_attrs[:2] + if blah: + print(" FuncID=%d name=%s nargs=%d" + % (funcx, func_name, nargs), file=bk.logfile) + assert len(stack) >= nargs + if nargs: + argtext = listsep.join(arg.text for arg in stack[-nargs:]) + otext = "%s(%s)" % (func_name, argtext) + del stack[-nargs:] + else: + otext = func_name + "()" + res = Operand(oUNK, None, FUNC_RANK, otext) + spush(res) + elif opcode == 0x02: #tFuncVar + nb = 1 + int(bv >= 40) + nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb]) + prompt, nargs = divmod(nargs, 128) + macro, funcx = divmod(funcx, 32768) + if blah: + print(" FuncID=%d nargs=%d macro=%d prompt=%d" + % (funcx, nargs, macro, prompt), file=bk.logfile) + func_attrs = func_defs.get(funcx, None) + if not func_attrs: + print("*** formula/tFuncVar unknown FuncID:%d" + % funcx, file=bk.logfile) + spush(unk_opnd) + else: + func_name, minargs, maxargs = func_attrs[:3] + if blah: + print(" name: %r, min~max args: %d~%d" + % (func_name, minargs, maxargs), file=bk.logfile) + assert minargs <= nargs <= maxargs + assert len(stack) >= nargs + assert len(stack) >= nargs + argtext = listsep.join(arg.text for arg in stack[-nargs:]) + otext = "%s(%s)" % (func_name, argtext) + res = Operand(oUNK, None, FUNC_RANK, otext) + if funcx == 1: # IF + testarg = stack[-nargs] + if testarg.kind not in (oNUM, oBOOL): + if blah and testarg.kind != oUNK: + print("IF testarg kind?", file=bk.logfile) + elif testarg.value not in (0, 1): + if blah and testarg.value is not None: + print("IF testarg value?", file=bk.logfile) + else: + if nargs == 2 and not testarg.value: + # IF(FALSE, tv) => FALSE + res.kind, res.value = oBOOL, 0 + else: + respos = -nargs + 2 - int(testarg.value) + chosen = stack[respos] + if chosen.kind == oMSNG: + res.kind, res.value = oNUM, 0 + else: + res.kind, res.value = chosen.kind, chosen.value + if blah: + print("$$$$$$ IF => constant", file=bk.logfile) + elif funcx == 100: # CHOOSE + testarg = stack[-nargs] + if testarg.kind == oNUM: + if 1 <= testarg.value < nargs: + chosen = stack[-nargs + int(testarg.value)] + if chosen.kind == oMSNG: + res.kind, res.value = oNUM, 0 + else: + res.kind, res.value = chosen.kind, chosen.value + del stack[-nargs:] + spush(res) + elif opcode == 0x03: #tName + tgtnamex = unpack("<H", data[pos+1:pos+3])[0] - 1 + # Only change with BIFF version is number of trailing UNUSED bytes! + if blah: print(" tgtnamex=%d" % tgtnamex, file=bk.logfile) + tgtobj = bk.name_obj_list[tgtnamex] + if not tgtobj.evaluated: + ### recursive ### + evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1) + if tgtobj.macro or tgtobj.binary or tgtobj.any_err: + if blah: + tgtobj.dump( + bk.logfile, + header="!!! tgtobj has problems!!!", + footer="----------- --------", + ) + res = Operand(oUNK, None) + any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err + any_rel = any_rel or tgtobj.any_rel + else: + assert len(tgtobj.stack) == 1 + res = copy.deepcopy(tgtobj.stack[0]) + res.rank = LEAF_RANK + if tgtobj.scope == -1: + res.text = tgtobj.name + else: + res.text = "%s!%s" \ + % (bk._sheet_names[tgtobj.scope], tgtobj.name) + if blah: + print(" tName: setting text to", repr(res.text), file=bk.logfile) + spush(res) + elif opcode == 0x04: # tRef + # not_in_name_formula(op, oname) + res = get_cell_addr(data, pos+1, bv, reldelta) + if blah: print(" ", res, file=bk.logfile) + rowx, colx, row_rel, col_rel = res + shx1 = shx2 = 0 ####### N.B. relative to the CURRENT SHEET + any_rel = 1 + coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) + if blah: print(" ", coords, file=bk.logfile) + res = Operand(oUNK, None) + if optype == 1: + relflags = (1, 1, row_rel, row_rel, col_rel, col_rel) + res = Operand(oREL, [Ref3D(coords + relflags)]) + spush(res) + elif opcode == 0x05: # tArea + # not_in_name_formula(op, oname) + res1, res2 = get_cell_range_addr(data, pos+1, bv, reldelta) + if blah: print(" ", res1, res2, file=bk.logfile) + rowx1, colx1, row_rel1, col_rel1 = res1 + rowx2, colx2, row_rel2, col_rel2 = res2 + shx1 = shx2 = 0 ####### N.B. relative to the CURRENT SHEET + any_rel = 1 + coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) + if blah: print(" ", coords, file=bk.logfile) + res = Operand(oUNK, None) + if optype == 1: + relflags = (1, 1, row_rel1, row_rel2, col_rel1, col_rel2) + res = Operand(oREL, [Ref3D(coords + relflags)]) + spush(res) + elif opcode == 0x06: # tMemArea + not_in_name_formula(op, oname) + elif opcode == 0x09: # tMemFunc + nb = unpack("<H", data[pos+1:pos+3])[0] + if blah: print(" %d bytes of cell ref formula" % nb, file=bk.logfile) + # no effect on stack + elif opcode == 0x0C: #tRefN + not_in_name_formula(op, oname) + # res = get_cell_addr(data, pos+1, bv, reldelta=1) + # # note *ALL* tRefN usage has signed offset for relative addresses + # any_rel = 1 + # if blah: print >> bk.logfile, " ", res + # spush(res) + elif opcode == 0x0D: #tAreaN + not_in_name_formula(op, oname) + # res = get_cell_range_addr(data, pos+1, bv, reldelta=1) + # # note *ALL* tAreaN usage has signed offset for relative addresses + # any_rel = 1 + # if blah: print >> bk.logfile, " ", res + elif opcode == 0x1A: # tRef3d + if bv >= 80: + res = get_cell_addr(data, pos+3, bv, reldelta) + refx = unpack("<H", data[pos+1:pos+3])[0] + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + else: + res = get_cell_addr(data, pos+15, bv, reldelta) + raw_extshtx, raw_shx1, raw_shx2 = unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) + if blah: + print("tRef3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile) + shx1, shx2 = get_externsheet_local_range_b57( + bk, raw_extshtx, raw_shx1, raw_shx2, blah) + rowx, colx, row_rel, col_rel = res + is_rel = row_rel or col_rel + any_rel = any_rel or is_rel + coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) + any_err |= shx1 < -1 + if blah: print(" ", coords, file=bk.logfile) + res = Operand(oUNK, None) + if is_rel: + relflags = (0, 0, row_rel, row_rel, col_rel, col_rel) + ref3d = Ref3D(coords + relflags) + res.kind = oREL + res.text = rangename3drel(bk, ref3d, r1c1=1) + else: + ref3d = Ref3D(coords) + res.kind = oREF + res.text = rangename3d(bk, ref3d) + res.rank = LEAF_RANK + if optype == 1: + res.value = [ref3d] + spush(res) + elif opcode == 0x1B: # tArea3d + if bv >= 80: + res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta) + refx = unpack("<H", data[pos+1:pos+3])[0] + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + else: + res1, res2 = get_cell_range_addr(data, pos+15, bv, reldelta) + raw_extshtx, raw_shx1, raw_shx2 = unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) + if blah: + print("tArea3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile) + shx1, shx2 = get_externsheet_local_range_b57( + bk, raw_extshtx, raw_shx1, raw_shx2, blah) + any_err |= shx1 < -1 + rowx1, colx1, row_rel1, col_rel1 = res1 + rowx2, colx2, row_rel2, col_rel2 = res2 + is_rel = row_rel1 or col_rel1 or row_rel2 or col_rel2 + any_rel = any_rel or is_rel + coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) + if blah: print(" ", coords, file=bk.logfile) + res = Operand(oUNK, None) + if is_rel: + relflags = (0, 0, row_rel1, row_rel2, col_rel1, col_rel2) + ref3d = Ref3D(coords + relflags) + res.kind = oREL + res.text = rangename3drel(bk, ref3d, r1c1=1) + else: + ref3d = Ref3D(coords) + res.kind = oREF + res.text = rangename3d(bk, ref3d) + res.rank = LEAF_RANK + if optype == 1: + res.value = [ref3d] + + spush(res) + elif opcode == 0x19: # tNameX + dodgy = 0 + res = Operand(oUNK, None) + if bv >= 80: + refx, tgtnamex = unpack("<HH", data[pos+1:pos+5]) + tgtnamex -= 1 + origrefx = refx + else: + refx, tgtnamex = unpack("<hxxxxxxxxH", data[pos+1:pos+13]) + tgtnamex -= 1 + origrefx = refx + if refx > 0: + refx -= 1 + elif refx < 0: + refx = -refx - 1 + else: + dodgy = 1 + if blah: + print(" origrefx=%d refx=%d tgtnamex=%d dodgy=%d" + % (origrefx, refx, tgtnamex, dodgy), file=bk.logfile) + if tgtnamex == namex: + if blah: print("!!!! Self-referential !!!!", file=bk.logfile) + dodgy = any_err = 1 + if not dodgy: + if bv >= 80: + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + elif origrefx > 0: + shx1, shx2 = (-4, -4) # external ref + else: + exty = bk._externsheet_type_b57[refx] + if exty == 4: # non-specific sheet in own doc't + shx1, shx2 = (-1, -1) # internal, any sheet + else: + shx1, shx2 = (-666, -666) + if dodgy or shx1 < -1: + otext = "<<Name #%d in external(?) file #%d>>" \ + % (tgtnamex, origrefx) + res = Operand(oUNK, None, LEAF_RANK, otext) + else: + tgtobj = bk.name_obj_list[tgtnamex] + if not tgtobj.evaluated: + ### recursive ### + evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1) + if tgtobj.macro or tgtobj.binary or tgtobj.any_err: + if blah: + tgtobj.dump( + bk.logfile, + header="!!! bad tgtobj !!!", + footer="------------------", + ) + res = Operand(oUNK, None) + any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err + any_rel = any_rel or tgtobj.any_rel + else: + assert len(tgtobj.stack) == 1 + res = copy.deepcopy(tgtobj.stack[0]) + res.rank = LEAF_RANK + if tgtobj.scope == -1: + res.text = tgtobj.name + else: + res.text = "%s!%s" \ + % (bk._sheet_names[tgtobj.scope], tgtobj.name) + if blah: + print(" tNameX: setting text to", repr(res.text), file=bk.logfile) + spush(res) + elif opcode in error_opcodes: + any_err = 1 + spush(error_opnd) + else: + if blah: + print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile) + any_err = 1 + if sz <= 0: + raise FormulaError("Fatal: token size is not positive") + pos += sz + any_rel = not not any_rel + if blah: + fprintf(bk.logfile, "End of formula. level=%d any_rel=%d any_err=%d stack=%r\n", + level, not not any_rel, any_err, stack) + if len(stack) >= 2: + print("*** Stack has unprocessed args", file=bk.logfile) + print(file=bk.logfile) + nobj.stack = stack + if len(stack) != 1: + nobj.result = None + else: + nobj.result = stack[0] + nobj.any_rel = any_rel + nobj.any_err = any_err + nobj.any_external = any_external + nobj.evaluated = 1 + +#### under construction ############################################################################# +def decompile_formula(bk, fmla, fmlalen, + fmlatype=None, browx=None, bcolx=None, + blah=0, level=0, r1c1=0): + if level > STACK_ALARM_LEVEL: + blah = 1 + reldelta = fmlatype in (FMLA_TYPE_SHARED, FMLA_TYPE_NAME, FMLA_TYPE_COND_FMT, FMLA_TYPE_DATA_VAL) + data = fmla + bv = bk.biff_version + if blah: + print("::: decompile_formula len=%d fmlatype=%r browx=%r bcolx=%r reldelta=%d %r level=%d" + % (fmlalen, fmlatype, browx, bcolx, reldelta, data, level), file=bk.logfile) + hex_char_dump(data, 0, fmlalen, fout=bk.logfile) + if level > STACK_PANIC_LEVEL: + raise XLRDError("Excessive indirect references in formula") + sztab = szdict[bv] + pos = 0 + stack = [] + any_rel = 0 + any_err = 0 + unk_opnd = Operand(oUNK, None) + error_opnd = Operand(oERR, None) + spush = stack.append + + def do_binop(opcd, stk): + assert len(stk) >= 2 + bop = stk.pop() + aop = stk.pop() + argdict, result_kind, func, rank, sym = binop_rules[opcd] + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + resop = Operand(result_kind, None, rank, otext) + stk.append(resop) + + def do_unaryop(opcode, result_kind, stk): + assert len(stk) >= 1 + aop = stk.pop() + func, rank, sym1, sym2 = unop_rules[opcode] + otext = ''.join([ + sym1, + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym2, + ]) + stk.append(Operand(result_kind, None, rank, otext)) + + def unexpected_opcode(op_arg, oname_arg): + msg = "ERROR *** Unexpected token 0x%02x (%s) found in formula type %s" \ + % (op_arg, oname_arg, FMLA_TYPEDESCR_MAP[fmlatype]) + print(msg, file=bk.logfile) + # raise FormulaError(msg) + + if fmlalen == 0: + stack = [unk_opnd] + + while 0 <= pos < fmlalen: + op = BYTES_ORD(data[pos]) + opcode = op & 0x1f + optype = (op & 0x60) >> 5 + if optype: + opx = opcode + 32 + else: + opx = opcode + oname = onames[opx] # + [" RVA"][optype] + sz = sztab[opx] + if blah: + print("Pos:%d Op:0x%02x opname:t%s Sz:%d opcode:%02xh optype:%02xh" + % (pos, op, oname, sz, opcode, optype), file=bk.logfile) + print("Stack =", stack, file=bk.logfile) + if sz == -2: + msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \ + % (op, oname, bv) + raise FormulaError(msg) + if _TOKEN_NOT_ALLOWED(opx, 0) & fmlatype: + unexpected_opcode(op, oname) + if not optype: + if opcode <= 0x01: # tExp + if bv >= 30: + fmt = '<x2H' + else: + fmt = '<xHB' + assert pos == 0 and fmlalen == sz and not stack + rowx, colx = unpack(fmt, data) + text = "SHARED FMLA at rowx=%d colx=%d" % (rowx, colx) + spush(Operand(oUNK, None, LEAF_RANK, text)) + if not fmlatype & (FMLA_TYPE_CELL | FMLA_TYPE_ARRAY): + unexpected_opcode(op, oname) + elif 0x03 <= opcode <= 0x0E: + # Add, Sub, Mul, Div, Power + # tConcat + # tLT, ..., tNE + do_binop(opcode, stack) + elif opcode == 0x0F: # tIsect + if blah: print("tIsect pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + sym = ' ' + rank = 80 ########## check ####### + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + res = Operand(oREF) + res.text = otext + if bop.kind == oERR or aop.kind == oERR: + res.kind = oERR + elif bop.kind == oUNK or aop.kind == oUNK: + # This can happen with undefined + # (go search in the current sheet) labels. + # For example =Bob Sales + # Each label gets a NAME record with an empty formula (!) + # Evaluation of the tName token classifies it as oUNK + # res.kind = oREF + pass + elif bop.kind == oREF == aop.kind: + pass + elif bop.kind == oREL == aop.kind: + res.kind = oREL + else: + pass + spush(res) + if blah: print("tIsect post", stack, file=bk.logfile) + elif opcode == 0x10: # tList + if blah: print("tList pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + sym = ',' + rank = 80 ########## check ####### + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + res = Operand(oREF, None, rank, otext) + if bop.kind == oERR or aop.kind == oERR: + res.kind = oERR + elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL): + res.kind = oREF + if aop.kind == oREL or bop.kind == oREL: + res.kind = oREL + else: + pass + spush(res) + if blah: print("tList post", stack, file=bk.logfile) + elif opcode == 0x11: # tRange + if blah: print("tRange pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + sym = ':' + rank = 80 ########## check ####### + otext = ''.join([ + '('[:aop.rank < rank], + aop.text, + ')'[:aop.rank < rank], + sym, + '('[:bop.rank < rank], + bop.text, + ')'[:bop.rank < rank], + ]) + res = Operand(oREF, None, rank, otext) + if bop.kind == oERR or aop.kind == oERR: + res = oERR + elif bop.kind == oREF == aop.kind: + pass + else: + pass + spush(res) + if blah: print("tRange post", stack, file=bk.logfile) + elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent + do_unaryop(opcode, oNUM, stack) + elif opcode == 0x15: # tParen + # source cosmetics + pass + elif opcode == 0x16: # tMissArg + spush(Operand(oMSNG, None, LEAF_RANK, '')) + elif opcode == 0x17: # tStr + if bv <= 70: + strg, newpos = unpack_string_update_pos( + data, pos+1, bk.encoding, lenlen=1) + else: + strg, newpos = unpack_unicode_update_pos( + data, pos+1, lenlen=1) + sz = newpos - pos + if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile) + text = '"' + strg.replace('"', '""') + '"' + spush(Operand(oSTRG, None, LEAF_RANK, text)) + elif opcode == 0x18: # tExtended + # new with BIFF 8 + assert bv >= 80 + # not in OOo docs, don't even know how to determine its length + raise FormulaError("tExtended token not implemented") + elif opcode == 0x19: # tAttr + subop, nc = unpack("<BH", data[pos+1:pos+4]) + subname = tAttrNames.get(subop, "??Unknown??") + if subop == 0x04: # Choose + sz = nc * 2 + 6 + elif subop == 0x10: # Sum (single arg) + sz = 4 + if blah: print("tAttrSum", stack, file=bk.logfile) + assert len(stack) >= 1 + aop = stack[-1] + otext = 'SUM(%s)' % aop.text + stack[-1] = Operand(oNUM, None, FUNC_RANK, otext) + else: + sz = 4 + if blah: + print(" subop=%02xh subname=t%s sz=%d nc=%02xh" + % (subop, subname, sz, nc), file=bk.logfile) + elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet + assert bv < 50 + raise FormulaError("tSheet & tEndsheet tokens not implemented") + elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum + inx = opcode - 0x1C + nb = [1, 1, 2, 8][inx] + kind = [oERR, oBOOL, oNUM, oNUM][inx] + value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb]) + if inx == 2: # tInt + value = float(value) + text = str(value) + elif inx == 3: # tNum + text = str(value) + elif inx == 1: # tBool + text = ('FALSE', 'TRUE')[value] + else: + text = '"' +error_text_from_code[value] + '"' + spush(Operand(kind, None, LEAF_RANK, text)) + else: + raise FormulaError("Unhandled opcode: 0x%02x" % opcode) + if sz <= 0: + raise FormulaError("Size not set for opcode 0x%02x" % opcode) + pos += sz + continue + if opcode == 0x00: # tArray + spush(unk_opnd) + elif opcode == 0x01: # tFunc + nb = 1 + int(bv >= 40) + funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0] + func_attrs = func_defs.get(funcx, None) + if not func_attrs: + print("*** formula/tFunc unknown FuncID:%d" % funcx, file=bk.logfile) + spush(unk_opnd) + else: + func_name, nargs = func_attrs[:2] + if blah: + print(" FuncID=%d name=%s nargs=%d" + % (funcx, func_name, nargs), file=bk.logfile) + assert len(stack) >= nargs + if nargs: + argtext = listsep.join(arg.text for arg in stack[-nargs:]) + otext = "%s(%s)" % (func_name, argtext) + del stack[-nargs:] + else: + otext = func_name + "()" + res = Operand(oUNK, None, FUNC_RANK, otext) + spush(res) + elif opcode == 0x02: #tFuncVar + nb = 1 + int(bv >= 40) + nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb]) + prompt, nargs = divmod(nargs, 128) + macro, funcx = divmod(funcx, 32768) + if blah: + print(" FuncID=%d nargs=%d macro=%d prompt=%d" + % (funcx, nargs, macro, prompt), file=bk.logfile) + #### TODO #### if funcx == 255: # call add-in function + if funcx == 255: + func_attrs = ("CALL_ADDIN", 1, 30) + else: + func_attrs = func_defs.get(funcx, None) + if not func_attrs: + print("*** formula/tFuncVar unknown FuncID:%d" + % funcx, file=bk.logfile) + spush(unk_opnd) + else: + func_name, minargs, maxargs = func_attrs[:3] + if blah: + print(" name: %r, min~max args: %d~%d" + % (func_name, minargs, maxargs), file=bk.logfile) + assert minargs <= nargs <= maxargs + assert len(stack) >= nargs + assert len(stack) >= nargs + argtext = listsep.join(arg.text for arg in stack[-nargs:]) + otext = "%s(%s)" % (func_name, argtext) + res = Operand(oUNK, None, FUNC_RANK, otext) + del stack[-nargs:] + spush(res) + elif opcode == 0x03: #tName + tgtnamex = unpack("<H", data[pos+1:pos+3])[0] - 1 + # Only change with BIFF version is number of trailing UNUSED bytes! + if blah: print(" tgtnamex=%d" % tgtnamex, file=bk.logfile) + tgtobj = bk.name_obj_list[tgtnamex] + if tgtobj.scope == -1: + otext = tgtobj.name + else: + otext = "%s!%s" % (bk._sheet_names[tgtobj.scope], tgtobj.name) + if blah: + print(" tName: setting text to", repr(otext), file=bk.logfile) + res = Operand(oUNK, None, LEAF_RANK, otext) + spush(res) + elif opcode == 0x04: # tRef + res = get_cell_addr(data, pos+1, bv, reldelta, browx, bcolx) + if blah: print(" ", res, file=bk.logfile) + rowx, colx, row_rel, col_rel = res + is_rel = row_rel or col_rel + if is_rel: + okind = oREL + else: + okind = oREF + otext = cellnamerel(rowx, colx, row_rel, col_rel, browx, bcolx, r1c1) + res = Operand(okind, None, LEAF_RANK, otext) + spush(res) + elif opcode == 0x05: # tArea + res1, res2 = get_cell_range_addr( + data, pos+1, bv, reldelta, browx, bcolx) + if blah: print(" ", res1, res2, file=bk.logfile) + rowx1, colx1, row_rel1, col_rel1 = res1 + rowx2, colx2, row_rel2, col_rel2 = res2 + coords = (rowx1, rowx2+1, colx1, colx2+1) + relflags = (row_rel1, row_rel2, col_rel1, col_rel2) + if sum(relflags): # relative + okind = oREL + else: + okind = oREF + if blah: print(" ", coords, relflags, file=bk.logfile) + otext = rangename2drel(coords, relflags, browx, bcolx, r1c1) + res = Operand(okind, None, LEAF_RANK, otext) + spush(res) + elif opcode == 0x06: # tMemArea + not_in_name_formula(op, oname) + elif opcode == 0x09: # tMemFunc + nb = unpack("<H", data[pos+1:pos+3])[0] + if blah: print(" %d bytes of cell ref formula" % nb, file=bk.logfile) + # no effect on stack + elif opcode == 0x0C: #tRefN + res = get_cell_addr(data, pos+1, bv, reldelta, browx, bcolx) + # note *ALL* tRefN usage has signed offset for relative addresses + any_rel = 1 + if blah: print(" ", res, file=bk.logfile) + rowx, colx, row_rel, col_rel = res + is_rel = row_rel or col_rel + if is_rel: + okind = oREL + else: + okind = oREF + otext = cellnamerel(rowx, colx, row_rel, col_rel, browx, bcolx, r1c1) + res = Operand(okind, None, LEAF_RANK, otext) + spush(res) + elif opcode == 0x0D: #tAreaN + # res = get_cell_range_addr(data, pos+1, bv, reldelta, browx, bcolx) + # # note *ALL* tAreaN usage has signed offset for relative addresses + # any_rel = 1 + # if blah: print >> bk.logfile, " ", res + res1, res2 = get_cell_range_addr( + data, pos+1, bv, reldelta, browx, bcolx) + if blah: print(" ", res1, res2, file=bk.logfile) + rowx1, colx1, row_rel1, col_rel1 = res1 + rowx2, colx2, row_rel2, col_rel2 = res2 + coords = (rowx1, rowx2+1, colx1, colx2+1) + relflags = (row_rel1, row_rel2, col_rel1, col_rel2) + if sum(relflags): # relative + okind = oREL + else: + okind = oREF + if blah: print(" ", coords, relflags, file=bk.logfile) + otext = rangename2drel(coords, relflags, browx, bcolx, r1c1) + res = Operand(okind, None, LEAF_RANK, otext) + spush(res) + elif opcode == 0x1A: # tRef3d + if bv >= 80: + res = get_cell_addr(data, pos+3, bv, reldelta, browx, bcolx) + refx = unpack("<H", data[pos+1:pos+3])[0] + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + else: + res = get_cell_addr(data, pos+15, bv, reldelta, browx, bcolx) + raw_extshtx, raw_shx1, raw_shx2 = unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) + if blah: + print("tRef3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile) + shx1, shx2 = get_externsheet_local_range_b57( + bk, raw_extshtx, raw_shx1, raw_shx2, blah) + rowx, colx, row_rel, col_rel = res + is_rel = row_rel or col_rel + any_rel = any_rel or is_rel + coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) + any_err |= shx1 < -1 + if blah: print(" ", coords, file=bk.logfile) + res = Operand(oUNK, None) + if is_rel: + relflags = (0, 0, row_rel, row_rel, col_rel, col_rel) + ref3d = Ref3D(coords + relflags) + res.kind = oREL + res.text = rangename3drel(bk, ref3d, browx, bcolx, r1c1) + else: + ref3d = Ref3D(coords) + res.kind = oREF + res.text = rangename3d(bk, ref3d) + res.rank = LEAF_RANK + res.value = None + spush(res) + elif opcode == 0x1B: # tArea3d + if bv >= 80: + res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta) + refx = unpack("<H", data[pos+1:pos+3])[0] + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + else: + res1, res2 = get_cell_range_addr(data, pos+15, bv, reldelta) + raw_extshtx, raw_shx1, raw_shx2 = unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) + if blah: + print("tArea3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile) + shx1, shx2 = get_externsheet_local_range_b57( + bk, raw_extshtx, raw_shx1, raw_shx2, blah) + any_err |= shx1 < -1 + rowx1, colx1, row_rel1, col_rel1 = res1 + rowx2, colx2, row_rel2, col_rel2 = res2 + is_rel = row_rel1 or col_rel1 or row_rel2 or col_rel2 + any_rel = any_rel or is_rel + coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) + if blah: print(" ", coords, file=bk.logfile) + res = Operand(oUNK, None) + if is_rel: + relflags = (0, 0, row_rel1, row_rel2, col_rel1, col_rel2) + ref3d = Ref3D(coords + relflags) + res.kind = oREL + res.text = rangename3drel(bk, ref3d, browx, bcolx, r1c1) + else: + ref3d = Ref3D(coords) + res.kind = oREF + res.text = rangename3d(bk, ref3d) + res.rank = LEAF_RANK + spush(res) + elif opcode == 0x19: # tNameX + dodgy = 0 + res = Operand(oUNK, None) + if bv >= 80: + refx, tgtnamex = unpack("<HH", data[pos+1:pos+5]) + tgtnamex -= 1 + origrefx = refx + else: + refx, tgtnamex = unpack("<hxxxxxxxxH", data[pos+1:pos+13]) + tgtnamex -= 1 + origrefx = refx + if refx > 0: + refx -= 1 + elif refx < 0: + refx = -refx - 1 + else: + dodgy = 1 + if blah: + print(" origrefx=%d refx=%d tgtnamex=%d dodgy=%d" + % (origrefx, refx, tgtnamex, dodgy), file=bk.logfile) + # if tgtnamex == namex: + # if blah: print >> bk.logfile, "!!!! Self-referential !!!!" + # dodgy = any_err = 1 + if not dodgy: + if bv >= 80: + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + elif origrefx > 0: + shx1, shx2 = (-4, -4) # external ref + else: + exty = bk._externsheet_type_b57[refx] + if exty == 4: # non-specific sheet in own doc't + shx1, shx2 = (-1, -1) # internal, any sheet + else: + shx1, shx2 = (-666, -666) + okind = oUNK + ovalue = None + if shx1 == -5: # addin func name + okind = oSTRG + ovalue = bk.addin_func_names[tgtnamex] + otext = '"' + ovalue.replace('"', '""') + '"' + elif dodgy or shx1 < -1: + otext = "<<Name #%d in external(?) file #%d>>" \ + % (tgtnamex, origrefx) + else: + tgtobj = bk.name_obj_list[tgtnamex] + if tgtobj.scope == -1: + otext = tgtobj.name + else: + otext = "%s!%s" \ + % (bk._sheet_names[tgtobj.scope], tgtobj.name) + if blah: + print(" tNameX: setting text to", repr(res.text), file=bk.logfile) + res = Operand(okind, ovalue, LEAF_RANK, otext) + spush(res) + elif opcode in error_opcodes: + any_err = 1 + spush(error_opnd) + else: + if blah: + print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile) + any_err = 1 + if sz <= 0: + raise FormulaError("Fatal: token size is not positive") + pos += sz + any_rel = not not any_rel + if blah: + print("End of formula. level=%d any_rel=%d any_err=%d stack=%r" % + (level, not not any_rel, any_err, stack), file=bk.logfile) + if len(stack) >= 2: + print("*** Stack has unprocessed args", file=bk.logfile) + print(file=bk.logfile) + + if len(stack) != 1: + result = None + else: + result = stack[0].text + return result + +#### under deconstruction ### +def dump_formula(bk, data, fmlalen, bv, reldelta, blah=0, isname=0): + if blah: + print("dump_formula", fmlalen, bv, len(data), file=bk.logfile) + hex_char_dump(data, 0, fmlalen, fout=bk.logfile) + assert bv >= 80 #### this function needs updating #### + sztab = szdict[bv] + pos = 0 + stack = [] + any_rel = 0 + any_err = 0 + spush = stack.append + while 0 <= pos < fmlalen: + op = BYTES_ORD(data[pos]) + opcode = op & 0x1f + optype = (op & 0x60) >> 5 + if optype: + opx = opcode + 32 + else: + opx = opcode + oname = onames[opx] # + [" RVA"][optype] + + sz = sztab[opx] + if blah: + print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" + % (pos, op, oname, sz, opcode, optype), file=bk.logfile) + if not optype: + if 0x01 <= opcode <= 0x02: # tExp, tTbl + # reference to a shared formula or table record + rowx, colx = unpack("<HH", data[pos+1:pos+5]) + if blah: print(" ", (rowx, colx), file=bk.logfile) + elif opcode == 0x10: # tList + if blah: print("tList pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + spush(aop + bop) + if blah: print("tlist post", stack, file=bk.logfile) + elif opcode == 0x11: # tRange + if blah: print("tRange pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + assert len(aop) == 1 + assert len(bop) == 1 + result = do_box_funcs(tRangeFuncs, aop[0], bop[0]) + spush(result) + if blah: print("tRange post", stack, file=bk.logfile) + elif opcode == 0x0F: # tIsect + if blah: print("tIsect pre", stack, file=bk.logfile) + assert len(stack) >= 2 + bop = stack.pop() + aop = stack.pop() + assert len(aop) == 1 + assert len(bop) == 1 + result = do_box_funcs(tIsectFuncs, aop[0], bop[0]) + spush(result) + if blah: print("tIsect post", stack, file=bk.logfile) + elif opcode == 0x19: # tAttr + subop, nc = unpack("<BH", data[pos+1:pos+4]) + subname = tAttrNames.get(subop, "??Unknown??") + if subop == 0x04: # Choose + sz = nc * 2 + 6 + else: + sz = 4 + if blah: print(" subop=%02xh subname=t%s sz=%d nc=%02xh" % (subop, subname, sz, nc), file=bk.logfile) + elif opcode == 0x17: # tStr + if bv <= 70: + nc = BYTES_ORD(data[pos+1]) + strg = data[pos+2:pos+2+nc] # left in 8-bit encoding + sz = nc + 2 + else: + strg, newpos = unpack_unicode_update_pos(data, pos+1, lenlen=1) + sz = newpos - pos + if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile) + else: + if sz <= 0: + print("**** Dud size; exiting ****", file=bk.logfile) + return + pos += sz + continue + if opcode == 0x00: # tArray + pass + elif opcode == 0x01: # tFunc + nb = 1 + int(bv >= 40) + funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb]) + if blah: print(" FuncID=%d" % funcx, file=bk.logfile) + elif opcode == 0x02: #tFuncVar + nb = 1 + int(bv >= 40) + nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb]) + prompt, nargs = divmod(nargs, 128) + macro, funcx = divmod(funcx, 32768) + if blah: print(" FuncID=%d nargs=%d macro=%d prompt=%d" % (funcx, nargs, macro, prompt), file=bk.logfile) + elif opcode == 0x03: #tName + namex = unpack("<H", data[pos+1:pos+3]) + # Only change with BIFF version is the number of trailing UNUSED bytes!!! + if blah: print(" namex=%d" % namex, file=bk.logfile) + elif opcode == 0x04: # tRef + res = get_cell_addr(data, pos+1, bv, reldelta) + if blah: print(" ", res, file=bk.logfile) + elif opcode == 0x05: # tArea + res = get_cell_range_addr(data, pos+1, bv, reldelta) + if blah: print(" ", res, file=bk.logfile) + elif opcode == 0x09: # tMemFunc + nb = unpack("<H", data[pos+1:pos+3])[0] + if blah: print(" %d bytes of cell ref formula" % nb, file=bk.logfile) + elif opcode == 0x0C: #tRefN + res = get_cell_addr(data, pos+1, bv, reldelta=1) + # note *ALL* tRefN usage has signed offset for relative addresses + any_rel = 1 + if blah: print(" ", res, file=bk.logfile) + elif opcode == 0x0D: #tAreaN + res = get_cell_range_addr(data, pos+1, bv, reldelta=1) + # note *ALL* tAreaN usage has signed offset for relative addresses + any_rel = 1 + if blah: print(" ", res, file=bk.logfile) + elif opcode == 0x1A: # tRef3d + refx = unpack("<H", data[pos+1:pos+3])[0] + res = get_cell_addr(data, pos+3, bv, reldelta) + if blah: print(" ", refx, res, file=bk.logfile) + rowx, colx, row_rel, col_rel = res + any_rel = any_rel or row_rel or col_rel + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + any_err |= shx1 < -1 + coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) + if blah: print(" ", coords, file=bk.logfile) + if optype == 1: spush([coords]) + elif opcode == 0x1B: # tArea3d + refx = unpack("<H", data[pos+1:pos+3])[0] + res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta) + if blah: print(" ", refx, res1, res2, file=bk.logfile) + rowx1, colx1, row_rel1, col_rel1 = res1 + rowx2, colx2, row_rel2, col_rel2 = res2 + any_rel = any_rel or row_rel1 or col_rel1 or row_rel2 or col_rel2 + shx1, shx2 = get_externsheet_local_range(bk, refx, blah) + any_err |= shx1 < -1 + coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) + if blah: print(" ", coords, file=bk.logfile) + if optype == 1: spush([coords]) + elif opcode == 0x19: # tNameX + refx, namex = unpack("<HH", data[pos+1:pos+5]) + if blah: print(" refx=%d namex=%d" % (refx, namex), file=bk.logfile) + elif opcode in error_opcodes: + any_err = 1 + else: + if blah: print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile) + any_err = 1 + if sz <= 0: + print("**** Dud size; exiting ****", file=bk.logfile) + return + pos += sz + if blah: + print("End of formula. any_rel=%d any_err=%d stack=%r" % + (not not any_rel, any_err, stack), file=bk.logfile) + if len(stack) >= 2: + print("*** Stack has unprocessed args", file=bk.logfile) + +# === Some helper functions for displaying cell references === + +# I'm aware of only one possibility of a sheet-relative component in +# a reference: a 2D reference located in the "current sheet". +# xlrd stores this internally with bounds of (0, 1, ...) and +# relative flags of (1, 1, ...). These functions display the +# sheet component as empty, just like Excel etc. + +def rownamerel(rowx, rowxrel, browx=None, r1c1=0): + # if no base rowx is provided, we have to return r1c1 + if browx is None: + r1c1 = True + if not rowxrel: + if r1c1: + return "R%d" % (rowx+1) + return "$%d" % (rowx+1) + if r1c1: + if rowx: + return "R[%d]" % rowx + return "R" + return "%d" % ((browx + rowx) % 65536 + 1) + +def colnamerel(colx, colxrel, bcolx=None, r1c1=0): + # if no base colx is provided, we have to return r1c1 + if bcolx is None: + r1c1 = True + if not colxrel: + if r1c1: + return "C%d" % (colx + 1) + return "$" + colname(colx) + if r1c1: + if colx: + return "C[%d]" % colx + return "C" + return colname((bcolx + colx) % 256) + +def cellname(rowx, colx): + """Utility function: ``(5, 7)`` => ``'H6'``""" + return "%s%d" % (colname(colx), rowx+1) + +def cellnameabs(rowx, colx, r1c1=0): + """Utility function: ``(5, 7)`` => ``'$H$6'``""" + if r1c1: + return "R%dC%d" % (rowx+1, colx+1) + return "$%s$%d" % (colname(colx), rowx+1) + +def cellnamerel(rowx, colx, rowxrel, colxrel, browx=None, bcolx=None, r1c1=0): + if not rowxrel and not colxrel: + return cellnameabs(rowx, colx, r1c1) + if (rowxrel and browx is None) or (colxrel and bcolx is None): + # must flip the whole cell into R1C1 mode + r1c1 = True + c = colnamerel(colx, colxrel, bcolx, r1c1) + r = rownamerel(rowx, rowxrel, browx, r1c1) + if r1c1: + return r + c + return c + r + +def colname(colx): + """Utility function: ``7`` => ``'H'``, ``27`` => ``'AB'``""" + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + if colx <= 25: + return alphabet[colx] + else: + xdiv26, xmod26 = divmod(colx, 26) + return alphabet[xdiv26 - 1] + alphabet[xmod26] + +def rangename2d(rlo, rhi, clo, chi, r1c1=0): + """ ``(5, 20, 7, 10)`` => ``'$H$6:$J$20'`` """ + if r1c1: + return + if rhi == rlo+1 and chi == clo+1: + return cellnameabs(rlo, clo, r1c1) + return "%s:%s" % (cellnameabs(rlo, clo, r1c1), cellnameabs(rhi-1, chi-1, r1c1)) + +def rangename2drel(rlo_rhi_clo_chi, rlorel_rhirel_clorel_chirel, browx=None, bcolx=None, r1c1=0): + rlo, rhi, clo, chi = rlo_rhi_clo_chi + rlorel, rhirel, clorel, chirel = rlorel_rhirel_clorel_chirel + if (rlorel or rhirel) and browx is None: + r1c1 = True + if (clorel or chirel) and bcolx is None: + r1c1 = True + return "%s:%s" % ( + cellnamerel(rlo, clo, rlorel, clorel, browx, bcolx, r1c1), + cellnamerel(rhi-1, chi-1, rhirel, chirel, browx, bcolx, r1c1), + ) + + +def rangename3d(book, ref3d): + """ + Utility function: + ``Ref3D(1, 4, 5, 20, 7, 10)`` => + ``'Sheet2:Sheet3!$H$6:$J$20'`` + (assuming Excel's default sheetnames) + """ + coords = ref3d.coords + return "%s!%s" % ( + sheetrange(book, *coords[:2]), + rangename2d(*coords[2:6])) + +def rangename3drel(book, ref3d, browx=None, bcolx=None, r1c1=0): + """ + Utility function: + ``Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))`` + + In R1C1 mode => ``'Sheet1!R[-32]C[-13]:R[-23]C[12]'`` + + In A1 mode => depends on base cell ``(browx, bcolx)`` + """ + coords = ref3d.coords + relflags = ref3d.relflags + shdesc = sheetrangerel(book, coords[:2], relflags[:2]) + rngdesc = rangename2drel(coords[2:6], relflags[2:6], browx, bcolx, r1c1) + if not shdesc: + return rngdesc + return "%s!%s" % (shdesc, rngdesc) + +def quotedsheetname(shnames, shx): + if shx >= 0: + shname = shnames[shx] + else: + shname = { + -1: "?internal; any sheet?", + -2: "internal; deleted sheet", + -3: "internal; macro sheet", + -4: "<<external>>", + }.get(shx, "?error %d?" % shx) + if "'" in shname: + return "'" + shname.replace("'", "''") + "'" + if " " in shname: + return "'" + shname + "'" + return shname + +def sheetrange(book, slo, shi): + shnames = book.sheet_names() + shdesc = quotedsheetname(shnames, slo) + if slo != shi-1: + shdesc += ":" + quotedsheetname(shnames, shi-1) + return shdesc + +def sheetrangerel(book, srange, srangerel): + slo, shi = srange + slorel, shirel = srangerel + if not slorel and not shirel: + return sheetrange(book, slo, shi) + assert (slo == 0 == shi-1) and slorel and shirel + return "" + +# ============================================================== diff --git a/.venv/lib/python3.9/site-packages/xlrd/info.py b/.venv/lib/python3.9/site-packages/xlrd/info.py new file mode 100644 index 00000000..f26b6bb2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/info.py @@ -0,0 +1 @@ +__version__ = __VERSION__ = "2.0.1" diff --git a/.venv/lib/python3.9/site-packages/xlrd/sheet.py b/.venv/lib/python3.9/site-packages/xlrd/sheet.py new file mode 100644 index 00000000..a8313980 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/sheet.py @@ -0,0 +1,2490 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2013 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. + +from __future__ import print_function + +from array import array +from struct import calcsize, unpack + +from .biffh import * +from .formatting import Format, nearest_colour_index +from .formula import ( + FMLA_TYPE_CELL, FMLA_TYPE_SHARED, decompile_formula, dump_formula, + rangename2d, +) +from .timemachine import * + +DEBUG = 0 +OBJ_MSO_DEBUG = 0 + +_WINDOW2_options = ( + # Attribute names and initial values to use in case + # a WINDOW2 record is not written. + ("show_formulas", 0), + ("show_grid_lines", 1), + ("show_sheet_headers", 1), + ("panes_are_frozen", 0), + ("show_zero_values", 1), + ("automatic_grid_line_colour", 1), + ("columns_from_right_to_left", 0), + ("show_outline_symbols", 1), + ("remove_splits_if_pane_freeze_is_removed", 0), + # Multiple sheets can be selected, but only one can be active + # (hold down Ctrl and click multiple tabs in the file in OOo) + ("sheet_selected", 0), + # "sheet_visible" should really be called "sheet_active" + # and is 1 when this sheet is the sheet displayed when the file + # is open. More than likely only one sheet should ever be set as + # visible. + # This would correspond to the Book's sheet_active attribute, but + # that doesn't exist as WINDOW1 records aren't currently processed. + # The real thing is the visibility attribute from the BOUNDSHEET record. + ("sheet_visible", 0), + ("show_in_page_break_preview", 0), +) + + + +class Sheet(BaseObject): + """ + Contains the data for one worksheet. + + In the cell access functions, ``rowx`` is a row index, counting from + zero, and ``colx`` is a column index, counting from zero. + Negative values for row/column indexes and slice positions are supported in + the expected fashion. + + For information about cell types and cell values, refer to the documentation + of the :class:`Cell` class. + + .. warning:: + + You don't instantiate this class yourself. You access :class:`Sheet` + objects via the :class:`~xlrd.book.Book` object that + was returned when you called :func:`xlrd.open_workbook`. + """ + + #: Name of sheet. + name = '' + + #: A reference to the :class:`~xlrd.book.Book` object to which this sheet + #: belongs. + #: + #: Example usage: ``some_sheet.book.datemode`` + book = None + + #: Number of rows in sheet. A row index is in ``range(thesheet.nrows)``. + nrows = 0 + + #: Nominal number of columns in sheet. It is one more than the maximum + #: column index found, ignoring trailing empty cells. + #: See also the ``ragged_rows`` parameter to :func:`~xlrd.open_workbook` + #: and :meth:`~xlrd.sheet.Sheet.row_len`. + ncols = 0 + + + #: The map from a column index to a :class:`Colinfo` object. Often there is + #: an entry in ``COLINFO`` records for all column indexes in ``range(257)``. + #: + #: .. note:: + #: xlrd ignores the entry for the non-existent + #: 257th column. + #: + #: On the other hand, there may be no entry for unused columns. + #: + #: .. versionadded:: 0.6.1 + #: + #: Populated only if ``open_workbook(..., formatting_info=True)`` + colinfo_map = {} + + #: The map from a row index to a :class:`Rowinfo` object. + #: + #: ..note:: + #: It is possible to have missing entries -- at least one source of + #: XLS files doesn't bother writing ``ROW`` records. + #: + #: .. versionadded:: 0.6.1 + #: + #: Populated only if ``open_workbook(..., formatting_info=True)`` + rowinfo_map = {} + + #: List of address ranges of cells containing column labels. + #: These are set up in Excel by Insert > Name > Labels > Columns. + #: + #: .. versionadded:: 0.6.0 + #: + #: How to deconstruct the list: + #: + #: .. code-block:: python + #: + #: for crange in thesheet.col_label_ranges: + #: rlo, rhi, clo, chi = crange + #: for rx in xrange(rlo, rhi): + #: for cx in xrange(clo, chi): + #: print "Column label at (rowx=%d, colx=%d) is %r" \ + #: (rx, cx, thesheet.cell_value(rx, cx)) + col_label_ranges = [] + + #: List of address ranges of cells containing row labels. + #: For more details, see :attr:`col_label_ranges`. + #: + #: .. versionadded:: 0.6.0 + row_label_ranges = [] + + #: List of address ranges of cells which have been merged. + #: These are set up in Excel by Format > Cells > Alignment, then ticking + #: the "Merge cells" box. + #: + #: .. note:: + #: The upper limits are exclusive: i.e. ``[2, 3, 7, 9]`` only + #: spans two cells. + #: + #: .. note:: Extracted only if ``open_workbook(..., formatting_info=True)`` + #: + #: .. versionadded:: 0.6.1 + #: + #: How to deconstruct the list: + #: + #: .. code-block:: python + #: + #: for crange in thesheet.merged_cells: + #: rlo, rhi, clo, chi = crange + #: for rowx in xrange(rlo, rhi): + #: for colx in xrange(clo, chi): + #: # cell (rlo, clo) (the top left one) will carry the data + #: # and formatting info; the remainder will be recorded as + #: # blank cells, but a renderer will apply the formatting info + #: # for the top left cell (e.g. border, pattern) to all cells in + #: # the range. + merged_cells = [] + + #: Mapping of ``(rowx, colx)`` to list of ``(offset, font_index)`` tuples. + #: The offset defines where in the string the font begins to be used. + #: Offsets are expected to be in ascending order. + #: If the first offset is not zero, the meaning is that the cell's ``XF``'s + #: font should be used from offset 0. + #: + #: This is a sparse mapping. There is no entry for cells that are not + #: formatted with rich text. + #: + #: How to use: + #: + #: .. code-block:: python + #: + #: runlist = thesheet.rich_text_runlist_map.get((rowx, colx)) + #: if runlist: + #: for offset, font_index in runlist: + #: # do work here. + #: pass + #: + #: .. versionadded:: 0.7.2 + #: + #: Populated only if ``open_workbook(..., formatting_info=True)`` + rich_text_runlist_map = {} + + #: Default column width from ``DEFCOLWIDTH`` record, else ``None``. + #: From the OOo docs: + #: + #: Column width in characters, using the width of the zero character + #: from default font (first FONT record in the file). Excel adds some + #: extra space to the default width, depending on the default font and + #: default font size. The algorithm how to exactly calculate the resulting + #: column width is not known. + #: Example: The default width of 8 set in this record results in a column + #: width of 8.43 using Arial font with a size of 10 points. + #: + #: For the default hierarchy, refer to the :class:`Colinfo` class. + #: + #: .. versionadded:: 0.6.1 + defcolwidth = None + + #: Default column width from ``STANDARDWIDTH`` record, else ``None``. + #: + #: From the OOo docs: + #: + #: Default width of the columns in 1/256 of the width of the zero + #: character, using default font (first FONT record in the file). + #: + #: For the default hierarchy, refer to the :class:`Colinfo` class. + #: + #: .. versionadded:: 0.6.1 + standardwidth = None + + #: Default value to be used for a row if there is + #: no ``ROW`` record for that row. + #: From the *optional* ``DEFAULTROWHEIGHT`` record. + default_row_height = None + + #: Default value to be used for a row if there is + #: no ``ROW`` record for that row. + #: From the *optional* ``DEFAULTROWHEIGHT`` record. + default_row_height_mismatch = None + + #: Default value to be used for a row if there is + #: no ``ROW`` record for that row. + #: From the *optional* ``DEFAULTROWHEIGHT`` record. + default_row_hidden = None + + #: Default value to be used for a row if there is + #: no ``ROW`` record for that row. + #: From the *optional* ``DEFAULTROWHEIGHT`` record. + default_additional_space_above = None + + #: Default value to be used for a row if there is + #: no ``ROW`` record for that row. + #: From the *optional* ``DEFAULTROWHEIGHT`` record. + default_additional_space_below = None + + #: Visibility of the sheet: + #: :: + #: + #: 0 = visible + #: 1 = hidden (can be unhidden by user -- Format -> Sheet -> Unhide) + #: 2 = "very hidden" (can be unhidden only by VBA macro). + visibility = 0 + + #: A 256-element tuple corresponding to the contents of the GCW record for + #: this sheet. If no such record, treat as all bits zero. + #: Applies to BIFF4-7 only. See docs of the :class:`Colinfo` class for + #: discussion. + gcw = (0, ) * 256 + + #: A list of :class:`Hyperlink` objects corresponding to ``HLINK`` records + #: found in the worksheet. + #: + #: .. versionadded:: 0.7.2 + hyperlink_list = [] + + #: A sparse mapping from ``(rowx, colx)`` to an item in + #: :attr:`~xlrd.sheet.Sheet.hyperlink_list`. + #: Cells not covered by a hyperlink are not mapped. + #: It is possible using the Excel UI to set up a hyperlink that + #: covers a larger-than-1x1 rectangle of cells. + #: Hyperlink rectangles may overlap (Excel doesn't check). + #: When a multiply-covered cell is clicked on, the hyperlink that is + #: activated + #: (and the one that is mapped here) is the last in + #: :attr:`~xlrd.sheet.Sheet.hyperlink_list`. + #: + #: .. versionadded:: 0.7.2 + hyperlink_map = {} + + #: A sparse mapping from ``(rowx, colx)`` to a :class:`Note` object. + #: Cells not containing a note ("comment") are not mapped. + #: + #: .. versionadded:: 0.7.2 + cell_note_map = {} + + #: Number of columns in left pane (frozen panes; for split panes, see + #: comments in code) + vert_split_pos = 0 + + #: Number of rows in top pane (frozen panes; for split panes, see comments + #: in code) + horz_split_pos = 0 + + #: Index of first visible row in bottom frozen/split pane + horz_split_first_visible = 0 + + #: Index of first visible column in right frozen/split pane + vert_split_first_visible = 0 + + #: Frozen panes: ignore it. Split panes: explanation and diagrams in + #: OOo docs. + split_active_pane = 0 + + #: Boolean specifying if a ``PANE`` record was present, ignore unless you're + #: ``xlutils.copy`` + has_pane_record = 0 + + #: A list of the horizontal page breaks in this sheet. + #: Breaks are tuples in the form + #: ``(index of row after break, start col index, end col index)``. + #: + #: Populated only if ``open_workbook(..., formatting_info=True)`` + #: + #: .. versionadded:: 0.7.2 + horizontal_page_breaks = [] + + #: A list of the vertical page breaks in this sheet. + #: Breaks are tuples in the form + #: ``(index of col after break, start row index, end row index)``. + #: + #: Populated only if ``open_workbook(..., formatting_info=True)`` + #: + #: .. versionadded:: 0.7.2 + vertical_page_breaks = [] + + def __init__(self, book, position, name, number): + self.book = book + self.biff_version = book.biff_version + self._position = position + self.logfile = book.logfile + self.bt = array('B', [XL_CELL_EMPTY]) + self.bf = array('h', [-1]) + self.name = name + self.number = number + self.verbosity = book.verbosity + self.formatting_info = book.formatting_info + self.ragged_rows = book.ragged_rows + if self.ragged_rows: + self.put_cell = self.put_cell_ragged + else: + self.put_cell = self.put_cell_unragged + self._xf_index_to_xl_type_map = book._xf_index_to_xl_type_map + self.nrows = 0 # actual, including possibly empty cells + self.ncols = 0 + self._maxdatarowx = -1 # highest rowx containing a non-empty cell + self._maxdatacolx = -1 # highest colx containing a non-empty cell + self._dimnrows = 0 # as per DIMENSIONS record + self._dimncols = 0 + self._cell_values = [] + self._cell_types = [] + self._cell_xf_indexes = [] + self.defcolwidth = None + self.standardwidth = None + self.default_row_height = None + self.default_row_height_mismatch = 0 + self.default_row_hidden = 0 + self.default_additional_space_above = 0 + self.default_additional_space_below = 0 + self.colinfo_map = {} + self.rowinfo_map = {} + self.col_label_ranges = [] + self.row_label_ranges = [] + self.merged_cells = [] + self.rich_text_runlist_map = {} + self.horizontal_page_breaks = [] + self.vertical_page_breaks = [] + self._xf_index_stats = [0, 0, 0, 0] + self.visibility = book._sheet_visibility[number] # from BOUNDSHEET record + for attr, defval in _WINDOW2_options: + setattr(self, attr, defval) + self.first_visible_rowx = 0 + self.first_visible_colx = 0 + self.gridline_colour_index = 0x40 + self.gridline_colour_rgb = None # pre-BIFF8 + self.hyperlink_list = [] + self.hyperlink_map = {} + self.cell_note_map = {} + + # Values calculated by xlrd to predict the mag factors that + # will actually be used by Excel to display your worksheet. + # Pass these values to xlwt when writing XLS files. + # Warning 1: Behaviour of OOo Calc and Gnumeric has been observed to differ from Excel's. + # Warning 2: A value of zero means almost exactly what it says. Your sheet will be + # displayed as a very tiny speck on the screen. xlwt will reject attempts to set + # a mag_factor that is not (10 <= mag_factor <= 400). + self.cooked_page_break_preview_mag_factor = 60 + self.cooked_normal_view_mag_factor = 100 + + # Values (if any) actually stored on the XLS file + self.cached_page_break_preview_mag_factor = 0 # default (60%), from WINDOW2 record + self.cached_normal_view_mag_factor = 0 # default (100%), from WINDOW2 record + self.scl_mag_factor = None # from SCL record + + self._ixfe = None # BIFF2 only + self._cell_attr_to_xfx = {} # BIFF2.0 only + + if self.biff_version >= 80: + self.utter_max_rows = 65536 + else: + self.utter_max_rows = 16384 + self.utter_max_cols = 256 + + self._first_full_rowx = -1 + + # self._put_cell_exceptions = 0 + # self._put_cell_row_widenings = 0 + # self._put_cell_rows_appended = 0 + # self._put_cell_cells_appended = 0 + + def cell(self, rowx, colx): + """ + :class:`Cell` object in the given row and column. + """ + if self.formatting_info: + xfx = self.cell_xf_index(rowx, colx) + else: + xfx = None + return Cell( + self._cell_types[rowx][colx], + self._cell_values[rowx][colx], + xfx, + ) + + def cell_value(self, rowx, colx): + "Value of the cell in the given row and column." + return self._cell_values[rowx][colx] + + def cell_type(self, rowx, colx): + """ + Type of the cell in the given row and column. + + Refer to the documentation of the :class:`Cell` class. + """ + return self._cell_types[rowx][colx] + + def cell_xf_index(self, rowx, colx): + """ + XF index of the cell in the given row and column. + This is an index into :attr:`~xlrd.book.Book.xf_list`. + + .. versionadded:: 0.6.1 + """ + self.req_fmt_info() + xfx = self._cell_xf_indexes[rowx][colx] + if xfx > -1: + self._xf_index_stats[0] += 1 + return xfx + # Check for a row xf_index + try: + xfx = self.rowinfo_map[rowx].xf_index + if xfx > -1: + self._xf_index_stats[1] += 1 + return xfx + except KeyError: + pass + # Check for a column xf_index + try: + xfx = self.colinfo_map[colx].xf_index + if xfx == -1: xfx = 15 + self._xf_index_stats[2] += 1 + return xfx + except KeyError: + # If all else fails, 15 is used as hardwired global default xf_index. + self._xf_index_stats[3] += 1 + return 15 + + def row_len(self, rowx): + """ + Returns the effective number of cells in the given row. For use with + ``open_workbook(ragged_rows=True)`` which is likely to produce rows + with fewer than :attr:`~Sheet.ncols` cells. + + .. versionadded:: 0.7.2 + """ + return len(self._cell_values[rowx]) + + def row(self, rowx): + """ + Returns a sequence of the :class:`Cell` objects in the given row. + """ + return [ + self.cell(rowx, colx) + for colx in xrange(len(self._cell_values[rowx])) + ] + + def __getitem__(self, item): + """ + Takes either rowindex or (rowindex, colindex) as an index, + and returns either row or cell respectively. + """ + try: + rowix, colix = item + except TypeError: + # it's not a tuple (or of right size), let's try indexing as is + # if this is a problem, let this error propagate back + return self.row(item) + else: + return self.cell(rowix, colix) + + def get_rows(self): + "Returns a generator for iterating through each row." + return (self.row(index) for index in range(self.nrows)) + + # makes `for row in sheet` natural and intuitive + __iter__ = get_rows + + def row_types(self, rowx, start_colx=0, end_colx=None): + """ + Returns a slice of the types of the cells in the given row. + """ + if end_colx is None: + return self._cell_types[rowx][start_colx:] + return self._cell_types[rowx][start_colx:end_colx] + + def row_values(self, rowx, start_colx=0, end_colx=None): + """ + Returns a slice of the values of the cells in the given row. + """ + if end_colx is None: + return self._cell_values[rowx][start_colx:] + return self._cell_values[rowx][start_colx:end_colx] + + def row_slice(self, rowx, start_colx=0, end_colx=None): + """ + Returns a slice of the :class:`Cell` objects in the given row. + """ + nc = len(self._cell_values[rowx]) + if start_colx < 0: + start_colx += nc + if start_colx < 0: + start_colx = 0 + if end_colx is None or end_colx > nc: + end_colx = nc + elif end_colx < 0: + end_colx += nc + return [ + self.cell(rowx, colx) + for colx in xrange(start_colx, end_colx) + ] + + def col_slice(self, colx, start_rowx=0, end_rowx=None): + """ + Returns a slice of the :class:`Cell` objects in the given column. + """ + nr = self.nrows + if start_rowx < 0: + start_rowx += nr + if start_rowx < 0: + start_rowx = 0 + if end_rowx is None or end_rowx > nr: + end_rowx = nr + elif end_rowx < 0: + end_rowx += nr + return [ + self.cell(rowx, colx) + for rowx in xrange(start_rowx, end_rowx) + ] + + def col_values(self, colx, start_rowx=0, end_rowx=None): + """ + Returns a slice of the values of the cells in the given column. + """ + nr = self.nrows + if start_rowx < 0: + start_rowx += nr + if start_rowx < 0: + start_rowx = 0 + if end_rowx is None or end_rowx > nr: + end_rowx = nr + elif end_rowx < 0: + end_rowx += nr + return [ + self._cell_values[rowx][colx] + for rowx in xrange(start_rowx, end_rowx) + ] + + def col_types(self, colx, start_rowx=0, end_rowx=None): + """ + Returns a slice of the types of the cells in the given column. + """ + nr = self.nrows + if start_rowx < 0: + start_rowx += nr + if start_rowx < 0: + start_rowx = 0 + if end_rowx is None or end_rowx > nr: + end_rowx = nr + elif end_rowx < 0: + end_rowx += nr + return [ + self._cell_types[rowx][colx] + for rowx in xrange(start_rowx, end_rowx) + ] + + col = col_slice + + # === Following methods are used in building the worksheet. + # === They are not part of the API. + + def tidy_dimensions(self): + if self.verbosity >= 3: + fprintf( + self.logfile, + "tidy_dimensions: nrows=%d ncols=%d \n", + self.nrows, self.ncols, + ) + if 1 and self.merged_cells: + nr = nc = 0 + umaxrows = self.utter_max_rows + umaxcols = self.utter_max_cols + for crange in self.merged_cells: + rlo, rhi, clo, chi = crange + if not (0 <= rlo < rhi <= umaxrows) or not (0 <= clo < chi <= umaxcols): + fprintf(self.logfile, + "*** WARNING: sheet #%d (%r), MERGEDCELLS bad range %r\n", + self.number, self.name, crange) + if rhi > nr: nr = rhi + if chi > nc: nc = chi + if nc > self.ncols: + self.ncols = nc + self._first_full_rowx = -2 + if nr > self.nrows: + # we put one empty cell at (nr-1,0) to make sure + # we have the right number of rows. The ragged rows + # will sort out the rest if needed. + self.put_cell(nr-1, 0, XL_CELL_EMPTY, UNICODE_LITERAL(''), -1) + if (self.verbosity >= 1 and + (self.nrows != self._dimnrows or self.ncols != self._dimncols)): + fprintf( + self.logfile, + "NOTE *** sheet %d (%r): DIMENSIONS R,C = %d,%d should be %d,%d\n", + self.number, + self.name, + self._dimnrows, + self._dimncols, + self.nrows, + self.ncols, + ) + if not self.ragged_rows: + # fix ragged rows + ncols = self.ncols + s_cell_types = self._cell_types + s_cell_values = self._cell_values + s_cell_xf_indexes = self._cell_xf_indexes + s_fmt_info = self.formatting_info + # for rowx in xrange(self.nrows): + if self._first_full_rowx == -2: + ubound = self.nrows + else: + ubound = self._first_full_rowx + for rowx in xrange(ubound): + trow = s_cell_types[rowx] + rlen = len(trow) + nextra = ncols - rlen + if nextra > 0: + s_cell_values[rowx][rlen:] = [UNICODE_LITERAL('')] * nextra + trow[rlen:] = self.bt * nextra + if s_fmt_info: + s_cell_xf_indexes[rowx][rlen:] = self.bf * nextra + + def put_cell_ragged(self, rowx, colx, ctype, value, xf_index): + if ctype is None: + # we have a number, so look up the cell type + ctype = self._xf_index_to_xl_type_map[xf_index] + assert 0 <= colx < self.utter_max_cols + assert 0 <= rowx < self.utter_max_rows + fmt_info = self.formatting_info + + try: + nr = rowx + 1 + if self.nrows < nr: + + scta = self._cell_types.append + scva = self._cell_values.append + scxa = self._cell_xf_indexes.append + bt = self.bt + bf = self.bf + for _unused in xrange(self.nrows, nr): + scta(bt * 0) + scva([]) + if fmt_info: + scxa(bf * 0) + self.nrows = nr + + types_row = self._cell_types[rowx] + values_row = self._cell_values[rowx] + if fmt_info: + fmt_row = self._cell_xf_indexes[rowx] + ltr = len(types_row) + if colx >= self.ncols: + self.ncols = colx + 1 + num_empty = colx - ltr + if not num_empty: + # most common case: colx == previous colx + 1 + # self._put_cell_cells_appended += 1 + types_row.append(ctype) + values_row.append(value) + if fmt_info: + fmt_row.append(xf_index) + return + if num_empty > 0: + num_empty += 1 + # self._put_cell_row_widenings += 1 + # types_row.extend(self.bt * num_empty) + # values_row.extend([UNICODE_LITERAL('')] * num_empty) + # if fmt_info: + # fmt_row.extend(self.bf * num_empty) + types_row[ltr:] = self.bt * num_empty + values_row[ltr:] = [UNICODE_LITERAL('')] * num_empty + if fmt_info: + fmt_row[ltr:] = self.bf * num_empty + types_row[colx] = ctype + values_row[colx] = value + if fmt_info: + fmt_row[colx] = xf_index + except: + print("put_cell", rowx, colx, file=self.logfile) + raise + + def put_cell_unragged(self, rowx, colx, ctype, value, xf_index): + if ctype is None: + # we have a number, so look up the cell type + ctype = self._xf_index_to_xl_type_map[xf_index] + # assert 0 <= colx < self.utter_max_cols + # assert 0 <= rowx < self.utter_max_rows + try: + self._cell_types[rowx][colx] = ctype + self._cell_values[rowx][colx] = value + if self.formatting_info: + self._cell_xf_indexes[rowx][colx] = xf_index + except IndexError: + # print >> self.logfile, "put_cell extending", rowx, colx + # self.extend_cells(rowx+1, colx+1) + # self._put_cell_exceptions += 1 + nr = rowx + 1 + nc = colx + 1 + assert 1 <= nc <= self.utter_max_cols + assert 1 <= nr <= self.utter_max_rows + if nc > self.ncols: + self.ncols = nc + # The row self._first_full_rowx and all subsequent rows + # are guaranteed to have length == self.ncols. Thus the + # "fix ragged rows" section of the tidy_dimensions method + # doesn't need to examine them. + if nr < self.nrows: + # cell data is not in non-descending row order *AND* + # self.ncols has been bumped up. + # This very rare case ruins this optimisation. + self._first_full_rowx = -2 + elif rowx > self._first_full_rowx > -2: + self._first_full_rowx = rowx + if nr <= self.nrows: + # New cell is in an existing row, so extend that row (if necessary). + # Note that nr < self.nrows means that the cell data + # is not in ascending row order!! + trow = self._cell_types[rowx] + nextra = self.ncols - len(trow) + if nextra > 0: + # self._put_cell_row_widenings += 1 + trow.extend(self.bt * nextra) + if self.formatting_info: + self._cell_xf_indexes[rowx].extend(self.bf * nextra) + self._cell_values[rowx].extend([UNICODE_LITERAL('')] * nextra) + else: + scta = self._cell_types.append + scva = self._cell_values.append + scxa = self._cell_xf_indexes.append + fmt_info = self.formatting_info + nc = self.ncols + bt = self.bt + bf = self.bf + for _unused in xrange(self.nrows, nr): + # self._put_cell_rows_appended += 1 + scta(bt * nc) + scva([UNICODE_LITERAL('')] * nc) + if fmt_info: + scxa(bf * nc) + self.nrows = nr + # === end of code from extend_cells() + try: + self._cell_types[rowx][colx] = ctype + self._cell_values[rowx][colx] = value + if self.formatting_info: + self._cell_xf_indexes[rowx][colx] = xf_index + except: + print("put_cell", rowx, colx, file=self.logfile) + raise + except: + print("put_cell", rowx, colx, file=self.logfile) + raise + + + # === Methods after this line neither know nor care about how cells are stored. + + def read(self, bk): + global rc_stats + DEBUG = 0 + blah = DEBUG or self.verbosity >= 2 + blah_rows = DEBUG or self.verbosity >= 4 + blah_formulas = 0 and blah + r1c1 = 0 + oldpos = bk._position + bk._position = self._position + XL_SHRFMLA_ETC_ETC = ( + XL_SHRFMLA, XL_ARRAY, XL_TABLEOP, XL_TABLEOP2, + XL_ARRAY2, XL_TABLEOP_B2, + ) + self_put_cell = self.put_cell + local_unpack = unpack + bk_get_record_parts = bk.get_record_parts + bv = self.biff_version + fmt_info = self.formatting_info + do_sst_rich_text = fmt_info and bk._rich_text_runlist_map + rowinfo_sharing_dict = {} + txos = {} + eof_found = 0 + while 1: + # if DEBUG: print "SHEET.READ: about to read from position %d" % bk._position + rc, data_len, data = bk_get_record_parts() + # if rc in rc_stats: + # rc_stats[rc] += 1 + # else: + # rc_stats[rc] = 1 + # if DEBUG: print "SHEET.READ: op 0x%04x, %d bytes %r" % (rc, data_len, data) + if rc == XL_NUMBER: + # [:14] in following stmt ignores extraneous rubbish at end of record. + # Sample file testEON-8.xls supplied by Jan Kraus. + rowx, colx, xf_index, d = local_unpack('<HHHd', data[:14]) + # if xf_index == 0: + # fprintf(self.logfile, + # "NUMBER: r=%d c=%d xfx=%d %f\n", rowx, colx, xf_index, d) + self_put_cell(rowx, colx, None, d, xf_index) + elif rc == XL_LABELSST: + rowx, colx, xf_index, sstindex = local_unpack('<HHHi', data) + # print "LABELSST", rowx, colx, sstindex, bk._sharedstrings[sstindex] + self_put_cell(rowx, colx, XL_CELL_TEXT, bk._sharedstrings[sstindex], xf_index) + if do_sst_rich_text: + runlist = bk._rich_text_runlist_map.get(sstindex) + if runlist: + self.rich_text_runlist_map[(rowx, colx)] = runlist + elif rc == XL_LABEL: + rowx, colx, xf_index = local_unpack('<HHH', data[0:6]) + if bv < BIFF_FIRST_UNICODE: + strg = unpack_string(data, 6, bk.encoding or bk.derive_encoding(), lenlen=2) + else: + strg = unpack_unicode(data, 6, lenlen=2) + self_put_cell(rowx, colx, XL_CELL_TEXT, strg, xf_index) + elif rc == XL_RSTRING: + rowx, colx, xf_index = local_unpack('<HHH', data[0:6]) + if bv < BIFF_FIRST_UNICODE: + strg, pos = unpack_string_update_pos(data, 6, bk.encoding or bk.derive_encoding(), lenlen=2) + nrt = BYTES_ORD(data[pos]) + pos += 1 + runlist = [] + for _unused in xrange(nrt): + runlist.append(unpack('<BB', data[pos:pos+2])) + pos += 2 + assert pos == len(data) + else: + strg, pos = unpack_unicode_update_pos(data, 6, lenlen=2) + nrt = unpack('<H', data[pos:pos+2])[0] + pos += 2 + runlist = [] + for _unused in xrange(nrt): + runlist.append(unpack('<HH', data[pos:pos+4])) + pos += 4 + assert pos == len(data) + self_put_cell(rowx, colx, XL_CELL_TEXT, strg, xf_index) + self.rich_text_runlist_map[(rowx, colx)] = runlist + elif rc == XL_RK: + rowx, colx, xf_index = local_unpack('<HHH', data[:6]) + d = unpack_RK(data[6:10]) + self_put_cell(rowx, colx, None, d, xf_index) + elif rc == XL_MULRK: + mulrk_row, mulrk_first = local_unpack('<HH', data[0:4]) + mulrk_last, = local_unpack('<H', data[-2:]) + pos = 4 + for colx in xrange(mulrk_first, mulrk_last+1): + xf_index, = local_unpack('<H', data[pos:pos+2]) + d = unpack_RK(data[pos+2:pos+6]) + pos += 6 + self_put_cell(mulrk_row, colx, None, d, xf_index) + elif rc == XL_ROW: + # Version 0.6.0a3: ROW records are just not worth using (for memory allocation). + # Version 0.6.1: now used for formatting info. + if not fmt_info: continue + rowx, bits1, bits2 = local_unpack('<H4xH4xi', data[0:16]) + if not(0 <= rowx < self.utter_max_rows): + print("*** NOTE: ROW record has row index %d; " + "should have 0 <= rowx < %d -- record ignored!" + % (rowx, self.utter_max_rows), file=self.logfile) + continue + key = (bits1, bits2) + r = rowinfo_sharing_dict.get(key) + if r is None: + rowinfo_sharing_dict[key] = r = Rowinfo() + # Using upkbits() is far too slow on a file + # with 30 sheets each with 10K rows :-( + # upkbits(r, bits1, ( + # ( 0, 0x7FFF, 'height'), + # (15, 0x8000, 'has_default_height'), + # )) + # upkbits(r, bits2, ( + # ( 0, 0x00000007, 'outline_level'), + # ( 4, 0x00000010, 'outline_group_starts_ends'), + # ( 5, 0x00000020, 'hidden'), + # ( 6, 0x00000040, 'height_mismatch'), + # ( 7, 0x00000080, 'has_default_xf_index'), + # (16, 0x0FFF0000, 'xf_index'), + # (28, 0x10000000, 'additional_space_above'), + # (29, 0x20000000, 'additional_space_below'), + # )) + # So: + r.height = bits1 & 0x7fff + r.has_default_height = (bits1 >> 15) & 1 + r.outline_level = bits2 & 7 + r.outline_group_starts_ends = (bits2 >> 4) & 1 + r.hidden = (bits2 >> 5) & 1 + r.height_mismatch = (bits2 >> 6) & 1 + r.has_default_xf_index = (bits2 >> 7) & 1 + r.xf_index = (bits2 >> 16) & 0xfff + r.additional_space_above = (bits2 >> 28) & 1 + r.additional_space_below = (bits2 >> 29) & 1 + if not r.has_default_xf_index: + r.xf_index = -1 + self.rowinfo_map[rowx] = r + if 0 and r.xf_index > -1: + fprintf(self.logfile, + "**ROW %d %d %d\n", + self.number, rowx, r.xf_index) + if blah_rows: + print('ROW', rowx, bits1, bits2, file=self.logfile) + r.dump(self.logfile, + header="--- sh #%d, rowx=%d ---" % (self.number, rowx)) + elif rc in XL_FORMULA_OPCODES: # 06, 0206, 0406 + # DEBUG = 1 + # if DEBUG: print "FORMULA: rc: 0x%04x data: %r" % (rc, data) + if bv >= 50: + rowx, colx, xf_index, result_str, flags = local_unpack('<HHH8sH', data[0:16]) + elif bv >= 30: + rowx, colx, xf_index, result_str, flags = local_unpack('<HHH8sH', data[0:16]) + else: # BIFF2 + rowx, colx, cell_attr, result_str, flags = local_unpack('<HH3s8sB', data[0:16]) + xf_index = self.fixed_BIFF2_xfindex(cell_attr, rowx, colx) + if blah_formulas: # testing formula dumper + #### XXXX FIXME + fprintf(self.logfile, "FORMULA: rowx=%d colx=%d\n", rowx, colx) + fmlalen = local_unpack("<H", data[20:22])[0] + decompile_formula(bk, data[22:], fmlalen, FMLA_TYPE_CELL, + browx=rowx, bcolx=colx, blah=1, r1c1=r1c1) + if result_str[6:8] == b"\xFF\xFF": + first_byte = BYTES_ORD(result_str[0]) + if first_byte == 0: + # need to read next record (STRING) + gotstring = 0 + # if flags & 8: + if 1: # "flags & 8" applies only to SHRFMLA + # actually there's an optional SHRFMLA or ARRAY etc record to skip over + rc2, data2_len, data2 = bk.get_record_parts() + if rc2 == XL_STRING or rc2 == XL_STRING_B2: + gotstring = 1 + elif rc2 == XL_ARRAY: + row1x, rownx, col1x, colnx, array_flags, tokslen = \ + local_unpack("<HHBBBxxxxxH", data2[:14]) + if blah_formulas: + fprintf(self.logfile, "ARRAY: %d %d %d %d %d\n", + row1x, rownx, col1x, colnx, array_flags) + # dump_formula(bk, data2[14:], tokslen, bv, reldelta=0, blah=1) + elif rc2 == XL_SHRFMLA: + row1x, rownx, col1x, colnx, nfmlas, tokslen = \ + local_unpack("<HHBBxBH", data2[:10]) + if blah_formulas: + fprintf(self.logfile, "SHRFMLA (sub): %d %d %d %d %d\n", + row1x, rownx, col1x, colnx, nfmlas) + decompile_formula(bk, data2[10:], tokslen, FMLA_TYPE_SHARED, + blah=1, browx=rowx, bcolx=colx, r1c1=r1c1) + elif rc2 not in XL_SHRFMLA_ETC_ETC: + raise XLRDError( + "Expected SHRFMLA, ARRAY, TABLEOP* or STRING record; found 0x%04x" % rc2) + # if DEBUG: print "gotstring:", gotstring + # now for the STRING record + if not gotstring: + rc2, _unused_len, data2 = bk.get_record_parts() + if rc2 not in (XL_STRING, XL_STRING_B2): + raise XLRDError("Expected STRING record; found 0x%04x" % rc2) + # if DEBUG: print "STRING: data=%r BIFF=%d cp=%d" % (data2, self.biff_version, bk.encoding) + strg = self.string_record_contents(data2) + self.put_cell(rowx, colx, XL_CELL_TEXT, strg, xf_index) + # if DEBUG: print "FORMULA strg %r" % strg + elif first_byte == 1: + # boolean formula result + value = BYTES_ORD(result_str[2]) + self_put_cell(rowx, colx, XL_CELL_BOOLEAN, value, xf_index) + elif first_byte == 2: + # Error in cell + value = BYTES_ORD(result_str[2]) + self_put_cell(rowx, colx, XL_CELL_ERROR, value, xf_index) + elif first_byte == 3: + # empty ... i.e. empty (zero-length) string, NOT an empty cell. + self_put_cell(rowx, colx, XL_CELL_TEXT, "", xf_index) + else: + raise XLRDError("unexpected special case (0x%02x) in FORMULA" % first_byte) + else: + # it is a number + d = local_unpack('<d', result_str)[0] + self_put_cell(rowx, colx, None, d, xf_index) + elif rc == XL_BOOLERR: + rowx, colx, xf_index, value, is_err = local_unpack('<HHHBB', data[:8]) + # Note OOo Calc 2.0 writes 9-byte BOOLERR records. + # OOo docs say 8. Excel writes 8. + cellty = (XL_CELL_BOOLEAN, XL_CELL_ERROR)[is_err] + # if DEBUG: print "XL_BOOLERR", rowx, colx, xf_index, value, is_err + self_put_cell(rowx, colx, cellty, value, xf_index) + elif rc == XL_COLINFO: + if not fmt_info: continue + c = Colinfo() + first_colx, last_colx, c.width, c.xf_index, flags \ + = local_unpack("<HHHHH", data[:10]) + #### Colinfo.width is denominated in 256ths of a character, + #### *not* in characters. + if not(0 <= first_colx <= last_colx <= 256): + # Note: 256 instead of 255 is a common mistake. + # We silently ignore the non-existing 257th column in that case. + print("*** NOTE: COLINFO record has first col index %d, last %d; " + "should have 0 <= first <= last <= 255 -- record ignored!" + % (first_colx, last_colx), file=self.logfile) + del c + continue + upkbits(c, flags, ( + ( 0, 0x0001, 'hidden'), + ( 1, 0x0002, 'bit1_flag'), + # *ALL* colinfos created by Excel in "default" cases are 0x0002!! + # Maybe it's "locked" by analogy with XFProtection data. + ( 8, 0x0700, 'outline_level'), + (12, 0x1000, 'collapsed'), + )) + for colx in xrange(first_colx, last_colx+1): + if colx > 255: break # Excel does 0 to 256 inclusive + self.colinfo_map[colx] = c + if 0: + fprintf(self.logfile, + "**COL %d %d %d\n", + self.number, colx, c.xf_index) + if blah: + fprintf( + self.logfile, + "COLINFO sheet #%d cols %d-%d: wid=%d xf_index=%d flags=0x%04x\n", + self.number, first_colx, last_colx, c.width, c.xf_index, flags, + ) + c.dump(self.logfile, header='===') + elif rc == XL_DEFCOLWIDTH: + self.defcolwidth, = local_unpack("<H", data[:2]) + if 0: print('DEFCOLWIDTH', self.defcolwidth, file=self.logfile) + elif rc == XL_STANDARDWIDTH: + if data_len != 2: + print('*** ERROR *** STANDARDWIDTH', data_len, repr(data), file=self.logfile) + self.standardwidth, = local_unpack("<H", data[:2]) + if 0: print('STANDARDWIDTH', self.standardwidth, file=self.logfile) + elif rc == XL_GCW: + if not fmt_info: continue # useless w/o COLINFO + assert data_len == 34 + assert data[0:2] == b"\x20\x00" + iguff = unpack("<8i", data[2:34]) + gcw = [] + for bits in iguff: + for j in xrange(32): + gcw.append(bits & 1) + bits >>= 1 + self.gcw = tuple(gcw) + if 0: + showgcw = "".join(map(lambda x: "F "[x], gcw)).rstrip().replace(' ', '.') + print("GCW:", showgcw, file=self.logfile) + elif rc == XL_BLANK: + if not fmt_info: continue + rowx, colx, xf_index = local_unpack('<HHH', data[:6]) + # if 0: print >> self.logfile, "BLANK", rowx, colx, xf_index + self_put_cell(rowx, colx, XL_CELL_BLANK, '', xf_index) + elif rc == XL_MULBLANK: # 00BE + if not fmt_info: continue + nitems = data_len >> 1 + result = local_unpack("<%dH" % nitems, data) + rowx, mul_first = result[:2] + mul_last = result[-1] + # print >> self.logfile, "MULBLANK", rowx, mul_first, mul_last, data_len, nitems, mul_last + 4 - mul_first + assert nitems == mul_last + 4 - mul_first + pos = 2 + for colx in xrange(mul_first, mul_last + 1): + self_put_cell(rowx, colx, XL_CELL_BLANK, '', result[pos]) + pos += 1 + elif rc == XL_DIMENSION or rc == XL_DIMENSION2: + if data_len == 0: + # Four zero bytes after some other record. See github issue 64. + continue + # if data_len == 10: + # Was crashing on BIFF 4.0 file w/o the two trailing unused bytes. + # Reported by Ralph Heimburger. + if bv < 80: + dim_tuple = local_unpack('<HxxH', data[2:8]) + else: + dim_tuple = local_unpack('<ixxH', data[4:12]) + self.nrows, self.ncols = 0, 0 + self._dimnrows, self._dimncols = dim_tuple + if bv in (21, 30, 40) and self.book.xf_list and not self.book._xf_epilogue_done: + self.book.xf_epilogue() + if blah: + fprintf( + self.logfile, + "sheet %d(%r) DIMENSIONS: ncols=%d nrows=%d\n", + self.number, self.name, self._dimncols, self._dimnrows + ) + elif rc == XL_HLINK: + self.handle_hlink(data) + elif rc == XL_QUICKTIP: + self.handle_quicktip(data) + elif rc == XL_EOF: + DEBUG = 0 + if DEBUG: print("SHEET.READ: EOF", file=self.logfile) + eof_found = 1 + break + elif rc == XL_OBJ: + # handle SHEET-level objects; note there's a separate Book.handle_obj + saved_obj = self.handle_obj(data) + if saved_obj: saved_obj_id = saved_obj.id + else: saved_obj_id = None + elif rc == XL_MSO_DRAWING: + self.handle_msodrawingetc(rc, data_len, data) + elif rc == XL_TXO: + txo = self.handle_txo(data) + if txo and saved_obj_id: + txos[saved_obj_id] = txo + saved_obj_id = None + elif rc == XL_NOTE: + self.handle_note(data, txos) + elif rc == XL_FEAT11: + self.handle_feat11(data) + elif rc in bofcodes: ##### EMBEDDED BOF ##### + version, boftype = local_unpack('<HH', data[0:4]) + if boftype != 0x20: # embedded chart + print("*** Unexpected embedded BOF (0x%04x) at offset %d: version=0x%04x type=0x%04x" + % (rc, bk._position - data_len - 4, version, boftype), file=self.logfile) + while 1: + code, data_len, data = bk.get_record_parts() + if code == XL_EOF: + break + if DEBUG: print("---> found EOF", file=self.logfile) + elif rc == XL_COUNTRY: + bk.handle_country(data) + elif rc == XL_LABELRANGES: + pos = 0 + pos = unpack_cell_range_address_list_update_pos( + self.row_label_ranges, data, pos, bv, addr_size=8, + ) + pos = unpack_cell_range_address_list_update_pos( + self.col_label_ranges, data, pos, bv, addr_size=8, + ) + assert pos == data_len + elif rc == XL_ARRAY: + row1x, rownx, col1x, colnx, array_flags, tokslen = \ + local_unpack("<HHBBBxxxxxH", data[:14]) + if blah_formulas: + print("ARRAY:", row1x, rownx, col1x, colnx, array_flags, file=self.logfile) + # dump_formula(bk, data[14:], tokslen, bv, reldelta=0, blah=1) + elif rc == XL_SHRFMLA: + row1x, rownx, col1x, colnx, nfmlas, tokslen = \ + local_unpack("<HHBBxBH", data[:10]) + if blah_formulas: + print("SHRFMLA (main):", row1x, rownx, col1x, colnx, nfmlas, file=self.logfile) + decompile_formula(bk, data[10:], tokslen, FMLA_TYPE_SHARED, + blah=1, browx=rowx, bcolx=colx, r1c1=r1c1) + elif rc == XL_CONDFMT: + if not fmt_info: continue + assert bv >= 80 + num_CFs, needs_recalc, browx1, browx2, bcolx1, bcolx2 = \ + unpack("<6H", data[0:12]) + if self.verbosity >= 1: + fprintf( + self.logfile, + "\n*** WARNING: Ignoring CONDFMT (conditional formatting) record\n" + "*** in Sheet %d (%r).\n" + "*** %d CF record(s); needs_recalc_or_redraw = %d\n" + "*** Bounding box is %s\n", + self.number, self.name, num_CFs, needs_recalc, + rangename2d(browx1, browx2+1, bcolx1, bcolx2+1), + ) + olist = [] # updated by the function + pos = unpack_cell_range_address_list_update_pos( + olist, data, 12, bv, addr_size=8) + # print >> self.logfile, repr(result), len(result) + if self.verbosity >= 1: + fprintf( + self.logfile, + "*** %d individual range(s):\n" + "*** %s\n", + len(olist), + ", ".join(rangename2d(*coords) for coords in olist), + ) + elif rc == XL_CF: + if not fmt_info: continue + cf_type, cmp_op, sz1, sz2, flags = unpack("<BBHHi", data[0:10]) + font_block = (flags >> 26) & 1 + bord_block = (flags >> 28) & 1 + patt_block = (flags >> 29) & 1 + if self.verbosity >= 1: + fprintf( + self.logfile, + "\n*** WARNING: Ignoring CF (conditional formatting) sub-record.\n" + "*** cf_type=%d, cmp_op=%d, sz1=%d, sz2=%d, flags=0x%08x\n" + "*** optional data blocks: font=%d, border=%d, pattern=%d\n", + cf_type, cmp_op, sz1, sz2, flags, + font_block, bord_block, patt_block, + ) + # hex_char_dump(data, 0, data_len, fout=self.logfile) + pos = 12 + if font_block: + (font_height, font_options, weight, escapement, underline, + font_colour_index, two_bits, font_esc, font_underl) = unpack("<64x i i H H B 3x i 4x i i i 18x", data[pos:pos+118]) + font_style = (two_bits > 1) & 1 + posture = (font_options > 1) & 1 + font_canc = (two_bits > 7) & 1 + cancellation = (font_options > 7) & 1 + if self.verbosity >= 1: + fprintf( + self.logfile, + "*** Font info: height=%d, weight=%d, escapement=%d,\n" + "*** underline=%d, colour_index=%d, esc=%d, underl=%d,\n" + "*** style=%d, posture=%d, canc=%d, cancellation=%d\n", + font_height, weight, escapement, underline, + font_colour_index, font_esc, font_underl, + font_style, posture, font_canc, cancellation, + ) + pos += 118 + if bord_block: + pos += 8 + if patt_block: + pos += 4 + fmla1 = data[pos:pos+sz1] + pos += sz1 + if blah and sz1: + fprintf(self.logfile, "*** formula 1:\n") + dump_formula(bk, fmla1, sz1, bv, reldelta=0, blah=1) + fmla2 = data[pos:pos+sz2] + pos += sz2 + assert pos == data_len + if blah and sz2: + fprintf(self.logfile, "*** formula 2:\n") + dump_formula(bk, fmla2, sz2, bv, reldelta=0, blah=1) + elif rc == XL_DEFAULTROWHEIGHT: + if data_len == 4: + bits, self.default_row_height = unpack("<HH", data[:4]) + elif data_len == 2: + self.default_row_height, = unpack("<H", data) + bits = 0 + fprintf(self.logfile, + "*** WARNING: DEFAULTROWHEIGHT record len is 2, " + "should be 4; assuming BIFF2 format\n") + else: + bits = 0 + fprintf(self.logfile, + "*** WARNING: DEFAULTROWHEIGHT record len is %d, " + "should be 4; ignoring this record\n", + data_len) + self.default_row_height_mismatch = bits & 1 + self.default_row_hidden = (bits >> 1) & 1 + self.default_additional_space_above = (bits >> 2) & 1 + self.default_additional_space_below = (bits >> 3) & 1 + elif rc == XL_MERGEDCELLS: + if not fmt_info: continue + pos = unpack_cell_range_address_list_update_pos( + self.merged_cells, data, 0, bv, addr_size=8) + if blah: + fprintf(self.logfile, + "MERGEDCELLS: %d ranges\n", (pos - 2) // 8) + assert pos == data_len, \ + "MERGEDCELLS: pos=%d data_len=%d" % (pos, data_len) + elif rc == XL_WINDOW2: + if bv >= 80 and data_len >= 14: + ( + options, + self.first_visible_rowx, self.first_visible_colx, + self.gridline_colour_index, + self.cached_page_break_preview_mag_factor, + self.cached_normal_view_mag_factor + ) = unpack("<HHHHxxHH", data[:14]) + else: + assert bv >= 30 # BIFF3-7 + ( + options, + self.first_visible_rowx, self.first_visible_colx, + ) = unpack("<HHH", data[:6]) + self.gridline_colour_rgb = unpack("<BBB", data[6:9]) + self.gridline_colour_index = nearest_colour_index( + self.book.colour_map, self.gridline_colour_rgb, debug=0) + # options -- Bit, Mask, Contents: + # 0 0001H 0 = Show formula results 1 = Show formulas + # 1 0002H 0 = Do not show grid lines 1 = Show grid lines + # 2 0004H 0 = Do not show sheet headers 1 = Show sheet headers + # 3 0008H 0 = Panes are not frozen 1 = Panes are frozen (freeze) + # 4 0010H 0 = Show zero values as empty cells 1 = Show zero values + # 5 0020H 0 = Manual grid line colour 1 = Automatic grid line colour + # 6 0040H 0 = Columns from left to right 1 = Columns from right to left + # 7 0080H 0 = Do not show outline symbols 1 = Show outline symbols + # 8 0100H 0 = Keep splits if pane freeze is removed 1 = Remove splits if pane freeze is removed + # 9 0200H 0 = Sheet not selected 1 = Sheet selected (BIFF5-BIFF8) + # 10 0400H 0 = Sheet not visible 1 = Sheet visible (BIFF5-BIFF8) + # 11 0800H 0 = Show in normal view 1 = Show in page break preview (BIFF8) + # The freeze flag specifies, if a following PANE record (6.71) describes unfrozen or frozen panes. + for attr, _unused_defval in _WINDOW2_options: + setattr(self, attr, options & 1) + options >>= 1 + elif rc == XL_SCL: + num, den = unpack("<HH", data) + result = 0 + if den: + result = (num * 100) // den + if not(10 <= result <= 400): + if DEBUG or self.verbosity >= 0: + print( + "WARNING *** SCL rcd sheet %d: should have 0.1 <= num/den <= 4; got %d/%d" + % (self.number, num, den), + file=self.logfile, + ) + result = 100 + self.scl_mag_factor = result + elif rc == XL_PANE: + ( + self.vert_split_pos, + self.horz_split_pos, + self.horz_split_first_visible, + self.vert_split_first_visible, + self.split_active_pane, + ) = unpack("<HHHHB", data[:9]) + self.has_pane_record = 1 + elif rc == XL_HORIZONTALPAGEBREAKS: + if not fmt_info: continue + num_breaks, = local_unpack("<H", data[:2]) + assert num_breaks * (2 + 4 * (bv >= 80)) + 2 == data_len + pos = 2 + if bv < 80: + while pos < data_len: + self.horizontal_page_breaks.append((local_unpack("<H", data[pos:pos+2])[0], 0, 255)) + pos += 2 + else: + while pos < data_len: + self.horizontal_page_breaks.append(local_unpack("<HHH", data[pos:pos+6])) + pos += 6 + elif rc == XL_VERTICALPAGEBREAKS: + if not fmt_info: continue + num_breaks, = local_unpack("<H", data[:2]) + assert num_breaks * (2 + 4 * (bv >= 80)) + 2 == data_len + pos = 2 + if bv < 80: + while pos < data_len: + self.vertical_page_breaks.append((local_unpack("<H", data[pos:pos+2])[0], 0, 65535)) + pos += 2 + else: + while pos < data_len: + self.vertical_page_breaks.append(local_unpack("<HHH", data[pos:pos+6])) + pos += 6 + #### all of the following are for BIFF <= 4W + elif bv <= 45: + if rc == XL_FORMAT or rc == XL_FORMAT2: + bk.handle_format(data, rc) + elif rc == XL_FONT or rc == XL_FONT_B3B4: + bk.handle_font(data) + elif rc == XL_STYLE: + if not self.book._xf_epilogue_done: + self.book.xf_epilogue() + bk.handle_style(data) + elif rc == XL_PALETTE: + bk.handle_palette(data) + elif rc == XL_BUILTINFMTCOUNT: + bk.handle_builtinfmtcount(data) + elif rc == XL_XF4 or rc == XL_XF3 or rc == XL_XF2: #### N.B. not XL_XF + bk.handle_xf(data) + elif rc == XL_DATEMODE: + bk.handle_datemode(data) + elif rc == XL_CODEPAGE: + bk.handle_codepage(data) + elif rc == XL_FILEPASS: + bk.handle_filepass(data) + elif rc == XL_WRITEACCESS: + bk.handle_writeaccess(data) + elif rc == XL_IXFE: + self._ixfe = local_unpack('<H', data)[0] + elif rc == XL_NUMBER_B2: + rowx, colx, cell_attr, d = local_unpack('<HH3sd', data) + self_put_cell(rowx, colx, None, d, self.fixed_BIFF2_xfindex(cell_attr, rowx, colx)) + elif rc == XL_INTEGER: + rowx, colx, cell_attr, d = local_unpack('<HH3sH', data) + self_put_cell(rowx, colx, None, float(d), self.fixed_BIFF2_xfindex(cell_attr, rowx, colx)) + elif rc == XL_LABEL_B2: + rowx, colx, cell_attr = local_unpack('<HH3s', data[0:7]) + strg = unpack_string(data, 7, bk.encoding or bk.derive_encoding(), lenlen=1) + self_put_cell(rowx, colx, XL_CELL_TEXT, strg, self.fixed_BIFF2_xfindex(cell_attr, rowx, colx)) + elif rc == XL_BOOLERR_B2: + rowx, colx, cell_attr, value, is_err = local_unpack('<HH3sBB', data) + cellty = (XL_CELL_BOOLEAN, XL_CELL_ERROR)[is_err] + # if DEBUG: print "XL_BOOLERR_B2", rowx, colx, cell_attr, value, is_err + self_put_cell(rowx, colx, cellty, value, self.fixed_BIFF2_xfindex(cell_attr, rowx, colx)) + elif rc == XL_BLANK_B2: + if not fmt_info: continue + rowx, colx, cell_attr = local_unpack('<HH3s', data[:7]) + self_put_cell(rowx, colx, XL_CELL_BLANK, '', self.fixed_BIFF2_xfindex(cell_attr, rowx, colx)) + elif rc == XL_EFONT: + bk.handle_efont(data) + elif rc == XL_ROW_B2: + if not fmt_info: continue + rowx, bits1, bits2 = local_unpack('<H4xH2xB', data[0:11]) + if not(0 <= rowx < self.utter_max_rows): + print("*** NOTE: ROW_B2 record has row index %d; " + "should have 0 <= rowx < %d -- record ignored!" + % (rowx, self.utter_max_rows), file=self.logfile) + continue + if not (bits2 & 1): # has_default_xf_index is false + xf_index = -1 + elif data_len == 18: + # Seems the XF index in the cell_attr is dodgy + xfx = local_unpack('<H', data[16:18])[0] + xf_index = self.fixed_BIFF2_xfindex(cell_attr=None, rowx=rowx, colx=-1, true_xfx=xfx) + else: + cell_attr = data[13:16] + xf_index = self.fixed_BIFF2_xfindex(cell_attr, rowx, colx=-1) + key = (bits1, bits2, xf_index) + r = rowinfo_sharing_dict.get(key) + if r is None: + rowinfo_sharing_dict[key] = r = Rowinfo() + r.height = bits1 & 0x7fff + r.has_default_height = (bits1 >> 15) & 1 + r.has_default_xf_index = bits2 & 1 + r.xf_index = xf_index + # r.outline_level = 0 # set in __init__ + # r.outline_group_starts_ends = 0 # set in __init__ + # r.hidden = 0 # set in __init__ + # r.height_mismatch = 0 # set in __init__ + # r.additional_space_above = 0 # set in __init__ + # r.additional_space_below = 0 # set in __init__ + self.rowinfo_map[rowx] = r + if 0 and r.xf_index > -1: + fprintf(self.logfile, + "**ROW %d %d %d\n", + self.number, rowx, r.xf_index) + if blah_rows: + print('ROW_B2', rowx, bits1, file=self.logfile) + r.dump(self.logfile, + header="--- sh #%d, rowx=%d ---" % (self.number, rowx)) + elif rc == XL_COLWIDTH: # BIFF2 only + if not fmt_info: continue + first_colx, last_colx, width\ + = local_unpack("<BBH", data[:4]) + if not(first_colx <= last_colx): + print("*** NOTE: COLWIDTH record has first col index %d, last %d; " + "should have first <= last -- record ignored!" + % (first_colx, last_colx), file=self.logfile) + continue + for colx in xrange(first_colx, last_colx+1): + if colx in self.colinfo_map: + c = self.colinfo_map[colx] + else: + c = Colinfo() + self.colinfo_map[colx] = c + c.width = width + if blah: + fprintf( + self.logfile, + "COLWIDTH sheet #%d cols %d-%d: wid=%d\n", + self.number, first_colx, last_colx, width, + ) + elif rc == XL_COLUMNDEFAULT: # BIFF2 only + if not fmt_info: continue + first_colx, last_colx = local_unpack("<HH", data[:4]) + #### Warning OOo docs wrong; first_colx <= colx < last_colx + if blah: + fprintf( + self.logfile, + "COLUMNDEFAULT sheet #%d cols in range(%d, %d)\n", + self.number, first_colx, last_colx, + ) + if not(0 <= first_colx < last_colx <= 256): + print("*** NOTE: COLUMNDEFAULT record has first col index %d, last %d; " + "should have 0 <= first < last <= 256" + % (first_colx, last_colx), file=self.logfile) + last_colx = min(last_colx, 256) + for colx in xrange(first_colx, last_colx): + offset = 4 + 3 * (colx - first_colx) + cell_attr = data[offset:offset+3] + xf_index = self.fixed_BIFF2_xfindex(cell_attr, rowx=-1, colx=colx) + if colx in self.colinfo_map: + c = self.colinfo_map[colx] + else: + c = Colinfo() + self.colinfo_map[colx] = c + c.xf_index = xf_index + elif rc == XL_WINDOW2_B2: # BIFF 2 only + attr_names = ("show_formulas", "show_grid_lines", "show_sheet_headers", + "panes_are_frozen", "show_zero_values") + for attr, char in zip(attr_names, data[0:5]): + setattr(self, attr, int(char != b'\0')) + ( + self.first_visible_rowx, self.first_visible_colx, + self.automatic_grid_line_colour, + ) = unpack("<HHB", data[5:10]) + self.gridline_colour_rgb = unpack("<BBB", data[10:13]) + self.gridline_colour_index = nearest_colour_index( + self.book.colour_map, self.gridline_colour_rgb, debug=0) + else: + # if DEBUG: print "SHEET.READ: Unhandled record type %02x %d bytes %r" % (rc, data_len, data) + pass + if not eof_found: + raise XLRDError("Sheet %d (%r) missing EOF record" + % (self.number, self.name)) + self.tidy_dimensions() + self.update_cooked_mag_factors() + bk._position = oldpos + return 1 + + def string_record_contents(self, data): + bv = self.biff_version + bk = self.book + lenlen = (bv >= 30) + 1 + nchars_expected = unpack("<" + "BH"[lenlen - 1], data[:lenlen])[0] + offset = lenlen + if bv < 80: + enc = bk.encoding or bk.derive_encoding() + nchars_found = 0 + result = UNICODE_LITERAL("") + while 1: + if bv >= 80: + flag = BYTES_ORD(data[offset]) & 1 + enc = ("latin_1", "utf_16_le")[flag] + offset += 1 + chunk = unicode(data[offset:], enc) + result += chunk + nchars_found += len(chunk) + if nchars_found == nchars_expected: + return result + if nchars_found > nchars_expected: + msg = ("STRING/CONTINUE: expected %d chars, found %d" + % (nchars_expected, nchars_found)) + raise XLRDError(msg) + rc, _unused_len, data = bk.get_record_parts() + if rc != XL_CONTINUE: + raise XLRDError( + "Expected CONTINUE record; found record-type 0x%04X" % rc) + offset = 0 + + def update_cooked_mag_factors(self): + # Cached values are used ONLY for the non-active view mode. + # When the user switches to the non-active view mode, + # if the cached value for that mode is not valid, + # Excel pops up a window which says: + # "The number must be between 10 and 400. Try again by entering a number in this range." + # When the user hits OK, it drops into the non-active view mode + # but uses the magn from the active mode. + # NOTE: definition of "valid" depends on mode ... see below + blah = DEBUG or self.verbosity > 0 + if self.show_in_page_break_preview: + if self.scl_mag_factor is None: # no SCL record + self.cooked_page_break_preview_mag_factor = 100 # Yes, 100, not 60, NOT a typo + else: + self.cooked_page_break_preview_mag_factor = self.scl_mag_factor + zoom = self.cached_normal_view_mag_factor + if not (10 <= zoom <=400): + if blah: + print( + "WARNING *** WINDOW2 rcd sheet %d: Bad cached_normal_view_mag_factor: %d" + % (self.number, self.cached_normal_view_mag_factor), + file=self.logfile, + ) + zoom = self.cooked_page_break_preview_mag_factor + self.cooked_normal_view_mag_factor = zoom + else: + # normal view mode + if self.scl_mag_factor is None: # no SCL record + self.cooked_normal_view_mag_factor = 100 + else: + self.cooked_normal_view_mag_factor = self.scl_mag_factor + zoom = self.cached_page_break_preview_mag_factor + if not zoom: + # VALID, defaults to 60 + zoom = 60 + elif not (10 <= zoom <= 400): + if blah: + print( + "WARNING *** WINDOW2 rcd sheet %r: Bad cached_page_break_preview_mag_factor: %r" + % (self.number, self.cached_page_break_preview_mag_factor), + file=self.logfile, + ) + zoom = self.cooked_normal_view_mag_factor + self.cooked_page_break_preview_mag_factor = zoom + + def fixed_BIFF2_xfindex(self, cell_attr, rowx, colx, true_xfx=None): + DEBUG = 0 + blah = DEBUG or self.verbosity >= 2 + if self.biff_version == 21: + if self.book.xf_list: + if true_xfx is not None: + xfx = true_xfx + else: + xfx = BYTES_ORD(cell_attr[0]) & 0x3F + if xfx == 0x3F: + if self._ixfe is None: + raise XLRDError("BIFF2 cell record has XF index 63 but no preceding IXFE record.") + xfx = self._ixfe + # OOo docs are capable of interpretation that each + # cell record is preceded immediately by its own IXFE record. + # Empirical evidence is that (sensibly) an IXFE record applies to all + # following cell records until another IXFE comes along. + return xfx + # Have either Excel 2.0, or broken 2.1 w/o XF records -- same effect. + self.biff_version = self.book.biff_version = 20 + #### check that XF slot in cell_attr is zero + xfx_slot = BYTES_ORD(cell_attr[0]) & 0x3F + assert xfx_slot == 0 + xfx = self._cell_attr_to_xfx.get(cell_attr) + if xfx is not None: + return xfx + if blah: + fprintf(self.logfile, "New cell_attr %r at (%r, %r)\n", cell_attr, rowx, colx) + if not self.book.xf_list: + for xfx in xrange(16): + self.insert_new_BIFF20_xf(cell_attr=b"\x40\x00\x00", style=xfx < 15) + xfx = self.insert_new_BIFF20_xf(cell_attr=cell_attr) + return xfx + + def insert_new_BIFF20_xf(self, cell_attr, style=0): + DEBUG = 0 + blah = DEBUG or self.verbosity >= 2 + book = self.book + xfx = len(book.xf_list) + xf = self.fake_XF_from_BIFF20_cell_attr(cell_attr, style) + xf.xf_index = xfx + book.xf_list.append(xf) + if blah: + xf.dump(self.logfile, header="=== Faked XF %d ===" % xfx, footer="======") + if xf.format_key not in book.format_map: + if xf.format_key: + msg = "ERROR *** XF[%d] unknown format key (%d, 0x%04x)\n" + fprintf(self.logfile, msg, + xf.xf_index, xf.format_key, xf.format_key) + fmt = Format(xf.format_key, FUN, UNICODE_LITERAL("General")) + book.format_map[xf.format_key] = fmt + book.format_list.append(fmt) + cellty_from_fmtty = { + FNU: XL_CELL_NUMBER, + FUN: XL_CELL_NUMBER, + FGE: XL_CELL_NUMBER, + FDT: XL_CELL_DATE, + FTX: XL_CELL_NUMBER, # Yes, a number can be formatted as text. + } + fmt = book.format_map[xf.format_key] + cellty = cellty_from_fmtty[fmt.type] + self._xf_index_to_xl_type_map[xf.xf_index] = cellty + self._cell_attr_to_xfx[cell_attr] = xfx + return xfx + + def fake_XF_from_BIFF20_cell_attr(self, cell_attr, style=0): + from .formatting import XF, XFAlignment, XFBorder, XFBackground, XFProtection + xf = XF() + xf.alignment = XFAlignment() + xf.alignment.indent_level = 0 + xf.alignment.shrink_to_fit = 0 + xf.alignment.text_direction = 0 + xf.border = XFBorder() + xf.border.diag_up = 0 + xf.border.diag_down = 0 + xf.border.diag_colour_index = 0 + xf.border.diag_line_style = 0 # no line + xf.background = XFBackground() + xf.protection = XFProtection() + (prot_bits, font_and_format, halign_etc) = unpack('<BBB', cell_attr) + xf.format_key = font_and_format & 0x3F + xf.font_index = (font_and_format & 0xC0) >> 6 + upkbits(xf.protection, prot_bits, ( + (6, 0x40, 'cell_locked'), + (7, 0x80, 'formula_hidden'), + )) + xf.alignment.hor_align = halign_etc & 0x07 + for mask, side in ((0x08, 'left'), (0x10, 'right'), (0x20, 'top'), (0x40, 'bottom')): + if halign_etc & mask: + colour_index, line_style = 8, 1 # black, thin + else: + colour_index, line_style = 0, 0 # none, none + setattr(xf.border, side + '_colour_index', colour_index) + setattr(xf.border, side + '_line_style', line_style) + bg = xf.background + if halign_etc & 0x80: + bg.fill_pattern = 17 + else: + bg.fill_pattern = 0 + bg.background_colour_index = 9 # white + bg.pattern_colour_index = 8 # black + xf.parent_style_index = (0x0FFF, 0)[style] + xf.alignment.vert_align = 2 # bottom + xf.alignment.rotation = 0 + attr_stems = [ + 'format', + 'font', + 'alignment', + 'border', + 'background', + 'protection', + ] + for attr_stem in attr_stems: + attr = "_" + attr_stem + "_flag" + setattr(xf, attr, 1) + return xf + + def req_fmt_info(self): + if not self.formatting_info: + raise XLRDError("Feature requires open_workbook(..., formatting_info=True)") + + def computed_column_width(self, colx): + """ + Determine column display width. + + :param colx: + Index of the queried column, range 0 to 255. + Note that it is possible to find out the width that will be used to + display columns with no cell information e.g. column IV (colx=255). + + :return: + The column width that will be used for displaying + the given column by Excel, in units of 1/256th of the width of a + standard character (the digit zero in the first font). + + .. versionadded:: 0.6.1 + """ + self.req_fmt_info() + if self.biff_version >= 80: + colinfo = self.colinfo_map.get(colx, None) + if colinfo is not None: + return colinfo.width + if self.standardwidth is not None: + return self.standardwidth + elif self.biff_version >= 40: + if self.gcw[colx]: + if self.standardwidth is not None: + return self.standardwidth + else: + colinfo = self.colinfo_map.get(colx, None) + if colinfo is not None: + return colinfo.width + elif self.biff_version == 30: + colinfo = self.colinfo_map.get(colx, None) + if colinfo is not None: + return colinfo.width + # All roads lead to Rome and the DEFCOLWIDTH ... + if self.defcolwidth is not None: + return self.defcolwidth * 256 + return 8 * 256 # 8 is what Excel puts in a DEFCOLWIDTH record + + def handle_hlink(self, data): + # DEBUG = 1 + if DEBUG: print("\n=== hyperlink ===", file=self.logfile) + record_size = len(data) + h = Hyperlink() + h.frowx, h.lrowx, h.fcolx, h.lcolx, guid0, dummy, options = unpack('<HHHH16s4si', data[:32]) + assert guid0 == b"\xD0\xC9\xEA\x79\xF9\xBA\xCE\x11\x8C\x82\x00\xAA\x00\x4B\xA9\x0B" + assert dummy == b"\x02\x00\x00\x00" + if DEBUG: print("options: %08X" % options, file=self.logfile) + offset = 32 + + def get_nul_terminated_unicode(buf, ofs): + nb = unpack('<L', buf[ofs:ofs+4])[0] * 2 + ofs += 4 + uc = unicode(buf[ofs:ofs+nb], 'UTF-16le')[:-1] + ofs += nb + return uc, ofs + + if options & 0x14: # has a description + h.desc, offset = get_nul_terminated_unicode(data, offset) + + if options & 0x80: # has a target + h.target, offset = get_nul_terminated_unicode(data, offset) + + if (options & 1) and not (options & 0x100): # HasMoniker and not MonikerSavedAsString + # an OLEMoniker structure + clsid, = unpack('<16s', data[offset:offset + 16]) + if DEBUG: fprintf(self.logfile, "clsid=%r\n", clsid) + offset += 16 + if clsid == b"\xE0\xC9\xEA\x79\xF9\xBA\xCE\x11\x8C\x82\x00\xAA\x00\x4B\xA9\x0B": + # E0H C9H EAH 79H F9H BAH CEH 11H 8CH 82H 00H AAH 00H 4BH A9H 0BH + # URL Moniker + h.type = UNICODE_LITERAL('url') + nbytes = unpack('<L', data[offset:offset + 4])[0] + offset += 4 + h.url_or_path = unicode(data[offset:offset + nbytes], 'UTF-16le') + if DEBUG: fprintf(self.logfile, "initial url=%r len=%d\n", h.url_or_path, len(h.url_or_path)) + endpos = h.url_or_path.find('\x00') + if DEBUG: print("endpos=%d" % endpos, file=self.logfile) + h.url_or_path = h.url_or_path[:endpos] + true_nbytes = 2 * (endpos + 1) + offset += true_nbytes + extra_nbytes = nbytes - true_nbytes + extra_data = data[offset:offset + extra_nbytes] + offset += extra_nbytes + if DEBUG: + fprintf( + self.logfile, + "url=%r\nextra=%r\nnbytes=%d true_nbytes=%d extra_nbytes=%d\n", + h.url_or_path, extra_data, nbytes, true_nbytes, extra_nbytes, + ) + assert extra_nbytes in (24, 0) + elif clsid == b"\x03\x03\x00\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46": + # file moniker + h.type = UNICODE_LITERAL('local file') + uplevels, nbytes = unpack("<Hi", data[offset:offset + 6]) + offset += 6 + shortpath = b"..\\" * uplevels + data[offset:offset + nbytes - 1] #### BYTES, not unicode + if DEBUG: fprintf(self.logfile, "uplevels=%d shortpath=%r\n", uplevels, shortpath) + offset += nbytes + offset += 24 # OOo: "unknown byte sequence" + # above is version 0xDEAD + 20 reserved zero bytes + sz = unpack('<i', data[offset:offset + 4])[0] + if DEBUG: print("sz=%d" % sz, file=self.logfile) + offset += 4 + if sz: + xl = unpack('<i', data[offset:offset + 4])[0] + offset += 4 + offset += 2 # "unknown byte sequence" MS: 0x0003 + extended_path = unicode(data[offset:offset + xl], 'UTF-16le') # not zero-terminated + offset += xl + h.url_or_path = extended_path + else: + h.url_or_path = shortpath + #### MS KLUDGE WARNING #### + # The "shortpath" is bytes encoded in the **UNKNOWN** creator's "ANSI" encoding. + else: + fprintf(self.logfile, "*** unknown clsid %r\n", clsid) + elif options & 0x163 == 0x103: # UNC + h.type = UNICODE_LITERAL('unc') + h.url_or_path, offset = get_nul_terminated_unicode(data, offset) + elif options & 0x16B == 8: + h.type = UNICODE_LITERAL('workbook') + else: + h.type = UNICODE_LITERAL('unknown') + + if options & 0x8: # has textmark + h.textmark, offset = get_nul_terminated_unicode(data, offset) + + if DEBUG: + h.dump(header="... object dump ...") + print("offset=%d record_size=%d" % (offset, record_size)) + + extra_nbytes = record_size - offset + if extra_nbytes > 0: + fprintf( + self.logfile, + "*** WARNING: hyperlink at R%dC%d has %d extra data bytes: %s\n", + h.frowx + 1, + h.fcolx + 1, + extra_nbytes, + REPR(data[-extra_nbytes:]), + ) + # Seen: b"\x00\x00" also b"A\x00", b"V\x00" + elif extra_nbytes < 0: + raise XLRDError("Bug or corrupt file, send copy of input file for debugging") + + self.hyperlink_list.append(h) + for rowx in xrange(h.frowx, h.lrowx+1): + for colx in xrange(h.fcolx, h.lcolx+1): + self.hyperlink_map[rowx, colx] = h + + def handle_quicktip(self, data): + rcx, frowx, lrowx, fcolx, lcolx = unpack('<5H', data[:10]) + assert rcx == XL_QUICKTIP + assert self.hyperlink_list + h = self.hyperlink_list[-1] + assert (frowx, lrowx, fcolx, lcolx) == (h.frowx, h.lrowx, h.fcolx, h.lcolx) + assert data[-2:] == b'\x00\x00' + h.quicktip = unicode(data[10:-2], 'utf_16_le') + + def handle_msodrawingetc(self, recid, data_len, data): + if not OBJ_MSO_DEBUG: + return + DEBUG = 1 + if self.biff_version < 80: + return + o = MSODrawing() + pos = 0 + while pos < data_len: + tmp, fbt, cb = unpack('<HHI', data[pos:pos+8]) + ver = tmp & 0xF + inst = (tmp >> 4) & 0xFFF + if ver == 0xF: + ndb = 0 # container + else: + ndb = cb + if DEBUG: + hex_char_dump(data, pos, ndb + 8, base=0, fout=self.logfile) + fprintf(self.logfile, + "fbt:0x%04X inst:%d ver:0x%X cb:%d (0x%04X)\n", + fbt, inst, ver, cb, cb) + if fbt == 0xF010: # Client Anchor + assert ndb == 18 + (o.anchor_unk, + o.anchor_colx_lo, o.anchor_rowx_lo, + o.anchor_colx_hi, o.anchor_rowx_hi) = unpack('<Hiiii', data[pos+8:pos+8+ndb]) + elif fbt == 0xF011: # Client Data + # must be followed by an OBJ record + assert cb == 0 + assert pos + 8 == data_len + else: + pass + pos += ndb + 8 + else: + # didn't break out of while loop + assert pos == data_len + if DEBUG: + o.dump(self.logfile, header="=== MSODrawing ===", footer= " ") + + + def handle_obj(self, data): + if self.biff_version < 80: + return None + o = MSObj() + data_len = len(data) + pos = 0 + if OBJ_MSO_DEBUG: + fprintf(self.logfile, "... OBJ record len=%d...\n", data_len) + while pos < data_len: + ft, cb = unpack('<HH', data[pos:pos+4]) + if OBJ_MSO_DEBUG: + fprintf(self.logfile, "pos=%d ft=0x%04X cb=%d\n", pos, ft, cb) + hex_char_dump(data, pos, cb + 4, base=0, fout=self.logfile) + if pos == 0 and not (ft == 0x15 and cb == 18): + if self.verbosity: + fprintf(self.logfile, "*** WARNING Ignoring antique or corrupt OBJECT record\n") + return None + if ft == 0x15: # ftCmo ... s/b first + assert pos == 0 + o.type, o.id, option_flags = unpack('<HHH', data[pos+4:pos+10]) + upkbits(o, option_flags, ( + ( 0, 0x0001, 'locked'), + ( 4, 0x0010, 'printable'), + ( 8, 0x0100, 'autofilter'), # not documented in Excel 97 dev kit + ( 9, 0x0200, 'scrollbar_flag'), # not documented in Excel 97 dev kit + (13, 0x2000, 'autofill'), + (14, 0x4000, 'autoline'), + )) + elif ft == 0x00: + if data[pos:data_len] == b'\0' * (data_len - pos): + # ignore "optional reserved" data at end of record + break + msg = "Unexpected data at end of OBJECT record" + fprintf(self.logfile, "*** ERROR %s\n" % msg) + hex_char_dump(data, pos, data_len - pos, base=0, fout=self.logfile) + raise XLRDError(msg) + elif ft == 0x0C: # Scrollbar + values = unpack('<5H', data[pos+8:pos+18]) + for value, tag in zip(values, ('value', 'min', 'max', 'inc', 'page')): + setattr(o, 'scrollbar_' + tag, value) + elif ft == 0x0D: # "Notes structure" [used for cell comments] + # not documented in Excel 97 dev kit + if OBJ_MSO_DEBUG: fprintf(self.logfile, "*** OBJ record has ft==0x0D 'notes' structure\n") + elif ft == 0x13: # list box data + if o.autofilter: # non standard exit. NOT documented + break + else: + pass + pos += cb + 4 + else: + # didn't break out of while loop + pass + if OBJ_MSO_DEBUG: + o.dump(self.logfile, header="=== MSOBj ===", footer= " ") + return o + + def handle_note(self, data, txos): + if OBJ_MSO_DEBUG: + fprintf(self.logfile, '... NOTE record ...\n') + hex_char_dump(data, 0, len(data), base=0, fout=self.logfile) + o = Note() + data_len = len(data) + if self.biff_version < 80: + o.rowx, o.colx, expected_bytes = unpack('<HHH', data[:6]) + nb = len(data) - 6 + assert nb <= expected_bytes + pieces = [data[6:]] + expected_bytes -= nb + while expected_bytes > 0: + rc2, data2_len, data2 = self.book.get_record_parts() + assert rc2 == XL_NOTE + dummy_rowx, nb = unpack('<H2xH', data2[:6]) + assert dummy_rowx == 0xFFFF + assert nb == data2_len - 6 + pieces.append(data2[6:]) + expected_bytes -= nb + assert expected_bytes == 0 + enc = self.book.encoding or self.book.derive_encoding() + o.text = unicode(b''.join(pieces), enc) + o.rich_text_runlist = [(0, 0)] + o.show = 0 + o.row_hidden = 0 + o.col_hidden = 0 + o.author = UNICODE_LITERAL('') + o._object_id = None + self.cell_note_map[o.rowx, o.colx] = o + return + # Excel 8.0+ + o.rowx, o.colx, option_flags, o._object_id = unpack('<4H', data[:8]) + o.show = (option_flags >> 1) & 1 + o.row_hidden = (option_flags >> 7) & 1 + o.col_hidden = (option_flags >> 8) & 1 + # XL97 dev kit book says NULL [sic] bytes padding between string count and string data + # to ensure that string is word-aligned. Appears to be nonsense. + o.author, endpos = unpack_unicode_update_pos(data, 8, lenlen=2) + # There is a random/undefined byte after the author string (not counted in the + # string length). + # Issue 4 on github: Google Spreadsheet doesn't write the undefined byte. + assert (data_len - endpos) in (0, 1) + if OBJ_MSO_DEBUG: + o.dump(self.logfile, header="=== Note ===", footer= " ") + txo = txos.get(o._object_id) + if txo: + o.text = txo.text + o.rich_text_runlist = txo.rich_text_runlist + self.cell_note_map[o.rowx, o.colx] = o + + def handle_txo(self, data): + if self.biff_version < 80: + return + o = MSTxo() + fmt = '<HH6sHHH' + fmtsize = calcsize(fmt) + option_flags, o.rot, controlInfo, cchText, cbRuns, o.ifntEmpty = unpack(fmt, data[:fmtsize]) + o.fmla = data[fmtsize:] + upkbits(o, option_flags, ( + ( 3, 0x000E, 'horz_align'), + ( 6, 0x0070, 'vert_align'), + ( 9, 0x0200, 'lock_text'), + (14, 0x4000, 'just_last'), + (15, 0x8000, 'secret_edit'), + )) + totchars = 0 + o.text = UNICODE_LITERAL('') + while totchars < cchText: + rc2, data2_len, data2 = self.book.get_record_parts() + assert rc2 == XL_CONTINUE + if OBJ_MSO_DEBUG: + hex_char_dump(data2, 0, data2_len, base=0, fout=self.logfile) + nb = BYTES_ORD(data2[0]) # 0 means latin1, 1 means utf_16_le + nchars = data2_len - 1 + if nb: + assert nchars % 2 == 0 + nchars //= 2 + utext, endpos = unpack_unicode_update_pos(data2, 0, known_len=nchars) + assert endpos == data2_len + o.text += utext + totchars += nchars + o.rich_text_runlist = [] + totruns = 0 + while totruns < cbRuns: # counts of BYTES, not runs + rc3, data3_len, data3 = self.book.get_record_parts() + # print totruns, cbRuns, rc3, data3_len, repr(data3) + assert rc3 == XL_CONTINUE + assert data3_len % 8 == 0 + for pos in xrange(0, data3_len, 8): + run = unpack('<HH4x', data3[pos:pos+8]) + o.rich_text_runlist.append(run) + totruns += 8 + # remove trailing entries that point to the end of the string + while o.rich_text_runlist and o.rich_text_runlist[-1][0] == cchText: + del o.rich_text_runlist[-1] + if OBJ_MSO_DEBUG: + o.dump(self.logfile, header="=== MSTxo ===", footer= " ") + print(o.rich_text_runlist, file=self.logfile) + return o + + def handle_feat11(self, data): + if not OBJ_MSO_DEBUG: + return + # rt: Record type; this matches the BIFF rt in the first two bytes of the record; =0872h + # grbitFrt: FRT cell reference flag (see table below for details) + # Ref0: Range reference to a worksheet cell region if grbitFrt=1 (bitFrtRef). Otherwise blank. + # isf: Shared feature type index =5 for Table + # fHdr: =0 since this is for feat not feat header + # reserved0: Reserved for future use =0 for Table + # cref: Count of ref ranges this feature is on + # cbFeatData: Count of byte for the current feature data. + # reserved1: =0 currently not used + # Ref1: Repeat of Ref0. UNDOCUMENTED + rt, grbitFrt, Ref0, isf, fHdr, reserved0, cref, cbFeatData, reserved1, Ref1 = unpack('<HH8sHBiHiH8s', data[0:35]) + assert reserved0 == 0 + assert reserved1 == 0 + assert isf == 5 + assert rt == 0x872 + assert fHdr == 0 + assert Ref1 == Ref0 + print(self.logfile, "FEAT11: grbitFrt=%d Ref0=%r cref=%d cbFeatData=%d\n", grbitFrt, Ref0, cref, cbFeatData) + # lt: Table data source type: + # =0 for Excel Worksheet Table =1 for read-write SharePoint linked List + # =2 for XML mapper Table =3 for Query Table + # idList: The ID of the Table (unique per worksheet) + # crwHeader: How many header/title rows the Table has at the top + # crwTotals: How many total rows the Table has at the bottom + # idFieldNext: Next id to try when assigning a unique id to a new field + # cbFSData: The size of the Fixed Data portion of the Table data structure. + # rupBuild: the rupBuild that generated the record + # unusedShort: UNUSED short that can be used later. The value is reserved during round-tripping. + # listFlags: Collection of bit flags: (see listFlags' bit setting table below for detail.) + # lPosStmCache: Table data stream position of cached data + # cbStmCache: Count of bytes of cached data + # cchStmCache: Count of characters of uncompressed cached data in the stream + # lem: Table edit mode (see List (Table) Editing Mode (lem) setting table below for details.) + # rgbHashParam: Hash value for SharePoint Table + # cchName: Count of characters in the Table name string rgbName + (lt, idList, crwHeader, crwTotals, idFieldNext, cbFSData, + rupBuild, unusedShort, listFlags, lPosStmCache, cbStmCache, + cchStmCache, lem, rgbHashParam, cchName) = unpack('<iiiiiiHHiiiii16sH', data[35:35+66]) + print("lt=%d idList=%d crwHeader=%d crwTotals=%d idFieldNext=%d cbFSData=%d\n" + "rupBuild=%d unusedShort=%d listFlags=%04X lPosStmCache=%d cbStmCache=%d\n" + "cchStmCache=%d lem=%d rgbHashParam=%r cchName=%d" % ( + lt, idList, crwHeader, crwTotals, idFieldNext, cbFSData, + rupBuild, unusedShort,listFlags, lPosStmCache, cbStmCache, + cchStmCache, lem, rgbHashParam, cchName), file=self.logfile) + + def __repr__(self): + return "Sheet {:>2}:<{}>".format(self.number, self.name) + + +class MSODrawing(BaseObject): + pass + + +class MSObj(BaseObject): + pass + + +class MSTxo(BaseObject): + pass + + +class Note(BaseObject): + """ + Represents a user "comment" or "note". + Note objects are accessible through :attr:`Sheet.cell_note_map`. + + .. versionadded:: 0.7.2 + """ + + #: Author of note + author = UNICODE_LITERAL('') + + #: ``True`` if the containing column is hidden + col_hidden = 0 + + #: Column index + colx = 0 + + #: List of ``(offset_in_string, font_index)`` tuples. + #: Unlike :attr:`Sheet.rich_text_runlist_map`, the first offset should + #: always be 0. + rich_text_runlist = None + + #: True if the containing row is hidden + row_hidden = 0 + + #: Row index + rowx = 0 + + #: True if note is always shown + show = 0 + + #: Text of the note + text = UNICODE_LITERAL('') + + +class Hyperlink(BaseObject): + """ + Contains the attributes of a hyperlink. + Hyperlink objects are accessible through :attr:`Sheet.hyperlink_list` + and :attr:`Sheet.hyperlink_map`. + + .. versionadded:: 0.7.2 + """ + + #: Index of first row + frowx = None + + #: Index of last row + lrowx = None + + #: Index of first column + fcolx = None + + #: Index of last column + lcolx = None + + #: Type of hyperlink. Unicode string, one of 'url', 'unc', + #: 'local file', 'workbook', 'unknown' + type = None + + #: The URL or file-path, depending in the type. Unicode string, except + #: in the rare case of a local but non-existent file with non-ASCII + #: characters in the name, in which case only the "8.3" filename is + #: available, as a :class:`bytes` (3.x) or :class:`str` (2.x) string, + #: *with unknown encoding.* + url_or_path = None + + #: Description. + #: This is displayed in the cell, + #: and should be identical to the cell value. Unicode string, or ``None``. + #: It seems impossible NOT to have a description created by the Excel UI. + desc = None + + #: Target frame. Unicode string. + #: + #: .. note:: + #: No cases of this have been seen in the wild. + #: It seems impossible to create one in the Excel UI. + target = None + + #: The piece after the "#" in + #: "http://docs.python.org/library#struct_module", or the ``Sheet1!A1:Z99`` + #: part when type is "workbook". + textmark = None + + #: The text of the "quick tip" displayed when the cursor + #: hovers over the hyperlink. + quicktip = None + +# === helpers === + +def unpack_RK(rk_str): + flags = BYTES_ORD(rk_str[0]) + if flags & 2: + # There's a SIGNED 30-bit integer in there! + i, = unpack('<i', rk_str) + i >>= 2 # div by 4 to drop the 2 flag bits + if flags & 1: + return i / 100.0 + return float(i) + else: + # It's the most significant 30 bits of an IEEE 754 64-bit FP number + d, = unpack('<d', b'\0\0\0\0' + BYTES_LITERAL(chr(flags & 252)) + rk_str[1:4]) + if flags & 1: + return d / 100.0 + return d + +##### =============== Cell ======================================== ##### + +cellty_from_fmtty = { + FNU: XL_CELL_NUMBER, + FUN: XL_CELL_NUMBER, + FGE: XL_CELL_NUMBER, + FDT: XL_CELL_DATE, + FTX: XL_CELL_NUMBER, # Yes, a number can be formatted as text. +} + +ctype_text = { + XL_CELL_EMPTY: 'empty', + XL_CELL_TEXT: 'text', + XL_CELL_NUMBER: 'number', + XL_CELL_DATE: 'xldate', + XL_CELL_BOOLEAN: 'bool', + XL_CELL_ERROR: 'error', + XL_CELL_BLANK: 'blank', +} + + +class Cell(BaseObject): + """ + Contains the data for one cell. + + .. warning:: + You don't call this class yourself. You access :class:`Cell` objects + via methods of the :class:`Sheet` object(s) that you found in the + :class:`~xlrd.book.Book` object that was returned when you called + :func:`~xlrd.open_workbook` + + Cell objects have three attributes: ``ctype`` is an int, ``value`` + (which depends on ``ctype``) and ``xf_index``. + If ``formatting_info`` is not enabled when the workbook is opened, + ``xf_index`` will be ``None``. + + The following table describes the types of cells and how their values + are represented in Python. + + .. raw:: html + + <table border="1" cellpadding="7"> + <tr> + <th>Type symbol</th> + <th>Type number</th> + <th>Python value</th> + </tr> + <tr> + <td>XL_CELL_EMPTY</td> + <td align="center">0</td> + <td>empty string ''</td> + </tr> + <tr> + <td>XL_CELL_TEXT</td> + <td align="center">1</td> + <td>a Unicode string</td> + </tr> + <tr> + <td>XL_CELL_NUMBER</td> + <td align="center">2</td> + <td>float</td> + </tr> + <tr> + <td>XL_CELL_DATE</td> + <td align="center">3</td> + <td>float</td> + </tr> + <tr> + <td>XL_CELL_BOOLEAN</td> + <td align="center">4</td> + <td>int; 1 means TRUE, 0 means FALSE</td> + </tr> + <tr> + <td>XL_CELL_ERROR</td> + <td align="center">5</td> + <td>int representing internal Excel codes; for a text representation, + refer to the supplied dictionary error_text_from_code</td> + </tr> + <tr> + <td>XL_CELL_BLANK</td> + <td align="center">6</td> + <td>empty string ''. Note: this type will appear only when + open_workbook(..., formatting_info=True) is used.</td> + </tr> + </table> + """ + + __slots__ = ['ctype', 'value', 'xf_index'] + + def __init__(self, ctype, value, xf_index=None): + self.ctype = ctype + self.value = value + self.xf_index = xf_index + + def __repr__(self): + if self.xf_index is None: + return "%s:%r" % (ctype_text[self.ctype], self.value) + else: + return "%s:%r (XF:%r)" % (ctype_text[self.ctype], self.value, self.xf_index) + +empty_cell = Cell(XL_CELL_EMPTY, UNICODE_LITERAL('')) + +##### =============== Colinfo and Rowinfo ============================== ##### + + +class Colinfo(BaseObject): + """ + Width and default formatting information that applies to one or + more columns in a sheet. Derived from ``COLINFO`` records. + + Here is the default hierarchy for width, according to the OOo docs: + + In BIFF3, if a ``COLINFO`` record is missing for a column, + the width specified in the record ``DEFCOLWIDTH`` is used instead. + + In BIFF4-BIFF7, the width set in this ``COLINFO`` record is only used, + if the corresponding bit for this column is cleared in the ``GCW`` + record, otherwise the column width set in the ``DEFCOLWIDTH`` record + is used (the ``STANDARDWIDTH`` record is always ignored in this case [#f1]_). + + In BIFF8, if a ``COLINFO`` record is missing for a column, + the width specified in the record ``STANDARDWIDTH`` is used. + If this ``STANDARDWIDTH`` record is also missing, + the column width of the record ``DEFCOLWIDTH`` is used instead. + + .. [#f1] The docs on the ``GCW`` record say this: + + If a bit is set, the corresponding column uses the width set in the + ``STANDARDWIDTH`` record. If a bit is cleared, the corresponding column + uses the width set in the ``COLINFO`` record for this column. + + If a bit is set, and the worksheet does not contain the ``STANDARDWIDTH`` + record, or if the bit is cleared, and the worksheet does not contain the + ``COLINFO`` record, the ``DEFCOLWIDTH`` record of the worksheet will be + used instead. + + xlrd goes with the GCW version of the story. + Reference to the source may be useful: see + :meth:`Sheet.computed_column_width`. + + .. versionadded:: 0.6.1 + """ + + #: Width of the column in 1/256 of the width of the zero character, + #: using default font (first ``FONT`` record in the file). + width = 0 + + #: XF index to be used for formatting empty cells. + xf_index = -1 + + #: 1 = column is hidden + hidden = 0 + + #: Value of a 1-bit flag whose purpose is unknown + #: but is often seen set to 1 + bit1_flag = 0 + + #: Outline level of the column, in ``range(7)``. + #: (0 = no outline) + outline_level = 0 + + #: 1 = column is collapsed + collapsed = 0 + +_USE_SLOTS = 1 + + +class Rowinfo(BaseObject): + """ + Height and default formatting information that applies to a row in a sheet. + Derived from ``ROW`` records. + + .. versionadded:: 0.6.1 + """ + + if _USE_SLOTS: + __slots__ = ( + "height", + "has_default_height", + "outline_level", + "outline_group_starts_ends", + "hidden", + "height_mismatch", + "has_default_xf_index", + "xf_index", + "additional_space_above", + "additional_space_below", + ) + + def __init__(self): + #: Height of the row, in twips. One twip == 1/20 of a point. + self.height = None + + #: 0 = Row has custom height; 1 = Row has default height. + self.has_default_height = None + + #: Outline level of the row (0 to 7) + self.outline_level = None + + #: 1 = Outline group starts or ends here (depending on where the + #: outline buttons are located, see ``WSBOOL`` record, which is not + #: parsed by xlrd), *and* is collapsed. + self.outline_group_starts_ends = None + + #: 1 = Row is hidden (manually, or by a filter or outline group) + self.hidden = None + + #: 1 = Row height and default font height do not match. + self.height_mismatch = None + + #: 1 = the xf_index attribute is usable; 0 = ignore it. + self.has_default_xf_index = None + + #: Index to default :class:`~xlrd.formatting.XF` record for empty cells + #: in this row. Don't use this if ``has_default_xf_index == 0``. + self.xf_index = None + + #: This flag is set if the upper border of at least one cell in this + #: row or if the lower border of at least one cell in the row above is + #: formatted with a thick line style. Thin and medium line styles are + #: not taken into account. + self.additional_space_above = None + + #: This flag is set if the lower border of at least one cell in this row + #: or if the upper border of at least one cell in the row below is + #: formatted with a medium or thick line style. Thin line styles are not + #: taken into account. + self.additional_space_below = None + + def __getstate__(self): + return ( + self.height, + self.has_default_height, + self.outline_level, + self.outline_group_starts_ends, + self.hidden, + self.height_mismatch, + self.has_default_xf_index, + self.xf_index, + self.additional_space_above, + self.additional_space_below, + ) + + def __setstate__(self, state): + ( + self.height, + self.has_default_height, + self.outline_level, + self.outline_group_starts_ends, + self.hidden, + self.height_mismatch, + self.has_default_xf_index, + self.xf_index, + self.additional_space_above, + self.additional_space_below, + ) = state diff --git a/.venv/lib/python3.9/site-packages/xlrd/timemachine.py b/.venv/lib/python3.9/site-packages/xlrd/timemachine.py new file mode 100644 index 00000000..a519299e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/timemachine.py @@ -0,0 +1,53 @@ +## +# <p>Copyright (c) 2006-2012 Stephen John Machin, Lingfo Pty Ltd</p> +# <p>This module is part of the xlrd package, which is released under a BSD-style licence.</p> +## + +# timemachine.py -- adaptation for single codebase. +# Currently supported: 2.6 to 2.7, 3.2+ +# usage: from timemachine import * + +from __future__ import print_function + +import sys + +python_version = sys.version_info[:2] # e.g. version 2.6 -> (2, 6) + +if python_version >= (3, 0): + # Python 3 + BYTES_LITERAL = lambda x: x.encode('latin1') + UNICODE_LITERAL = lambda x: x + BYTES_ORD = lambda byte: byte + from io import BytesIO as BYTES_IO + def fprintf(f, fmt, *vargs): + fmt = fmt.replace("%r", "%a") + if fmt.endswith('\n'): + print(fmt[:-1] % vargs, file=f) + else: + print(fmt % vargs, end=' ', file=f) + EXCEL_TEXT_TYPES = (str, bytes, bytearray) # xlwt: isinstance(obj, EXCEL_TEXT_TYPES) + REPR = ascii + xrange = range + unicode = lambda b, enc: b.decode(enc) + ensure_unicode = lambda s: s + unichr = chr +else: + # Python 2 + BYTES_LITERAL = lambda x: x + UNICODE_LITERAL = lambda x: x.decode('latin1') + BYTES_ORD = ord + from cStringIO import StringIO as BYTES_IO + def fprintf(f, fmt, *vargs): + if fmt.endswith('\n'): + print(fmt[:-1] % vargs, file=f) + else: + print(fmt % vargs, end=' ', file=f) + try: + EXCEL_TEXT_TYPES = basestring # xlwt: isinstance(obj, EXCEL_TEXT_TYPES) + except NameError: + EXCEL_TEXT_TYPES = (str, unicode) + REPR = repr + xrange = xrange + # following used only to overcome 2.x ElementTree gimmick which + # returns text as `str` if it's ascii, otherwise `unicode` + ensure_unicode = unicode # used only in xlsx.py diff --git a/.venv/lib/python3.9/site-packages/xlrd/xldate.py b/.venv/lib/python3.9/site-packages/xlrd/xldate.py new file mode 100644 index 00000000..d84c6508 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlrd/xldate.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2008 Stephen John Machin, Lingfo Pty Ltd +# This module is part of the xlrd package, which is released under a +# BSD-style licence. +# No part of the content of this file was derived from the works of David Giffin. +""" +Tools for working with dates and times in Excel files. + +The conversion from ``days`` to ``(year, month, day)`` starts with +an integral "julian day number" aka JDN. +FWIW: + +- JDN 0 corresponds to noon on Monday November 24 in Gregorian year -4713. + +More importantly: + +- Noon on Gregorian 1900-03-01 (day 61 in the 1900-based system) is JDN 2415080.0 +- Noon on Gregorian 1904-01-02 (day 1 in the 1904-based system) is JDN 2416482.0 + +""" +import datetime + +_JDN_delta = (2415080 - 61, 2416482 - 1) +assert _JDN_delta[1] - _JDN_delta[0] == 1462 + +# Pre-calculate the datetime epochs for efficiency. +epoch_1904 = datetime.datetime(1904, 1, 1) +epoch_1900 = datetime.datetime(1899, 12, 31) +epoch_1900_minus_1 = datetime.datetime(1899, 12, 30) + +# This is equivalent to 10000-01-01: +_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) + + +class XLDateError(ValueError): + "A base class for all datetime-related errors." + + +class XLDateNegative(XLDateError): + "``xldate < 0.00``" + + +class XLDateAmbiguous(XLDateError): + "The 1900 leap-year problem ``(datemode == 0 and 1.0 <= xldate < 61.0)``" + + +class XLDateTooLarge(XLDateError): + "Gregorian year 10000 or later" + + +class XLDateBadDatemode(XLDateError): + "``datemode`` arg is neither 0 nor 1" + + +class XLDateBadTuple(XLDateError): + pass + + +def xldate_as_tuple(xldate, datemode): + """ + Convert an Excel number (presumed to represent a date, a datetime or a time) into + a tuple suitable for feeding to datetime or mx.DateTime constructors. + + :param xldate: The Excel number + :param datemode: 0: 1900-based, 1: 1904-based. + :raises xlrd.xldate.XLDateNegative: + :raises xlrd.xldate.XLDateAmbiguous: + + :raises xlrd.xldate.XLDateTooLarge: + :raises xlrd.xldate.XLDateBadDatemode: + :raises xlrd.xldate.XLDateError: + :returns: Gregorian ``(year, month, day, hour, minute, nearest_second)``. + + .. warning:: + + When using this function to interpret the contents of a workbook, you + should pass in the :attr:`~xlrd.book.Book.datemode` + attribute of that workbook. Whether the workbook has ever been anywhere + near a Macintosh is irrelevant. + + .. admonition:: Special case + + If ``0.0 <= xldate < 1.0``, it is assumed to represent a time; + ``(0, 0, 0, hour, minute, second)`` will be returned. + + .. note:: + + ``1904-01-01`` is not regarded as a valid date in the ``datemode==1`` + system; its "serial number" is zero. + """ + if datemode not in (0, 1): + raise XLDateBadDatemode(datemode) + if xldate == 0.00: + return (0, 0, 0, 0, 0, 0) + if xldate < 0.00: + raise XLDateNegative(xldate) + xldays = int(xldate) + frac = xldate - xldays + seconds = int(round(frac * 86400.0)) + assert 0 <= seconds <= 86400 + if seconds == 86400: + hour = minute = second = 0 + xldays += 1 + else: + # second = seconds % 60; minutes = seconds // 60 + minutes, second = divmod(seconds, 60) + # minute = minutes % 60; hour = minutes // 60 + hour, minute = divmod(minutes, 60) + if xldays >= _XLDAYS_TOO_LARGE[datemode]: + raise XLDateTooLarge(xldate) + + if xldays == 0: + return (0, 0, 0, hour, minute, second) + + if xldays < 61 and datemode == 0: + raise XLDateAmbiguous(xldate) + + jdn = xldays + _JDN_delta[datemode] + yreg = ((((jdn * 4 + 274277) // 146097) * 3 // 4) + jdn + 1363) * 4 + 3 + mp = ((yreg % 1461) // 4) * 535 + 333 + d = ((mp % 16384) // 535) + 1 + # mp /= 16384 + mp >>= 14 + if mp >= 10: + return ((yreg // 1461) - 4715, mp - 9, d, hour, minute, second) + else: + return ((yreg // 1461) - 4716, mp + 3, d, hour, minute, second) + + +def xldate_as_datetime(xldate, datemode): + """ + Convert an Excel date/time number into a :class:`datetime.datetime` object. + + :param xldate: The Excel number + :param datemode: 0: 1900-based, 1: 1904-based. + + :returns: A :class:`datetime.datetime` object. + """ + + # Set the epoch based on the 1900/1904 datemode. + if datemode: + epoch = epoch_1904 + else: + if xldate < 60: + epoch = epoch_1900 + else: + # Workaround Excel 1900 leap year bug by adjusting the epoch. + epoch = epoch_1900_minus_1 + + # The integer part of the Excel date stores the number of days since + # the epoch and the fractional part stores the percentage of the day. + days = int(xldate) + fraction = xldate - days + + # Get the the integer and decimal seconds in Excel's millisecond resolution. + seconds = int(round(fraction * 86400000.0)) + seconds, milliseconds = divmod(seconds, 1000) + + return epoch + datetime.timedelta(days, seconds, 0, milliseconds) + + +# === conversions from date/time to xl numbers + +def _leap(y): + if y % 4: return 0 + if y % 100: return 1 + if y % 400: return 0 + return 1 + +_days_in_month = (None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + + +def xldate_from_date_tuple(date_tuple, datemode): + """ + Convert a date tuple (year, month, day) to an Excel date. + + :param year: Gregorian year. + :param month: ``1 <= month <= 12`` + :param day: ``1 <= day <= last day of that (year, month)`` + :param datemode: 0: 1900-based, 1: 1904-based. + :raises xlrd.xldate.XLDateAmbiguous: + :raises xlrd.xldate.XLDateBadDatemode: + :raises xlrd.xldate.XLDateBadTuple: + ``(year, month, day)`` is too early/late or has invalid component(s) + :raises xlrd.xldate.XLDateError: + """ + year, month, day = date_tuple + + if datemode not in (0, 1): + raise XLDateBadDatemode(datemode) + + if year == 0 and month == 0 and day == 0: + return 0.00 + + if not (1900 <= year <= 9999): + raise XLDateBadTuple("Invalid year: %r" % ((year, month, day),)) + if not (1 <= month <= 12): + raise XLDateBadTuple("Invalid month: %r" % ((year, month, day),)) + if (day < 1 or + (day > _days_in_month[month] and not(day == 29 and month == 2 and _leap(year)))): + raise XLDateBadTuple("Invalid day: %r" % ((year, month, day),)) + + Yp = year + 4716 + M = month + if M <= 2: + Yp = Yp - 1 + Mp = M + 9 + else: + Mp = M - 3 + jdn = (1461 * Yp // 4) + ((979 * Mp + 16) // 32) + \ + day - 1364 - (((Yp + 184) // 100) * 3 // 4) + xldays = jdn - _JDN_delta[datemode] + if xldays <= 0: + raise XLDateBadTuple("Invalid (year, month, day): %r" % ((year, month, day),)) + if xldays < 61 and datemode == 0: + raise XLDateAmbiguous("Before 1900-03-01: %r" % ((year, month, day),)) + return float(xldays) + + +def xldate_from_time_tuple(time_tuple): + """ + Convert a time tuple ``(hour, minute, second)`` to an Excel "date" value + (fraction of a day). + + :param hour: ``0 <= hour < 24`` + :param minute: ``0 <= minute < 60`` + :param second: ``0 <= second < 60`` + :raises xlrd.xldate.XLDateBadTuple: Out-of-range hour, minute, or second + """ + hour, minute, second = time_tuple + if 0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60: + return ((second / 60.0 + minute) / 60.0 + hour) / 24.0 + raise XLDateBadTuple("Invalid (hour, minute, second): %r" % ((hour, minute, second),)) + + +def xldate_from_datetime_tuple(datetime_tuple, datemode): + """ + Convert a datetime tuple ``(year, month, day, hour, minute, second)`` to an + Excel date value. + For more details, refer to other xldate_from_*_tuple functions. + + :param datetime_tuple: ``(year, month, day, hour, minute, second)`` + :param datemode: 0: 1900-based, 1: 1904-based. + """ + return ( + xldate_from_date_tuple(datetime_tuple[:3], datemode) + + xldate_from_time_tuple(datetime_tuple[3:]) + ) diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst new file mode 100644 index 00000000..bc339693 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/DESCRIPTION.rst @@ -0,0 +1,85 @@ +|Travis|_ |Coveralls|_ |Docs|_ |PyPI|_ + +.. |Travis| image:: https://api.travis-ci.org/python-excel/xlwt.svg?branch=master +.. _Travis: https://travis-ci.org/python-excel/xlwt + +.. |Coveralls| image:: https://coveralls.io/repos/python-excel/xlwt/badge.svg?branch=master +.. _Coveralls: https://coveralls.io/r/python-excel/xlwt?branch=master + +.. |Docs| image:: https://readthedocs.org/projects/xlwt/badge/?version=latest +.. _Docs: https://xlwt.readthedocs.org/en/latest/ + +.. |PyPI| image:: https://badge.fury.io/py/xlwt.svg +.. _PyPI: https://badge.fury.io/py/xlwt + +xlwt +==== + +This is a library for developers to use to generate +spreadsheet files compatible with Microsoft Excel versions 95 to 2003. + +The package itself is pure Python with no dependencies on modules or packages +outside the standard Python distribution. + +Please read this before using this package: +https://groups.google.com/d/msg/python-excel/P6TjJgFVjMI/g8d0eWxTBQAJ + +Installation +============ + +Do the following in your virtualenv:: + + pip install xlwt + +Quick start +=========== + +.. code-block:: python + + import xlwt + from datetime import datetime + + style0 = xlwt.easyxf('font: name Times New Roman, color-index red, bold on', + num_format_str='#,##0.00') + style1 = xlwt.easyxf(num_format_str='D-MMM-YY') + + wb = xlwt.Workbook() + ws = wb.add_sheet('A Test Sheet') + + ws.write(0, 0, 1234.56, style0) + ws.write(1, 0, datetime.now(), style1) + ws.write(2, 0, 1) + ws.write(2, 1, 1) + ws.write(2, 2, xlwt.Formula("A3+B3")) + + wb.save('example.xls') + + +Documentation +============= + +Documentation can be found in the ``docs`` directory of the xlwt package. +If these aren't sufficient, please consult the code in the +examples directory and the source code itself. + +The latest documentation can also be found at: +https://xlwt.readthedocs.org/en/latest/ + +Problems? +========= +Try the following in this order: + +- Read the source + +- Ask a question on https://groups.google.com/group/python-excel/ + +Acknowledgements +================ + +xlwt is a fork of the pyExcelerator package, which was developed by +Roman V. Kiseliov. This product includes software developed by +Roman V. Kiseliov <roman@kiseliov.ru>. + +xlwt uses ANTLR v 2.7.7 to generate its formula compiler. + + diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA new file mode 100644 index 00000000..1960bc95 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.0 +Name: xlwt +Version: 1.3.0 +Summary: Library to create spreadsheet files compatible with MS Excel 97/2000/XP/2003 XLS files, on any platform, with Python 2.6, 2.7, 3.3+ +Home-page: http://www.python-excel.org/ +Author: John Machin +Author-email: sjmachin@lexicon.net +License: BSD +Download-URL: https://pypi.python.org/pypi/xlwt +Keywords: xls excel spreadsheet workbook worksheet pyExcelerator +Platform: Platform Independent +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: License :: OSI Approved :: BSD License +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Office/Business :: Financial :: Spreadsheet +Classifier: Topic :: Database +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 + +|Travis|_ |Coveralls|_ |Docs|_ |PyPI|_ + +.. |Travis| image:: https://api.travis-ci.org/python-excel/xlwt.svg?branch=master +.. _Travis: https://travis-ci.org/python-excel/xlwt + +.. |Coveralls| image:: https://coveralls.io/repos/python-excel/xlwt/badge.svg?branch=master +.. _Coveralls: https://coveralls.io/r/python-excel/xlwt?branch=master + +.. |Docs| image:: https://readthedocs.org/projects/xlwt/badge/?version=latest +.. _Docs: https://xlwt.readthedocs.org/en/latest/ + +.. |PyPI| image:: https://badge.fury.io/py/xlwt.svg +.. _PyPI: https://badge.fury.io/py/xlwt + +xlwt +==== + +This is a library for developers to use to generate +spreadsheet files compatible with Microsoft Excel versions 95 to 2003. + +The package itself is pure Python with no dependencies on modules or packages +outside the standard Python distribution. + +Please read this before using this package: +https://groups.google.com/d/msg/python-excel/P6TjJgFVjMI/g8d0eWxTBQAJ + +Installation +============ + +Do the following in your virtualenv:: + + pip install xlwt + +Quick start +=========== + +.. code-block:: python + + import xlwt + from datetime import datetime + + style0 = xlwt.easyxf('font: name Times New Roman, color-index red, bold on', + num_format_str='#,##0.00') + style1 = xlwt.easyxf(num_format_str='D-MMM-YY') + + wb = xlwt.Workbook() + ws = wb.add_sheet('A Test Sheet') + + ws.write(0, 0, 1234.56, style0) + ws.write(1, 0, datetime.now(), style1) + ws.write(2, 0, 1) + ws.write(2, 1, 1) + ws.write(2, 2, xlwt.Formula("A3+B3")) + + wb.save('example.xls') + + +Documentation +============= + +Documentation can be found in the ``docs`` directory of the xlwt package. +If these aren't sufficient, please consult the code in the +examples directory and the source code itself. + +The latest documentation can also be found at: +https://xlwt.readthedocs.org/en/latest/ + +Problems? +========= +Try the following in this order: + +- Read the source + +- Ask a question on https://groups.google.com/group/python-excel/ + +Acknowledgements +================ + +xlwt is a fork of the pyExcelerator package, which was developed by +Roman V. Kiseliov. This product includes software developed by +Roman V. Kiseliov <roman@kiseliov.ru>. + +xlwt uses ANTLR v 2.7.7 to generate its formula compiler. + + diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD new file mode 100644 index 00000000..faa8d39c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/RECORD @@ -0,0 +1,47 @@ +xlwt-1.3.0.dist-info/DESCRIPTION.rst,sha256=Wd9ksNg8D2Z8Ly_hTdFNt5MbC0sDd-kgkuHFyoriPjw,2249 +xlwt-1.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +xlwt-1.3.0.dist-info/METADATA,sha256=f90RqO9sD7PFwnWjetPQ-GT36U1mXzm0xYynVzkwhRY,3530 +xlwt-1.3.0.dist-info/RECORD,, +xlwt-1.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +xlwt-1.3.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +xlwt-1.3.0.dist-info/metadata.json,sha256=bMNWYp1NWNRK9gf0AG8xPg3B8hRK4naNQAafW_Jtpa0,1390 +xlwt-1.3.0.dist-info/top_level.txt,sha256=PoPrMegX_ucpxfEzLnC48zwqCZ5Hg_dd2RJPyetxXeM,5 +xlwt/BIFFRecords.py,sha256=40mRM4FcmyX56EE4hxnKa2g4X-qM8uk2BaeogIiKq6M,97384 +xlwt/Bitmap.py,sha256=kRgc798XDjOQO40y_1l0nAGQ8GY8-p-97wAcmkSTs1g,10930 +xlwt/Cell.py,sha256=Krnmim2i1YMdWHg84QmTqtSq75TYFqsP7uLVunGUZ1Q,8559 +xlwt/Column.py,sha256=ESDuWPhep6ep43QV1v7lki4Swc_U1vKBg8edgQpy3Js,1510 +xlwt/CompoundDoc.py,sha256=24OGRRYNSu7k4ZcfLel3_GuV0Zm4qze1PoPXA6mduKk,9931 +xlwt/ExcelFormula.py,sha256=mXvz4Gzapq9_oMsuOqBe5DeYa9HvcTbFoBFpbgdrg8o,1382 +xlwt/ExcelFormulaLexer.py,sha256=aVlKlztxjs9St00BKa4RZGmg8n_ibvR5jM5oGaKFfn4,4197 +xlwt/ExcelFormulaParser.py,sha256=oYVXnJdDWVE8AyYOptf35zMJCW_GFGtFrLTn8izUI5c,22209 +xlwt/ExcelMagic.py,sha256=Iiie4crRsWMNoKiBXabjpREizmSg4viJvVlwHkmOo6w,28839 +xlwt/Formatting.py,sha256=im51qDuey32heZBUby9yvzm0MzCkDvdScunNYrIPARQ,8542 +xlwt/Row.py,sha256=yPY8Pek6_XvOtWqf-LuLZQlwUwGoY2WiQSU9g50CD4o,11732 +xlwt/Style.py,sha256=yBovNDMAqwnT0zbLGZlkHx28yfnIaWxGxvnQM3CZ2Jg,23716 +xlwt/UnicodeUtils.py,sha256=GKCZs3-0goLp45_UZT-JVIoFQQMmQVeG1xHJvkdHNCs,5033 +xlwt/Utils.py,sha256=wJvREgjXgMPOXjdYhrIaOXOmdd1W4RPAWCMYUSCgU8Q,5150 +xlwt/Workbook.py,sha256=h7y2DfyLeK45HO3_sMuzKp__5bM9Vh_turYu98AWoLk,23632 +xlwt/Worksheet.py,sha256=6BEdViMAiMao9fe-mCvZl0SPfw-oedaQpXIFQxUwew4,47776 +xlwt/__init__.py,sha256=rrcaxLu2cY1M0Qcs1tNzx1__ELofdtunClAMAbGMWkU,298 +xlwt/__pycache__/BIFFRecords.cpython-39.pyc,, +xlwt/__pycache__/Bitmap.cpython-39.pyc,, +xlwt/__pycache__/Cell.cpython-39.pyc,, +xlwt/__pycache__/Column.cpython-39.pyc,, +xlwt/__pycache__/CompoundDoc.cpython-39.pyc,, +xlwt/__pycache__/ExcelFormula.cpython-39.pyc,, +xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc,, +xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc,, +xlwt/__pycache__/ExcelMagic.cpython-39.pyc,, +xlwt/__pycache__/Formatting.cpython-39.pyc,, +xlwt/__pycache__/Row.cpython-39.pyc,, +xlwt/__pycache__/Style.cpython-39.pyc,, +xlwt/__pycache__/UnicodeUtils.cpython-39.pyc,, +xlwt/__pycache__/Utils.cpython-39.pyc,, +xlwt/__pycache__/Workbook.cpython-39.pyc,, +xlwt/__pycache__/Worksheet.cpython-39.pyc,, +xlwt/__pycache__/__init__.cpython-39.pyc,, +xlwt/__pycache__/antlr.cpython-39.pyc,, +xlwt/__pycache__/compat.cpython-39.pyc,, +xlwt/antlr.py,sha256=WKDx0cGuoefxGmuKEI6QFhQoLha_ayZagDeRrCL_8u0,84153 +xlwt/compat.py,sha256=HDct50mxSl7XfVYPi3PqfS5ub-bcjTvtPuBjZnF8sNo,544 +xlwt/excel-formula.g,sha256=F3vU8gQSdz7-9qiBI-G6ax9RZ7Aa-eq1laMj5zxnn7E,11337 diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL new file mode 100644 index 00000000..8b6dd1b5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json new file mode 100644 index 00000000..9f1162f5 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Operating System :: OS Independent", "Programming Language :: Python", "License :: OSI Approved :: BSD License", "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Office/Business :: Financial :: Spreadsheet", "Topic :: Database", "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6"], "download_url": "https://pypi.python.org/pypi/xlwt", "extensions": {"python.details": {"contacts": [{"email": "sjmachin@lexicon.net", "name": "John Machin", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://www.python-excel.org/"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["xls", "excel", "spreadsheet", "workbook", "worksheet", "pyExcelerator"], "license": "BSD", "metadata_version": "2.0", "name": "xlwt", "platform": "Platform Independent", "summary": "Library to create spreadsheet files compatible with MS Excel 97/2000/XP/2003 XLS files, on any platform, with Python 2.6, 2.7, 3.3+", "version": "1.3.0"} \ No newline at end of file diff --git a/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt new file mode 100644 index 00000000..688866a1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt-1.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +xlwt diff --git a/.venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py b/.venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py new file mode 100644 index 00000000..5b831376 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/BIFFRecords.py @@ -0,0 +1,2457 @@ +# -*- coding: cp1252 -*- +from struct import pack +from .UnicodeUtils import upack1, upack2, upack2rt +from .compat import basestring, unicode, unicode_type, xrange, iteritems + +class SharedStringTable(object): + _SST_ID = 0x00FC + _CONTINUE_ID = 0x003C + + def __init__(self, encoding): + self.encoding = encoding + self._str_indexes = {} + self._rt_indexes = {} + self._tally = [] + self._add_calls = 0 + # Following 3 attrs are used for temporary storage in the + # get_biff_record() method and methods called by it. The pseudo- + # initialisation here is for documentation purposes only. + self._sst_record = None + self._continues = None + self._current_piece = None + + def add_str(self, s): + if self.encoding != 'ascii' and not isinstance(s, unicode_type): + s = unicode(s, self.encoding) + self._add_calls += 1 + if s not in self._str_indexes: + idx = len(self._str_indexes) + len(self._rt_indexes) + self._str_indexes[s] = idx + self._tally.append(1) + else: + idx = self._str_indexes[s] + self._tally[idx] += 1 + return idx + + def add_rt(self, rt): + rtList = [] + for s, xf in rt: + if self.encoding != 'ascii' and not isinstance(s, unicode_type): + s = unicode(s, self.encoding) + rtList.append((s, xf)) + rt = tuple(rtList) + self._add_calls += 1 + if rt not in self._rt_indexes: + idx = len(self._str_indexes) + len(self._rt_indexes) + self._rt_indexes[rt] = idx + self._tally.append(1) + else: + idx = self._rt_indexes[rt] + self._tally[idx] += 1 + return idx + + def del_str(self, idx): + # This is called when we are replacing the contents of a string cell. + # handles both regular and rt strings + assert self._tally[idx] > 0 + self._tally[idx] -= 1 + self._add_calls -= 1 + + def str_index(self, s): + return self._str_indexes[s] + + def rt_index(self, rt): + return self._rt_indexes[rt] + + def get_biff_record(self): + self._sst_record = b'' + self._continues = [None, None] + self._current_piece = pack('<II', 0, 0) + data = [(idx, s) for s, idx in iteritems(self._str_indexes)] + data.extend((idx, s) for s, idx in iteritems(self._rt_indexes)) + data.sort() # in index order + for idx, s in data: + if self._tally[idx] == 0: + s = u'' + if isinstance(s, basestring): + self._add_to_sst(s) + else: + self._add_rt_to_sst(s) + del data + self._new_piece() + self._continues[0] = pack('<2HII', self._SST_ID, len(self._sst_record), self._add_calls, len(self._str_indexes) + len(self._rt_indexes)) + self._continues[1] = self._sst_record[8:] + self._sst_record = None + self._current_piece = None + result = b''.join(self._continues) + self._continues = None + return result + + + def _add_to_sst(self, s): + u_str = upack2(s, self.encoding) + + is_unicode_str = u_str[2] == b'\x01'[0] + if is_unicode_str: + atom_len = 5 # 2 byte -- len, + # 1 byte -- options, + # 2 byte -- 1st sym + else: + atom_len = 4 # 2 byte -- len, + # 1 byte -- options, + # 1 byte -- 1st sym + + self._save_atom(u_str[0:atom_len]) + self._save_splitted(u_str[atom_len:], is_unicode_str) + + def _add_rt_to_sst(self, rt): + rt_str, rt_fr = upack2rt(rt, self.encoding) + is_unicode_str = rt_str[2] == b'\x09'[0] + if is_unicode_str: + atom_len = 7 # 2 byte -- len, + # 1 byte -- options, + # 2 byte -- number of rt runs + # 2 byte -- 1st sym + else: + atom_len = 6 # 2 byte -- len, + # 1 byte -- options, + # 2 byte -- number of rt runs + # 1 byte -- 1st sym + self._save_atom(rt_str[0:atom_len]) + self._save_splitted(rt_str[atom_len:], is_unicode_str) + for i in range(0, len(rt_fr), 4): + self._save_atom(rt_fr[i:i+4]) + + def _new_piece(self): + if self._sst_record == b'': + self._sst_record = self._current_piece + else: + curr_piece_len = len(self._current_piece) + self._continues.append(pack('<2H%ds'%curr_piece_len, self._CONTINUE_ID, curr_piece_len, self._current_piece)) + self._current_piece = b'' + + def _save_atom(self, s): + atom_len = len(s) + free_space = 0x2020 - len(self._current_piece) + if free_space < atom_len: + self._new_piece() + self._current_piece += s + + def _save_splitted(self, s, is_unicode_str): + i = 0 + str_len = len(s) + while i < str_len: + piece_len = len(self._current_piece) + free_space = 0x2020 - piece_len + tail_len = str_len - i + need_more_space = free_space < tail_len + + if not need_more_space: + atom_len = tail_len + else: + if is_unicode_str: + atom_len = free_space & 0xFFFE + else: + atom_len = free_space + + self._current_piece += s[i:i+atom_len] + + if need_more_space: + self._new_piece() + if is_unicode_str: + self._current_piece += b'\x01' + else: + self._current_piece += b'\x00' + + i += atom_len + + +class BiffRecord(object): + + _rec_data = b'' # class attribute; child classes need to set this. + + def get_rec_header(self): + return pack('<2H', self._REC_ID, len(self._rec_data)) + + # Not over-ridden by any child classes, never called (except by "get"; see below). + # def get_rec_data(self): + # return self._rec_data + + def get(self): + # data = self.get_rec_data() + data = self._rec_data + if len(data) > 0x2020: # limit for BIFF7/8 + chunks = [] + pos = 0 + while pos < len(data): + chunk_pos = pos + 0x2020 + chunk = data[pos:chunk_pos] + chunks.append(chunk) + pos = chunk_pos + continues = pack('<2H', self._REC_ID, len(chunks[0])) + chunks[0] + for chunk in chunks[1:]: + continues += pack('<2H%ds'%len(chunk), 0x003C, len(chunk), chunk) + # 0x003C -- CONTINUE record id + return continues + else: + return self.get_rec_header() + data + + +class Biff8BOFRecord(BiffRecord): + """ + Offset Size Contents + 0 2 Version, contains 0600H for BIFF8 and BIFF8X + 2 2 Type of the following data: + 0005H = Workbook globals + 0006H = Visual Basic module + 0010H = Worksheet + 0020H = Chart + 0040H = Macro sheet + 0100H = Workspace file + 4 2 Build identifier + 6 2 Build year + 8 4 File history flags + 12 4 Lowest Excel version that can read all records in this file + """ + _REC_ID = 0x0809 + # stream types + BOOK_GLOBAL = 0x0005 + VB_MODULE = 0x0006 + WORKSHEET = 0x0010 + CHART = 0x0020 + MACROSHEET = 0x0040 + WORKSPACE = 0x0100 + + def __init__(self, rec_type): + version = 0x0600 + build = 0x0DBB + year = 0x07CC + file_hist_flags = 0x00 + ver_can_read = 0x06 + + self._rec_data = pack('<4H2I', version, rec_type, build, year, file_hist_flags, ver_can_read) + + +class InteraceHdrRecord(BiffRecord): + _REC_ID = 0x00E1 + + def __init__(self): + self._rec_data = pack('BB', 0xB0, 0x04) + + +class InteraceEndRecord(BiffRecord): + _REC_ID = 0x00E2 + + def __init__(self): + self._rec_data = b'' + + +class MMSRecord(BiffRecord): + _REC_ID = 0x00C1 + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + +class WriteAccessRecord(BiffRecord): + """ + This record is part of the file protection. It contains the name of the + user that has saved the file. The user name is always stored as an + equal-sized string. All unused characters after the name are filled + with space characters. It is not required to write the mentioned string + length. Every other length will be accepted too. + """ + _REC_ID = 0x005C + + def __init__(self, owner): + uowner = owner[0:0x30] + uowner_len = len(uowner) + if isinstance(uowner, unicode_type): + uowner = uowner.encode('ascii') # probably not ascii, but play it safe until we know more + self._rec_data = pack('%ds%ds' % (uowner_len, 0x70 - uowner_len), uowner, b' '*(0x70 - uowner_len)) + + +class DSFRecord(BiffRecord): + """ + This record specifies if the file contains an additional BIFF5/BIFF7 + workbook stream. + Record DSF, BIFF8: + Offset Size Contents + 0 2 0 = Only the BIFF8 Workbook stream is present + 1 = Additional BIFF5/BIFF7 Book stream is in the file + A double stream file can be read by Excel 5.0 and Excel 95, and still + contains all new features added to BIFF8 (which are left out in the + BIFF5/BIFF7 Book stream). + """ + _REC_ID = 0x0161 + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + +class TabIDRecord(BiffRecord): + _REC_ID = 0x013D + + def __init__(self, sheetcount): + for i in range(sheetcount): + self._rec_data += pack('<H', i+1) + + +class FnGroupCountRecord(BiffRecord): + _REC_ID = 0x009C + + def __init__(self): + self._rec_data = pack('BB', 0x0E, 0x00) + + +class WindowProtectRecord(BiffRecord): + """ + This record is part of the worksheet/workbook protection. It determines + whether the window configuration of this document is protected. Window + protection is not active, if this record is omitted. + """ + _REC_ID = 0x0019 + + def __init__(self, wndprotect): + self._rec_data = pack('<H', wndprotect) + + +class ObjectProtectRecord(BiffRecord): + """ + This record is part of the worksheet/workbook protection. + It determines whether the objects of the current sheet are protected. + Object protection is not active, if this record is omitted. + """ + _REC_ID = 0x0063 + + + def __init__(self, objprotect): + self._rec_data = pack('<H', objprotect) + + +class ScenProtectRecord(BiffRecord): + """ + This record is part of the worksheet/workbook protection. It + determines whether the scenarios of the current sheet are protected. + Scenario protection is not active, if this record is omitted. + """ + _REC_ID = 0x00DD + + + def __init__(self, scenprotect): + self._rec_data = pack('<H', scenprotect) + + +class ProtectRecord(BiffRecord): + """ + This record is part of the worksheet/workbook protection. It specifies + whether a worksheet or a workbook is protected against modification. + Protection is not active, if this record is omitted. + """ + + _REC_ID = 0x0012 + + def __init__(self, protect): + self._rec_data = pack('<H', protect) + + +class PasswordRecord(BiffRecord): + """ + This record is part of the worksheet/workbook protection. It + stores a 16-bit hash value, calculated from the worksheet or workbook + protection password. + """ + _REC_ID = 0x0013 + def passwd_hash(self, plaintext): + """ + Based on the algorithm provided by Daniel Rentz of OpenOffice. + """ + if plaintext == "": + return 0 + + passwd_hash = 0x0000 + for i, char in enumerate(plaintext): + c = ord(char) << (i + 1) + low_15 = c & 0x7fff + high_15 = c & 0x7fff << 15 + high_15 = high_15 >> 15 + c = low_15 | high_15 + passwd_hash ^= c + passwd_hash ^= len(plaintext) + passwd_hash ^= 0xCE4B + return passwd_hash + + def __init__(self, passwd = ""): + self._rec_data = pack('<H', self.passwd_hash(passwd)) + + +class Prot4RevRecord(BiffRecord): + _REC_ID = 0x01AF + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + +class Prot4RevPassRecord(BiffRecord): + _REC_ID = 0x01BC + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + +class BackupRecord(BiffRecord): + """ + This record contains a Boolean value determining whether Excel makes + a backup of the file while saving. + """ + _REC_ID = 0x0040 + + def __init__(self, backup): + self._rec_data = pack('<H', backup) + +class HideObjRecord(BiffRecord): + """ + This record specifies whether and how to show objects in the workbook. + + Record HIDEOBJ, BIFF3-BIFF8: + Offset Size Contents + 0 2 Viewing mode for objects: + 0 = Show all objects + 1 = Show placeholders + 2 = Do not show objects + """ + _REC_ID = 0x008D + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + + +class RefreshAllRecord(BiffRecord): + """ + """ + + _REC_ID = 0x01B7 + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + +class BookBoolRecord(BiffRecord): + """ + This record contains a Boolean value determining whether to save values + linked from external workbooks (CRN records and XCT records). In BIFF3 + and BIFF4 this option is stored in the WSBOOL record. + + Record BOOKBOOL, BIFF5-BIFF8: + + Offset Size Contents + 0 2 0 = Save external linked values; + 1 = Do not save external linked values + """ + + _REC_ID = 0x00DA + + def __init__(self): + self._rec_data = pack('<H', 0x00) + + +class CountryRecord(BiffRecord): + """ + This record stores two Windows country identifiers. The first + represents the user interface language of the Excel version that has + saved the file, and the second represents the system regional settings + at the time the file was saved. + + Record COUNTRY, BIFF3-BIFF8: + + Offset Size Contents + 0 2 Windows country identifier of the user interface language of Excel + 2 2 Windows country identifier of the system regional settings + + The following table shows most of the used country identifiers. Most + of these identifiers are equal to the international country calling + codes. + + 1 USA + 2 Canada + 7 Russia + """ + + _REC_ID = 0x008C + + def __init__(self, ui_id, sys_settings_id): + self._rec_data = pack('<2H', ui_id, sys_settings_id) + + +class UseSelfsRecord(BiffRecord): + """ + This record specifies if the formulas in the workbook can use natural + language formulas. This type of formula can refer to cells by its + content or the content of the column or row header cell. + + Record USESELFS, BIFF8: + + Offset Size Contents + 0 2 0 = Do not use natural language formulas + 1 = Use natural language formulas + + """ + + _REC_ID = 0x0160 + + def __init__(self): + self._rec_data = pack('<H', 0x01) + + +class EOFRecord(BiffRecord): + _REC_ID = 0x000A + + def __init__(self): + self._rec_data = b'' + + +class DateModeRecord(BiffRecord): + """ + This record specifies the base date for displaying date values. All + dates are stored as count of days past this base date. In BIFF2-BIFF4 + this record is part of the Calculation Settings Block. + In BIFF5-BIFF8 it is stored in the Workbook Globals Substream. + + Record DATEMODE, BIFF2-BIFF8: + + Offset Size Contents + 0 2 0 = Base is 1899-Dec-31 (the cell = 1 represents 1900-Jan-01) + 1 = Base is 1904-Jan-01 (the cell = 1 represents 1904-Jan-02) + """ + _REC_ID = 0x0022 + + def __init__(self, from1904): + if from1904: + self._rec_data = pack('<H', 1) + else: + self._rec_data = pack('<H', 0) + + +class PrecisionRecord(BiffRecord): + """ + This record stores if formulas use the real cell values for calculation + or the values displayed on the screen. In BIFF2- BIFF4 this record + is part of the Calculation Settings Block. In BIFF5-BIFF8 it is stored + in the Workbook Globals Substream. + + Record PRECISION, BIFF2-BIFF8: + + Offset Size Contents + 0 2 0 = Use displayed values; + 1 = Use real cell values + """ + _REC_ID = 0x000E + + def __init__(self, use_real_values): + if use_real_values: + self._rec_data = pack('<H', 1) + else: + self._rec_data = pack('<H', 0) + + +class CodepageBiff8Record(BiffRecord): + """ + This record stores the text encoding used to write byte strings, stored + as MS Windows code page identifier. The CODEPAGE record in BIFF8 always + contains the code page 1200 (UTF-16). Therefore it is not + possible to obtain the encoding used for a protection password (it is + not UTF-16). + + Record CODEPAGE, BIFF2-BIFF8: + + Offset Size Contents + 0 2 Code page identifier used for byte string text encoding: + 016FH = 367 = ASCII + 01B5H = 437 = IBM PC CP-437 (US) + 02D0H = 720 = IBM PC CP-720 (OEM Arabic) + 02E1H = 737 = IBM PC CP-737 (Greek) + 0307H = 775 = IBM PC CP-775 (Baltic) + 0352H = 850 = IBM PC CP-850 (Latin I) + 0354H = 852 = IBM PC CP-852 (Latin II (Central European)) + 0357H = 855 = IBM PC CP-855 (Cyrillic) + 0359H = 857 = IBM PC CP-857 (Turkish) + 035AH = 858 = IBM PC CP-858 (Multilingual Latin I with Euro) + 035CH = 860 = IBM PC CP-860 (Portuguese) + 035DH = 861 = IBM PC CP-861 (Icelandic) + 035EH = 862 = IBM PC CP-862 (Hebrew) + 035FH = 863 = IBM PC CP-863 (Canadian (French)) + 0360H = 864 = IBM PC CP-864 (Arabic) + 0361H = 865 = IBM PC CP-865 (Nordic) + 0362H = 866 = IBM PC CP-866 (Cyrillic (Russian)) + 0365H = 869 = IBM PC CP-869 (Greek (Modern)) + 036AH = 874 = Windows CP-874 (Thai) + 03A4H = 932 = Windows CP-932 (Japanese Shift-JIS) + 03A8H = 936 = Windows CP-936 (Chinese Simplified GBK) + 03B5H = 949 = Windows CP-949 (Korean (Wansung)) + 03B6H = 950 = Windows CP-950 (Chinese Traditional BIG5) + 04B0H = 1200 = UTF-16 (BIFF8) + 04E2H = 1250 = Windows CP-1250 (Latin II) (Central European) + 04E3H = 1251 = Windows CP-1251 (Cyrillic) + 04E4H = 1252 = Windows CP-1252 (Latin I) (BIFF4-BIFF7) + 04E5H = 1253 = Windows CP-1253 (Greek) + 04E6H = 1254 = Windows CP-1254 (Turkish) + 04E7H = 1255 = Windows CP-1255 (Hebrew) + 04E8H = 1256 = Windows CP-1256 (Arabic) + 04E9H = 1257 = Windows CP-1257 (Baltic) + 04EAH = 1258 = Windows CP-1258 (Vietnamese) + 0551H = 1361 = Windows CP-1361 (Korean (Johab)) + 2710H = 10000 = Apple Roman + 8000H = 32768 = Apple Roman + 8001H = 32769 = Windows CP-1252 (Latin I) (BIFF2-BIFF3) + """ + _REC_ID = 0x0042 + UTF_16 = 0x04B0 + + def __init__(self): + self._rec_data = pack('<H', self.UTF_16) + +class Window1Record(BiffRecord): + """ + Offset Size Contents + 0 2 Horizontal position of the document window (in twips = 1/20 of a point) + 2 2 Vertical position of the document window (in twips = 1/20 of a point) + 4 2 Width of the document window (in twips = 1/20 of a point) + 6 2 Height of the document window (in twips = 1/20 of a point) + 8 2 Option flags: + Bits Mask Contents + 0 0001H 0 = Window is visible 1 = Window is hidden + 1 0002H 0 = Window is open 1 = Window is minimised + 3 0008H 0 = Horizontal scroll bar hidden 1 = Horizontal scroll bar visible + 4 0010H 0 = Vertical scroll bar hidden 1 = Vertical scroll bar visible + 5 0020H 0 = Worksheet tab bar hidden 1 = Worksheet tab bar visible + 10 2 Index to active (displayed) worksheet + 12 2 Index of first visible tab in the worksheet tab bar + 14 2 Number of selected worksheets (highlighted in the worksheet tab bar) + 16 2 Width of worksheet tab bar (in 1/1000 of window width). The remaining space is used by the + horizontal scrollbar. + """ + _REC_ID = 0x003D + # flags + + def __init__(self, + hpos_twips, vpos_twips, + width_twips, height_twips, + flags, + active_sheet, + first_tab_index, selected_tabs, tab_width): + self._rec_data = pack('<9H', hpos_twips, vpos_twips, + width_twips, height_twips, + flags, + active_sheet, + first_tab_index, selected_tabs, tab_width) + +class FontRecord(BiffRecord): + """ + WARNING + The font with index 4 is omitted in all BIFF versions. + This means the first four fonts have zero-based indexes, and + the fifth font and all following fonts are referenced with one-based + indexes. + + Offset Size Contents + 0 2 Height of the font (in twips = 1/20 of a point) + 2 2 Option flags: + Bit Mask Contents + 0 0001H 1 = Characters are bold (redundant, see below) + 1 0002H 1 = Characters are italic + 2 0004H 1 = Characters are underlined (redundant, see below) + 3 0008H 1 = Characters are struck out + 0010H 1 = Outline + 0020H 1 = Shadow + 4 2 Colour index + 6 2 Font weight (100-1000). + Standard values are 0190H (400) for normal text and 02BCH + (700) for bold text. + 8 2 Escapement type: + 0000H = None + 0001H = Superscript + 0002H = Subscript + 10 1 Underline type: + 00H = None + 01H = Single + 21H = Single accounting + 02H = Double + 22H = Double accounting + 11 1 Font family: + 00H = None (unknown or don't care) + 01H = Roman (variable width, serifed) + 02H = Swiss (variable width, sans-serifed) + 03H = Modern (fixed width, serifed or sans-serifed) + 04H = Script (cursive) + 05H = Decorative (specialised, i.e. Old English, Fraktur) + 12 1 Character set: + 00H = 0 = ANSI Latin + 01H = 1 = System default + 02H = 2 = Symbol + 4DH = 77 = Apple Roman + 80H = 128 = ANSI Japanese Shift-JIS + 81H = 129 = ANSI Korean (Hangul) + 82H = 130 = ANSI Korean (Johab) + 86H = 134 = ANSI Chinese Simplified GBK + 88H = 136 = ANSI Chinese Traditional BIG5 + A1H = 161 = ANSI Greek + A2H = 162 = ANSI Turkish + A3H = 163 = ANSI Vietnamese + B1H = 177 = ANSI Hebrew + B2H = 178 = ANSI Arabic + BAH = 186 = ANSI Baltic + CCH = 204 = ANSI Cyrillic + DEH = 222 = ANSI Thai + EEH = 238 = ANSI Latin II (Central European) + FFH = 255 = OEM Latin I + 13 1 Not used + 14 var. Font name: + BIFF5/BIFF7: Byte string, 8-bit string length + BIFF8: Unicode string, 8-bit string length + The boldness and underline flags are still set in the options field, + but not used on reading the font. Font weight and underline type + are specified in separate fields instead. + """ + _REC_ID = 0x0031 + + def __init__(self, + height, options, colour_index, weight, escapement, + underline, family, charset, + name): + uname = upack1(name) + uname_len = len(uname) + + self._rec_data = pack('<5H4B%ds' % uname_len, height, options, colour_index, weight, escapement, + underline, family, charset, 0x00, + uname) + +class NumberFormatRecord(BiffRecord): + """ + Record FORMAT, BIFF8: + Offset Size Contents + 0 2 Format index used in other records + 2 var. Number format string (Unicode string, 16-bit string length) + + From BIFF5 on, the built-in number formats will be omitted. The built-in + formats are dependent on the current regional settings of the operating + system. The following table shows which number formats are used by default + in a US-English environment. All indexes from 0 to 163 are reserved for + built-in formats. The first user-defined format starts at 164. + + The built-in number formats, BIFF5-BIFF8 + + Index Type Format string + 0 General General + 1 Decimal 0 + 2 Decimal 0.00 + 3 Decimal #,##0 + 4 Decimal #,##0.00 + 5 Currency "$"#,##0_);("$"#,## + 6 Currency "$"#,##0_);[Red]("$"#,## + 7 Currency "$"#,##0.00_);("$"#,## + 8 Currency "$"#,##0.00_);[Red]("$"#,## + 9 Percent 0% + 10 Percent 0.00% + 11 Scientific 0.00E+00 + 12 Fraction # ?/? + 13 Fraction # ??/?? + 14 Date M/D/YY + 15 Date D-MMM-YY + 16 Date D-MMM + 17 Date MMM-YY + 18 Time h:mm AM/PM + 19 Time h:mm:ss AM/PM + 20 Time h:mm + 21 Time h:mm:ss + 22 Date/Time M/D/YY h:mm + 37 Account _(#,##0_);(#,##0) + 38 Account _(#,##0_);[Red](#,##0) + 39 Account _(#,##0.00_);(#,##0.00) + 40 Account _(#,##0.00_);[Red](#,##0.00) + 41 Currency _("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_) + 42 Currency _(* #,##0_);_(* (#,##0);_(* "-"_);_(@_) + 43 Currency _("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_) + 44 Currency _(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_) + 45 Time mm:ss + 46 Time [h]:mm:ss + 47 Time mm:ss.0 + 48 Scientific ##0.0E+0 + 49 Text @ + """ + _REC_ID = 0x041E + + def __init__(self, idx, fmtstr): + ufmtstr = upack2(fmtstr) + ufmtstr_len = len(ufmtstr) + + self._rec_data = pack('<H%ds' % ufmtstr_len, idx, ufmtstr) + + +class XFRecord(BiffRecord): + """ + XF Substructures + ------------------------------------------------------------------------- + XF_TYPE_PROT XF Type and Cell Protection (3 Bits), BIFF3-BIFF8 + These 3 bits are part of a specific data byte. + Bit Mask Contents + 0 01H 1 = Cell is locked + 1 02H 1 = Formula is hidden + 2 04H 0 = Cell XF; 1 = Style XF + + XF_USED_ATTRIB Attributes Used from Parent Style XF (6 Bits), + BIFF3-BIFF8 Each bit describes the validity of a specific group + of attributes. In cell XFs a cleared bit means the attributes of the + parent style XF are used (but only if the attributes are valid there), + a set bit means the attributes of this XF are used. In style XFs + a cleared bit means the attribute setting is valid, a set bit means the + attribute should be ignored. + Bit Mask Contents + 0 01H Flag for number format + 1 02H Flag for font + 2 04H Flag for horizontal and vertical alignment, text wrap, indentation, orientation, rotation, and + text direction + 3 08H Flag for border lines + 4 10H Flag for background area style + 5 20H Flag for cell protection (cell locked and formula hidden) + + XF_HOR_ALIGN Horizontal Alignment (3 Bits), BIFF2-BIFF8 The horizontal + alignment consists of 3 bits and is part of a specific data byte. + Value Horizontal alignment + 00H General + 01H Left + 02H Centred + 03H Right + 04H Filled + 05H Justified (BIFF4-BIFF8X) + 06H Centred across selection (BIFF4-BIFF8X) + 07H Distributed (BIFF8X) + + XF_VERT_ALIGN Vertical Alignment (2 or 3 Bits), BIFF4-BIFF8 + The vertical alignment consists of 2 bits (BIFF4) or 3 bits (BIFF5-BIFF8) + and is part of a specific data byte. Vertical alignment is not available + in BIFF2 and BIFF3. + Value Vertical alignment + 00H Top + 01H Centred + 02H Bottom + 03H Justified (BIFF5-BIFF8X) + 04H Distributed (BIFF8X) + + XF_ORIENTATION Text Orientation (2 Bits), BIFF4-BIFF7 In the BIFF + versions BIFF4-BIFF7, text can be rotated in steps of 90 degrees + or stacked. The orientation mode consists of 2 bits and is part of + a specific data byte. In BIFF8 a rotation angle occurs instead of these + flags. + Value Text orientation + 00H Not rotated + 01H Letters are stacked top-to-bottom, but not rotated + 02H Text is rotated 90 degrees counterclockwise + 03H Text is rotated 90 degrees clockwise + + XF_ROTATION Text Rotation Angle (1 Byte), BIFF8 + Value Text rotation + 0 Not rotated + 1-90 1 to 90 degrees counterclockwise + 91-180 1 to 90 degrees clockwise + 255 Letters are stacked top-to-bottom, but not rotated + + XF_BORDER_34 Cell Border Style (4 Bytes), BIFF3-BIFF4 Cell borders + contain a line style and a line colour for each line of the border. + Bit Mask Contents + 2-0 00000007H Top line style + 7-3 000000F8H Colour index for top line colour + 10-8 00000700H Left line style + 15-11 0000F800H Colour index for left line colour + 18-16 00070000H Bottom line style + 23-19 00F80000H Colour index for bottom line colour + 26-24 07000000H Right line style + 31-27 F8000000H Colour index for right line colour + + XF_AREA_34 Cell Background Area Style (2 Bytes), BIFF3-BIFF4 A cell + background area style contains an area pattern and a foreground and + background colour. + Bit Mask Contents + 5-0 003FH Fill pattern + 10-6 07C0H Colour index for pattern colour + 15-11 F800H Colour index for pattern background + --------------------------------------------------------------------------------------------- + Record XF, BIFF8: + Offset Size Contents + 0 2 Index to FONT record + 2 2 Index to FORMAT record + 4 2 Bit Mask Contents + 2-0 0007H XF_TYPE_PROT . XF type, cell protection (see above) + 15-4 FFF0H Index to parent style XF (always FFFH in style XFs) + 6 1 Bit Mask Contents + 2-0 07H XF_HOR_ALIGN . Horizontal alignment (see above) + 3 08H 1 = Text is wrapped at right border + 6-4 70H XF_VERT_ALIGN . Vertical alignment (see above) + 7 1 XF_ROTATION: Text rotation angle (see above) + 8 1 Bit Mask Contents + 3-0 0FH Indent level + 4 10H 1 = Shrink content to fit into cell + 5 merge + 7-6 C0H Text direction (BIFF8X only) + 00b = According to context + 01b = Left-to-right + 10b = Right-to-left + 9 1 Bit Mask Contents + 7-2 FCH XF_USED_ATTRIB . Used attributes (see above) + 10 4 Cell border lines and background area: + Bit Mask Contents + 3-0 0000000FH Left line style + 7-4 000000F0H Right line style + 11-8 00000F00H Top line style + 15-12 0000F000H Bottom line style + 22-16 007F0000H Colour index for left line colour + 29-23 3F800000H Colour index for right line colour + 30 40000000H 1 = Diagonal line from top left to right bottom + 31 80000000H 1 = Diagonal line from bottom left to right top + 14 4 Bit Mask Contents + 6-0 0000007FH Colour index for top line colour + 13-7 00003F80H Colour index for bottom line colour + 20-14 001FC000H Colour index for diagonal line colour + 24-21 01E00000H Diagonal line style + 31-26 FC000000H Fill pattern + 18 2 Bit Mask Contents + 6-0 007FH Colour index for pattern colour + 13-7 3F80H Colour index for pattern background + + """ + _REC_ID = 0x00E0 + + def __init__(self, xf, xftype='cell'): + font_xf_idx, fmt_str_xf_idx, alignment, borders, pattern, protection = xf + fnt = pack('<H', font_xf_idx) + fmt = pack('<H', fmt_str_xf_idx) + if xftype == 'cell': + prt = pack('<H', + ((protection.cell_locked & 0x01) << 0) | + ((protection.formula_hidden & 0x01) << 1) + ) + else: + prt = pack('<H', 0xFFF5) + aln = pack('B', + ((alignment.horz & 0x07) << 0) | + ((alignment.wrap & 0x01) << 3) | + ((alignment.vert & 0x07) << 4) + ) + rot = pack('B', alignment.rota) + txt = pack('B', + ((alignment.inde & 0x0F) << 0) | + ((alignment.shri & 0x01) << 4) | + ((alignment.merg & 0x01) << 5) | + ((alignment.dire & 0x03) << 6) + ) + if xftype == 'cell': + used_attr = pack('B', 0xF8) + else: + used_attr = pack('B', 0xF4) + + if borders.left == borders.NO_LINE: + borders.left_colour = 0x00 + if borders.right == borders.NO_LINE: + borders.right_colour = 0x00 + if borders.top == borders.NO_LINE: + borders.top_colour = 0x00 + if borders.bottom == borders.NO_LINE: + borders.bottom_colour = 0x00 + if borders.diag == borders.NO_LINE: + borders.diag_colour = 0x00 + brd1 = pack('<L', + ((borders.left & 0x0F) << 0 ) | + ((borders.right & 0x0F) << 4 ) | + ((borders.top & 0x0F) << 8 ) | + ((borders.bottom & 0x0F) << 12) | + ((borders.left_colour & 0x7F) << 16) | + ((borders.right_colour & 0x7F) << 23) | + ((borders.need_diag1 & 0x01) << 30) | + ((borders.need_diag2 & 0x01) << 31) + ) + brd2 = pack('<L', + ((borders.top_colour & 0x7F) << 0 ) | + ((borders.bottom_colour & 0x7F) << 7 ) | + ((borders.diag_colour & 0x7F) << 14) | + ((borders.diag & 0x0F) << 21) | + ((pattern.pattern & 0x3F) << 26) + ) + pat = pack('<H', + ((pattern.pattern_fore_colour & 0x7F) << 0 ) | + ((pattern.pattern_back_colour & 0x7F) << 7 ) + ) + self._rec_data = fnt + fmt + prt + \ + aln + rot + txt + used_attr + \ + brd1 + brd2 + \ + pat + +class StyleRecord(BiffRecord): + """ + STYLE record for user-defined cell styles, BIFF3-BIFF8: + Offset Size Contents + 0 2 Bit Mask Contents + 11-0 0FFFH Index to style XF record + 15 8000H Always 0 for user-defined styles + 2 var. BIFF2-BIFF7: Non-empty byte string, 8-bit string length + BIFF8: Non-empty Unicode string, 16-bit string length + STYLE record for built-in cell styles, BIFF3-BIFF8: + Offset Size Contents + 0 2 Bit Mask Contents + 11-0 0FFFH Index to style XF record + 15 8000H Always 1 for built-in styles + 2 1 Identifier of the built-in cell style: + 00H = Normal + 01H = RowLevel_lv (see next field) + 02H = ColLevel_lv (see next field) + 03H = Comma + 04H = Currency + 05H = Percent + 06H = Comma [0] (BIFF4-BIFF8) + 07H = Currency [0] (BIFF4-BIFF8) + 08H = Hyperlink (BIFF8) + 09H = Followed Hyperlink (BIFF8) + 3 1 Level for RowLevel or ColLevel style + (zero-based, lv), FFH otherwise + The RowLevel and ColLevel styles specify the formatting of subtotal + cells in a specific outline level. The level is specified by the last + field in the STYLE record. Valid values are 0-6 for the outline levels + 1-7. + """ + _REC_ID = 0x0293 + + def __init__(self): + self._rec_data = pack('<HBB', 0x8000, 0x00, 0xFF) + # TODO: implement user-defined styles??? + + +class PaletteRecord(BiffRecord): + """ + This record contains the definition of all user-defined colours + available for cell and object formatting. + + Record PALETTE, BIFF3-BIFF8: + + Offset Size Contents + 0 2 Number of following colours (nm). Contains 16 in BIFF3-BIFF4 and 56 in BIFF5-BIFF8. + 2 4*nm List of nm RGB colours + + The following table shows how colour indexes are used in other records: + + Colour index Resulting colour or internal list index + 00H Built-in Black (R = 00H, G = 00H, B = 00H) + 01H Built-in White (R = FFH, G = FFH, B = FFH) + 02H Built-in Red (R = FFH, G = 00H, B = 00H) + 03H Built-in Green (R = 00H, G = FFH, B = 00H) + 04H Built-in Blue (R = 00H, G = 00H, B = FFH) + 05H Built-in Yellow (R = FFH, G = FFH, B = 00H) + 06H Built-in Magenta (R = FFH, G = 00H, B = FFH) + 07H Built-in Cyan (R = 00H, G = FFH, B = FFH) + 08H First user-defined colour from the PALETTE record (entry 0 from record colour list) + ......................... + + 17H (BIFF3-BIFF4) Last user-defined colour from the PALETTE record (entry 15 or 55 from record colour list) + 3FH (BIFF5-BIFF8) + + 18H (BIFF3-BIFF4) System window text colour for border lines (used in records XF, CF, and + 40H (BIFF5-BIFF8) WINDOW2 (BIFF8 only)) + + 19H (BIFF3-BIFF4) System window background colour for pattern background (used in records XF, and CF) + 41H (BIFF5-BIFF8) + + 43H System face colour (dialogue background colour) + 4DH System window text colour for chart border lines + 4EH System window background colour for chart areas + 4FH Automatic colour for chart border lines (seems to be always Black) + 50H System ToolTip background colour (used in note objects) + 51H System ToolTip text colour (used in note objects) + 7FFFH System window text colour for fonts (used in records FONT, EFONT, and CF) + + """ + _REC_ID = 0x0092 + + def __init__(self, custom_palette): + n_colours = len(custom_palette) + assert n_colours == 56 + # Pack number of colors with little-endian, what xlrd and excel expect. + self._rec_data = pack('<H', n_colours) + # Microsoft lists colors in big-endian format with 24 bits/color. + # Pad LSB of each color with 0x00, and write out in big-endian. + fmt = '>%dI' % n_colours + self._rec_data += pack(fmt, *(custom_palette)) + +class BoundSheetRecord(BiffRecord): + """ + This record is located in the workbook globals area and represents + a sheet inside of the workbook. For each sheet a BOUNDSHEET record + is written. It stores the sheet name and a stream offset to the BOF + record within the workbook stream. The record is also known + as BUNDLESHEET. + + Record BOUNDSHEET, BIFF5-BIFF8: + Offset Size Contents + 0 4 Absolute stream position of the BOF record of the sheet represented by this record. This + field is never encrypted in protected files. + 4 1 Visibility: + 00H = Visible + 01H = Hidden + 02H = Strong hidden + 5 1 Sheet type: + 00H = Worksheet + 02H = Chart + 06H = Visual Basic module + 6 var. Sheet name: + BIFF5/BIFF7: Byte string, 8-bit string length + BIFF8: Unicode string, 8-bit string length + """ + _REC_ID = 0x0085 + + def __init__(self, stream_pos, visibility, sheetname, encoding='ascii'): + usheetname = upack1(sheetname, encoding) + uusheetname_len = len(usheetname) + + self._rec_data = pack('<LBB%ds' % uusheetname_len, stream_pos, visibility, 0x00, usheetname) + + +class ContinueRecord(BiffRecord): + """ + Whenever the content of a record exceeds the given limits (see table), + the record must be split. Several CONTINUE records containing the + additional data are added after the parent record. + + BIFF version Maximum data size of a record + BIFF2-BIFF7 2080 bytes (2084 bytes including record header) + BIFF8 8224 bytes (8228 bytes including record header) (0x2020) + + Record CONTINUE, BIFF2-BIFF8: + Offset Size Contents + 0 var. Data continuation of the previous record + + Unicode strings are split in a special way. At the beginning of each + CONTINUE record the option flags byte is repeated. Only the character + size flag will be set in this flags byte, the Rich-Text flag and the + Far-East flag are set to zero. In each CONTINUE record it is possible + that the character size changes from 8-bit characters to 16-bit + characters and vice versa. + + Never a Unicode string is split until and including the first + character. That means, all header fields (string length, option flags, + optional Rich-Text size, and optional Far-East data size) and the first + character of the string have to occur together in the leading record, + or have to be moved completely into the CONTINUE record. Formatting + runs cannot be split between their components (character index and FONT + record index). If a string is split between two formatting runs, the + option flags field will not be repeated in the CONTINUE record. + """ + _REC_ID = 0x003C + + +class SSTRecord(BiffRecord): + """ + This record contains a list of all strings used anywhere in the + workbook. Each string occurs only once. The workbook uses indexes into + the list to reference the strings. + + Record SST, BIFF8: + Offset Size Contents + 0 4 Total number of strings in the workbook (see below) + 4 4 Number of following strings (nm) + 8 var. List of nm Unicode strings, 16-bit string length + + The first field of the SST record counts the total occurrence + of strings in the workbook. For instance, the string AAA is used + 3 times and the string BBB is used 2 times. The first field contains + 5 and the second field contains 2, followed by the two strings. + """ + _REC_ID = 0x00FC + + +class ExtSSTRecord(BiffRecord): + """ + This record occurs in conjunction with the SST record. It is used + by Excel to create a hash table with stream offsets to the SST record + to optimise string search operations. Excel may not shorten this record + if strings are deleted from the shared string table, so the last part + might contain invalid data. The stream indexes in this record divide + the SST into portions containing a constant number of strings. + + Record EXTSST, BIFF8: + + Offset Size Contents + 0 2 Number of strings in a portion, this number is >=8 + 2 var. List of OFFSET structures for all portions. Each OFFSET contains the following data: + Offset Size Contents + 0 4 Absolute stream position of first string of the portion + 4 2 Position of first string of the portion inside of current record, + including record header. This counter restarts at zero, if the SST + record is continued with a CONTINUE record. + 6 2 Not used + """ + _REC_ID = 0x00FF + + def __init__(self, sst_stream_pos, str_placement, portions_len): + extsst = {} + abs_stream_pos = sst_stream_pos + str_counter = 0 + portion_counter = 0 + while str_counter < len(str_placement): + str_chunk_num, pos_in_chunk = str_placement[str_counter] + if str_chunk_num != portion_counter: + portion_counter = str_chunk_num + abs_stream_pos += portions_len[portion_counter-1] + #print hex(abs_stream_pos) + str_stream_pos = abs_stream_pos + pos_in_chunk + 4 # header + extsst[str_counter] = (pos_in_chunk, str_stream_pos) + str_counter += 1 + + exsst_str_count_delta = max(8, len(str_placement)*8/0x2000) # maybe smth else? + self._rec_data = pack('<H', exsst_str_count_delta) + str_counter = 0 + while str_counter < len(str_placement): + self._rec_data += pack('<IHH', extsst[str_counter][1], extsst[str_counter][0], 0) + str_counter += exsst_str_count_delta + +class DimensionsRecord(BiffRecord): + """ + Record DIMENSIONS, BIFF8: + + Offset Size Contents + 0 4 Index to first used row + 4 4 Index to last used row, increased by 1 + 8 2 Index to first used column + 10 2 Index to last used column, increased by 1 + 12 2 Not used + """ + _REC_ID = 0x0200 + def __init__(self, first_used_row, last_used_row, first_used_col, last_used_col): + if first_used_row > last_used_row or first_used_col > last_used_col: + # Special case: empty worksheet + first_used_row = first_used_col = 0 + last_used_row = last_used_col = -1 + self._rec_data = pack('<2L3H', + first_used_row, last_used_row + 1, + first_used_col, last_used_col + 1, + 0x00) + + +class Window2Record(BiffRecord): + """ + Record WINDOW2, BIFF8: + + Offset Size Contents + 0 2 Option flags (see below) + 2 2 Index to first visible row + 4 2 Index to first visible column + 6 2 Colour index of grid line colour. Note that in BIFF2-BIFF7 an RGB colour is + written instead. + 8 2 Not used + 10 2 Cached magnification factor in page break preview (in percent); 0 = Default (60%) + 12 2 Cached magnification factor in normal view (in percent); 0 = Default (100%) + 14 4 Not used + + In BIFF8 this record stores used magnification factors for page break + preview and normal view. These values are used to restore the + magnification, when the view is changed. The real magnification of the + currently active view is stored in the SCL record. The type of the + active view is stored in the option flags field (see below). + + 0 0001H 0 = Show formula results 1 = Show formulas + 1 0002H 0 = Do not show grid lines 1 = Show grid lines + 2 0004H 0 = Do not show sheet headers 1 = Show sheet headers + 3 0008H 0 = Panes are not frozen 1 = Panes are frozen (freeze) + 4 0010H 0 = Show zero values as empty cells 1 = Show zero values + 5 0020H 0 = Manual grid line colour 1 = Automatic grid line colour + 6 0040H 0 = Columns from left to right 1 = Columns from right to left + 7 0080H 0 = Do not show outline symbols 1 = Show outline symbols + 8 0100H 0 = Keep splits if pane freeze is removed 1 = Remove splits if pane freeze is removed + 9 0200H 0 = Sheet not selected 1 = Sheet selected (BIFF5-BIFF8) + 10 0400H 0 = Sheet not visible 1 = Sheet visible (BIFF5-BIFF8) + 11 0800H 0 = Show in normal view 1 = Show in page break preview (BIFF8) + + The freeze flag specifies, if a following PANE record describes unfrozen or frozen panes. + + *** This class appends the optional SCL record *** + + Record SCL, BIFF4-BIFF8: + + This record stores the magnification of the active view of the current worksheet. + In BIFF8 this can be either the normal view or the page break preview. + This is determined in the WINDOW2 record. The SCL record is part of the + Sheet View Settings Block. + + Offset Size Contents + 0 2 Numerator of the view magnification fraction (num) + 2 2 Denumerator [denominator] of the view magnification fraction (den) + The magnification is stored as reduced fraction. The magnification results from num/den. + + SJM note: Excel expresses (e.g.) 25% in reduced form i.e. 1/4. Reason unknown. This code + writes 25/100, and Excel is happy with that. + + """ + _REC_ID = 0x023E + + def __init__(self, options, first_visible_row, first_visible_col, + grid_colour, preview_magn, normal_magn, scl_magn): + self._rec_data = pack('<7HL', options, + first_visible_row, first_visible_col, + grid_colour, + 0x00, + preview_magn, normal_magn, + 0x00) + if scl_magn is not None: + self._scl_rec = pack('<4H', 0x00A0, 4, scl_magn, 100) + else: + self._scl_rec = b'' + + def get(self): + return self.get_rec_header() + self._rec_data + self._scl_rec + + +class PanesRecord(BiffRecord): + """ + This record stores the position of window panes. It is part of the Sheet + View Settings Block. If the sheet does not contain any splits, this + record will not occur. + A sheet can be split in two different ways, with unfrozen panes or with + frozen panes. A flag in the WINDOW2 record specifies, if the panes are + frozen, which affects the contents of this record. + + Record PANE, BIFF2-BIFF8: + Offset Size Contents + 0 2 Position of the vertical split + (px, 0 = No vertical split): + Unfrozen pane: Width of the left pane(s) + (in twips = 1/20 of a point) + Frozen pane: Number of visible + columns in left pane(s) + 2 2 Position of the horizontal split + (py, 0 = No horizontal split): + Unfrozen pane: Height of the top pane(s) + (in twips = 1/20 of a point) + Frozen pane: Number of visible + rows in top pane(s) + 4 2 Index to first visible row + in bottom pane(s) + 6 2 Index to first visible column + in right pane(s) + 8 1 Identifier of pane with active + cell cursor + [9] 1 Not used (BIFF5-BIFF8 only, not written + in BIFF2-BIFF4) + + If the panes are frozen, pane 0 is always active, regardless + of the cursor position. The correct identifiers for all possible + combinations of visible panes are shown in the following pictures. + + px = 0, py = 0 px = 0, py > 0 + -------------------------- ------------|------------- + | | | | + | | | 3 | + | | | | + - 3 - -------------------------- + | | | | + | | | 2 | + | | | | + -------------------------- ------------|------------- + + px > 0, py = 0 px > 0, py > 0 + ------------|------------- ------------|------------- + | | | | | | + | | | | 3 | 2 | + | | | | | | + - 3 | 1 - -------------------------- + | | | | | | + | | | | 1 | 0 | + | | | | | | + ------------|------------- ------------|------------- + """ + _REC_ID = 0x0041 + + valid_active_pane = { + # entries are of the form: + # (int(px > 0),int(px>0)) -> allowed values + (0,0):(3,), + (0,1):(2,3), + (1,0):(1,3), + (1,1):(0,1,2,3), + } + + def __init__(self, px, py, first_row_bottom, first_col_right, active_pane): + allowed = self.valid_active_pane.get( + (int(px > 0),int(py > 0)) + ) + if active_pane not in allowed: + raise ValueError('Cannot set active_pane to %i, must be one of %s' % ( + active_pane, ', '.join(allowed) + )) + self._rec_data = pack('<5H', + px, py, + first_row_bottom, first_col_right, + active_pane) + + +class RowRecord(BiffRecord): + """ + This record contains the properties of a single row in a sheet. Rows + and cells in a sheet are divided into blocks of 32 rows. + + Record ROW, BIFF3-BIFF8: + + Offset Size Contents + 0 2 Index of this row + 2 2 Index to column of the first cell which is described by a cell record + 4 2 Index to column of the last cell which is described by a cell record, + increased by 1 + 6 2 Bit Mask Contents + 14-0 7FFFH Height of the row, in twips = 1/20 of a point + 15 8000H 0 = Row has custom height; 1 = Row has default height + 8 2 Not used + 10 2 In BIFF3-BIFF4 this field contains a relative offset + to calculate stream position of the first cell record + for this row. In BIFF5-BIFF8 this field is not used + anymore, but the DBCELL record instead. + 12 4 Option flags and default row formatting: + Bit Mask Contents + 2-0 00000007H Outline level of the row + 4 00000010H 1 = Outline group starts or ends here (depending + on where the outline buttons are located, + see WSBOOL record), and is collapsed + 5 00000020H 1 = Row is hidden (manually, or by a filter or outline group) + 6 00000040H 1 = Row height and default font height do not match + 7 00000080H 1 = Row has explicit default format (fl) + 8 00000100H Always 1 + 27-16 0FFF0000H If fl=1: Index to default XF record + 28 10000000H 1 = Additional space above the row. This flag is set, + if the upper border of at least one cell in this row + or if the lower border of at least one cell in the row + above is formatted with a thick line style. + Thin and medium line styles are not taken into account. + 29 20000000H 1 = Additional space below the row. This flag is set, + if the lower border of at least one cell in this row + or if the upper border of at least one cell in the row + below is formatted with a medium or thick line style. + Thin line styles are not taken into account. + """ + + _REC_ID = 0x0208 + + def __init__(self, index, first_col, last_col, height_options, options): + self._rec_data = pack('<6HL', index, first_col, last_col + 1, + height_options, + 0x00, 0x00, + options) + +class LabelSSTRecord(BiffRecord): + """ + This record represents a cell that contains a string. It replaces the + LABEL record and RSTRING record used in BIFF2-BIFF7. + """ + _REC_ID = 0x00FD + + def __init__(self, row, col, xf_idx, sst_idx): + self._rec_data = pack('<3HL', row, col, xf_idx, sst_idx) + + +class MergedCellsRecord(BiffRecord): + """ + This record contains all merged cell ranges of the current sheet. + + Record MERGEDCELLS, BIFF8: + + Offset Size Contents + 0 var. Cell range address list with all merged ranges + + ------------------------------------------------------------------ + + A cell range address list consists of a field with the number of ranges + and the list of the range addresses. + + Cell range address list, BIFF8: + + Offset Size Contents + 0 2 Number of following cell range addresses (nm) + 2 8*nm List of nm cell range addresses + + --------------------------------------------------------------------- + Cell range address, BIFF8: + + Offset Size Contents + 0 2 Index to first row + 2 2 Index to last row + 4 2 Index to first column + 6 2 Index to last column + + """ + _REC_ID = 0x00E5 + + def __init__(self, merged_list): + i = len(merged_list) - 1 + while i >= 0: + j = 0 + merged = b'' + while (i >= 0) and (j < 0x403): + r1, r2, c1, c2 = merged_list[i] + merged += pack('<4H', r1, r2, c1, c2) + i -= 1 + j += 1 + self._rec_data += pack('<3H', self._REC_ID, len(merged) + 2, j) + \ + merged + + # for some reason Excel doesn't use CONTINUE + def get(self): + return self._rec_data + +class MulBlankRecord(BiffRecord): + """ + This record represents a cell range of empty cells. All cells are + located in the same row. + + Record MULBLANK, BIFF5-BIFF8: + + Offset Size Contents + 0 2 Index to row + 2 2 Index to first column (fc) + 4 2*nc List of nc=lc-fc+1 16-bit indexes to XF records + 4+2*nc 2 Index to last column (lc) + """ + _REC_ID = 0x00BE + + def __init__(self, row, first_col, last_col, xf_index): + blanks_count = last_col-first_col+1 + self._rec_data = pack('<%dH' % blanks_count, *([xf_index] * blanks_count)) + self._rec_data = pack('<2H', row, first_col) + self._rec_data + pack('<H', last_col) + + +class BlankRecord(BiffRecord): + """ + This record represents an empty cell. + + Record BLANK, BIFF5-BIFF8: + + Offset Size Contents + 0 2 Index to row + 2 2 Index to first column (fc) + 4 2 indexes to XF record + """ + _REC_ID = 0x0201 + + def __init__(self, row, col, xf_index): + self._rec_data = pack('<3H', row, col, xf_index) + + +class RKRecord(BiffRecord): + """ + This record represents a cell that contains an RK value (encoded integer or + floating-point value). If a floating-point value cannot be encoded to an RK value, + a NUMBER record will be written. + """ + _REC_ID = 0x027E + + def __init__(self, row, col, xf_index, rk_encoded): + self._rec_data = pack('<3Hi', row, col, xf_index, rk_encoded) + + +class NumberRecord(BiffRecord): + """ + This record represents a cell that contains an IEEE-754 floating-point value. + """ + _REC_ID = 0x0203 + + def __init__(self, row, col, xf_index, number): + self._rec_data = pack('<3Hd', row, col, xf_index, number) + +class BoolErrRecord(BiffRecord): + """ + This record represents a cell that contains a boolean or error value. + """ + _REC_ID = 0x0205 + + def __init__(self, row, col, xf_index, number, is_error): + self._rec_data = pack('<3HBB', row, col, xf_index, number, is_error) + + +class FormulaRecord(BiffRecord): + """ + Offset Size Contents + 0 2 Index to row + 2 2 Index to column + 4 2 Index to XF record + 6 8 Result of the formula + 14 2 Option flags: + Bit Mask Contents + 0 0001H 1 = Recalculate always + 1 0002H 1 = Calculate on open + 3 0008H 1 = Part of a shared formula + 16 4 Not used + 20 var. Formula data (RPN token array) + + """ + _REC_ID = 0x0006 + + def __init__(self, row, col, xf_index, rpn, calc_flags=0): + self._rec_data = pack('<3HQHL', row, col, xf_index, 0xFFFF000000000003, calc_flags & 3, 0) + rpn + + +class GutsRecord(BiffRecord): + """ + This record contains information about the layout of outline symbols. + + Record GUTS, BIFF3-BIFF8: + + Offset Size Contents + 0 2 Width of the area to display row outlines (left of the sheet), in pixel + 2 2 Height of the area to display column outlines (above the sheet), in pixel + 4 2 Number of visible row outline levels (used row levels + 1; or 0, if not used) + 6 2 Number of visible column outline levels (used column levels + 1; or 0, if not used) + + """ + + _REC_ID = 0x0080 + + def __init__(self, row_gut_width, col_gut_height, row_visible_levels, col_visible_levels): + self._rec_data = pack('<4H', row_gut_width, col_gut_height, row_visible_levels, col_visible_levels) + +class WSBoolRecord(BiffRecord): + """ + This record stores a 16 bit value with Boolean options for the current + sheet. From BIFF5 on the "Save external linked values" option is moved + to the record BOOKBOOL. + + Option flags of record WSBOOL, BIFF3-BIFF8: + + Bit Mask Contents + 0 0001H 0 = Do not show automatic page breaks + 1 = Show automatic page breaks + 4 0010H 0 = Standard sheet + 1 = Dialogue sheet (BIFF5-BIFF8) + 5 0020H 0 = No automatic styles in outlines + 1 = Apply automatic styles to outlines + 6 0040H 0 = Outline buttons above outline group + 1 = Outline buttons below outline group + 7 0080H 0 = Outline buttons left of outline group + 1 = Outline buttons right of outline group + 8 0100H 0 = Scale printout in percent + 1 = Fit printout to number of pages + 9 0200H 0 = Save external linked values (BIFF3?BIFF4 only) + 1 = Do not save external linked values (BIFF3?BIFF4 only) + 10 0400H 0 = Do not show row outline symbols + 1 = Show row outline symbols + 11 0800H 0 = Do not show column outline symbols + 1 = Show column outline symbols + 13-12 3000H These flags specify the arrangement of windows. + They are stored in BIFF4 only. + 00 = Arrange windows tiled + 01 = Arrange windows horizontal + 10 = Arrange windows vertical112 = Arrange windows cascaded + The following flags are valid for BIFF4-BIFF8 only: + 14 4000H 0 = Standard expression evaluation + 1 = Alternative expression evaluation + 15 8000H 0 = Standard formula entries + 1 = Alternative formula entries + + """ + _REC_ID = 0x0081 + + def __init__(self, options): + self._rec_data = pack('<H', options) + +class ColInfoRecord(BiffRecord): + """ + This record specifies the width for a given range of columns. + If a column does not have a corresponding COLINFO record, + the width specified in the record STANDARDWIDTH is used. If + this record is also not present, the contents of the record + DEFCOLWIDTH is used instead. + This record also specifies a default XF record to use for + cells in the columns that are not described by any cell record + (which contain the XF index for that cell). Additionally, + the option flags field contains hidden, outline, and collapsed + options applied at the columns. + + Record COLINFO, BIFF3-BIFF8: + + Offset Size Contents + 0 2 Index to first column in the range + 2 2 Index to last column in the range + 4 2 Width of the columns in 1/256 of the width of the zero character, using default font + (first FONT record in the file) + 6 2 Index to XF record for default column formatting + 8 2 Option flags: + Bits Mask Contents + 0 0001H 1 = Columns are hidden + 10-8 0700H Outline level of the columns (0 = no outline) + 12 1000H 1 = Columns are collapsed + 10 2 Not used + + """ + _REC_ID = 0x007D + + def __init__(self, first_col, last_col, width, xf_index, options, unused): + self._rec_data = pack('<6H', first_col, last_col, width, xf_index, options, unused) + +class CalcModeRecord(BiffRecord): + """ + This record is part of the Calculation Settings Block. + It specifies whether to calculate formulas manually, + automatically or automatically except for multiple table operations. + + Record CALCMODE, BIFF2-BIFF8: + + Offset Size Contents + 0 2 FFFFH = automatic except for multiple table operations + 0000H = manually + 0001H = automatically (default) + """ + _REC_ID = 0x000D + + def __init__(self, calc_mode): + self._rec_data = pack('<h', calc_mode) + + +class CalcCountRecord(BiffRecord): + """ + This record is part of the Calculation Settings Block. It specifies the maximum + number of times the formulas should be iteratively calculated. This is a fail-safe + against mutually recursive formulas locking up a spreadsheet application. + + Record CALCCOUNT, BIFF2-BIFF8: + + Offset Size Contents + 0 2 Maximum number of iterations allowed in circular references + """ + + _REC_ID = 0x000C + + def __init__(self, calc_count): + self._rec_data = pack('<H', calc_count) + +class RefModeRecord(BiffRecord): + """ + This record is part of the Calculation Settings Block. + It stores which method is used to show cell addresses in formulas. + The “RC” mode uses numeric indexes for rows and columns, + i.e. “R(1)C(-1)”, or “R1C1:R2C2”. + The “A1” mode uses characters for columns and numbers for rows, + i.e. “B1”, or “$A$1:$B$2”. + + Record REFMODE, BIFF2-BIFF8: + + Offset Size Contents + 0 2 0 = RC mode; 1 = A1 mode + + """ + _REC_ID = 0x00F + + def __init__(self, ref_mode): + self._rec_data = pack('<H', ref_mode) + +class IterationRecord(BiffRecord): + """ + This record is part of the Calculation Settings Block. + It stores if iterations are allowed while calculating recursive formulas. + + Record ITERATION, BIFF2-BIFF8: + + Offset Size Contents + 0 2 0 = Iterations off; 1 = Iterations on + """ + _REC_ID = 0x011 + + def __init__(self, iterations_on): + self._rec_data = pack('<H', iterations_on) + +class DeltaRecord(BiffRecord): + """ + This record is part of the Calculation Settings Block. + It stores the maximum change of the result to exit an iteration. + + Record DELTA, BIFF2-BIFF8: + + Offset Size Contents + 0 8 Maximum change in iteration + (IEEE 754 floating-point value, + 64bit double precision) + """ + _REC_ID = 0x010 + + def __init__(self, delta): + self._rec_data = pack('<d', delta) + +class SaveRecalcRecord(BiffRecord): + """ + This record is part of the Calculation Settings Block. + It contains the “Recalculate before save” option in + Excel's calculation settings dialogue. + + Record SAVERECALC, BIFF3-BIFF8: + + Offset Size Contents + 0 2 0 = Do not recalculate; + 1 = Recalculate before saving the document + + """ + _REC_ID = 0x05F + + def __init__(self, recalc): + self._rec_data = pack('<H', recalc) + +class PrintHeadersRecord(BiffRecord): + """ + This record stores if the row and column headers + (the areas with row numbers and column letters) will be printed. + + Record PRINTHEADERS, BIFF2-BIFF8: + + Offset Size Contents + 0 2 0 = Do not print row/column headers; + 1 = Print row/column headers + """ + _REC_ID = 0x02A + + def __init__(self, print_headers): + self._rec_data = pack('<H', print_headers) + + +class PrintGridLinesRecord(BiffRecord): + """ + This record stores if sheet grid lines will be printed. + + Record PRINTGRIDLINES, BIFF2-BIFF8: + + Offset Size Contents + 0 2 0 = Do not print sheet grid lines; + 1 = Print sheet grid lines + + """ + _REC_ID = 0x02B + + def __init__(self, print_grid): + self._rec_data = pack('<H', print_grid) + + +class GridSetRecord(BiffRecord): + """ + This record specifies if the option to print sheet grid lines + (record PRINTGRIDLINES) has ever been changed. + + Record GRIDSET, BIFF3-BIFF8: + + Offset Size Contents + 0 2 0 = Print grid lines option never changed + 1 = Print grid lines option changed + """ + _REC_ID = 0x082 + + def __init__(self, print_grid_changed): + self._rec_data = pack('<H', print_grid_changed) + + +class DefaultRowHeightRecord(BiffRecord): + """ + This record specifies the default height and default flags + for rows that do not have a corresponding ROW record. + + Record DEFAULTROWHEIGHT, BIFF3-BIFF8: + + Offset Size Contents + 0 2 Option flags: + Bit Mask Contents + 0 0001H 1 = Row height and default font height do not match + 1 0002H 1 = Row is hidden + 2 0004H 1 = Additional space above the row + 3 0008H 1 = Additional space below the row + 2 2 Default height for unused rows, in twips = 1/20 of a point + + """ + _REC_ID = 0x0225 + + def __init__(self, options, def_height): + self._rec_data = pack('<2H', options, def_height) + + +class DefColWidthRecord(BiffRecord): + """ + This record specifies the default column width for columns that + do not have a specific width set using the record COLINFO or COLWIDTH. + This record has no effect, if a STANDARDWIDTH record is present in the file. + + Record DEFCOLWIDTH, BIFF2-BIFF8: + + Offset Size Contents + 0 2 Column width in characters, using the width of the zero + character from default font (first FONT record in the file) + """ + _REC_ID = 0x0055 + + def __init__(self, def_width): + self._rec_data = pack('<H', options, def_width) + +class HorizontalPageBreaksRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It contains all + horizontal manual page breaks. + + Record HORIZONTALPAGEBREAKS, BIFF8: + Offset Size Contents + 0 2 Number of following row index structures (nm) + 2 6nm List of nm row index structures. Each row index + structure contains: + Offset Size Contents + 0 2 Index to first row below the page break + 2 2 Index to first column of this page break + 4 2 Index to last column of this page break + + The row indexes in the lists must be ordered ascending. + If in BIFF8 a row contains several page breaks, they must be ordered + ascending by start column index. + """ + _REC_ID = 0x001B + + def __init__(self, breaks_list): + self._rec_data = pack('<H', len(breaks_list)) + for r, c1, c2 in breaks_list: + self._rec_data += pack('<3H', r, c1, c2) + +class VerticalPageBreaksRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It contains all + vertical manual page breaks. + + Record VERTICALPAGEBREAKS, BIFF8: + Offset Size Contents + 0 2 Number of following column index structures (nm) + 2 6nm List of nm column index structures. Each column index + structure contains: + Offset Size Contents + 0 2 Index to first column following the page + break + 2 2 Index to first row of this page break + 4 2 Index to last row of this page break + + The column indexes in the lists must be ordered ascending. + If in BIFF8 a column contains several page breaks, they must be ordered + ascending by start row index. + """ + _REC_ID = 0x001A + + def __init__(self, breaks_list): + self._rec_data = pack('<H', len(breaks_list)) + for r, c1, c2 in breaks_list: + self._rec_data += pack('<3H', r, c1, c2) + +class HeaderRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It specifies the + page header string for the current worksheet. If this record is not + present or completely empty (record size is 0), the sheet does not + contain a page header. + + Record HEADER for non-empty page header, BIFF2-BIFF8: + Offset Size Contents + 0 var. Page header string + BIFF2-BIFF7: Non-empty byte string, 8bit string + length + BIFF8: Non-empty Unicode string, 16bit string length + The header string may contain special commands, i.e. placeholders for + the page number, current date, or text formatting attributes. These + fields are represented by single letters (exception: font name and + size, see below) with a leading ampersand ("&"). If the ampersand + is part of the regular header text, it will be duplicated ("&&"). The + page header is divided into 3 sections: the left, the centred, and the + right section. Each section is introduced by a special command. All + text and all commands following are part of the selected section. Each + section starts with the text formatting specified in the default font + (first FONT record in the file). Active formatting attributes from + a previous section do not go into the next section. + + The following table shows all available commands: + + Command Contents + && The "&" character itself + &L Start of the left section + &C Start of the centred section + &R Start of the right section + &P Current page number + &N Page count + &D Current date + &T Current time + &A Sheet name (BIFF5-BIFF8) + &F File name without path + &Z File path without file name (BIFF8X) + &G Picture (BIFF8X) + &B Bold on/off (BIFF2-BIFF4) + &I Italic on/off (BIFF2-BIFF4) + &U Underlining on/off + &E Double underlining on/off (BIFF5-BIFF8) + &S Strikeout on/off + &X Superscript on/off (BIFF5-BIFF8) + &Y Subscript on/off (BIFF5-BIFF8) + &"<fontname>" Set new font <fontname> + &"<fontname>,<fontstyle>" + Set new font with specified style <fontstyle>. + The style <fontstyle> is in most cases one of + "Regular", "Bold", "Italic", or "Bold Italic". + But this setting is dependent on the used font, + it may differ (localised style names, or "Standard", + "Oblique", ...). (BIFF5-BIFF8) + &<fontheight> Set font height in points (<fontheight> is a decimal value). + If this command is followed by a plain number to be printed + in the header, it will be separated from the font height + with a space character. + + """ + _REC_ID = 0x0014 + + def __init__(self, header_str): + self._rec_data = upack2(header_str) + +class FooterRecord(BiffRecord): + """ + Semantic is equal to HEADER record + """ + _REC_ID = 0x0015 + + def __init__(self, footer_str): + self._rec_data = upack2(footer_str) + + +class HCenterRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It specifies if the + sheet is centred horizontally when printed. + + Record HCENTER, BIFF3-BIFF8: + + Offset Size Contents + 0 2 0 = Print sheet left aligned + 1 = Print sheet centred horizontally + + """ + _REC_ID = 0x0083 + + def __init__(self, is_horz_center): + self._rec_data = pack('<H', is_horz_center) + + +class VCenterRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It specifies if the + sheet is centred vertically when printed. + + Record VCENTER, BIFF3-BIFF8: + + Offset Size Contents + 0 2 0 = Print sheet aligned at top page border + 1 = Print sheet vertically centred + + """ + _REC_ID = 0x0084 + + def __init__(self, is_vert_center): + self._rec_data = pack('<H', is_vert_center) + + +class LeftMarginRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It contains the left + page margin of the current worksheet. + + Record LEFTMARGIN, BIFF2-BIFF8: + + Offset Size Contents + 0 8 Left page margin in inches + (IEEE 754 floating-point value, 64bit double precision) + + """ + _REC_ID = 0x0026 + + def __init__(self, margin): + self._rec_data = pack('<d', margin) + + +class RightMarginRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It contains the right + page margin of the current worksheet. + + Offset Size Contents + 0 8 Right page margin in inches + (IEEE 754 floating-point value, 64?bit double precision) + + """ + _REC_ID = 0x0027 + + def __init__(self, margin): + self._rec_data = pack('<d', margin) + +class TopMarginRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It contains the top + page margin of the current worksheet. + + Offset Size Contents + 0 8 Top page margin in inches + (IEEE 754 floating-point value, 64?bit double precision) + + """ + _REC_ID = 0x0028 + + def __init__(self, margin): + self._rec_data = pack('<d', margin) + + +class BottomMarginRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It contains the bottom + page margin of the current worksheet. + + Offset Size Contents + 0 8 Bottom page margin in inches + (IEEE 754 floating-point value, 64?bit double precision) + + """ + _REC_ID = 0x0029 + + def __init__(self, margin): + self._rec_data = pack('<d', margin) + +class SetupPageRecord(BiffRecord): + """ + This record is part of the Page Settings Block. It stores the page + format settings of the current sheet. The pages may be scaled in + percent or by using an absolute number of pages. This setting is + located in the WSBOOL record. If pages are scaled in percent, + the scaling factor in this record is used, otherwise the "Fit to + pages" values. One of the "Fit to pages" values may be 0. In this case + the sheet is scaled to fit only to the other value. + + Record SETUP, BIFF5-BIFF8: + + Offset Size Contents + 0 2 Paper size (see below) + 2 2 Scaling factor in percent + 4 2 Start page number + 6 2 Fit worksheet width to this number of pages + (0 = use as many as needed) + 8 2 Fit worksheet height to this number of pages + (0 = use as many as needed) + 10 2 Option flags: + Bit Mask Contents + 0 0001H 0 = Print pages in columns + 1 = Print pages in rows + 1 0002H 0 = Landscape + 1 = Portrait + 2 0004H 1 = Paper size, scaling factor, + paper orientation (portrait/landscape), + print resolution and number of copies + are not initialised + 3 0008H 0 = Print coloured + 1 = Print black and white + 4 0010H 0 = Default print quality + 1 = Draft quality + 5 0020H 0 = Do not print cell notes + 1 = Print cell notes + 6 0040H 0 = Paper orientation setting is valid + 1 = Paper orientation setting not + initialised + 7 0080H 0 = Automatic page numbers + 1 = Use start page number + The following flags are valid for BIFF8 only: + 9 0200H 0 = Print notes as displayed + 1 = Print notes at end of sheet + 11-10 0C00H 00 = Print errors as displayed + 01 = Do not print errors + 10 = Print errors as "--" + 11 = Print errors as "#N/A!" + 12 2 Print resolution in dpi + 14 2 Vertical print resolution in dpi + 16 8 Header margin (IEEE 754 floating-point value, + 64bit double precision) + 24 8 Footer margin (IEEE 754 floating-point value, + 64bit double precision) + 32 2 Number of copies to print + + + PAPER TYPES: + + Index Paper type Paper size + 0 Undefined + 1 Letter 8 1/2" x 11" + 2 Letter small 8 1/2" x 11" + 3 Tabloid 11" x 17" + 4 Ledger 17" x 11" + 5 Legal 8 1/2" x 14" + 6 Statement 5 1/2" x 8 1/2" + 7 Executive 7 1/4" x 10 1/2" + 8 A3 297mm x 420mm + 9 A4 210mm x 297mm + 10 A4 small 210mm x 297mm + 11 A5 148mm x 210mm + 12 B4 (JIS) 257mm x 364mm + 13 B5 (JIS) 182mm x 257mm + 14 Folio 8 1/2" x 13" + 15 Quarto 215mm x 275mm + 16 10x14 10" x 14" + 17 11x17 11" x 17" + 18 Note 8 1/2" x 11" + 19 Envelope #9 3 7/8" x 8 7/8" + 20 Envelope #10 4 1/8" x 9 1/2" + 21 Envelope #11 4 1/2" x 10 3/8" + 22 Envelope #12 4 3/4" x 11" + 23 Envelope #14 5" x 11 1/2" + 24 C 17" x 22" + 25 D 22" x 34" + 26 E 34" x 44" + 27 Envelope DL 110mm x 220mm + 28 Envelope C5 162mm x 229mm + 29 Envelope C3 324mm x 458mm + 30 Envelope C4 229mm x 324mm + 31 Envelope C6 114mm x 162mm + 32 Envelope C6/C5 114mm x 229mm + 33 B4 (ISO) 250mm x 353mm + 34 B5 (ISO) 176mm x 250mm + 35 B6 (ISO) 125mm x 176mm + 36 Envelope Italy 110mm x 230mm + 37 Envelope Monarch 3 7/8" x 7 1/2" + 38 63/4 Envelope 3 5/8" x 6 1/2" + 39 US Standard Fanfold 14 7/8" x 11" + 40 German Std. Fanfold 8 1/2" x 12" + 41 German Legal Fanfold 8 1/2" x 13" + 42 B4 (ISO) 250mm x 353mm + 43 Japanese Postcard 100mm x 148mm + 44 9x11 9" x 11" + 45 10x11 10" x 11" + 46 15x11 15" x 11" + 47 Envelope Invite 220mm x 220mm + 48 Undefined + 49 Undefined + 50 Letter Extra 9 1/2" x 12" + 51 Legal Extra 9 1/2" x 15" + 52 Tabloid Extra 11 11/16" x 18" + 53 A4 Extra 235mm x 322mm + 54 Letter Transverse 8 1/2" x 11" + 55 A4 Transverse 210mm x 297mm + 56 Letter Extra Transv. 9 1/2" x 12" + 57 Super A/A4 227mm x 356mm + 58 Super B/A3 305mm x 487mm + 59 Letter Plus 8 1/2" x 12 11/16" + 60 A4 Plus 210mm x 330mm + 61 A5 Transverse 148mm x 210mm + 62 B5 (JIS) Transverse 182mm x 257mm + 63 A3 Extra 322mm x 445mm + 64 A5 Extra 174mm x 235mm + 65 B5 (ISO) Extra 201mm x 276mm + 66 A2 420mm x 594mm + 67 A3 Transverse 297mm x 420mm + 68 A3 Extra Transverse 322mm x 445mm + 69 Dbl. Japanese Postcard 200mm x 148mm + 70 A6 105mm x 148mm + 71 + 72 + 73 + 74 + 75 Letter Rotated 11" x 8 1/2" + 76 A3 Rotated 420mm x 297mm + 77 A4 Rotated 297mm x 210mm + 78 A5 Rotated 210mm x 148mm + 79 B4 (JIS) Rotated 364mm x 257mm + 80 B5 (JIS) Rotated 257mm x 182mm + 81 Japanese Postcard Rot. 148mm x 100mm + 82 Dbl. Jap. Postcard Rot. 148mm x 200mm + 83 A6 Rotated 148mm x 105mm + 84 + 85 + 86 + 87 + 88 B6 (JIS) 128mm x 182mm + 89 B6 (JIS) Rotated 182mm x 128mm + 90 12x11 12" x 11" + + """ + _REC_ID = 0x00A1 + def __init__(self, paper, scaling, start_num, fit_width_to, fit_height_to, + options, + hres, vres, + header_margin, footer_margin, + num_copies): + self._rec_data = pack('<8H2dH', paper, scaling, start_num, + fit_width_to, fit_height_to, \ + options, + hres, vres, + header_margin, footer_margin, + num_copies) + +class NameRecord(BiffRecord): + """ + This record is part of a Link Table. It contains the name and the token + array of an internal defined name. Token arrays of defined names + contain tokens with aberrant token classes. + + Record NAME, BIFF5/BIFF7: + Offset Size Contents + 0 2 Option flags, see below + 2 1 Keyboard shortcut (only for command macro names, see below) + 3 1 Length of the name (character count, ln) + 4 2 Size of the formula data (sz) + 6 2 0 = Global name, otherwise index to EXTERNSHEET record (one-based) + 8 2 0 = Global name, otherwise index to sheet (one-based) + 10 1 Length of menu text (character count, lm) + 11 1 Length of description text (character count, ld) + 12 1 Length of help topic text (character count, lh) + 13 1 Length of status bar text (character count, ls) + 14 ln Character array of the name + 14+ln sz Formula data (RPN token array without size field, 4) + 14+ln+sz lm Character array of menu text + var. ld Character array of description text + var. lh Character array of help topic text + var. ls Character array of status bar text + + Record NAME, BIFF8: + Offset Size Contents + 0 2 Option flags, see below + 2 1 Keyboard shortcut (only for command macro names, see below) + 3 1 Length of the name (character count, ln) + 4 2 Size of the formula data (sz) + 6 2 Not used + 8 2 0 = Global name, otherwise index to sheet (one-based) + 10 1 Length of menu text (character count, lm) + 11 1 Length of description text (character count, ld) + 12 1 Length of help topic text (character count, lh) + 13 1 Length of status bar text (character count, ls) + 14 var. Name (Unicode string without length field, 3.4) + var. sz Formula data (RPN token array without size field, 4) + [var.] var. (optional, only if lm > 0) Menu text (Unicode string without length field, 3.4) + [var.] var. (optional, only if ld > 0) Description text (Unicode string without length field, 3.4) + [var.] var. (optional, only if lh > 0) Help topic text (Unicode string without length field, 3.4) + [var.] var. (optional, only if ls > 0) Status bar text (Unicode string without length field, 3.4) + """ + _REC_ID = 0x0018 + + def __init__(self, options, keyboard_shortcut, name, sheet_index, rpn, menu_text='', desc_text='', help_text='', status_text=''): + if type(name) == int: + uname = chr(name) + else: + uname = upack1(name)[1:] + uname_len = len(uname) + + #~ self._rec_data = pack('<HBBHHHBBBB%ds%ds' % (uname_len, len(rpn)), options, keyboard_shortcut, uname_len, len(rpn), 0x0000, sheet_index, len(menu_text), len(desc_text), len(help_text), len(status_text), uname, rpn) + menu_text + desc_text + help_text + status_text + self._rec_data = pack('<HBBHHHBBBBB%ds%ds' % (uname_len, len(rpn)), options, keyboard_shortcut, uname_len, len(rpn), 0x0000, sheet_index, 0x00, len(menu_text), len(desc_text), len(help_text), len(status_text), uname, rpn) + menu_text + desc_text + help_text + status_text + +# Excel (both 2003 and 2007) don't like refs +# split over a record boundary, which is what the +# standard BiffRecord.get method does. + +# 8224 max data bytes in a BIFF record +# 6 bytes per ref +# 1370 = floor((8224 - 2) / 6.0) max refs in a record + +_maxRefPerRecord = 1370 + +class ExternSheetRecord(BiffRecord): + """ + In BIFF8 the record stores a list with indexes to SUPBOOK + records (list of REF structures, 6.100). See 5.10.3 for + details about external references in BIFF8. + + Record EXTERNSHEET, BIFF8: + Offset Size Contents + 0 2 Number of following REF structures (nm) + 2 6nm List of nm REF structures. Each REF contains the following data: + Offset Size Contents + 0 2 Index to SUPBOOK record + 2 2 Index to first SUPBOOK sheet + 4 2 Index to last SUPBOOK sheet + """ + _REC_ID = 0x0017 + + def __init__(self, refs): + + # do we always need this ref? or only if there are no refs? + # (I believe that if there are no refs then we should not generate the link table - Ruben) + #refs.insert(0, (0,0,0)) + + self.refs = refs + + def get(self): + res = [] + nrefs = len(self.refs) + for idx in xrange(0, nrefs, _maxRefPerRecord): + chunk = self.refs[idx:idx+_maxRefPerRecord] + krefs = len(chunk) + if idx: # CONTINUE record + header = pack("<HH", 0x003C, 6 * krefs) + else: # ExternSheetRecord + header = pack("<HHH", self._REC_ID, 6 * krefs + 2, nrefs) + res.append(header) + res.extend(pack("<HHH", *r) for r in chunk) + return b''.join(res) + +class SupBookRecord(BiffRecord): + """ + This record mainly stores the URL of an external document + and a list of sheet names inside this document. Furthermore + it is used to store DDE and OLE object links, or to indicate + an internal 3D reference or an add-in function. See 5.10.3 + for details about external references in BIFF8. + + """ + _REC_ID = 0x01AE + +class InternalReferenceSupBookRecord(SupBookRecord): + """ + In each file occurs a SUPBOOK that is used for internal 3D + references. It stores the number of sheets of the own document. + + Record SUPBOOK for 3D references, BIFF8: + Offset Size Contents + 0 2 Number of sheets in this document + 2 2 01H 04H (relict of BIFF5/BIFF7, the byte string "<04H>", see 3.9.1) + + """ + + def __init__(self, num_sheets): + self._rec_data = pack('<HBB', num_sheets, 0x01, 0x04) + +class XcallSupBookRecord(SupBookRecord): + """ + Add-in function names are stored in EXTERNNAME records following this record. + + Offset Size Contents + 0 2 0001H + 2 2 01H 3AH (relict of BIFF5, the byte string ':', see EXTERNSHEET record, 5.41) + + """ + + def __init__(self): + self._rec_data = pack('<HBB', 1, 0x01, 0x3A) + + +class ExternnameRecord(BiffRecord): + """ + Record EXTERNNAME for external names and Analysis add-in functions, BIFF5-BIFF8: + Offset Size Contents + 0 2 Option flags (see below) + 2 2 0 for global names, or: + BIFF5: One-based index to EXTERNSHEET record containing the sheet name, + BIFF8: One-based index to sheet list in preceding EXTERNALBOOK record. + 4 2 Not used + 6 var. BIFF5: Name (byte string, 8-bit string length, ?2.5.2). + BIFF8: Name (Unicode string, 8-bit string length, ?2.5.3). + See DEFINEDNAME record (?5.33) for a list of built-in names, if the built-in flag is set + in the option flags above. + var. var. Formula data (RPN token array, ?3) + + Option flags for external names (BIFF5-BIFF8) + Bit Mask Contents + 0 0001H 0 = Standard name; 1 = Built-in name + 1 0002H 0 = Manual link; 1 = Automatic link (DDE links and OLE links only) + 2 0004H 1 = Picture link (DDE links and OLE links only) + 3 0008H 1 = This is the “StdDocumentName” identifier (DDE links only) + 4 0010H 1 = OLE link + 14-5 7FE0H Clipboard format of last successful update (DDE links and OLE links only) + 15 8000H 1 = Iconified picture link (BIFF8 OLE links only) + """ + _REC_ID = 0x0023 + + def __init__(self, options=0, index=0, name=None, fmla=None): + self._rec_data = pack('<HHH', options, index, 0) + upack1(name) + fmla + diff --git a/.venv/lib/python3.9/site-packages/xlwt/Bitmap.py b/.venv/lib/python3.9/site-packages/xlwt/Bitmap.py new file mode 100644 index 00000000..346183a1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Bitmap.py @@ -0,0 +1,275 @@ +# -*- coding: windows-1251 -*- + +# Portions are Copyright (C) 2005 Roman V. Kiseliov +# Portions are Copyright (c) 2004 Evgeny Filatov <fufff@users.sourceforge.net> +# Portions are Copyright (c) 2002-2004 John McNamara (Perl Spreadsheet::WriteExcel) + +from .BIFFRecords import BiffRecord +from struct import pack, unpack + + +def _size_col(sheet, col): + return sheet.col_width(col) + + +def _size_row(sheet, row): + return sheet.row_height(row) + + +def _position_image(sheet, row_start, col_start, x1, y1, width, height): + """Calculate the vertices that define the position of the image as required by + the OBJ record. + + +------------+------------+ + | A | B | + +-----+------------+------------+ + | |(x1,y1) | | + | 1 |(A1)._______|______ | + | | | | | + | | | | | + +-----+----| BITMAP |-----+ + | | | | | + | 2 | |______________. | + | | | (B2)| + | | | (x2,y2)| + +---- +------------+------------+ + + Example of a bitmap that covers some of the area from cell A1 to cell B2. + + Based on the width and height of the bitmap we need to calculate 8 vars: + col_start, row_start, col_end, row_end, x1, y1, x2, y2. + The width and height of the cells are also variable and have to be taken into + account. + The values of col_start and row_start are passed in from the calling + function. The values of col_end and row_end are calculated by subtracting + the width and height of the bitmap from the width and height of the + underlying cells. + The vertices are expressed as a percentage of the underlying cell width as + follows (rhs values are in pixels): + + x1 = X / W *1024 + y1 = Y / H *256 + x2 = (X-1) / W *1024 + y2 = (Y-1) / H *256 + + Where: X is distance from the left side of the underlying cell + Y is distance from the top of the underlying cell + W is the width of the cell + H is the height of the cell + + Note: the SDK incorrectly states that the height should be expressed as a + percentage of 1024. + + col_start - Col containing upper left corner of object + row_start - Row containing top left corner of object + x1 - Distance to left side of object + y1 - Distance to top of object + width - Width of image frame + height - Height of image frame + + """ + # Adjust start column for offsets that are greater than the col width + while x1 >= _size_col(sheet, col_start): + x1 -= _size_col(sheet, col_start) + col_start += 1 + # Adjust start row for offsets that are greater than the row height + while y1 >= _size_row(sheet, row_start): + y1 -= _size_row(sheet, row_start) + row_start += 1 + # Initialise end cell to the same as the start cell + row_end = row_start # Row containing bottom right corner of object + col_end = col_start # Col containing lower right corner of object + width = width + x1 - 1 + height = height + y1 - 1 + # Subtract the underlying cell widths to find the end cell of the image + while (width >= _size_col(sheet, col_end)): + width -= _size_col(sheet, col_end) + col_end += 1 + # Subtract the underlying cell heights to find the end cell of the image + while (height >= _size_row(sheet, row_end)): + height -= _size_row(sheet, row_end) + row_end += 1 + # Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell + # with zero height or width. + if ((_size_col(sheet, col_start) == 0) or (_size_col(sheet, col_end) == 0) + or (_size_row(sheet, row_start) == 0) or (_size_row(sheet, row_end) == 0)): + return + # Convert the pixel values to the percentage value expected by Excel + x1 = int(float(x1) / _size_col(sheet, col_start) * 1024) + y1 = int(float(y1) / _size_row(sheet, row_start) * 256) + # Distance to right side of object + x2 = int(float(width) / _size_col(sheet, col_end) * 1024) + # Distance to bottom of object + y2 = int(float(height) / _size_row(sheet, row_end) * 256) + return (col_start, x1, row_start, y1, col_end, x2, row_end, y2) + + +class ObjBmpRecord(BiffRecord): + _REC_ID = 0x005D # Record identifier + + def __init__(self, row, col, sheet, im_data_bmp, x, y, scale_x, scale_y): + # Scale the frame of the image. + width = im_data_bmp.width * scale_x + height = im_data_bmp.height * scale_y + + # Calculate the vertices of the image and write the OBJ record + coordinates = _position_image(sheet, row, col, x, y, width, height) + # print coordinates + col_start, x1, row_start, y1, col_end, x2, row_end, y2 = coordinates + + """Store the OBJ record that precedes an IMDATA record. This could be generalise + to support other Excel objects. + + """ + cObj = 0x0001 # Count of objects in file (set to 1) + OT = 0x0008 # Object type. 8 = Picture + id = 0x0001 # Object ID + grbit = 0x0614 # Option flags + colL = col_start # Col containing upper left corner of object + dxL = x1 # Distance from left side of cell + rwT = row_start # Row containing top left corner of object + dyT = y1 # Distance from top of cell + colR = col_end # Col containing lower right corner of object + dxR = x2 # Distance from right of cell + rwB = row_end # Row containing bottom right corner of object + dyB = y2 # Distance from bottom of cell + cbMacro = 0x0000 # Length of FMLA structure + Reserved1 = 0x0000 # Reserved + Reserved2 = 0x0000 # Reserved + icvBack = 0x09 # Background colour + icvFore = 0x09 # Foreground colour + fls = 0x00 # Fill pattern + fAuto = 0x00 # Automatic fill + icv = 0x08 # Line colour + lns = 0xff # Line style + lnw = 0x01 # Line weight + fAutoB = 0x00 # Automatic border + frs = 0x0000 # Frame style + cf = 0x0009 # Image format, 9 = bitmap + Reserved3 = 0x0000 # Reserved + cbPictFmla = 0x0000 # Length of FMLA structure + Reserved4 = 0x0000 # Reserved + grbit2 = 0x0001 # Option flags + Reserved5 = 0x0000 # Reserved + + data = pack("<L", cObj) + data += pack("<H", OT) + data += pack("<H", id) + data += pack("<H", grbit) + data += pack("<H", colL) + data += pack("<H", dxL) + data += pack("<H", rwT) + data += pack("<H", dyT) + data += pack("<H", colR) + data += pack("<H", dxR) + data += pack("<H", rwB) + data += pack("<H", dyB) + data += pack("<H", cbMacro) + data += pack("<L", Reserved1) + data += pack("<H", Reserved2) + data += pack("<B", icvBack) + data += pack("<B", icvFore) + data += pack("<B", fls) + data += pack("<B", fAuto) + data += pack("<B", icv) + data += pack("<B", lns) + data += pack("<B", lnw) + data += pack("<B", fAutoB) + data += pack("<H", frs) + data += pack("<L", cf) + data += pack("<H", Reserved3) + data += pack("<H", cbPictFmla) + data += pack("<H", Reserved4) + data += pack("<H", grbit2) + data += pack("<L", Reserved5) + + self._rec_data = data + +def _process_bitmap(bitmap): + """Convert a 24 bit bitmap into the modified internal format used by Windows. + This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the + MSDN library. + + """ + # Open file and binmode the data in case the platform needs it. + with open(bitmap, "rb") as fh: + # Slurp the file into a string. + data = fh.read() + return _process_bitmap_data(data) + +def _process_bitmap_data(data): + # Check that the file is big enough to be a bitmap. + if len(data) <= 0x36: + raise Exception("bitmap doesn't contain enough data.") + # The first 2 bytes are used to identify the bitmap. + if (data[:2] != b"BM"): + raise Exception("bitmap doesn't appear to to be a valid bitmap image.") + # Remove bitmap data: ID. + data = data[2:] + # Read and remove the bitmap size. This is more reliable than reading + # the data size at offset 0x22. + # + size = unpack("<L", data[:4])[0] + size -= 0x36 # Subtract size of bitmap header. + size += 0x0C # Add size of BIFF header. + data = data[4:] + # Remove bitmap data: reserved, offset, header length. + data = data[12:] + # Read and remove the bitmap width and height. Verify the sizes. + width, height = unpack("<LL", data[:8]) + data = data[8:] + if (width > 0xFFFF): + raise Exception("bitmap: largest image width supported is 65k.") + if (height > 0xFFFF): + raise Exception("bitmap: largest image height supported is 65k.") + # Read and remove the bitmap planes and bpp data. Verify them. + planes, bitcount = unpack("<HH", data[:4]) + data = data[4:] + if (bitcount != 24): + raise Exception("bitmap isn't a 24bit true color bitmap.") + if (planes != 1): + raise Exception("bitmap: only 1 plane supported in bitmap image.") + # Read and remove the bitmap compression. Verify compression. + compression = unpack("<L", data[:4])[0] + data = data[4:] + if (compression != 0): + raise Exception("bitmap: compression not supported in bitmap image.") + # Remove bitmap data: data size, hres, vres, colours, imp. colours. + data = data[20:] + # Add the BITMAPCOREHEADER data + header = pack("<LHHHH", 0x000c, width, height, 0x01, 0x18) + data = header + data + return (width, height, size, data) + + +class ImRawDataBmpRecord(BiffRecord): + _REC_ID = 0x007F + + def __init__(self, data): + """Insert a 24bit bitmap image in a worksheet. The main record required is + IMDATA but it must be proceeded by a OBJ record to define its position. + + """ + BiffRecord.__init__(self) + + self.width, self.height, self.size, data = _process_bitmap_data(data) + self._write_imdata(data) + + def _write_imdata(self, data): + # Write the IMDATA record to store the bitmap data + cf = 0x09 + env = 0x01 + lcb = self.size + self._rec_data = pack("<HHL", cf, env, lcb) + data + +class ImDataBmpRecord(ImRawDataBmpRecord): + def __init__(self, filename): + """Insert a 24bit bitmap image in a worksheet. The main record required is + IMDATA but it must be proceeded by a OBJ record to define its position. + + """ + BiffRecord.__init__(self) + + self.width, self.height, self.size, data = _process_bitmap(filename) + self._write_imdata(data) + diff --git a/.venv/lib/python3.9/site-packages/xlwt/Cell.py b/.venv/lib/python3.9/site-packages/xlwt/Cell.py new file mode 100644 index 00000000..d1fadc38 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Cell.py @@ -0,0 +1,244 @@ +# -*- coding: windows-1252 -*- + +from struct import unpack, pack +from . import BIFFRecords +from .compat import xrange + +class StrCell(object): + __slots__ = ["rowx", "colx", "xf_idx", "sst_idx"] + + def __init__(self, rowx, colx, xf_idx, sst_idx): + self.rowx = rowx + self.colx = colx + self.xf_idx = xf_idx + self.sst_idx = sst_idx + + def get_biff_data(self): + # return BIFFRecords.LabelSSTRecord(self.rowx, self.colx, self.xf_idx, self.sst_idx).get() + return pack('<5HL', 0x00FD, 10, self.rowx, self.colx, self.xf_idx, self.sst_idx) + +class BlankCell(object): + __slots__ = ["rowx", "colx", "xf_idx"] + + def __init__(self, rowx, colx, xf_idx): + self.rowx = rowx + self.colx = colx + self.xf_idx = xf_idx + + def get_biff_data(self): + # return BIFFRecords.BlankRecord(self.rowx, self.colx, self.xf_idx).get() + return pack('<5H', 0x0201, 6, self.rowx, self.colx, self.xf_idx) + +class MulBlankCell(object): + __slots__ = ["rowx", "colx1", "colx2", "xf_idx"] + + def __init__(self, rowx, colx1, colx2, xf_idx): + self.rowx = rowx + self.colx1 = colx1 + self.colx2 = colx2 + self.xf_idx = xf_idx + + def get_biff_data(self): + return BIFFRecords.MulBlankRecord(self.rowx, + self.colx1, self.colx2, self.xf_idx).get() + +class NumberCell(object): + __slots__ = ["rowx", "colx", "xf_idx", "number"] + + def __init__(self, rowx, colx, xf_idx, number): + self.rowx = rowx + self.colx = colx + self.xf_idx = xf_idx + self.number = float(number) + + def get_encoded_data(self): + rk_encoded = 0 + num = self.number + + # The four possible kinds of RK encoding are *not* mutually exclusive. + # The 30-bit integer variety picks up the most. + # In the code below, the four varieties are checked in descending order + # of bangs per buck, or not at all. + # SJM 2007-10-01 + + if -0x20000000 <= num < 0x20000000: # fits in 30-bit *signed* int + inum = int(num) + if inum == num: # survives round-trip + # print "30-bit integer RK", inum, hex(inum) + rk_encoded = 2 | (inum << 2) + return 1, rk_encoded + + temp = num * 100 + + if -0x20000000 <= temp < 0x20000000: + # That was step 1: the coded value will fit in + # a 30-bit signed integer. + itemp = int(round(temp, 0)) + # That was step 2: "itemp" is the best candidate coded value. + # Now for step 3: simulate the decoding, + # to check for round-trip correctness. + if itemp / 100.0 == num: + # print "30-bit integer RK*100", itemp, hex(itemp) + rk_encoded = 3 | (itemp << 2) + return 1, rk_encoded + + if 0: # Cost of extra pack+unpack not justified by tiny yield. + packed = pack('<d', num) + w01, w23 = unpack('<2i', packed) + if not w01 and not(w23 & 3): + # 34 lsb are 0 + # print "float RK", w23, hex(w23) + return 1, w23 + + packed100 = pack('<d', temp) + w01, w23 = unpack('<2i', packed100) + if not w01 and not(w23 & 3): + # 34 lsb are 0 + # print "float RK*100", w23, hex(w23) + return 1, w23 | 1 + + #print "Number" + #print + return 0, pack('<5Hd', 0x0203, 14, self.rowx, self.colx, self.xf_idx, num) + + def get_biff_data(self): + isRK, value = self.get_encoded_data() + if isRK: + return pack('<5Hi', 0x27E, 10, self.rowx, self.colx, self.xf_idx, value) + return value # NUMBER record already packed + +class BooleanCell(object): + __slots__ = ["rowx", "colx", "xf_idx", "number"] + + def __init__(self, rowx, colx, xf_idx, number): + self.rowx = rowx + self.colx = colx + self.xf_idx = xf_idx + self.number = number + + def get_biff_data(self): + return BIFFRecords.BoolErrRecord(self.rowx, + self.colx, self.xf_idx, self.number, 0).get() + +error_code_map = { + 0x00: 0, # Intersection of two cell ranges is empty + 0x07: 7, # Division by zero + 0x0F: 15, # Wrong type of operand + 0x17: 23, # Illegal or deleted cell reference + 0x1D: 29, # Wrong function or range name + 0x24: 36, # Value range overflow + 0x2A: 42, # Argument or function not available + '#NULL!' : 0, # Intersection of two cell ranges is empty + '#DIV/0!': 7, # Division by zero + '#VALUE!': 36, # Wrong type of operand + '#REF!' : 23, # Illegal or deleted cell reference + '#NAME?' : 29, # Wrong function or range name + '#NUM!' : 36, # Value range overflow + '#N/A!' : 42, # Argument or function not available +} + +class ErrorCell(object): + __slots__ = ["rowx", "colx", "xf_idx", "number"] + + def __init__(self, rowx, colx, xf_idx, error_string_or_code): + self.rowx = rowx + self.colx = colx + self.xf_idx = xf_idx + try: + self.number = error_code_map[error_string_or_code] + except KeyError: + raise Exception('Illegal error value (%r)' % error_string_or_code) + + def get_biff_data(self): + return BIFFRecords.BoolErrRecord(self.rowx, + self.colx, self.xf_idx, self.number, 1).get() + +class FormulaCell(object): + __slots__ = ["rowx", "colx", "xf_idx", "frmla", "calc_flags"] + + def __init__(self, rowx, colx, xf_idx, frmla, calc_flags=0): + self.rowx = rowx + self.colx = colx + self.xf_idx = xf_idx + self.frmla = frmla + self.calc_flags = calc_flags + + def get_biff_data(self): + return BIFFRecords.FormulaRecord(self.rowx, + self.colx, self.xf_idx, self.frmla.rpn(), self.calc_flags).get() + +# module-level function for *internal* use by the Row module + +def _get_cells_biff_data_mul(rowx, cell_items): + # Return the BIFF data for all cell records in the row. + # Adjacent BLANK|RK records are combined into MUL(BLANK|RK) records. + pieces = [] + nitems = len(cell_items) + i = 0 + while i < nitems: + icolx, icell = cell_items[i] + if isinstance(icell, NumberCell): + isRK, value = icell.get_encoded_data() + if not isRK: + pieces.append(value) # pre-packed NUMBER record + i += 1 + continue + muldata = [(value, icell.xf_idx)] + target = NumberCell + elif isinstance(icell, BlankCell): + muldata = [icell.xf_idx] + target = BlankCell + else: + pieces.append(icell.get_biff_data()) + i += 1 + continue + lastcolx = icolx + j = i + packed_record = '' + for j in xrange(i+1, nitems): + jcolx, jcell = cell_items[j] + if jcolx != lastcolx + 1: + nexti = j + break + if not isinstance(jcell, target): + nexti = j + break + if target == NumberCell: + isRK, value = jcell.get_encoded_data() + if not isRK: + packed_record = value + nexti = j + 1 + break + muldata.append((value, jcell.xf_idx)) + else: + muldata.append(jcell.xf_idx) + lastcolx = jcolx + else: + nexti = j + 1 + if target == NumberCell: + if lastcolx == icolx: + # RK record + value, xf_idx = muldata[0] + pieces.append(pack('<5Hi', 0x027E, 10, rowx, icolx, xf_idx, value)) + else: + # MULRK record + nc = lastcolx - icolx + 1 + pieces.append(pack('<4H', 0x00BD, 6 * nc + 6, rowx, icolx)) + pieces.append(b''.join(pack('<Hi', xf_idx, value) for value, xf_idx in muldata)) + pieces.append(pack('<H', lastcolx)) + else: + if lastcolx == icolx: + # BLANK record + xf_idx = muldata[0] + pieces.append(pack('<5H', 0x0201, 6, rowx, icolx, xf_idx)) + else: + # MULBLANK record + nc = lastcolx - icolx + 1 + pieces.append(pack('<4H', 0x00BE, 2 * nc + 6, rowx, icolx)) + pieces.append(b''.join(pack('<H', xf_idx) for xf_idx in muldata)) + pieces.append(pack('<H', lastcolx)) + if packed_record: + pieces.append(packed_record) + i = nexti + return b''.join(pieces) + diff --git a/.venv/lib/python3.9/site-packages/xlwt/Column.py b/.venv/lib/python3.9/site-packages/xlwt/Column.py new file mode 100644 index 00000000..15f64e99 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Column.py @@ -0,0 +1,49 @@ +# -*- coding: windows-1252 -*- + +from .BIFFRecords import ColInfoRecord + +class Column(object): + def __init__(self, colx, parent_sheet): + if not(isinstance(colx, int) and 0 <= colx <= 255): + raise ValueError("column index (%r) not an int in range(256)" % colx) + self._index = colx + self._parent = parent_sheet + self._parent_wb = parent_sheet.get_parent() + self._xf_index = 0x0F + + self.width = 0x0B92 + self.hidden = 0 + self.level = 0 + self.collapse = 0 + self.user_set = 0 + self.best_fit = 0 + self.unused = 0 + + def set_width(self, width): + if not(isinstance(width, int) and 0 <= width <= 65535): + raise ValueError("column width (%r) not an int in range(65536)" % width) + self._width = width + + def get_width(self): + return self._width + + width = property(get_width, set_width) + + def set_style(self, style): + self._xf_index = self._parent_wb.add_style(style) + + def width_in_pixels(self): + # *** Approximation **** + return int(round(self.width * 0.0272 + 0.446, 0)) + + def get_biff_record(self): + options = (self.hidden & 0x01) << 0 + options |= (self.user_set & 0x01) << 1 + options |= (self.best_fit & 0x01) << 2 + options |= (self.level & 0x07) << 8 + options |= (self.collapse & 0x01) << 12 + + return ColInfoRecord(self._index, self._index, self.width, self._xf_index, options, self.unused).get() + + + diff --git a/.venv/lib/python3.9/site-packages/xlwt/CompoundDoc.py b/.venv/lib/python3.9/site-packages/xlwt/CompoundDoc.py new file mode 100644 index 00000000..379835d4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/CompoundDoc.py @@ -0,0 +1,283 @@ +# -*- coding: windows-1252 -*- + +import struct +from .compat import xrange + +# This implementation writes only 'Root Entry', 'Workbook' streams +# and 2 empty streams for aligning directory stream on sector boundary +# +# LAYOUT: +# 0 header +# 76 MSAT (1st part: 109 SID) +# 512 workbook stream +# ... additional MSAT sectors if streams' size > about 7 Mb == (109*512 * 128) +# ... SAT +# ... directory stream +# +# NOTE: this layout is "ad hoc". It can be more general. RTFM + +class XlsDoc: + SECTOR_SIZE = 0x0200 + MIN_LIMIT = 0x1000 + + SID_FREE_SECTOR = -1 + SID_END_OF_CHAIN = -2 + SID_USED_BY_SAT = -3 + SID_USED_BY_MSAT = -4 + + def __init__(self): + #self.book_stream = '' # padded + self.book_stream_sect = [] + + self.dir_stream = '' + self.dir_stream_sect = [] + + self.packed_SAT = '' + self.SAT_sect = [] + + self.packed_MSAT_1st = '' + self.packed_MSAT_2nd = '' + self.MSAT_sect_2nd = [] + + self.header = '' + + def _build_directory(self): # align on sector boundary + self.dir_stream = b'' + + dentry_name = u'Root Entry\x00'.encode('utf-16-le') + dentry_name_sz = len(dentry_name) + dentry_name_pad = b'\x00'*(64 - dentry_name_sz) + dentry_type = 0x05 # root storage + dentry_colour = 0x01 # black + dentry_did_left = -1 + dentry_did_right = -1 + dentry_did_root = 1 + dentry_start_sid = -2 + dentry_stream_sz = 0 + + self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L', + dentry_name + dentry_name_pad, + dentry_name_sz, + dentry_type, + dentry_colour, + dentry_did_left, + dentry_did_right, + dentry_did_root, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + dentry_start_sid, + dentry_stream_sz, + 0 + ) + + dentry_name = u'Workbook\x00'.encode('utf-16-le') + dentry_name_sz = len(dentry_name) + dentry_name_pad = b'\x00'*(64 - dentry_name_sz) + dentry_type = 0x02 # user stream + dentry_colour = 0x01 # black + dentry_did_left = -1 + dentry_did_right = -1 + dentry_did_root = -1 + dentry_start_sid = 0 + dentry_stream_sz = self.book_stream_len + + self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L', + dentry_name + dentry_name_pad, + dentry_name_sz, + dentry_type, + dentry_colour, + dentry_did_left, + dentry_did_right, + dentry_did_root, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + dentry_start_sid, + dentry_stream_sz, + 0 + ) + + # padding + dentry_name = b'' + dentry_name_sz = len(dentry_name) + dentry_name_pad = b'\x00'*(64 - dentry_name_sz) + dentry_type = 0x00 # empty + dentry_colour = 0x01 # black + dentry_did_left = -1 + dentry_did_right = -1 + dentry_did_root = -1 + dentry_start_sid = -2 + dentry_stream_sz = 0 + + self.dir_stream += struct.pack('<64s H 2B 3l 9L l L L', + dentry_name + dentry_name_pad, + dentry_name_sz, + dentry_type, + dentry_colour, + dentry_did_left, + dentry_did_right, + dentry_did_root, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + dentry_start_sid, + dentry_stream_sz, + 0 + ) * 2 + + def _build_sat(self): + # Build SAT + book_sect_count = self.book_stream_len >> 9 + dir_sect_count = len(self.dir_stream) >> 9 + + total_sect_count = book_sect_count + dir_sect_count + SAT_sect_count = 0 + MSAT_sect_count = 0 + SAT_sect_count_limit = 109 + while total_sect_count > 128*SAT_sect_count or SAT_sect_count > SAT_sect_count_limit: + SAT_sect_count += 1 + total_sect_count += 1 + if SAT_sect_count > SAT_sect_count_limit: + MSAT_sect_count += 1 + total_sect_count += 1 + SAT_sect_count_limit += 127 + + + SAT = [self.SID_FREE_SECTOR]*128*SAT_sect_count + + sect = 0 + while sect < book_sect_count - 1: + self.book_stream_sect.append(sect) + SAT[sect] = sect + 1 + sect += 1 + self.book_stream_sect.append(sect) + SAT[sect] = self.SID_END_OF_CHAIN + sect += 1 + + while sect < book_sect_count + MSAT_sect_count: + self.MSAT_sect_2nd.append(sect) + SAT[sect] = self.SID_USED_BY_MSAT + sect += 1 + + while sect < book_sect_count + MSAT_sect_count + SAT_sect_count: + self.SAT_sect.append(sect) + SAT[sect] = self.SID_USED_BY_SAT + sect += 1 + + while sect < book_sect_count + MSAT_sect_count + SAT_sect_count + dir_sect_count - 1: + self.dir_stream_sect.append(sect) + SAT[sect] = sect + 1 + sect += 1 + self.dir_stream_sect.append(sect) + SAT[sect] = self.SID_END_OF_CHAIN + sect += 1 + + self.packed_SAT = struct.pack('<%dl' % (SAT_sect_count*128), *SAT) + + MSAT_1st = [self.SID_FREE_SECTOR]*109 + for i, SAT_sect_num in zip(range(0, 109), self.SAT_sect): + MSAT_1st[i] = SAT_sect_num + self.packed_MSAT_1st = struct.pack('<109l', *MSAT_1st) + + MSAT_2nd = [self.SID_FREE_SECTOR]*128*MSAT_sect_count + if MSAT_sect_count > 0: + MSAT_2nd[- 1] = self.SID_END_OF_CHAIN + + i = 109 + msat_sect = 0 + sid_num = 0 + while i < SAT_sect_count: + if (sid_num + 1) % 128 == 0: + #print 'link: ', + msat_sect += 1 + if msat_sect < len(self.MSAT_sect_2nd): + MSAT_2nd[sid_num] = self.MSAT_sect_2nd[msat_sect] + else: + #print 'sid: ', + MSAT_2nd[sid_num] = self.SAT_sect[i] + i += 1 + #print sid_num, MSAT_2nd[sid_num] + sid_num += 1 + + self.packed_MSAT_2nd = struct.pack('<%dl' % (MSAT_sect_count*128), *MSAT_2nd) + + #print vars() + #print zip(range(0, sect), SAT) + #print self.book_stream_sect + #print self.MSAT_sect_2nd + #print MSAT_2nd + #print self.SAT_sect + #print self.dir_stream_sect + + + def _build_header(self): + doc_magic = b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1' + file_uid = b'\x00'*16 + rev_num = b'\x3E\x00' + ver_num = b'\x03\x00' + byte_order = b'\xFE\xFF' + log_sect_size = struct.pack('<H', 9) + log_short_sect_size = struct.pack('<H', 6) + not_used0 = b'\x00'*10 + total_sat_sectors = struct.pack('<L', len(self.SAT_sect)) + dir_start_sid = struct.pack('<l', self.dir_stream_sect[0]) + not_used1 = b'\x00'*4 + min_stream_size = struct.pack('<L', 0x1000) + ssat_start_sid = struct.pack('<l', -2) + total_ssat_sectors = struct.pack('<L', 0) + + if len(self.MSAT_sect_2nd) == 0: + msat_start_sid = struct.pack('<l', -2) + else: + msat_start_sid = struct.pack('<l', self.MSAT_sect_2nd[0]) + + total_msat_sectors = struct.pack('<L', len(self.MSAT_sect_2nd)) + + self.header = b''.join([ doc_magic, + file_uid, + rev_num, + ver_num, + byte_order, + log_sect_size, + log_short_sect_size, + not_used0, + total_sat_sectors, + dir_start_sid, + not_used1, + min_stream_size, + ssat_start_sid, + total_ssat_sectors, + msat_start_sid, + total_msat_sectors + ]) + + + def save(self, file_name_or_filelike_obj, stream): + # 1. Align stream on 0x1000 boundary (and therefore on sector boundary) + padding = b'\x00' * (0x1000 - (len(stream) % 0x1000)) + self.book_stream_len = len(stream) + len(padding) + + self._build_directory() + self._build_sat() + self._build_header() + + f = file_name_or_filelike_obj + we_own_it = not hasattr(f, 'write') + if we_own_it: + f = open(file_name_or_filelike_obj, 'w+b') + f.write(self.header) + f.write(self.packed_MSAT_1st) + # There are reports of large writes failing when writing to "network shares" on Windows. + # MS says in KB899149 that it happens at 32KB less than 64MB. + # This is said to be alleviated by using "w+b" mode instead of "wb". + # One xlwt user has reported anomalous results at much smaller sizes, + # The fallback is to write the stream in 4 MB chunks. + try: + f.write(stream) + except IOError as e: + if e.errno != 22: # "Invalid argument" i.e. 'stream' is too big + raise # some other problem + chunk_size = 4 * 1024 * 1024 + for offset in xrange(0, len(stream), chunk_size): + f.write(buffer(stream, offset, chunk_size)) + f.write(padding) + f.write(self.packed_MSAT_2nd) + f.write(self.packed_SAT) + f.write(self.dir_stream) + if we_own_it: + f.close() diff --git a/.venv/lib/python3.9/site-packages/xlwt/ExcelFormula.py b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormula.py new file mode 100644 index 00000000..ac701477 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormula.py @@ -0,0 +1,43 @@ +# -*- coding: windows-1252 -*- + +from . import ExcelFormulaParser, ExcelFormulaLexer +import struct +from .antlr import ANTLRException + + +class Formula(object): + __slots__ = ["__s", "__parser", "__sheet_refs", "__xcall_refs"] + + + def __init__(self, s): + try: + self.__s = s + lexer = ExcelFormulaLexer.Lexer(s) + self.__parser = ExcelFormulaParser.Parser(lexer) + self.__parser.formula() + self.__sheet_refs = self.__parser.sheet_references + self.__xcall_refs = self.__parser.xcall_references + except ANTLRException as e: + # print e + raise ExcelFormulaParser.FormulaParseException("can't parse formula " + s) + + def get_references(self): + return self.__sheet_refs, self.__xcall_refs + + def patch_references(self, patches): + for offset, idx in patches: + self.__parser.rpn = self.__parser.rpn[:offset] + struct.pack('<H', idx) + self.__parser.rpn[offset+2:] + + def text(self): + return self.__s + + def rpn(self): + ''' + Offset Size Contents + 0 2 Size of the following formula data (sz) + 2 sz Formula data (RPN token array) + [2+sz] var. (optional) Additional data for specific tokens + + ''' + return struct.pack("<H", len(self.__parser.rpn)) + self.__parser.rpn + diff --git a/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaLexer.py b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaLexer.py new file mode 100644 index 00000000..3acf665d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaLexer.py @@ -0,0 +1,127 @@ +from __future__ import print_function +# -*- coding: windows-1252 -*- + +from .antlr import EOF, CommonToken as Tok, TokenStream, TokenStreamException +from . import ExcelFormulaParser +from re import compile as recompile, IGNORECASE, VERBOSE + + +int_const_pattern = r"\d+\b" +flt_const_pattern = r""" + (?: + (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc + | + (?: \d+ \. ) # 1. 12. 123. etc + ) + # followed by optional exponent part + (?: [Ee] [+-]? \d+ ) ? + """ +str_const_pattern = r'"(?:[^"]|"")*"' +#range2d_pattern = recompile(r"\$?[A-I]?[A-Z]\$?\d+:\$?[A-I]?[A-Z]\$?\d+" +ref2d_r1c1_pattern = r"[Rr]0*[1-9][0-9]*[Cc]0*[1-9][0-9]*" +ref2d_pattern = r"\$?[A-I]?[A-Z]\$?0*[1-9][0-9]*" +true_pattern = r"TRUE\b" +false_pattern = r"FALSE\b" +if_pattern = r"IF\b" +choose_pattern = r"CHOOSE\b" +name_pattern = r"\w[\.\w]*" +quotename_pattern = r"'(?:[^']|'')*'" #### It's essential that this bracket be non-grouping. +ne_pattern = r"<>" +ge_pattern = r">=" +le_pattern = r"<=" + +pattern_type_tuples = ( + (flt_const_pattern, ExcelFormulaParser.NUM_CONST), + (int_const_pattern, ExcelFormulaParser.INT_CONST), + (str_const_pattern, ExcelFormulaParser.STR_CONST), +# (range2d_pattern , ExcelFormulaParser.RANGE2D), + (ref2d_r1c1_pattern, ExcelFormulaParser.REF2D_R1C1), + (ref2d_pattern , ExcelFormulaParser.REF2D), + (true_pattern , ExcelFormulaParser.TRUE_CONST), + (false_pattern , ExcelFormulaParser.FALSE_CONST), + (if_pattern , ExcelFormulaParser.FUNC_IF), + (choose_pattern , ExcelFormulaParser.FUNC_CHOOSE), + (name_pattern , ExcelFormulaParser.NAME), + (quotename_pattern, ExcelFormulaParser.QUOTENAME), + (ne_pattern, ExcelFormulaParser.NE), + (ge_pattern, ExcelFormulaParser.GE), + (le_pattern, ExcelFormulaParser.LE), +) + +_re = recompile( + '(' + ')|('.join(i[0] for i in pattern_type_tuples) + ')', + VERBOSE+IGNORECASE) + +_toktype = [None] + [i[1] for i in pattern_type_tuples] +# need dummy at start because re.MatchObject.lastindex counts from 1 + +single_char_lookup = { + '=': ExcelFormulaParser.EQ, + '<': ExcelFormulaParser.LT, + '>': ExcelFormulaParser.GT, + '+': ExcelFormulaParser.ADD, + '-': ExcelFormulaParser.SUB, + '*': ExcelFormulaParser.MUL, + '/': ExcelFormulaParser.DIV, + ':': ExcelFormulaParser.COLON, + ';': ExcelFormulaParser.SEMICOLON, + ',': ExcelFormulaParser.COMMA, + '(': ExcelFormulaParser.LP, + ')': ExcelFormulaParser.RP, + '&': ExcelFormulaParser.CONCAT, + '%': ExcelFormulaParser.PERCENT, + '^': ExcelFormulaParser.POWER, + '!': ExcelFormulaParser.BANG, + } + +class Lexer(TokenStream): + def __init__(self, text): + self._text = text[:] + self._pos = 0 + self._line = 0 + + def isEOF(self): + return len(self._text) <= self._pos + + def curr_ch(self): + return self._text[self._pos] + + def next_ch(self, n = 1): + self._pos += n + + def is_whitespace(self): + return self.curr_ch() in " \t\n\r\f\v" + + def match_pattern(self): + m = _re.match(self._text, self._pos) + if not m: + return None + self._pos = m.end(0) + return Tok(type = _toktype[m.lastindex], text = m.group(0), col = m.start(0) + 1) + + def nextToken(self): + # skip whitespace + while not self.isEOF() and self.is_whitespace(): + self.next_ch() + if self.isEOF(): + return Tok(type = EOF) + # first, try to match token with 2 or more chars + t = self.match_pattern() + if t: + return t + # second, we want 1-char tokens + te = self.curr_ch() + try: + ty = single_char_lookup[te] + except KeyError: + raise TokenStreamException( + "Unexpected char %r in column %u." % (self.curr_ch(), self._pos)) + self.next_ch() + return Tok(type=ty, text=te, col=self._pos) + +if __name__ == '__main__': + try: + for t in Lexer(""" 1.23 456 "abcd" R2C2 a1 iv65536 true false if choose a_name 'qname' <> >= <= """): + print(t) + except TokenStreamException as e: + print("error:", e) diff --git a/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py new file mode 100644 index 00000000..ece64e6d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/ExcelFormulaParser.py @@ -0,0 +1,669 @@ +### $ANTLR 2.7.7 (20060930): "xlwt/excel-formula.g" -> "ExcelFormulaParser.py"$ +### import antlr and other modules .. +from . import antlr + +### header action >>> +import struct +from . import Utils +from .UnicodeUtils import upack1 +from .ExcelMagic import * + +_RVAdelta = {"R": 0, "V": 0x20, "A": 0x40} +_RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20} +_RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0} + + +class FormulaParseException(Exception): + """ + An exception indicating that a Formula could not be successfully parsed. + """ +### header action <<< +### preamble action>>> + +### preamble action <<< + +### >>>The Known Token Types <<< +SKIP = antlr.SKIP +INVALID_TYPE = antlr.INVALID_TYPE +EOF_TYPE = antlr.EOF_TYPE +EOF = antlr.EOF +NULL_TREE_LOOKAHEAD = antlr.NULL_TREE_LOOKAHEAD +MIN_USER_TYPE = antlr.MIN_USER_TYPE +TRUE_CONST = 4 +FALSE_CONST = 5 +STR_CONST = 6 +NUM_CONST = 7 +INT_CONST = 8 +FUNC_IF = 9 +FUNC_CHOOSE = 10 +NAME = 11 +QUOTENAME = 12 +EQ = 13 +NE = 14 +GT = 15 +LT = 16 +GE = 17 +LE = 18 +ADD = 19 +SUB = 20 +MUL = 21 +DIV = 22 +POWER = 23 +PERCENT = 24 +LP = 25 +RP = 26 +LB = 27 +RB = 28 +COLON = 29 +COMMA = 30 +SEMICOLON = 31 +REF2D = 32 +REF2D_R1C1 = 33 +BANG = 34 +CONCAT = 35 + +class Parser(antlr.LLkParser): + ### user action >>> + ### user action <<< + + def __init__(self, *args, **kwargs): + antlr.LLkParser.__init__(self, *args, **kwargs) + self.tokenNames = _tokenNames + ### __init__ header action >>> + self.rpn = b"" + self.sheet_references = [] + self.xcall_references = [] + ### __init__ header action <<< + + def formula(self): + + pass + self.expr("V") + + def expr(self, + arg_type + ): + + pass + self.prec0_expr(arg_type) + while True: + if ((self.LA(1) >= EQ and self.LA(1) <= LE)): + pass + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [EQ]: + pass + self.match(EQ) + op = struct.pack('B', ptgEQ) + elif la1 and la1 in [NE]: + pass + self.match(NE) + op = struct.pack('B', ptgNE) + elif la1 and la1 in [GT]: + pass + self.match(GT) + op = struct.pack('B', ptgGT) + elif la1 and la1 in [LT]: + pass + self.match(LT) + op = struct.pack('B', ptgLT) + elif la1 and la1 in [GE]: + pass + self.match(GE) + op = struct.pack('B', ptgGE) + elif la1 and la1 in [LE]: + pass + self.match(LE) + op = struct.pack('B', ptgLE) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.prec0_expr(arg_type) + self.rpn += op + else: + break + + + def prec0_expr(self, + arg_type + ): + + pass + self.prec1_expr(arg_type) + while True: + if (self.LA(1)==CONCAT): + pass + pass + self.match(CONCAT) + op = struct.pack('B', ptgConcat) + self.prec1_expr(arg_type) + self.rpn += op + else: + break + + + def prec1_expr(self, + arg_type + ): + + pass + self.prec2_expr(arg_type) + while True: + if (self.LA(1)==ADD or self.LA(1)==SUB): + pass + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [ADD]: + pass + self.match(ADD) + op = struct.pack('B', ptgAdd) + elif la1 and la1 in [SUB]: + pass + self.match(SUB) + op = struct.pack('B', ptgSub) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.prec2_expr(arg_type) + self.rpn += op; + # print "**prec1_expr4 %s" % arg_type + else: + break + + + def prec2_expr(self, + arg_type + ): + + pass + self.prec3_expr(arg_type) + while True: + if (self.LA(1)==MUL or self.LA(1)==DIV): + pass + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [MUL]: + pass + self.match(MUL) + op = struct.pack('B', ptgMul) + elif la1 and la1 in [DIV]: + pass + self.match(DIV) + op = struct.pack('B', ptgDiv) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.prec3_expr(arg_type) + self.rpn += op + else: + break + + + def prec3_expr(self, + arg_type + ): + + pass + self.prec4_expr(arg_type) + while True: + if (self.LA(1)==POWER): + pass + pass + self.match(POWER) + op = struct.pack('B', ptgPower) + self.prec4_expr(arg_type) + self.rpn += op + else: + break + + + def prec4_expr(self, + arg_type + ): + + pass + self.prec5_expr(arg_type) + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [PERCENT]: + pass + self.match(PERCENT) + self.rpn += struct.pack('B', ptgPercent) + elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,RP,COMMA,SEMICOLON,CONCAT]: + pass + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + + def prec5_expr(self, + arg_type + ): + + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,LP,REF2D]: + pass + self.primary(arg_type) + elif la1 and la1 in [SUB]: + pass + self.match(SUB) + self.primary(arg_type) + self.rpn += struct.pack('B', ptgUminus) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + + def primary(self, + arg_type + ): + + str_tok = None + int_tok = None + num_tok = None + ref2d_tok = None + ref2d1_tok = None + ref2d2_tok = None + ref3d_ref2d = None + ref3d_ref2d2 = None + name_tok = None + func_tok = None + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [TRUE_CONST]: + pass + self.match(TRUE_CONST) + self.rpn += struct.pack("2B", ptgBool, 1) + elif la1 and la1 in [FALSE_CONST]: + pass + self.match(FALSE_CONST) + self.rpn += struct.pack("2B", ptgBool, 0) + elif la1 and la1 in [STR_CONST]: + pass + str_tok = self.LT(1) + self.match(STR_CONST) + self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\"")) + elif la1 and la1 in [NUM_CONST]: + pass + num_tok = self.LT(1) + self.match(NUM_CONST) + self.rpn += struct.pack("<Bd", ptgNum, float(num_tok.text)) + elif la1 and la1 in [FUNC_IF]: + pass + self.match(FUNC_IF) + self.match(LP) + self.expr("V") + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [SEMICOLON]: + pass + self.match(SEMICOLON) + elif la1 and la1 in [COMMA]: + pass + self.match(COMMA) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.rpn += struct.pack("<BBH", ptgAttr, 0x02, 0) # tAttrIf + pos0 = len(self.rpn) - 2 + self.expr(arg_type) + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [SEMICOLON]: + pass + self.match(SEMICOLON) + elif la1 and la1 in [COMMA]: + pass + self.match(COMMA) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 0) # tAttrSkip + pos1 = len(self.rpn) - 2 + self.rpn = self.rpn[:pos0] + struct.pack("<H", pos1-pos0) + self.rpn[pos0+2:] + self.expr(arg_type) + self.match(RP) + self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 3) # tAttrSkip + self.rpn += struct.pack("<BBH", ptgFuncVarR, 3, 1) # 3 = nargs, 1 = IF func + pos2 = len(self.rpn) + self.rpn = self.rpn[:pos1] + struct.pack("<H", pos2-(pos1+2)-1) + self.rpn[pos1+2:] + elif la1 and la1 in [FUNC_CHOOSE]: + pass + self.match(FUNC_CHOOSE) + arg_type = "R" + rpn_chunks = [] + self.match(LP) + self.expr("V") + rpn_start = len(self.rpn) + ref_markers = [len(self.sheet_references)] + while True: + if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON): + pass + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [SEMICOLON]: + pass + self.match(SEMICOLON) + elif la1 and la1 in [COMMA]: + pass + self.match(COMMA) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + mark = len(self.rpn) + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]: + pass + self.expr(arg_type) + elif la1 and la1 in [RP,COMMA,SEMICOLON]: + pass + self.rpn += struct.pack("B", ptgMissArg) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + rpn_chunks.append(self.rpn[mark:]) + ref_markers.append(len(self.sheet_references)) + else: + break + + self.match(RP) + self.rpn = self.rpn[:rpn_start] + nc = len(rpn_chunks) + chunklens = [len(chunk) for chunk in rpn_chunks] + skiplens = [0] * nc + skiplens[-1] = 3 + for ic in xrange(nc-1, 0, -1): + skiplens[ic-1] = skiplens[ic] + chunklens[ic] + 4 + jump_pos = [2 * nc + 2] + for ic in xrange(nc): + jump_pos.append(jump_pos[-1] + chunklens[ic] + 4) + chunk_shift = 2 * nc + 6 # size of tAttrChoose + for ic in xrange(nc): + for refx in xrange(ref_markers[ic], ref_markers[ic+1]): + ref = self.sheet_references[refx] + self.sheet_references[refx] = (ref[0], ref[1], ref[2] + chunk_shift) + chunk_shift += 4 # size of tAttrSkip + choose_rpn = [] + choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x04, nc)) # 0x04 is tAttrChoose + choose_rpn.append(struct.pack("<%dH" % (nc+1), *jump_pos)) + for ic in xrange(nc): + choose_rpn.append(rpn_chunks[ic]) + choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x08, skiplens[ic])) # 0x08 is tAttrSkip + choose_rpn.append(struct.pack("<BBH", ptgFuncVarV, nc+1, 100)) # 100 is CHOOSE fn + self.rpn += "".join(choose_rpn) + elif la1 and la1 in [LP]: + pass + self.match(LP) + self.expr(arg_type) + self.match(RP) + self.rpn += struct.pack("B", ptgParen) + else: + if (self.LA(1)==INT_CONST) and (_tokenSet_0.member(self.LA(2))): + pass + int_tok = self.LT(1) + self.match(INT_CONST) + # print "**int_const", int_tok.text + int_value = int(int_tok.text) + if int_value <= 65535: + self.rpn += struct.pack("<BH", ptgInt, int_value) + else: + self.rpn += struct.pack("<Bd", ptgNum, float(int_value)) + elif (self.LA(1)==REF2D) and (_tokenSet_0.member(self.LA(2))): + pass + ref2d_tok = self.LT(1) + self.match(REF2D) + # print "**ref2d %s %s" % (ref2d_tok.text, arg_type) + r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text) + ptg = ptgRefR + _RVAdeltaRef[arg_type] + self.rpn += struct.pack("<B2H", ptg, r, c) + elif (self.LA(1)==REF2D) and (self.LA(2)==COLON): + pass + ref2d1_tok = self.LT(1) + self.match(REF2D) + self.match(COLON) + ref2d2_tok = self.LT(1) + self.match(REF2D) + r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text) + r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text) + ptg = ptgAreaR + _RVAdeltaArea[arg_type] + self.rpn += struct.pack("<B4H", ptg, r1, r2, c1, c2) + elif (self.LA(1)==INT_CONST or self.LA(1)==NAME or self.LA(1)==QUOTENAME) and (self.LA(2)==COLON or self.LA(2)==BANG): + pass + sheet1=self.sheet() + sheet2 = sheet1 + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [COLON]: + pass + self.match(COLON) + sheet2=self.sheet() + elif la1 and la1 in [BANG]: + pass + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.match(BANG) + ref3d_ref2d = self.LT(1) + self.match(REF2D) + ptg = ptgRef3dR + _RVAdeltaRef[arg_type] + rpn_ref2d = "" + r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text) + rpn_ref2d = struct.pack("<3H", 0x0000, r1, c1) + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [COLON]: + pass + self.match(COLON) + ref3d_ref2d2 = self.LT(1) + self.match(REF2D) + ptg = ptgArea3dR + _RVAdeltaArea[arg_type] + r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text) + rpn_ref2d = struct.pack("<5H", 0x0000, r1, r2, c1, c2) + elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,PERCENT,RP,COMMA,SEMICOLON,CONCAT]: + pass + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + self.rpn += struct.pack("<B", ptg) + self.sheet_references.append((sheet1, sheet2, len(self.rpn))) + self.rpn += rpn_ref2d + elif (self.LA(1)==NAME) and (_tokenSet_0.member(self.LA(2))): + pass + name_tok = self.LT(1) + self.match(NAME) + raise Exception("[formula] found unexpected NAME token (%r)" % name_tok.txt) + # #### TODO: handle references to defined names here + elif (self.LA(1)==NAME) and (self.LA(2)==LP): + pass + func_tok = self.LT(1) + self.match(NAME) + func_toku = func_tok.text.upper() + if func_toku in all_funcs_by_name: + (opcode, + min_argc, + max_argc, + func_type, + arg_type_str) = all_funcs_by_name[func_toku] + arg_type_list = list(arg_type_str) + else: + raise Exception("[formula] unknown function (%s)" % func_tok.text) + # print "**func_tok1 %s %s" % (func_toku, func_type) + xcall = opcode < 0 + if xcall: + # The name of the add-in function is passed as the 1st arg + # of the hidden XCALL function + self.xcall_references.append((func_toku, len(self.rpn) + 1)) + self.rpn += struct.pack("<BHHH", + ptgNameXR, + 0xadde, # ##PATCHME## index to REF entry in EXTERNSHEET record + 0xefbe, # ##PATCHME## one-based index to EXTERNNAME record + 0x0000) # unused + self.match(LP) + arg_count=self.expr_list(arg_type_list, min_argc, max_argc) + self.match(RP) + if arg_count > max_argc or arg_count < min_argc: + raise Exception("%d parameters for function: %s" % (arg_count, func_tok.text)) + if xcall: + func_ptg = ptgFuncVarR + _RVAdelta[func_type] + self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function + elif min_argc == max_argc: + func_ptg = ptgFuncR + _RVAdelta[func_type] + self.rpn += struct.pack("<BH", func_ptg, opcode) + elif arg_count == 1 and func_tok.text.upper() == "SUM": + self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) # tAttrSum + else: + func_ptg = ptgFuncVarR + _RVAdelta[func_type] + self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + + def sheet(self): + ref = None + + sheet_ref_name = None + sheet_ref_int = None + sheet_ref_quote = None + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [NAME]: + pass + sheet_ref_name = self.LT(1) + self.match(NAME) + ref = sheet_ref_name.text + elif la1 and la1 in [INT_CONST]: + pass + sheet_ref_int = self.LT(1) + self.match(INT_CONST) + ref = sheet_ref_int.text + elif la1 and la1 in [QUOTENAME]: + pass + sheet_ref_quote = self.LT(1) + self.match(QUOTENAME) + ref = sheet_ref_quote.text[1:-1].replace("''", "'") + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + return ref + + def expr_list(self, + arg_type_list, min_argc, max_argc + ): + arg_cnt = None + + arg_cnt = 0 + arg_type = arg_type_list[arg_cnt] + # print "**expr_list1[%d] req=%s" % (arg_cnt, arg_type) + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]: + pass + self.expr(arg_type) + arg_cnt += 1 + while True: + if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON): + pass + if arg_cnt < len(arg_type_list): + arg_type = arg_type_list[arg_cnt] + else: + arg_type = arg_type_list[-1] + if arg_type == "+": + arg_type = arg_type_list[-2] + # print "**expr_list2[%d] req=%s" % (arg_cnt, arg_type) + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [SEMICOLON]: + pass + self.match(SEMICOLON) + elif la1 and la1 in [COMMA]: + pass + self.match(COMMA) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + la1 = self.LA(1) + if False: + pass + elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]: + pass + self.expr(arg_type) + elif la1 and la1 in [RP,COMMA,SEMICOLON]: + pass + self.rpn += struct.pack("B", ptgMissArg) + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + arg_cnt += 1 + else: + break + + elif la1 and la1 in [RP]: + pass + else: + raise antlr.NoViableAltException(self.LT(1), self.getFilename()) + + return arg_cnt + + +_tokenNames = [ + "<0>", + "EOF", + "<2>", + "NULL_TREE_LOOKAHEAD", + "TRUE_CONST", + "FALSE_CONST", + "STR_CONST", + "NUM_CONST", + "INT_CONST", + "FUNC_IF", + "FUNC_CHOOSE", + "NAME", + "QUOTENAME", + "EQ", + "NE", + "GT", + "LT", + "GE", + "LE", + "ADD", + "SUB", + "MUL", + "DIV", + "POWER", + "PERCENT", + "LP", + "RP", + "LB", + "RB", + "COLON", + "COMMA", + "SEMICOLON", + "REF2D", + "REF2D_R1C1", + "BANG", + "CONCAT" +] + + +### generate bit set +def mk_tokenSet_0(): + ### var1 + data = [ 37681618946, 0] + return data +_tokenSet_0 = antlr.BitSet(mk_tokenSet_0()) + diff --git a/.venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py b/.venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py new file mode 100644 index 00000000..2c292621 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/ExcelMagic.py @@ -0,0 +1,862 @@ +# -*- coding: ascii -*- +""" +lots of Excel Magic Numbers +""" + +# Boundaries BIFF8+ + +MAX_ROW = 65536 +MAX_COL = 256 + + +biff_records = { + 0x0000: "DIMENSIONS", + 0x0001: "BLANK", + 0x0002: "INTEGER", + 0x0003: "NUMBER", + 0x0004: "LABEL", + 0x0005: "BOOLERR", + 0x0006: "FORMULA", + 0x0007: "STRING", + 0x0008: "ROW", + 0x0009: "BOF", + 0x000A: "EOF", + 0x000B: "INDEX", + 0x000C: "CALCCOUNT", + 0x000D: "CALCMODE", + 0x000E: "PRECISION", + 0x000F: "REFMODE", + 0x0010: "DELTA", + 0x0011: "ITERATION", + 0x0012: "PROTECT", + 0x0013: "PASSWORD", + 0x0014: "HEADER", + 0x0015: "FOOTER", + 0x0016: "EXTERNCOUNT", + 0x0017: "EXTERNSHEET", + 0x0018: "NAME", + 0x0019: "WINDOWPROTECT", + 0x001A: "VERTICALPAGEBREAKS", + 0x001B: "HORIZONTALPAGEBREAKS", + 0x001C: "NOTE", + 0x001D: "SELECTION", + 0x001E: "FORMAT", + 0x001F: "FORMATCOUNT", + 0x0020: "COLUMNDEFAULT", + 0x0021: "ARRAY", + 0x0022: "1904", + 0x0023: "EXTERNNAME", + 0x0024: "COLWIDTH", + 0x0025: "DEFAULTROWHEIGHT", + 0x0026: "LEFTMARGIN", + 0x0027: "RIGHTMARGIN", + 0x0028: "TOPMARGIN", + 0x0029: "BOTTOMMARGIN", + 0x002A: "PRINTHEADERS", + 0x002B: "PRINTGRIDLINES", + 0x002F: "FILEPASS", + 0x0031: "FONT", + 0x0036: "TABLE", + 0x003C: "CONTINUE", + 0x003D: "WINDOW1", + 0x003E: "WINDOW2", + 0x0040: "BACKUP", + 0x0041: "PANE", + 0x0042: "CODEPAGE", + 0x0043: "XF", + 0x0044: "IXFE", + 0x0045: "EFONT", + 0x004D: "PLS", + 0x0050: "DCON", + 0x0051: "DCONREF", + 0x0053: "DCONNAME", + 0x0055: "DEFCOLWIDTH", + 0x0056: "BUILTINFMTCNT", + 0x0059: "XCT", + 0x005A: "CRN", + 0x005B: "FILESHARING", + 0x005C: "WRITEACCESS", + 0x005D: "OBJ", + 0x005E: "UNCALCED", + 0x005F: "SAFERECALC", + 0x0060: "TEMPLATE", + 0x0063: "OBJPROTECT", + 0x007D: "COLINFO", + 0x007E: "RK", + 0x007F: "IMDATA", + 0x0080: "GUTS", + 0x0081: "WSBOOL", + 0x0082: "GRIDSET", + 0x0083: "HCENTER", + 0x0084: "VCENTER", + 0x0085: "BOUNDSHEET", + 0x0086: "WRITEPROT", + 0x0087: "ADDIN", + 0x0088: "EDG", + 0x0089: "PUB", + 0x008C: "COUNTRY", + 0x008D: "HIDEOBJ", + 0x008E: "BUNDLESOFFSET", + 0x008F: "BUNDLEHEADER", + 0x0090: "SORT", + 0x0091: "SUB", + 0x0092: "PALETTE", + 0x0093: "STYLE", + 0x0094: "LHRECORD", + 0x0095: "LHNGRAPH", + 0x0096: "SOUND", + 0x0098: "LPR", + 0x0099: "STANDARDWIDTH", + 0x009A: "FNGROUPNAME", + 0x009B: "FILTERMODE", + 0x009C: "FNGROUPCOUNT", + 0x009D: "AUTOFILTERINFO", + 0x009E: "AUTOFILTER", + 0x00A0: "SCL", + 0x00A1: "SETUP", + 0x00A9: "COORDLIST", + 0x00AB: "GCW", + 0x00AE: "SCENMAN", + 0x00AF: "SCENARIO", + 0x00B0: "SXVIEW", + 0x00B1: "SXVD", + 0x00B2: "SXVI", + 0x00B4: "SXIVD", + 0x00B5: "SXLI", + 0x00B6: "SXPI", + 0x00B8: "DOCROUTE", + 0x00B9: "RECIPNAME", + 0x00BC: "SHRFMLA", + 0x00BD: "MULRK", + 0x00BE: "MULBLANK", + 0x00C1: "MMS", + 0x00C2: "ADDMENU", + 0x00C3: "DELMENU", + 0x00C5: "SXDI", + 0x00C6: "SXDB", + 0x00C7: "SXFIELD", + 0x00C8: "SXINDEXLIST", + 0x00C9: "SXDOUBLE", + 0x00CD: "SXSTRING", + 0x00CE: "SXDATETIME", + 0x00D0: "SXTBL", + 0x00D1: "SXTBRGITEM", + 0x00D2: "SXTBPG", + 0x00D3: "OBPROJ", + 0x00D5: "SXIDSTM", + 0x00D6: "RSTRING", + 0x00D7: "DBCELL", + 0x00DA: "BOOKBOOL", + 0x00DC: "SXEXT|PARAMQRY", + 0x00DD: "SCENPROTECT", + 0x00DE: "OLESIZE", + 0x00DF: "UDDESC", + 0x00E0: "XF", + 0x00E1: "INTERFACEHDR", + 0x00E2: "INTERFACEEND", + 0x00E3: "SXVS", + 0x00E5: "MERGEDCELLS", + 0x00E9: "BITMAP", + 0x00EB: "MSODRAWINGGROUP", + 0x00EC: "MSODRAWING", + 0x00ED: "MSODRAWINGSELECTION", + 0x00F0: "SXRULE", + 0x00F1: "SXEX", + 0x00F2: "SXFILT", + 0x00F6: "SXNAME", + 0x00F7: "SXSELECT", + 0x00F8: "SXPAIR", + 0x00F9: "SXFMLA", + 0x00FB: "SXFORMAT", + 0x00FC: "SST", + 0x00FD: "LABELSST", + 0x00FF: "EXTSST", + 0x0100: "SXVDEX", + 0x0103: "SXFORMULA", + 0x0122: "SXDBEX", + 0x0137: "CHTRINSERT", + 0x0138: "CHTRINFO", + 0x013B: "CHTRCELLCONTENT", + 0x013D: "TABID", + 0x0140: "CHTRMOVERANGE", + 0x014D: "CHTRINSERTTAB", + 0x015F: "LABELRANGES", + 0x0160: "USESELFS", + 0x0161: "DSF", + 0x0162: "XL5MODIFY", + 0x0196: "CHTRHEADER", + 0x01A9: "USERBVIEW", + 0x01AA: "USERSVIEWBEGIN", + 0x01AB: "USERSVIEWEND", + 0x01AD: "QSI", + 0x01AE: "SUPBOOK", + 0x01AF: "PROT4REV", + 0x01B0: "CONDFMT", + 0x01B1: "CF", + 0x01B2: "DVAL", + 0x01B5: "DCONBIN", + 0x01B6: "TXO", + 0x01B7: "REFRESHALL", + 0x01B8: "HLINK", + 0x01BA: "CODENAME", + 0x01BB: "SXFDBTYPE", + 0x01BC: "PROT4REVPASS", + 0x01BE: "DV", + 0x01C0: "XL9FILE", + 0x01C1: "RECALCID", + 0x0200: "DIMENSIONS", + 0x0201: "BLANK", + 0x0203: "NUMBER", + 0x0204: "LABEL", + 0x0205: "BOOLERR", + 0x0206: "FORMULA", + 0x0207: "STRING", + 0x0208: "ROW", + 0x0209: "BOF", + 0x020B: "INDEX", + 0x0218: "NAME", + 0x0221: "ARRAY", + 0x0223: "EXTERNNAME", + 0x0225: "DEFAULTROWHEIGHT", + 0x0231: "FONT", + 0x0236: "TABLE", + 0x023E: "WINDOW2", + 0x0243: "XF", + 0x027E: "RK", + 0x0293: "STYLE", + 0x0406: "FORMULA", + 0x0409: "BOF", + 0x041E: "FORMAT", + 0x0443: "XF", + 0x04BC: "SHRFMLA", + 0x0800: "SCREENTIP", + 0x0803: "WEBQRYSETTINGS", + 0x0804: "WEBQRYTABLES", + 0x0809: "BOF", + 0x0862: "SHEETLAYOUT", + 0x0867: "SHEETPROTECTION", + 0x1001: "UNITS", + 0x1002: "ChartChart", + 0x1003: "ChartSeries", + 0x1006: "ChartDataformat", + 0x1007: "ChartLineformat", + 0x1009: "ChartMarkerformat", + 0x100A: "ChartAreaformat", + 0x100B: "ChartPieformat", + 0x100C: "ChartAttachedlabel", + 0x100D: "ChartSeriestext", + 0x1014: "ChartChartformat", + 0x1015: "ChartLegend", + 0x1016: "ChartSerieslist", + 0x1017: "ChartBar", + 0x1018: "ChartLine", + 0x1019: "ChartPie", + 0x101A: "ChartArea", + 0x101B: "ChartScatter", + 0x101C: "ChartChartline", + 0x101D: "ChartAxis", + 0x101E: "ChartTick", + 0x101F: "ChartValuerange", + 0x1020: "ChartCatserrange", + 0x1021: "ChartAxislineformat", + 0x1022: "ChartFormatlink", + 0x1024: "ChartDefaulttext", + 0x1025: "ChartText", + 0x1026: "ChartFontx", + 0x1027: "ChartObjectLink", + 0x1032: "ChartFrame", + 0x1033: "BEGIN", + 0x1034: "END", + 0x1035: "ChartPlotarea", + 0x103A: "Chart3D", + 0x103C: "ChartPicf", + 0x103D: "ChartDropbar", + 0x103E: "ChartRadar", + 0x103F: "ChartSurface", + 0x1040: "ChartRadararea", + 0x1041: "ChartAxisparent", + 0x1043: "ChartLegendxn", + 0x1044: "ChartShtprops", + 0x1045: "ChartSertocrt", + 0x1046: "ChartAxesused", + 0x1048: "ChartSbaseref", + 0x104A: "ChartSerparent", + 0x104B: "ChartSerauxtrend", + 0x104E: "ChartIfmt", + 0x104F: "ChartPos", + 0x1050: "ChartAlruns", + 0x1051: "ChartAI", + 0x105B: "ChartSerauxerrbar", + 0x105D: "ChartSerfmt", + 0x105F: "Chart3DDataFormat", + 0x1060: "ChartFbi", + 0x1061: "ChartBoppop", + 0x1062: "ChartAxcext", + 0x1063: "ChartDat", + 0x1064: "ChartPlotgrowth", + 0x1065: "ChartSiindex", + 0x1066: "ChartGelframe", + 0x1067: "ChartBoppcustom", + 0xFFFF: "" +} + + +all_funcs_by_name = { + # Includes Analysis ToolPak aka ATP aka add-in aka xcall functions, + # distinguished by -ve opcode. + # name: (opcode, min # args, max # args, func return type, func arg types) + # + in func arg types means more of the same. + 'ABS' : ( 24, 1, 1, 'V', 'V'), + 'ACCRINT' : ( -1, 6, 7, 'V', 'VVVVVVV'), + 'ACCRINTM' : ( -1, 3, 5, 'V', 'VVVVV'), + 'ACOS' : ( 99, 1, 1, 'V', 'V'), + 'ACOSH' : (233, 1, 1, 'V', 'V'), + 'ADDRESS' : (219, 2, 5, 'V', 'VVVVV'), + 'AMORDEGRC' : ( -1, 7, 7, 'V', 'VVVVVVV'), + 'AMORLINC' : ( -1, 7, 7, 'V', 'VVVVVVV'), + 'AND' : ( 36, 1, 30, 'V', 'D+'), + 'AREAS' : ( 75, 1, 1, 'V', 'R'), + 'ASC' : (214, 1, 1, 'V', 'V'), + 'ASIN' : ( 98, 1, 1, 'V', 'V'), + 'ASINH' : (232, 1, 1, 'V', 'V'), + 'ATAN' : ( 18, 1, 1, 'V', 'V'), + 'ATAN2' : ( 97, 2, 2, 'V', 'VV'), + 'ATANH' : (234, 1, 1, 'V', 'V'), + 'AVEDEV' : (269, 1, 30, 'V', 'D+'), + 'AVERAGE' : ( 5, 1, 30, 'V', 'D+'), + 'AVERAGEA' : (361, 1, 30, 'V', 'D+'), + 'BAHTTEXT' : (368, 1, 1, 'V', 'V'), + 'BESSELI' : ( -1, 2, 2, 'V', 'VV'), + 'BESSELJ' : ( -1, 2, 2, 'V', 'VV'), + 'BESSELK' : ( -1, 2, 2, 'V', 'VV'), + 'BESSELY' : ( -1, 2, 2, 'V', 'VV'), + 'BETADIST' : (270, 3, 5, 'V', 'VVVVV'), + 'BETAINV' : (272, 3, 5, 'V', 'VVVVV'), + 'BIN2DEC' : ( -1, 1, 1, 'V', 'V'), + 'BIN2HEX' : ( -1, 1, 2, 'V', 'VV'), + 'BIN2OCT' : ( -1, 1, 2, 'V', 'VV'), + 'BINOMDIST' : (273, 4, 4, 'V', 'VVVV'), + 'CEILING' : (288, 2, 2, 'V', 'VV'), + 'CELL' : (125, 1, 2, 'V', 'VR'), + 'CHAR' : (111, 1, 1, 'V', 'V'), + 'CHIDIST' : (274, 2, 2, 'V', 'VV'), + 'CHIINV' : (275, 2, 2, 'V', 'VV'), + 'CHITEST' : (306, 2, 2, 'V', 'AA'), + 'CHOOSE' : (100, 2, 30, 'R', 'VR+'), + 'CLEAN' : (162, 1, 1, 'V', 'V'), + 'CODE' : (121, 1, 1, 'V', 'V'), + 'COLUMN' : ( 9, 0, 1, 'V', 'R'), + 'COLUMNS' : ( 77, 1, 1, 'V', 'R'), + 'COMBIN' : (276, 2, 2, 'V', 'VV'), + 'COMPLEX' : ( -1, 2, 3, 'V', 'VVV'), + 'CONCATENATE' : (336, 1, 30, 'V', 'V+'), + 'CONFIDENCE' : (277, 3, 3, 'V', 'VVV'), + 'CONVERT' : ( -1, 3, 3, 'V', 'VVV'), + 'CORREL' : (307, 2, 2, 'V', 'AA'), + 'COS' : ( 16, 1, 1, 'V', 'V'), + 'COSH' : (230, 1, 1, 'V', 'V'), + 'COUNT' : ( 0, 1, 30, 'V', 'D+'), + 'COUNTA' : (169, 1, 30, 'V', 'D+'), + 'COUNTBLANK' : (347, 1, 1, 'V', 'R'), + 'COUNTIF' : (346, 2, 2, 'V', 'RV'), + 'COUPDAYBS' : ( -1, 3, 5, 'V', 'VVVVV'), + 'COUPDAYS' : ( -1, 3, 5, 'V', 'VVVVV'), + 'COUPDAYSNC' : ( -1, 3, 5, 'V', 'VVVVV'), + 'COUPNCD' : ( -1, 3, 5, 'V', 'VVVVV'), + 'COUPNUM' : ( -1, 3, 5, 'V', 'VVVVV'), + 'COUPPCD' : ( -1, 3, 5, 'V', 'VVVVV'), + 'COVAR' : (308, 2, 2, 'V', 'AA'), + 'CRITBINOM' : (278, 3, 3, 'V', 'VVV'), + 'CUMIPMT' : ( -1, 6, 6, 'V', 'VVVVVV'), + 'CUMPRINC' : ( -1, 6, 6, 'V', 'VVVVVV'), + 'DATE' : ( 65, 3, 3, 'V', 'VVV'), + 'DATEDIF' : (351, 3, 3, 'V', 'VVV'), + 'DATEVALUE' : (140, 1, 1, 'V', 'V'), + 'DAVERAGE' : ( 42, 3, 3, 'V', 'RRR'), + 'DAY' : ( 67, 1, 1, 'V', 'V'), + 'DAYS360' : (220, 2, 3, 'V', 'VVV'), + 'DB' : (247, 4, 5, 'V', 'VVVVV'), + 'DBCS' : (215, 1, 1, 'V', 'V'), + 'DCOUNT' : ( 40, 3, 3, 'V', 'RRR'), + 'DCOUNTA' : (199, 3, 3, 'V', 'RRR'), + 'DDB' : (144, 4, 5, 'V', 'VVVVV'), + 'DEC2BIN' : ( -1, 1, 2, 'V', 'VV'), + 'DEC2HEX' : ( -1, 1, 2, 'V', 'VV'), + 'DEC2OCT' : ( -1, 1, 2, 'V', 'VV'), + 'DEGREES' : (343, 1, 1, 'V', 'V'), + 'DELTA' : ( -1, 1, 2, 'V', 'VV'), + 'DEVSQ' : (318, 1, 30, 'V', 'D+'), + 'DGET' : (235, 3, 3, 'V', 'RRR'), + 'DISC' : ( -1, 4, 5, 'V', 'VVVVV'), + 'DMAX' : ( 44, 3, 3, 'V', 'RRR'), + 'DMIN' : ( 43, 3, 3, 'V', 'RRR'), + 'DOLLAR' : ( 13, 1, 2, 'V', 'VV'), + 'DOLLARDE' : ( -1, 2, 2, 'V', 'VV'), + 'DOLLARFR' : ( -1, 2, 2, 'V', 'VV'), + 'DPRODUCT' : (189, 3, 3, 'V', 'RRR'), + 'DSTDEV' : ( 45, 3, 3, 'V', 'RRR'), + 'DSTDEVP' : (195, 3, 3, 'V', 'RRR'), + 'DSUM' : ( 41, 3, 3, 'V', 'RRR'), + 'DURATION' : ( -1, 5, 6, 'V', 'VVVVVV'), + 'DVAR' : ( 47, 3, 3, 'V', 'RRR'), + 'DVARP' : (196, 3, 3, 'V', 'RRR'), + 'EDATE' : ( -1, 2, 2, 'V', 'VV'), + 'EFFECT' : ( -1, 2, 2, 'V', 'VV'), + 'EOMONTH' : ( -1, 1, 2, 'V', 'VV'), + 'ERF' : ( -1, 1, 2, 'V', 'VV'), + 'ERFC' : ( -1, 1, 1, 'V', 'V'), + 'ERROR.TYPE' : (261, 1, 1, 'V', 'V'), + 'EVEN' : (279, 1, 1, 'V', 'V'), + 'EXACT' : (117, 2, 2, 'V', 'VV'), + 'EXP' : ( 21, 1, 1, 'V', 'V'), + 'EXPONDIST' : (280, 3, 3, 'V', 'VVV'), + 'FACT' : (184, 1, 1, 'V', 'V'), + 'FACTDOUBLE' : ( -1, 1, 1, 'V', 'V'), + 'FALSE' : ( 35, 0, 0, 'V', '-'), + 'FDIST' : (281, 3, 3, 'V', 'VVV'), + 'FIND' : (124, 2, 3, 'V', 'VVV'), + 'FINDB' : (205, 2, 3, 'V', 'VVV'), + 'FINV' : (282, 3, 3, 'V', 'VVV'), + 'FISHER' : (283, 1, 1, 'V', 'V'), + 'FISHERINV' : (284, 1, 1, 'V', 'V'), + 'FIXED' : ( 14, 2, 3, 'V', 'VVV'), + 'FLOOR' : (285, 2, 2, 'V', 'VV'), + 'FORECAST' : (309, 3, 3, 'V', 'VAA'), + 'FREQUENCY' : (252, 2, 2, 'A', 'RR'), + 'FTEST' : (310, 2, 2, 'V', 'AA'), + 'FV' : ( 57, 3, 5, 'V', 'VVVVV'), + 'FVSCHEDULE' : ( -1, 2, 2, 'V', 'VA'), + 'GAMMADIST' : (286, 4, 4, 'V', 'VVVV'), + 'GAMMAINV' : (287, 3, 3, 'V', 'VVV'), + 'GAMMALN' : (271, 1, 1, 'V', 'V'), + 'GCD' : ( -1, 1, 29, 'V', 'V+'), + 'GEOMEAN' : (319, 1, 30, 'V', 'D+'), + 'GESTEP' : ( -1, 1, 2, 'V', 'VV'), + 'GETPIVOTDATA': (358, 2, 30, 'A', 'VAV+'), + 'GROWTH' : ( 52, 1, 4, 'A', 'RRRV'), + 'HARMEAN' : (320, 1, 30, 'V', 'D+'), + 'HEX2BIN' : ( -1, 1, 2, 'V', 'VV'), + 'HEX2DEC' : ( -1, 1, 1, 'V', 'V'), + 'HEX2OCT' : ( -1, 1, 2, 'V', 'VV'), + 'HLOOKUP' : (101, 3, 4, 'V', 'VRRV'), + 'HOUR' : ( 71, 1, 1, 'V', 'V'), + 'HYPERLINK' : (359, 1, 2, 'V', 'VV'), + 'HYPGEOMDIST' : (289, 4, 4, 'V', 'VVVV'), + 'IF' : ( 1, 2, 3, 'R', 'VRR'), + 'IMABS' : ( -1, 1, 1, 'V', 'V'), + 'IMAGINARY' : ( -1, 1, 1, 'V', 'V'), + 'IMARGUMENT' : ( -1, 1, 1, 'V', 'V'), + 'IMCONJUGATE' : ( -1, 1, 1, 'V', 'V'), + 'IMCOS' : ( -1, 1, 1, 'V', 'V'), + 'IMDIV' : ( -1, 2, 2, 'V', 'VV'), + 'IMEXP' : ( -1, 1, 1, 'V', 'V'), + 'IMLN' : ( -1, 1, 1, 'V', 'V'), + 'IMLOG10' : ( -1, 1, 1, 'V', 'V'), + 'IMLOG2' : ( -1, 1, 1, 'V', 'V'), + 'IMPOWER' : ( -1, 2, 2, 'V', 'VV'), + 'IMPRODUCT' : ( -1, 2, 2, 'V', 'VV'), + 'IMREAL' : ( -1, 1, 1, 'V', 'V'), + 'IMSIN' : ( -1, 1, 1, 'V', 'V'), + 'IMSQRT' : ( -1, 1, 1, 'V', 'V'), + 'IMSUB' : ( -1, 2, 2, 'V', 'VV'), + 'IMSUM' : ( -1, 1, 29, 'V', 'V+'), + 'INDEX' : ( 29, 2, 4, 'R', 'RVVV'), + 'INDIRECT' : (148, 1, 2, 'R', 'VV'), + 'INFO' : (244, 1, 1, 'V', 'V'), + 'INT' : ( 25, 1, 1, 'V', 'V'), + 'INTERCEPT' : (311, 2, 2, 'V', 'AA'), + 'INTRATE' : ( -1, 4, 5, 'V', 'VVVVV'), + 'IPMT' : (167, 4, 6, 'V', 'VVVVVV'), + 'IRR' : ( 62, 1, 2, 'V', 'RV'), + 'ISBLANK' : (129, 1, 1, 'V', 'V'), + 'ISERR' : (126, 1, 1, 'V', 'V'), + 'ISERROR' : ( 3, 1, 1, 'V', 'V'), + 'ISEVEN' : ( -1, 1, 1, 'V', 'V'), + 'ISLOGICAL' : (198, 1, 1, 'V', 'V'), + 'ISNA' : ( 2, 1, 1, 'V', 'V'), + 'ISNONTEXT' : (190, 1, 1, 'V', 'V'), + 'ISNUMBER' : (128, 1, 1, 'V', 'V'), + 'ISODD' : ( -1, 1, 1, 'V', 'V'), + 'ISPMT' : (350, 4, 4, 'V', 'VVVV'), + 'ISREF' : (105, 1, 1, 'V', 'R'), + 'ISTEXT' : (127, 1, 1, 'V', 'V'), + 'KURT' : (322, 1, 30, 'V', 'D+'), + 'LARGE' : (325, 2, 2, 'V', 'RV'), + 'LCM' : ( -1, 1, 29, 'V', 'V+'), + 'LEFT' : (115, 1, 2, 'V', 'VV'), + 'LEFTB' : (208, 1, 2, 'V', 'VV'), + 'LEN' : ( 32, 1, 1, 'V', 'V'), + 'LENB' : (211, 1, 1, 'V', 'V'), + 'LINEST' : ( 49, 1, 4, 'A', 'RRVV'), + 'LN' : ( 22, 1, 1, 'V', 'V'), + 'LOG' : (109, 1, 2, 'V', 'VV'), + 'LOG10' : ( 23, 1, 1, 'V', 'V'), + 'LOGEST' : ( 51, 1, 4, 'A', 'RRVV'), + 'LOGINV' : (291, 3, 3, 'V', 'VVV'), + 'LOGNORMDIST' : (290, 3, 3, 'V', 'VVV'), + 'LOOKUP' : ( 28, 2, 3, 'V', 'VRR'), + 'LOWER' : (112, 1, 1, 'V', 'V'), + 'MATCH' : ( 64, 2, 3, 'V', 'VRR'), + 'MAX' : ( 7, 1, 30, 'V', 'D+'), + 'MAXA' : (362, 1, 30, 'V', 'D+'), + 'MDETERM' : (163, 1, 1, 'V', 'A'), + 'MDURATION' : ( -1, 5, 6, 'V', 'VVVVVV'), + 'MEDIAN' : (227, 1, 30, 'V', 'D+'), + 'MID' : ( 31, 3, 3, 'V', 'VVV'), + 'MIDB' : (210, 3, 3, 'V', 'VVV'), + 'MIN' : ( 6, 1, 30, 'V', 'D+'), + 'MINA' : (363, 1, 30, 'V', 'D+'), + 'MINUTE' : ( 72, 1, 1, 'V', 'V'), + 'MINVERSE' : (164, 1, 1, 'A', 'A'), + 'MIRR' : ( 61, 3, 3, 'V', 'RVV'), + 'MMULT' : (165, 2, 2, 'A', 'AA'), + 'MOD' : ( 39, 2, 2, 'V', 'VV'), + 'MODE' : (330, 1, 30, 'V', 'A+'), ################ weird ################# + 'MONTH' : ( 68, 1, 1, 'V', 'V'), + 'MROUND' : ( -1, 2, 2, 'V', 'VV'), + 'MULTINOMIAL' : ( -1, 1, 29, 'V', 'V+'), + 'N' : (131, 1, 1, 'V', 'R'), + 'NA' : ( 10, 0, 0, 'V', '-'), + 'NEGBINOMDIST': (292, 3, 3, 'V', 'VVV'), + 'NETWORKDAYS' : ( -1, 2, 3, 'V', 'VVR'), + 'NOMINAL' : ( -1, 2, 2, 'V', 'VV'), + 'NORMDIST' : (293, 4, 4, 'V', 'VVVV'), + 'NORMINV' : (295, 3, 3, 'V', 'VVV'), + 'NORMSDIST' : (294, 1, 1, 'V', 'V'), + 'NORMSINV' : (296, 1, 1, 'V', 'V'), + 'NOT' : ( 38, 1, 1, 'V', 'V'), + 'NOW' : ( 74, 0, 0, 'V', '-'), + 'NPER' : ( 58, 3, 5, 'V', 'VVVVV'), + 'NPV' : ( 11, 2, 30, 'V', 'VD+'), + 'OCT2BIN' : ( -1, 1, 2, 'V', 'VV'), + 'OCT2DEC' : ( -1, 1, 1, 'V', 'V'), + 'OCT2HEX' : ( -1, 1, 2, 'V', 'VV'), + 'ODD' : (298, 1, 1, 'V', 'V'), + 'ODDFPRICE' : ( -1, 9, 9, 'V', 'VVVVVVVVV'), + 'ODDFYIELD' : ( -1, 9, 9, 'V', 'VVVVVVVVV'), + 'ODDLPRICE' : ( -1, 8, 8, 'V', 'VVVVVVVV'), + 'ODDLYIELD' : ( -1, 8, 8, 'V', 'VVVVVVVV'), + 'OFFSET' : ( 78, 3, 5, 'R', 'RVVVV'), + 'OR' : ( 37, 1, 30, 'V', 'D+'), + 'PEARSON' : (312, 2, 2, 'V', 'AA'), + 'PERCENTILE' : (328, 2, 2, 'V', 'RV'), + 'PERCENTRANK' : (329, 2, 3, 'V', 'RVV'), + 'PERMUT' : (299, 2, 2, 'V', 'VV'), + 'PHONETIC' : (360, 1, 1, 'V', 'R'), + 'PI' : ( 19, 0, 0, 'V', '-'), + 'PMT' : ( 59, 3, 5, 'V', 'VVVVV'), + 'POISSON' : (300, 3, 3, 'V', 'VVV'), + 'POWER' : (337, 2, 2, 'V', 'VV'), + 'PPMT' : (168, 4, 6, 'V', 'VVVVVV'), + 'PRICE' : ( -1, 6, 7, 'V', 'VVVVVVV'), + 'PRICEDISC' : ( -1, 4, 5, 'V', 'VVVVV'), + 'PRICEMAT' : ( -1, 5, 6, 'V', 'VVVVVV'), + 'PROB' : (317, 3, 4, 'V', 'AAVV'), + 'PRODUCT' : (183, 1, 30, 'V', 'D+'), + 'PROPER' : (114, 1, 1, 'V', 'V'), + 'PV' : ( 56, 3, 5, 'V', 'VVVVV'), + 'QUARTILE' : (327, 2, 2, 'V', 'RV'), + 'QUOTIENT' : ( -1, 2, 2, 'V', 'VV'), + 'RADIANS' : (342, 1, 1, 'V', 'V'), + 'RAND' : ( 63, 0, 0, 'V', '-'), + 'RANDBETWEEN' : ( -1, 2, 2, 'V', 'VV'), + 'RANK' : (216, 2, 3, 'V', 'VRV'), + 'RATE' : ( 60, 3, 6, 'V', 'VVVVVV'), + 'RECEIVED' : ( -1, 4, 5, 'V', 'VVVVV'), + 'REPLACE' : (119, 4, 4, 'V', 'VVVV'), + 'REPLACEB' : (207, 4, 4, 'V', 'VVVV'), + 'REPT' : ( 30, 2, 2, 'V', 'VV'), + 'RIGHT' : (116, 1, 2, 'V', 'VV'), + 'RIGHTB' : (209, 1, 2, 'V', 'VV'), + 'ROMAN' : (354, 1, 2, 'V', 'VV'), + 'ROUND' : ( 27, 2, 2, 'V', 'VV'), + 'ROUNDDOWN' : (213, 2, 2, 'V', 'VV'), + 'ROUNDUP' : (212, 2, 2, 'V', 'VV'), + 'ROW' : ( 8, 0, 1, 'V', 'R'), + 'ROWS' : ( 76, 1, 1, 'V', 'R'), + 'RSQ' : (313, 2, 2, 'V', 'AA'), + 'RTD' : (379, 3, 30, 'A', 'VVV+'), + 'SEARCH' : ( 82, 2, 3, 'V', 'VVV'), + 'SEARCHB' : (206, 2, 3, 'V', 'VVV'), + 'SECOND' : ( 73, 1, 1, 'V', 'V'), + 'SERIESSUM' : ( -1, 4, 4, 'V', 'VVVA'), + 'SIGN' : ( 26, 1, 1, 'V', 'V'), + 'SIN' : ( 15, 1, 1, 'V', 'V'), + 'SINH' : (229, 1, 1, 'V', 'V'), + 'SKEW' : (323, 1, 30, 'V', 'D+'), + 'SLN' : (142, 3, 3, 'V', 'VVV'), + 'SLOPE' : (315, 2, 2, 'V', 'AA'), + 'SMALL' : (326, 2, 2, 'V', 'RV'), + 'SQRT' : ( 20, 1, 1, 'V', 'V'), + 'SQRTPI' : ( -1, 1, 1, 'V', 'V'), + 'STANDARDIZE' : (297, 3, 3, 'V', 'VVV'), + 'STDEV' : ( 12, 1, 30, 'V', 'D+'), + 'STDEVA' : (366, 1, 30, 'V', 'D+'), + 'STDEVP' : (193, 1, 30, 'V', 'D+'), + 'STDEVPA' : (364, 1, 30, 'V', 'D+'), + 'STEYX' : (314, 2, 2, 'V', 'AA'), + 'SUBSTITUTE' : (120, 3, 4, 'V', 'VVVV'), + 'SUBTOTAL' : (344, 2, 30, 'V', 'VR+'), + 'SUM' : ( 4, 1, 30, 'V', 'D+'), + 'SUMIF' : (345, 2, 3, 'V', 'RVR'), + 'SUMPRODUCT' : (228, 1, 30, 'V', 'A+'), + 'SUMSQ' : (321, 1, 30, 'V', 'D+'), + 'SUMX2MY2' : (304, 2, 2, 'V', 'AA'), + 'SUMX2PY2' : (305, 2, 2, 'V', 'AA'), + 'SUMXMY2' : (303, 2, 2, 'V', 'AA'), + 'SYD' : (143, 4, 4, 'V', 'VVVV'), + 'T' : (130, 1, 1, 'V', 'R'), + 'TAN' : ( 17, 1, 1, 'V', 'V'), + 'TANH' : (231, 1, 1, 'V', 'V'), + 'TBILLEQ' : ( -1, 3, 3, 'V', 'VVV'), + 'TBILLPRICE' : ( -1, 3, 3, 'V', 'VVV'), + 'TBILLYIELD' : ( -1, 3, 3, 'V', 'VVV'), + 'TDIST' : (301, 3, 3, 'V', 'VVV'), + 'TEXT' : ( 48, 2, 2, 'V', 'VV'), + 'TIME' : ( 66, 3, 3, 'V', 'VVV'), + 'TIMEVALUE' : (141, 1, 1, 'V', 'V'), + 'TINV' : (332, 2, 2, 'V', 'VV'), + 'TODAY' : (221, 0, 0, 'V', '-'), + 'TRANSPOSE' : ( 83, 1, 1, 'A', 'A'), + 'TREND' : ( 50, 1, 4, 'A', 'RRRV'), + 'TRIM' : (118, 1, 1, 'V', 'V'), + 'TRIMMEAN' : (331, 2, 2, 'V', 'RV'), + 'TRUE' : ( 34, 0, 0, 'V', '-'), + 'TRUNC' : (197, 1, 2, 'V', 'VV'), + 'TTEST' : (316, 4, 4, 'V', 'AAVV'), + 'TYPE' : ( 86, 1, 1, 'V', 'V'), + 'UPPER' : (113, 1, 1, 'V', 'V'), + 'USDOLLAR' : (204, 1, 2, 'V', 'VV'), + 'VALUE' : ( 33, 1, 1, 'V', 'V'), + 'VAR' : ( 46, 1, 30, 'V', 'D+'), + 'VARA' : (367, 1, 30, 'V', 'D+'), + 'VARP' : (194, 1, 30, 'V', 'D+'), + 'VARPA' : (365, 1, 30, 'V', 'D+'), + 'VDB' : (222, 5, 7, 'V', 'VVVVVVV'), + 'VLOOKUP' : (102, 3, 4, 'V', 'VRRV'), + 'WEEKDAY' : ( 70, 1, 2, 'V', 'VV'), + 'WEEKNUM' : ( -1, 1, 2, 'V', 'VV'), + 'WEIBULL' : (302, 4, 4, 'V', 'VVVV'), + 'WORKDAY' : ( -1, 2, 3, 'V', 'VVR'), + 'XIRR' : ( -1, 2, 3, 'V', 'AAV'), + 'XNPV' : ( -1, 3, 3, 'V', 'VAA'), + 'YEAR' : ( 69, 1, 1, 'V', 'V'), + 'YEARFRAC' : ( -1, 2, 3, 'V', 'VVV'), + 'YIELD' : ( -1, 6, 7, 'V', 'VVVVVVV'), + 'YIELDDISC' : ( -1, 4, 5, 'V', 'VVVVV'), + 'YIELDMAT' : ( -1, 5, 6, 'V', 'VVVVVV'), + 'ZTEST' : (324, 2, 3, 'V', 'RVV'), + } + +# Formulas Parse things + +ptgExp = 0x01 +ptgTbl = 0x02 +ptgAdd = 0x03 +ptgSub = 0x04 +ptgMul = 0x05 +ptgDiv = 0x06 +ptgPower = 0x07 +ptgConcat = 0x08 +ptgLT = 0x09 +ptgLE = 0x0a +ptgEQ = 0x0b +ptgGE = 0x0c +ptgGT = 0x0d +ptgNE = 0x0e +ptgIsect = 0x0f +ptgUnion = 0x10 +ptgRange = 0x11 +ptgUplus = 0x12 +ptgUminus = 0x13 +ptgPercent = 0x14 +ptgParen = 0x15 +ptgMissArg = 0x16 +ptgStr = 0x17 +ptgExtend = 0x18 +ptgAttr = 0x19 +ptgSheet = 0x1a +ptgEndSheet = 0x1b +ptgErr = 0x1c +ptgBool = 0x1d +ptgInt = 0x1e +ptgNum = 0x1f + +ptgArrayR = 0x20 +ptgFuncR = 0x21 +ptgFuncVarR = 0x22 +ptgNameR = 0x23 +ptgRefR = 0x24 +ptgAreaR = 0x25 +ptgMemAreaR = 0x26 +ptgMemErrR = 0x27 +ptgMemNoMemR = 0x28 +ptgMemFuncR = 0x29 +ptgRefErrR = 0x2a +ptgAreaErrR = 0x2b +ptgRefNR = 0x2c +ptgAreaNR = 0x2d +ptgMemAreaNR = 0x2e +ptgMemNoMemNR = 0x2f +ptgNameXR = 0x39 +ptgRef3dR = 0x3a +ptgArea3dR = 0x3b +ptgRefErr3dR = 0x3c +ptgAreaErr3dR = 0x3d + +ptgArrayV = 0x40 +ptgFuncV = 0x41 +ptgFuncVarV = 0x42 +ptgNameV = 0x43 +ptgRefV = 0x44 +ptgAreaV = 0x45 +ptgMemAreaV = 0x46 +ptgMemErrV = 0x47 +ptgMemNoMemV = 0x48 +ptgMemFuncV = 0x49 +ptgRefErrV = 0x4a +ptgAreaErrV = 0x4b +ptgRefNV = 0x4c +ptgAreaNV = 0x4d +ptgMemAreaNV = 0x4e +ptgMemNoMemNV = 0x4f +ptgFuncCEV = 0x58 +ptgNameXV = 0x59 +ptgRef3dV = 0x5a +ptgArea3dV = 0x5b +ptgRefErr3dV = 0x5c +ptgAreaErr3dV = 0x5d + +ptgArrayA = 0x60 +ptgFuncA = 0x61 +ptgFuncVarA = 0x62 +ptgNameA = 0x63 +ptgRefA = 0x64 +ptgAreaA = 0x65 +ptgMemAreaA = 0x66 +ptgMemErrA = 0x67 +ptgMemNoMemA = 0x68 +ptgMemFuncA = 0x69 +ptgRefErrA = 0x6a +ptgAreaErrA = 0x6b +ptgRefNA = 0x6c +ptgAreaNA = 0x6d +ptgMemAreaNA = 0x6e +ptgMemNoMemNA = 0x6f +ptgFuncCEA = 0x78 +ptgNameXA = 0x79 +ptgRef3dA = 0x7a +ptgArea3dA = 0x7b +ptgRefErr3dA = 0x7c +ptgAreaErr3dA = 0x7d + + +PtgNames = { + ptgExp : "ptgExp", + ptgTbl : "ptgTbl", + ptgAdd : "ptgAdd", + ptgSub : "ptgSub", + ptgMul : "ptgMul", + ptgDiv : "ptgDiv", + ptgPower : "ptgPower", + ptgConcat : "ptgConcat", + ptgLT : "ptgLT", + ptgLE : "ptgLE", + ptgEQ : "ptgEQ", + ptgGE : "ptgGE", + ptgGT : "ptgGT", + ptgNE : "ptgNE", + ptgIsect : "ptgIsect", + ptgUnion : "ptgUnion", + ptgRange : "ptgRange", + ptgUplus : "ptgUplus", + ptgUminus : "ptgUminus", + ptgPercent : "ptgPercent", + ptgParen : "ptgParen", + ptgMissArg : "ptgMissArg", + ptgStr : "ptgStr", + ptgExtend : "ptgExtend", + ptgAttr : "ptgAttr", + ptgSheet : "ptgSheet", + ptgEndSheet : "ptgEndSheet", + ptgErr : "ptgErr", + ptgBool : "ptgBool", + ptgInt : "ptgInt", + ptgNum : "ptgNum", + ptgArrayR : "ptgArrayR", + ptgFuncR : "ptgFuncR", + ptgFuncVarR : "ptgFuncVarR", + ptgNameR : "ptgNameR", + ptgRefR : "ptgRefR", + ptgAreaR : "ptgAreaR", + ptgMemAreaR : "ptgMemAreaR", + ptgMemErrR : "ptgMemErrR", + ptgMemNoMemR : "ptgMemNoMemR", + ptgMemFuncR : "ptgMemFuncR", + ptgRefErrR : "ptgRefErrR", + ptgAreaErrR : "ptgAreaErrR", + ptgRefNR : "ptgRefNR", + ptgAreaNR : "ptgAreaNR", + ptgMemAreaNR : "ptgMemAreaNR", + ptgMemNoMemNR : "ptgMemNoMemNR", + ptgNameXR : "ptgNameXR", + ptgRef3dR : "ptgRef3dR", + ptgArea3dR : "ptgArea3dR", + ptgRefErr3dR : "ptgRefErr3dR", + ptgAreaErr3dR : "ptgAreaErr3dR", + ptgArrayV : "ptgArrayV", + ptgFuncV : "ptgFuncV", + ptgFuncVarV : "ptgFuncVarV", + ptgNameV : "ptgNameV", + ptgRefV : "ptgRefV", + ptgAreaV : "ptgAreaV", + ptgMemAreaV : "ptgMemAreaV", + ptgMemErrV : "ptgMemErrV", + ptgMemNoMemV : "ptgMemNoMemV", + ptgMemFuncV : "ptgMemFuncV", + ptgRefErrV : "ptgRefErrV", + ptgAreaErrV : "ptgAreaErrV", + ptgRefNV : "ptgRefNV", + ptgAreaNV : "ptgAreaNV", + ptgMemAreaNV : "ptgMemAreaNV", + ptgMemNoMemNV : "ptgMemNoMemNV", + ptgFuncCEV : "ptgFuncCEV", + ptgNameXV : "ptgNameXV", + ptgRef3dV : "ptgRef3dV", + ptgArea3dV : "ptgArea3dV", + ptgRefErr3dV : "ptgRefErr3dV", + ptgAreaErr3dV : "ptgAreaErr3dV", + ptgArrayA : "ptgArrayA", + ptgFuncA : "ptgFuncA", + ptgFuncVarA : "ptgFuncVarA", + ptgNameA : "ptgNameA", + ptgRefA : "ptgRefA", + ptgAreaA : "ptgAreaA", + ptgMemAreaA : "ptgMemAreaA", + ptgMemErrA : "ptgMemErrA", + ptgMemNoMemA : "ptgMemNoMemA", + ptgMemFuncA : "ptgMemFuncA", + ptgRefErrA : "ptgRefErrA", + ptgAreaErrA : "ptgAreaErrA", + ptgRefNA : "ptgRefNA", + ptgAreaNA : "ptgAreaNA", + ptgMemAreaNA : "ptgMemAreaNA", + ptgMemNoMemNA : "ptgMemNoMemNA", + ptgFuncCEA : "ptgFuncCEA", + ptgNameXA : "ptgNameXA", + ptgRef3dA : "ptgRef3dA", + ptgArea3dA : "ptgArea3dA", + ptgRefErr3dA : "ptgRefErr3dA", + ptgAreaErr3dA : "ptgAreaErr3dA" +} + + +error_msg_by_code = { + 0x00: u"#NULL!", # intersection of two cell ranges is empty + 0x07: u"#DIV/0!", # division by zero + 0x0F: u"#VALUE!", # wrong type of operand + 0x17: u"#REF!", # illegal or deleted cell reference + 0x1D: u"#NAME?", # wrong function or range name + 0x24: u"#NUM!", # value range overflow + 0x2A: u"#N/A" # argument or function not available +} diff --git a/.venv/lib/python3.9/site-packages/xlwt/Formatting.py b/.venv/lib/python3.9/site-packages/xlwt/Formatting.py new file mode 100644 index 00000000..5405d659 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Formatting.py @@ -0,0 +1,265 @@ +''' +Formatting +========== + +The XF record is able to store explicit cell formatting attributes or the +attributes of a cell style. Explicit formatting includes the reference to +a cell style XF record. This allows to extend a defined cell style with +some explicit attributes. The formatting attributes are divided into +6 groups: + +============= ========================================================== +Group Attributes +============= ========================================================== +Number format Number format index (index to FORMAT record) +Font Font index (index to FONT record) +Alignment Horizontal and vertical alignment, text wrap, indentation, + orientation/rotation, text direction +Border Border line styles and colours +Background Background area style and colours +Protection Cell locked, formula hidden +============= ========================================================== + +For each group a flag in the cell XF record specifies whether to use the +attributes contained in that XF record or in the referenced style +XF record. In style XF records, these flags specify whether the attributes +will overwrite explicit cell formatting when the style is applied to +a cell. Changing a cell style (without applying this style to a cell) will +change all cells which already use that style and do not contain explicit +cell attributes for the changed style attributes. If a cell XF record does +not contain explicit attributes in a group (if the attribute group flag +is not set), it repeats the attributes of its style XF record. + +''' + +from . import BIFFRecords + +class Font(object): + + ESCAPEMENT_NONE = 0x00 + ESCAPEMENT_SUPERSCRIPT = 0x01 + ESCAPEMENT_SUBSCRIPT = 0x02 + + UNDERLINE_NONE = 0x00 + UNDERLINE_SINGLE = 0x01 + UNDERLINE_SINGLE_ACC = 0x21 + UNDERLINE_DOUBLE = 0x02 + UNDERLINE_DOUBLE_ACC = 0x22 + + FAMILY_NONE = 0x00 + FAMILY_ROMAN = 0x01 + FAMILY_SWISS = 0x02 + FAMILY_MODERN = 0x03 + FAMILY_SCRIPT = 0x04 + FAMILY_DECORATIVE = 0x05 + + CHARSET_ANSI_LATIN = 0x00 + CHARSET_SYS_DEFAULT = 0x01 + CHARSET_SYMBOL = 0x02 + CHARSET_APPLE_ROMAN = 0x4D + CHARSET_ANSI_JAP_SHIFT_JIS = 0x80 + CHARSET_ANSI_KOR_HANGUL = 0x81 + CHARSET_ANSI_KOR_JOHAB = 0x82 + CHARSET_ANSI_CHINESE_GBK = 0x86 + CHARSET_ANSI_CHINESE_BIG5 = 0x88 + CHARSET_ANSI_GREEK = 0xA1 + CHARSET_ANSI_TURKISH = 0xA2 + CHARSET_ANSI_VIETNAMESE = 0xA3 + CHARSET_ANSI_HEBREW = 0xB1 + CHARSET_ANSI_ARABIC = 0xB2 + CHARSET_ANSI_BALTIC = 0xBA + CHARSET_ANSI_CYRILLIC = 0xCC + CHARSET_ANSI_THAI = 0xDE + CHARSET_ANSI_LATIN_II = 0xEE + CHARSET_OEM_LATIN_I = 0xFF + + def __init__(self): + # twip = 1/20 of a point = 1/1440 of a inch + # usually resolution == 96 pixels per 1 inch + # (rarely 120 pixels per 1 inch or another one) + + self.height = 0x00C8 # 200: this is font with height 10 points + self.italic = False + self.struck_out = False + self.outline = False + self.shadow = False + self.colour_index = 0x7FFF + self.bold = False + self._weight = 0x0190 # 0x02BC gives bold font + self.escapement = self.ESCAPEMENT_NONE + self.underline = self.UNDERLINE_NONE + self.family = self.FAMILY_NONE + self.charset = self.CHARSET_SYS_DEFAULT + self.name = 'Arial' + + def get_biff_record(self): + height = self.height + + options = 0x00 + if self.bold: + options |= 0x01 + self._weight = 0x02BC + if self.italic: + options |= 0x02 + if self.underline != self.UNDERLINE_NONE: + options |= 0x04 + if self.struck_out: + options |= 0x08 + if self.outline: + options |= 0x010 + if self.shadow: + options |= 0x020 + + colour_index = self.colour_index + weight = self._weight + escapement = self.escapement + underline = self.underline + family = self.family + charset = self.charset + name = self.name + + return BIFFRecords.FontRecord(height, options, colour_index, weight, escapement, + underline, family, charset, + name) + + def _search_key(self): + return ( + self.height, + self.italic, + self.struck_out, + self.outline, + self.shadow, + self.colour_index, + self.bold, + self._weight, + self.escapement, + self.underline, + self.family, + self.charset, + self.name, + ) + +class Alignment(object): + HORZ_GENERAL = 0x00 + HORZ_LEFT = 0x01 + HORZ_CENTER = 0x02 + HORZ_RIGHT = 0x03 + HORZ_FILLED = 0x04 + HORZ_JUSTIFIED = 0x05 # BIFF4-BIFF8X + HORZ_CENTER_ACROSS_SEL = 0x06 # Centred across selection (BIFF4-BIFF8X) + HORZ_DISTRIBUTED = 0x07 # Distributed (BIFF8X) + + VERT_TOP = 0x00 + VERT_CENTER = 0x01 + VERT_BOTTOM = 0x02 + VERT_JUSTIFIED = 0x03 # Justified (BIFF5-BIFF8X) + VERT_DISTRIBUTED = 0x04 # Distributed (BIFF8X) + + DIRECTION_GENERAL = 0x00 # BIFF8X + DIRECTION_LR = 0x01 + DIRECTION_RL = 0x02 + + ORIENTATION_NOT_ROTATED = 0x00 + ORIENTATION_STACKED = 0x01 + ORIENTATION_90_CC = 0x02 + ORIENTATION_90_CW = 0x03 + + ROTATION_0_ANGLE = 0x00 + ROTATION_STACKED = 0xFF + + WRAP_AT_RIGHT = 0x01 + NOT_WRAP_AT_RIGHT = 0x00 + + SHRINK_TO_FIT = 0x01 + NOT_SHRINK_TO_FIT = 0x00 + + def __init__(self): + self.horz = self.HORZ_GENERAL + self.vert = self.VERT_BOTTOM + self.dire = self.DIRECTION_GENERAL + self.orie = self.ORIENTATION_NOT_ROTATED + self.rota = self.ROTATION_0_ANGLE + self.wrap = self.NOT_WRAP_AT_RIGHT + self.shri = self.NOT_SHRINK_TO_FIT + self.inde = 0 + self.merg = 0 + + def _search_key(self): + return ( + self.horz, self.vert, self.dire, self.orie, self.rota, + self.wrap, self.shri, self.inde, self.merg, + ) + +class Borders(object): + NO_LINE = 0x00 + THIN = 0x01 + MEDIUM = 0x02 + DASHED = 0x03 + DOTTED = 0x04 + THICK = 0x05 + DOUBLE = 0x06 + HAIR = 0x07 + #The following for BIFF8 + MEDIUM_DASHED = 0x08 + THIN_DASH_DOTTED = 0x09 + MEDIUM_DASH_DOTTED = 0x0A + THIN_DASH_DOT_DOTTED = 0x0B + MEDIUM_DASH_DOT_DOTTED = 0x0C + SLANTED_MEDIUM_DASH_DOTTED = 0x0D + + NEED_DIAG1 = 0x01 + NEED_DIAG2 = 0x01 + NO_NEED_DIAG1 = 0x00 + NO_NEED_DIAG2 = 0x00 + + def __init__(self): + self.left = self.NO_LINE + self.right = self.NO_LINE + self.top = self.NO_LINE + self.bottom = self.NO_LINE + self.diag = self.NO_LINE + + self.left_colour = 0x40 + self.right_colour = 0x40 + self.top_colour = 0x40 + self.bottom_colour = 0x40 + self.diag_colour = 0x40 + + self.need_diag1 = self.NO_NEED_DIAG1 + self.need_diag2 = self.NO_NEED_DIAG2 + + def _search_key(self): + return ( + self.left, self.right, self.top, self.bottom, self.diag, + self.left_colour, self.right_colour, self.top_colour, + self.bottom_colour, self.diag_colour, + self.need_diag1, self.need_diag2, + ) + +class Pattern(object): + # patterns 0x00 - 0x12 + NO_PATTERN = 0x00 + SOLID_PATTERN = 0x01 + + def __init__(self): + self.pattern = self.NO_PATTERN + self.pattern_fore_colour = 0x40 + self.pattern_back_colour = 0x41 + + def _search_key(self): + return ( + self.pattern, + self.pattern_fore_colour, + self.pattern_back_colour, + ) + +class Protection(object): + def __init__(self): + self.cell_locked = 1 + self.formula_hidden = 0 + + def _search_key(self): + return ( + self.cell_locked, + self.formula_hidden, + ) diff --git a/.venv/lib/python3.9/site-packages/xlwt/Row.py b/.venv/lib/python3.9/site-packages/xlwt/Row.py new file mode 100644 index 00000000..f597a9c7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Row.py @@ -0,0 +1,293 @@ +# -*- coding: windows-1252 -*- + +from decimal import Decimal +from . import BIFFRecords +from . import Style +from .Cell import StrCell, BlankCell, NumberCell, FormulaCell, MulBlankCell, BooleanCell, ErrorCell, \ + _get_cells_biff_data_mul +from . import ExcelFormula +import datetime as dt +from .Formatting import Font +from .compat import basestring, xrange, int_types, iteritems + + +class Row(object): + __slots__ = [# private variables + "__idx", + "__parent", + "__parent_wb", + "__cells", + "__min_col_idx", + "__max_col_idx", + "__xf_index", + "__has_default_xf_index", + "__height_in_pixels", + # public variables + "height", + "has_default_height", + "height_mismatch", + "level", + "collapse", + "hidden", + "space_above", + "space_below"] + + def __init__(self, rowx, parent_sheet): + if not (isinstance(rowx, int_types) and 0 <= rowx <= 65535): + raise ValueError("row index was %r, not allowed by .xls format" % rowx) + self.__idx = rowx + self.__parent = parent_sheet + self.__parent_wb = parent_sheet.get_parent() + self.__cells = {} + self.__min_col_idx = 0 + self.__max_col_idx = 0 + self.__xf_index = 0x0F + self.__has_default_xf_index = 0 + self.__height_in_pixels = 0x11 + + self.height = 0x00FF + self.has_default_height = 0x00 + self.height_mismatch = 0 + self.level = 0 + self.collapse = 0 + self.hidden = 0 + self.space_above = 0 + self.space_below = 0 + + + def __adjust_height(self, style): + twips = style.font.height + points = float(twips)/20.0 + # Cell height in pixels can be calcuted by following approx. formula: + # cell height in pixels = font height in points * 83/50 + 2/5 + # It works when screen resolution is 96 dpi + pix = int(round(points*83.0/50.0 + 2.0/5.0)) + if pix > self.__height_in_pixels: + self.__height_in_pixels = pix + + + def __adjust_bound_col_idx(self, *args): + for arg in args: + iarg = int(arg) + if not ((0 <= iarg <= 255) and arg == iarg): + raise ValueError("column index (%r) not an int in range(256)" % arg) + sheet = self.__parent + if iarg < self.__min_col_idx: + self.__min_col_idx = iarg + if iarg > self.__max_col_idx: + self.__max_col_idx = iarg + if iarg < sheet.first_used_col: + sheet.first_used_col = iarg + if iarg > sheet.last_used_col: + sheet.last_used_col = iarg + + def __excel_date_dt(self, date): + adj = False + if isinstance(date, dt.date): + if self.__parent_wb.dates_1904: + epoch_tuple = (1904, 1, 1) + else: + epoch_tuple = (1899, 12, 31) + adj = True + if isinstance(date, dt.datetime): + epoch = dt.datetime(*epoch_tuple) + else: + epoch = dt.date(*epoch_tuple) + else: # it's a datetime.time instance + date = dt.datetime.combine(dt.datetime(1900, 1, 1), date) + epoch = dt.datetime(1900, 1, 1) + delta = date - epoch + xldate = delta.days + delta.seconds / 86400.0 + # Add a day for Excel's missing leap day in 1900 + if adj and xldate > 59: + xldate += 1 + return xldate + + def get_height_in_pixels(self): + return self.__height_in_pixels + + + def set_style(self, style): + self.__adjust_height(style) + self.__xf_index = self.__parent_wb.add_style(style) + self.__has_default_xf_index = 1 + + + def get_xf_index(self): + return self.__xf_index + + + def get_cells_count(self): + return len(self.__cells) + + + def get_min_col(self): + return self.__min_col_idx + + + def get_max_col(self): + return self.__max_col_idx + + + def get_row_biff_data(self): + height_options = (self.height & 0x07FFF) + height_options |= (self.has_default_height & 0x01) << 15 + + options = (self.level & 0x07) << 0 + options |= (self.collapse & 0x01) << 4 + options |= (self.hidden & 0x01) << 5 + options |= (self.height_mismatch & 0x01) << 6 + options |= (self.__has_default_xf_index & 0x01) << 7 + options |= (0x01 & 0x01) << 8 + options |= (self.__xf_index & 0x0FFF) << 16 + options |= (self.space_above & 1) << 28 + options |= (self.space_below & 1) << 29 + + return BIFFRecords.RowRecord(self.__idx, self.__min_col_idx, + self.__max_col_idx, height_options, options).get() + + def insert_cell(self, col_index, cell_obj): + if col_index in self.__cells: + if not self.__parent._cell_overwrite_ok: + msg = "Attempt to overwrite cell: sheetname=%r rowx=%d colx=%d" \ + % (self.__parent.name, self.__idx, col_index) + raise Exception(msg) + prev_cell_obj = self.__cells[col_index] + sst_idx = getattr(prev_cell_obj, 'sst_idx', None) + if sst_idx is not None: + self.__parent_wb.del_str(sst_idx) + self.__cells[col_index] = cell_obj + + def insert_mulcells(self, colx1, colx2, cell_obj): + self.insert_cell(colx1, cell_obj) + for col_index in xrange(colx1+1, colx2+1): + self.insert_cell(col_index, None) + + def get_cells_biff_data(self): + cell_items = [item for item in iteritems(self.__cells) if item[1] is not None] + cell_items.sort() # in column order + return _get_cells_biff_data_mul(self.__idx, cell_items) + # previously: + # return ''.join([cell.get_biff_data() for colx, cell in cell_items]) + + def get_index(self): + return self.__idx + + def set_cell_text(self, colx, value, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.insert_cell(colx, StrCell(self.__idx, colx, xf_index, self.__parent_wb.add_str(value))) + + def set_cell_blank(self, colx, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.insert_cell(colx, BlankCell(self.__idx, colx, xf_index)) + + def set_cell_mulblanks(self, first_colx, last_colx, style=Style.default_style): + assert 0 <= first_colx <= last_colx <= 255 + self.__adjust_height(style) + self.__adjust_bound_col_idx(first_colx, last_colx) + xf_index = self.__parent_wb.add_style(style) + # ncols = last_colx - first_colx + 1 + self.insert_mulcells(first_colx, last_colx, MulBlankCell(self.__idx, first_colx, last_colx, xf_index)) + + def set_cell_number(self, colx, number, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.insert_cell(colx, NumberCell(self.__idx, colx, xf_index, number)) + + def set_cell_date(self, colx, datetime_obj, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.insert_cell(colx, + NumberCell(self.__idx, colx, xf_index, self.__excel_date_dt(datetime_obj))) + + def set_cell_formula(self, colx, formula, style=Style.default_style, calc_flags=0): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.__parent_wb.add_sheet_reference(formula) + self.insert_cell(colx, FormulaCell(self.__idx, colx, xf_index, formula, calc_flags=0)) + + def set_cell_boolean(self, colx, value, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.insert_cell(colx, BooleanCell(self.__idx, colx, xf_index, bool(value))) + + def set_cell_error(self, colx, error_string_or_code, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(colx) + xf_index = self.__parent_wb.add_style(style) + self.insert_cell(colx, ErrorCell(self.__idx, colx, xf_index, error_string_or_code)) + + def write(self, col, label, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(col) + style_index = self.__parent_wb.add_style(style) + if isinstance(label, basestring): + if len(label) > 0: + self.insert_cell(col, + StrCell(self.__idx, col, style_index, self.__parent_wb.add_str(label)) + ) + else: + self.insert_cell(col, BlankCell(self.__idx, col, style_index)) + elif isinstance(label, bool): # bool is subclass of int; test bool first + self.insert_cell(col, BooleanCell(self.__idx, col, style_index, label)) + elif isinstance(label, int_types+(float, Decimal)): + self.insert_cell(col, NumberCell(self.__idx, col, style_index, label)) + elif isinstance(label, (dt.datetime, dt.date, dt.time)): + date_number = self.__excel_date_dt(label) + self.insert_cell(col, NumberCell(self.__idx, col, style_index, date_number)) + elif label is None: + self.insert_cell(col, BlankCell(self.__idx, col, style_index)) + elif isinstance(label, ExcelFormula.Formula): + self.__parent_wb.add_sheet_reference(label) + self.insert_cell(col, FormulaCell(self.__idx, col, style_index, label)) + elif isinstance(label, (list, tuple)): + self.__rich_text_helper(col, label, style, style_index) + else: + raise Exception("Unexpected data type %r" % type(label)) + + def set_cell_rich_text(self, col, rich_text_list, style=Style.default_style): + self.__adjust_height(style) + self.__adjust_bound_col_idx(col) + if not isinstance(rich_text_list, (list, tuple)): + raise Exception("Unexpected data type %r" % type(rich_text_list)) + self.__rich_text_helper(col, rich_text_list, style) + + def __rich_text_helper(self, col, rich_text_list, style, style_index=None): + if style_index is None: + style_index = self.__parent_wb.add_style(style) + default_font = None + rt = [] + for data in rich_text_list: + if isinstance(data, basestring): + s = data + font = default_font + elif isinstance(data, (list, tuple)): + if not isinstance(data[0], basestring) or not isinstance(data[1], Font): + raise Exception ("Unexpected data type %r, %r" % (type(data[0]), type(data[1]))) + s = data[0] + font = self.__parent_wb.add_font(data[1]) + else: + raise Exception ("Unexpected data type %r" % type(data)) + if s: + rt.append((s, font)) + if default_font is None: + default_font = self.__parent_wb.add_font(style.font) + if rt: + self.insert_cell(col, StrCell(self.__idx, col, style_index, self.__parent_wb.add_rt(rt))) + else: + self.insert_cell(col, BlankCell(self.__idx, col, style_index)) + + write_blanks = set_cell_mulblanks + write_rich_text = set_cell_rich_text + + + + diff --git a/.venv/lib/python3.9/site-packages/xlwt/Style.py b/.venv/lib/python3.9/site-packages/xlwt/Style.py new file mode 100644 index 00000000..dc9fc949 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Style.py @@ -0,0 +1,741 @@ +from __future__ import print_function +# -*- coding: windows-1252 -*- + +from . import Formatting +from .BIFFRecords import NumberFormatRecord, XFRecord, StyleRecord +from .compat import basestring, xrange + +FIRST_USER_DEFINED_NUM_FORMAT_IDX = 164 + +class XFStyle(object): + + def __init__(self): + self.num_format_str = 'General' + self.font = Formatting.Font() + self.alignment = Formatting.Alignment() + self.borders = Formatting.Borders() + self.pattern = Formatting.Pattern() + self.protection = Formatting.Protection() + +default_style = XFStyle() + +class StyleCollection(object): + _std_num_fmt_list = [ + 'general', + '0', + '0.00', + '#,##0', + '#,##0.00', + '"$"#,##0_);("$"#,##0)', + '"$"#,##0_);[Red]("$"#,##0)', + '"$"#,##0.00_);("$"#,##0.00)', + '"$"#,##0.00_);[Red]("$"#,##0.00)', + '0%', + '0.00%', + '0.00E+00', + '# ?/?', + '# ??/??', + 'M/D/YY', + 'D-MMM-YY', + 'D-MMM', + 'MMM-YY', + 'h:mm AM/PM', + 'h:mm:ss AM/PM', + 'h:mm', + 'h:mm:ss', + 'M/D/YY h:mm', + '_(#,##0_);(#,##0)', + '_(#,##0_);[Red](#,##0)', + '_(#,##0.00_);(#,##0.00)', + '_(#,##0.00_);[Red](#,##0.00)', + '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)', + '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', + '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)', + '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', + 'mm:ss', + '[h]:mm:ss', + 'mm:ss.0', + '##0.0E+0', + '@' + ] + + def __init__(self, style_compression=0): + self.style_compression = style_compression + self.stats = [0, 0, 0, 0, 0, 0] + self._font_id2x = {} + self._font_x2id = {} + self._font_val2x = {} + + for x in (0, 1, 2, 3, 5): # The font with index 4 is omitted in all BIFF versions + font = Formatting.Font() + search_key = font._search_key() + self._font_id2x[font] = x + self._font_x2id[x] = font + self._font_val2x[search_key] = x + + self._xf_id2x = {} + self._xf_x2id = {} + self._xf_val2x = {} + + self._num_formats = {} + for fmtidx, fmtstr in zip(range(0, 23), StyleCollection._std_num_fmt_list[0:23]): + self._num_formats[fmtstr] = fmtidx + for fmtidx, fmtstr in zip(range(37, 50), StyleCollection._std_num_fmt_list[23:]): + self._num_formats[fmtstr] = fmtidx + + self.default_style = XFStyle() + self._default_xf = self._add_style(self.default_style)[0] + + def add(self, style): + if style == None: + return 0x10 + return self._add_style(style)[1] + + def _add_style(self, style): + num_format_str = style.num_format_str + if num_format_str in self._num_formats: + num_format_idx = self._num_formats[num_format_str] + else: + num_format_idx = ( + FIRST_USER_DEFINED_NUM_FORMAT_IDX + + len(self._num_formats) + - len(StyleCollection._std_num_fmt_list) + ) + self._num_formats[num_format_str] = num_format_idx + + font = style.font + if font in self._font_id2x: + font_idx = self._font_id2x[font] + self.stats[0] += 1 + elif self.style_compression: + search_key = font._search_key() + font_idx = self._font_val2x.get(search_key) + if font_idx is not None: + self._font_id2x[font] = font_idx + self.stats[1] += 1 + else: + font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing + self._font_id2x[font] = font_idx + self._font_val2x[search_key] = font_idx + self._font_x2id[font_idx] = font + self.stats[2] += 1 + else: + font_idx = len(self._font_id2x) + 1 + self._font_id2x[font] = font_idx + self.stats[2] += 1 + + gof = (style.alignment, style.borders, style.pattern, style.protection) + xf = (font_idx, num_format_idx) + gof + if xf in self._xf_id2x: + xf_index = self._xf_id2x[xf] + self.stats[3] += 1 + elif self.style_compression == 2: + xf_key = (font_idx, num_format_idx) + tuple(obj._search_key() for obj in gof) + xf_index = self._xf_val2x.get(xf_key) + if xf_index is not None: + self._xf_id2x[xf] = xf_index + self.stats[4] += 1 + else: + xf_index = 0x10 + len(self._xf_x2id) + self._xf_id2x[xf] = xf_index + self._xf_val2x[xf_key] = xf_index + self._xf_x2id[xf_index] = xf + self.stats[5] += 1 + else: + xf_index = 0x10 + len(self._xf_id2x) + self._xf_id2x[xf] = xf_index + self.stats[5] += 1 + + if xf_index >= 0xFFF: + # 12 bits allowed, 0xFFF is a sentinel value + raise ValueError("More than 4094 XFs (styles)") + + return xf, xf_index + + def add_font(self, font): + return self._add_font(font) + + def _add_font(self, font): + if font in self._font_id2x: + font_idx = self._font_id2x[font] + self.stats[0] += 1 + elif self.style_compression: + search_key = font._search_key() + font_idx = self._font_val2x.get(search_key) + if font_idx is not None: + self._font_id2x[font] = font_idx + self.stats[1] += 1 + else: + font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing + self._font_id2x[font] = font_idx + self._font_val2x[search_key] = font_idx + self._font_x2id[font_idx] = font + self.stats[2] += 1 + else: + font_idx = len(self._font_id2x) + 1 + self._font_id2x[font] = font_idx + self.stats[2] += 1 + + return font_idx + + + def get_biff_data(self): + result = b'' + result += self._all_fonts() + result += self._all_num_formats() + result += self._all_cell_styles() + result += self._all_styles() + return result + + def _all_fonts(self): + result = b'' + if self.style_compression: + fonts = self._font_x2id.items() + else: + fonts = [(x, o) for o, x in self._font_id2x.items()] + for font_idx, font in sorted(fonts): + result += font.get_biff_record().get() + return result + + def _all_num_formats(self): + result = b'' + alist = [ + (v, k) + for k, v in self._num_formats.items() + if v >= FIRST_USER_DEFINED_NUM_FORMAT_IDX + ] + alist.sort() + for fmtidx, fmtstr in alist: + result += NumberFormatRecord(fmtidx, fmtstr).get() + return result + + def _all_cell_styles(self): + result = b'' + for i in range(0, 16): + result += XFRecord(self._default_xf, 'style').get() + if self.style_compression == 2: + styles = self._xf_x2id.items() + else: + styles = [(x, o) for o, x in self._xf_id2x.items()] + for xf_idx, xf in sorted(styles): + result += XFRecord(xf).get() + return result + + def _all_styles(self): + return StyleRecord().get() + +# easyxf and its supporting objects ################################### + +class EasyXFException(Exception): + pass + +class EasyXFCallerError(EasyXFException): + pass + +class EasyXFAuthorError(EasyXFException): + pass + +class IntULim(object): + # If astring represents a valid unsigned integer ('123', '0xabcd', etc) + # and it is <= limit, return the int value; otherwise return None. + + def __init__(self, limit): + self.limit = limit + + def __call__(self, astring): + try: + value = int(astring, 0) + except ValueError: + return None + if not 0 <= value <= self.limit: + return None + return value + +bool_map = { + # Text values for all Boolean attributes + '1': 1, 'yes': 1, 'true': 1, 'on': 1, + '0': 0, 'no': 0, 'false': 0, 'off': 0, + } + +border_line_map = { + # Text values for these borders attributes: + # left, right, top, bottom and diag + 'no_line': 0x00, + 'thin': 0x01, + 'medium': 0x02, + 'dashed': 0x03, + 'dotted': 0x04, + 'thick': 0x05, + 'double': 0x06, + 'hair': 0x07, + 'medium_dashed': 0x08, + 'thin_dash_dotted': 0x09, + 'medium_dash_dotted': 0x0a, + 'thin_dash_dot_dotted': 0x0b, + 'medium_dash_dot_dotted': 0x0c, + 'slanted_medium_dash_dotted': 0x0d, + } + +charset_map = { + # Text values for font.charset + 'ansi_latin': 0x00, + 'sys_default': 0x01, + 'symbol': 0x02, + 'apple_roman': 0x4d, + 'ansi_jap_shift_jis': 0x80, + 'ansi_kor_hangul': 0x81, + 'ansi_kor_johab': 0x82, + 'ansi_chinese_gbk': 0x86, + 'ansi_chinese_big5': 0x88, + 'ansi_greek': 0xa1, + 'ansi_turkish': 0xa2, + 'ansi_vietnamese': 0xa3, + 'ansi_hebrew': 0xb1, + 'ansi_arabic': 0xb2, + 'ansi_baltic': 0xba, + 'ansi_cyrillic': 0xcc, + 'ansi_thai': 0xde, + 'ansi_latin_ii': 0xee, + 'oem_latin_i': 0xff, + } + + +# Text values for colour indices. "grey" is a synonym of "gray". +# The names are those given by Microsoft Excel 2003 to the colours +# in the default palette. There is no great correspondence with +# any W3C name-to-RGB mapping. +_colour_map_text = """\ +aqua 0x31 +black 0x08 +blue 0x0C +blue_gray 0x36 +bright_green 0x0B +brown 0x3C +coral 0x1D +cyan_ega 0x0F +dark_blue 0x12 +dark_blue_ega 0x12 +dark_green 0x3A +dark_green_ega 0x11 +dark_purple 0x1C +dark_red 0x10 +dark_red_ega 0x10 +dark_teal 0x38 +dark_yellow 0x13 +gold 0x33 +gray_ega 0x17 +gray25 0x16 +gray40 0x37 +gray50 0x17 +gray80 0x3F +green 0x11 +ice_blue 0x1F +indigo 0x3E +ivory 0x1A +lavender 0x2E +light_blue 0x30 +light_green 0x2A +light_orange 0x34 +light_turquoise 0x29 +light_yellow 0x2B +lime 0x32 +magenta_ega 0x0E +ocean_blue 0x1E +olive_ega 0x13 +olive_green 0x3B +orange 0x35 +pale_blue 0x2C +periwinkle 0x18 +pink 0x0E +plum 0x3D +purple_ega 0x14 +red 0x0A +rose 0x2D +sea_green 0x39 +silver_ega 0x16 +sky_blue 0x28 +tan 0x2F +teal 0x15 +teal_ega 0x15 +turquoise 0x0F +violet 0x14 +white 0x09 +yellow 0x0D""" + +colour_map = {} +for _line in _colour_map_text.splitlines(): + _name, _num = _line.split() + _num = int(_num, 0) + colour_map[_name] = _num + if 'gray' in _name: + colour_map[_name.replace('gray', 'grey')] = _num +del _colour_map_text, _line, _name, _num + +def add_palette_colour(colour_str, colour_index): + if not (8 <= colour_index <= 63): + raise Exception("add_palette_colour: colour_index (%d) not in range(8, 64)" % + (colour_index)) + colour_map[colour_str] = colour_index + +# user-defined palette defines 56 RGB colors from entry 8 - 64 +#excel_default_palette_b8 = [ # (red, green, blue) +# ( 0, 0, 0), (255,255,255), (255, 0, 0), ( 0,255, 0), +# ( 0, 0,255), (255,255, 0), (255, 0,255), ( 0,255,255), +# (128, 0, 0), ( 0,128, 0), ( 0, 0,128), (128,128, 0), +# (128, 0,128), ( 0,128,128), (192,192,192), (128,128,128), +# (153,153,255), (153, 51,102), (255,255,204), (204,255,255), +# (102, 0,102), (255,128,128), ( 0,102,204), (204,204,255), +# ( 0, 0,128), (255, 0,255), (255,255, 0), ( 0,255,255), +# (128, 0,128), (128, 0, 0), ( 0,128,128), ( 0, 0,255), +# ( 0,204,255), (204,255,255), (204,255,204), (255,255,153), +# (153,204,255), (255,153,204), (204,153,255), (255,204,153), +# ( 51,102,255), ( 51,204,204), (153,204, 0), (255,204, 0), +# (255,153, 0), (255,102, 0), (102,102,153), (150,150,150), +# ( 0, 51,102), ( 51,153,102), ( 0, 51, 0), ( 51, 51, 0), +# (153, 51, 0), (153, 51,102), ( 51, 51,153), ( 51, 51, 51), +# ] + +# Default colour table for BIFF8 copied from +# OpenOffice.org's Documentation of the Microsoft Excel File Format, Excel Version 2003 +# Note palette has LSB padded with 2 bytes 0x00 +excel_default_palette_b8 = ( +0x00000000, +0xFFFFFF00, +0xFF000000, +0x00FF0000, +0x0000FF00, +0xFFFF0000, +0xFF00FF00, +0x00FFFF00, +0x80000000, +0x00800000, +0x00008000, +0x80800000, +0x80008000, +0x00808000, +0xC0C0C000, +0x80808000, +0x9999FF00, +0x99336600, +0xFFFFCC00, +0xCCFFFF00, +0x66006600, +0xFF808000, +0x0066CC00, +0xCCCCFF00, +0x00008000, +0xFF00FF00, +0xFFFF0000, +0x00FFFF00, +0x80008000, +0x80000000, +0x00808000, +0x0000FF00, +0x00CCFF00, +0xCCFFFF00, +0xCCFFCC00, +0xFFFF9900, +0x99CCFF00, +0xFF99CC00, +0xCC99FF00, +0xFFCC9900, +0x3366FF00, +0x33CCCC00, +0x99CC0000, +0xFFCC0000, +0xFF990000, +0xFF660000, +0x66669900, +0x96969600, +0x00336600, +0x33996600, +0x00330000, +0x33330000, +0x99330000, +0x99336600, +0x33339900, +0x33333300) + +assert len(excel_default_palette_b8) == 56 + +pattern_map = { + # Text values for pattern.pattern + # xlwt/doc/pattern_examples.xls showcases all of these patterns. + 'no_fill': 0, + 'none': 0, + 'solid': 1, + 'solid_fill': 1, + 'solid_pattern': 1, + 'fine_dots': 2, + 'alt_bars': 3, + 'sparse_dots': 4, + 'thick_horz_bands': 5, + 'thick_vert_bands': 6, + 'thick_backward_diag': 7, + 'thick_forward_diag': 8, + 'big_spots': 9, + 'bricks': 10, + 'thin_horz_bands': 11, + 'thin_vert_bands': 12, + 'thin_backward_diag': 13, + 'thin_forward_diag': 14, + 'squares': 15, + 'diamonds': 16, + } + +def any_str_func(s): + return s.strip() + +def colour_index_func(s, maxval=0x7F): + try: + value = int(s, 0) + except ValueError: + return None + if not (0 <= value <= maxval): + return None + return value + +colour_index_func_7 = colour_index_func + +def colour_index_func_15(s): + return colour_index_func(s, maxval=0x7FFF) + +def rotation_func(s): + try: + value = int(s, 0) + except ValueError: + return None + if not (-90 <= value <= 90): + raise EasyXFCallerError("rotation %d: should be -90 to +90 degrees" % value) + if value < 0: + value = 90 - value # encode as 91 to 180 (clockwise) + return value + +xf_dict = { + 'align': 'alignment', # synonym + 'alignment': { + 'dire': { + 'general': 0, + 'lr': 1, + 'rl': 2, + }, + 'direction': 'dire', + 'horiz': 'horz', + 'horizontal': 'horz', + 'horz': { + 'general': 0, + 'left': 1, + 'center': 2, + 'centre': 2, # "align: horiz centre" means xf.alignment.horz is set to 2 + 'right': 3, + 'filled': 4, + 'justified': 5, + 'center_across_selection': 6, + 'centre_across_selection': 6, + 'distributed': 7, + }, + 'inde': IntULim(15), # restriction: 0 <= value <= 15 + 'indent': 'inde', + 'rota': [{'stacked': 255, 'none': 0, }, rotation_func], + 'rotation': 'rota', + 'shri': bool_map, + 'shrink': 'shri', + 'shrink_to_fit': 'shri', + 'vert': { + 'top': 0, + 'center': 1, + 'centre': 1, + 'bottom': 2, + 'justified': 3, + 'distributed': 4, + }, + 'vertical': 'vert', + 'wrap': bool_map, + }, + 'border': 'borders', + 'borders': { + 'left': [border_line_map, IntULim(0x0d)], + 'right': [border_line_map, IntULim(0x0d)], + 'top': [border_line_map, IntULim(0x0d)], + 'bottom': [border_line_map, IntULim(0x0d)], + 'diag': [border_line_map, IntULim(0x0d)], + 'top_colour': [colour_map, colour_index_func_7], + 'bottom_colour': [colour_map, colour_index_func_7], + 'left_colour': [colour_map, colour_index_func_7], + 'right_colour': [colour_map, colour_index_func_7], + 'diag_colour': [colour_map, colour_index_func_7], + 'top_color': 'top_colour', + 'bottom_color': 'bottom_colour', + 'left_color': 'left_colour', + 'right_color': 'right_colour', + 'diag_color': 'diag_colour', + 'need_diag1': bool_map, + 'need_diag2': bool_map, + }, + 'font': { + 'bold': bool_map, + 'charset': charset_map, + 'color': 'colour_index', + 'color_index': 'colour_index', + 'colour': 'colour_index', + 'colour_index': [colour_map, colour_index_func_15], + 'escapement': {'none': 0, 'superscript': 1, 'subscript': 2}, + 'family': {'none': 0, 'roman': 1, 'swiss': 2, 'modern': 3, 'script': 4, 'decorative': 5, }, + 'height': IntULim(0xFFFF), # practical limits are much narrower e.g. 160 to 1440 (8pt to 72pt) + 'italic': bool_map, + 'name': any_str_func, + 'outline': bool_map, + 'shadow': bool_map, + 'struck_out': bool_map, + 'underline': [bool_map, {'none': 0, 'single': 1, 'single_acc': 0x21, 'double': 2, 'double_acc': 0x22, }], + }, + 'pattern': { + 'back_color': 'pattern_back_colour', + 'back_colour': 'pattern_back_colour', + 'fore_color': 'pattern_fore_colour', + 'fore_colour': 'pattern_fore_colour', + 'pattern': [pattern_map, IntULim(16)], + 'pattern_back_color': 'pattern_back_colour', + 'pattern_back_colour': [colour_map, colour_index_func_7], + 'pattern_fore_color': 'pattern_fore_colour', + 'pattern_fore_colour': [colour_map, colour_index_func_7], + }, + 'protection': { + 'cell_locked' : bool_map, + 'formula_hidden': bool_map, + }, + } + +def _esplit(s, split_char, esc_char="\\"): + escaped = False + olist = [''] + for c in s: + if escaped: + olist[-1] += c + escaped = False + elif c == esc_char: + escaped = True + elif c == split_char: + olist.append('') + else: + olist[-1] += c + return olist + +def _parse_strg_to_obj(strg, obj, parse_dict, + field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False): + for line in _esplit(strg, line_sep, esc_char): + line = line.strip() + if not line: + break + split_line = _esplit(line, intro_sep, esc_char) + if len(split_line) != 2: + raise EasyXFCallerError('line %r should have exactly 1 "%c"' % (line, intro_sep)) + section, item_str = split_line + section = section.strip().lower() + for counter in range(2): + result = parse_dict.get(section) + if result is None: + raise EasyXFCallerError('section %r is unknown' % section) + if isinstance(result, dict): + break + if not isinstance(result, str): + raise EasyXFAuthorError( + 'section %r should map to dict or str object; found %r' % (section, type(result))) + # synonym + old_section = section + section = result + else: + raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_section, result)) + section_dict = result + section_obj = getattr(obj, section, None) + if section_obj is None: + raise EasyXFAuthorError('instance of %s class has no attribute named %s' % (obj.__class__.__name__, section)) + for kv_str in _esplit(item_str, field_sep, esc_char): + guff = kv_str.split() + if not guff: + continue + k = guff[0].lower().replace('-', '_') + v = ' '.join(guff[1:]) + if not v: + raise EasyXFCallerError("no value supplied for %s.%s" % (section, k)) + for counter in xrange(2): + result = section_dict.get(k) + if result is None: + raise EasyXFCallerError('%s.%s is not a known attribute' % (section, k)) + if not isinstance(result, basestring): + break + # synonym + old_k = k + k = result + else: + raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_k, result)) + value_info = result + if not isinstance(value_info, list): + value_info = [value_info] + for value_rule in value_info: + if isinstance(value_rule, dict): + # dict maps strings to integer field values + vl = v.lower().replace('-', '_') + if vl in value_rule: + value = value_rule[vl] + break + elif callable(value_rule): + value = value_rule(v) + if value is not None: + break + else: + raise EasyXFAuthorError("unknown value rule for attribute %r: %r" % (k, value_rule)) + else: + raise EasyXFCallerError("unexpected value %r for %s.%s" % (v, section, k)) + try: + orig = getattr(section_obj, k) + except AttributeError: + raise EasyXFAuthorError('%s.%s in dictionary but not in supplied object' % (section, k)) + if debug: print("+++ %s.%s = %r # %s; was %r" % (section, k, value, v, orig)) + setattr(section_obj, k, value) + +def easyxf(strg_to_parse="", num_format_str=None, + field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False): + """ + This function is used to create and configure + :class:`XFStyle` objects for use with (for example) the + :meth:`Worksheet.write` method. + + It takes a string to be parsed to obtain attribute values for + :class:`Alignment`, :class:`Borders`, :class:`Font`, :class:`Pattern` and + :class:`Protection` objects. + + Refer to the examples in the file `examples/xlwt_easyxf_simple_demo.py` + and to the `xf_dict` dictionary in :mod:`xlwt.Style`. + + Various synonyms including color/colour, center/centre and gray/grey are + allowed. Case is irrelevant (except maybe in font names). ``-`` may be used + instead of ``_``. + + Example: ``font: bold on; align: wrap on, vert centre, horiz center`` + + :param num_format_str: + + To get the "number format string" of an existing + cell whose format you want to reproduce, select the cell and click on + Format/Cells/Number/Custom. Otherwise, refer to Excel help. + + Examples: ``"#,##0.00"``, ``"dd/mm/yyyy"`` + + :return: An :class:`XFstyle` object. + + """ + xfobj = XFStyle() + if num_format_str is not None: + xfobj.num_format_str = num_format_str + if strg_to_parse: + _parse_strg_to_obj(strg_to_parse, xfobj, xf_dict, + field_sep=field_sep, line_sep=line_sep, intro_sep=intro_sep, esc_char=esc_char, debug=debug) + return xfobj + +def easyfont(strg_to_parse="", field_sep=",", esc_char="\\", debug=False): + xfobj = XFStyle() + if strg_to_parse: + _parse_strg_to_obj("font: " + strg_to_parse, xfobj, xf_dict, + field_sep=field_sep, line_sep=";", intro_sep=":", esc_char=esc_char, debug=debug) + return xfobj.font diff --git a/.venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py b/.venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py new file mode 100644 index 00000000..2ec45df4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/UnicodeUtils.py @@ -0,0 +1,122 @@ +# -*- coding: windows-1252 -*- + +''' +From BIFF8 on, strings are always stored using UTF-16LE text encoding. The +character array is a sequence of 16-bit values4. Additionally it is +possible to use a compressed format, which omits the high bytes of all +characters, if they are all zero. + +The following tables describe the standard format of the entire string, but +in many records the strings differ from this format. This will be mentioned +separately. It is possible (but not required) to store Rich-Text formatting +information and Asian phonetic information inside a Unicode string. This +results in four different ways to store a string. The character array +is not zero-terminated. + +The string consists of the character count (as usual an 8-bit value or +a 16-bit value), option flags, the character array and optional formatting +information. If the string is empty, sometimes the option flags field will +not occur. This is mentioned at the respective place. + +Offset Size Contents +0 1 or 2 Length of the string (character count, ln) +1 or 2 1 Option flags: + Bit Mask Contents + 0 01H Character compression (ccompr): + 0 = Compressed (8-bit characters) + 1 = Uncompressed (16-bit characters) + 2 04H Asian phonetic settings (phonetic): + 0 = Does not contain Asian phonetic settings + 1 = Contains Asian phonetic settings + 3 08H Rich-Text settings (richtext): + 0 = Does not contain Rich-Text settings + 1 = Contains Rich-Text settings +[2 or 3] 2 (optional, only if richtext=1) Number of Rich-Text formatting runs (rt) +[var.] 4 (optional, only if phonetic=1) Size of Asian phonetic settings block (in bytes, sz) +var. ln or + 2·ln Character array (8-bit characters or 16-bit characters, dependent on ccompr) +[var.] 4·rt (optional, only if richtext=1) List of rt formatting runs +[var.] sz (optional, only if phonetic=1) Asian Phonetic Settings Block +''' + +from .compat import unicode, unicode_type +from struct import pack + +def upack2(s, encoding='ascii'): + # If not unicode, make it so. + if isinstance(s, unicode_type): + us = s + else: + us = unicode(s, encoding) + # Limit is based on number of content characters + # (not on number of bytes in packed result) + len_us = len(us) + if len_us > 32767: + raise Exception('String longer than 32767 characters') + try: + encs = us.encode('latin1') + # Success here means all chars are in U+0000 to U+00FF + # inclusive, meaning that we can use "compressed format". + flag = 0 + n_items = len_us + except UnicodeEncodeError: + encs = us.encode('utf_16_le') + flag = 1 + n_items = len(encs) // 2 + # n_items is the number of "double byte characters" i.e. MS C wchars + # Can't use len(us). + # len(u"\U0001D400") -> 1 on a wide-unicode build + # and 2 on a narrow-unicode build. + # We need n_items == 2 in this case. + return pack('<HB', n_items, flag) + encs + +def upack2rt(rt, encoding='ascii'): + us = u'' + fr = b'' + offset = 0 + # convert rt strings to unicode if not already unicode + # also generate the formatting run for the styles added + for s, fontx in rt: + if not isinstance(s, unicode_type): + s = unicode(s, encoding) + us += s + if fontx is not None: + # code in Rows.py ensures that + # fontx can be None only for the first piece + fr += pack('<HH', offset, fontx) + # offset is the number of MS C wchar characters. + # That is 1 if c <= u'\uFFFF' else 2 + offset += len(s.encode('utf_16_le')) // 2 + num_fr = len(fr) // 4 # ensure result is int + if offset > 32767: + raise Exception('String longer than 32767 characters') + try: + encs = us.encode('latin1') + # Success here means all chars are in U+0000 to U+00FF + # inclusive, meaning that we can use "compressed format". + flag = 0 | 8 + n_items = len(encs) + except UnicodeEncodeError: + encs = us.encode('utf_16_le') + flag = 1 | 8 + n_items = len(encs) // 2 # see comments in upack2 function above + return pack('<HBH', n_items, flag, num_fr) + encs, fr + +def upack1(s, encoding='ascii'): + # Same as upack2(), but with a one-byte length field. + if isinstance(s, unicode_type): + us = s + else: + us = unicode(s, encoding) + len_us = len(us) + if len_us > 255: + raise Exception('String longer than 255 characters') + try: + encs = us.encode('latin1') + flag = 0 + n_items = len_us + except UnicodeEncodeError: + encs = us.encode('utf_16_le') + flag = 1 + n_items = len(encs) // 2 + return pack('<BB', n_items, flag) + encs diff --git a/.venv/lib/python3.9/site-packages/xlwt/Utils.py b/.venv/lib/python3.9/site-packages/xlwt/Utils.py new file mode 100644 index 00000000..4f6e9180 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Utils.py @@ -0,0 +1,167 @@ +# see the xlwt.license module for details of licensing. + +# Utilities for work with reference to cells and with sheetnames + +import re +from .ExcelMagic import MAX_ROW, MAX_COL +from .compat import xrange + +_re_cell_ex = re.compile(r"(\$?)([A-I]?[A-Z])(\$?)(\d+)", re.IGNORECASE) +_re_row_range = re.compile(r"\$?(\d+):\$?(\d+)") +_re_col_range = re.compile(r"\$?([A-I]?[A-Z]):\$?([A-I]?[A-Z])", re.IGNORECASE) +_re_cell_range = re.compile(r"\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE) +_re_cell_ref = re.compile(r"\$?([A-I]?[A-Z]\$?\d+)", re.IGNORECASE) + + +def col_by_name(colname): + """'A' -> 0, 'Z' -> 25, 'AA' -> 26, etc + """ + col = 0 + power = 1 + for i in xrange(len(colname)-1, -1, -1): + ch = colname[i] + col += (ord(ch) - ord('A') + 1) * power + power *= 26 + return col - 1 + + +def cell_to_rowcol(cell): + """Convert an Excel cell reference string in A1 notation + to numeric row/col notation. + + Returns: row, col, row_abs, col_abs + + """ + m = _re_cell_ex.match(cell) + if not m: + raise Exception("Ill-formed single_cell reference: %s" % cell) + col_abs, col, row_abs, row = m.groups() + row_abs = bool(row_abs) + col_abs = bool(col_abs) + row = int(row) - 1 + col = col_by_name(col.upper()) + return row, col, row_abs, col_abs + + +def cell_to_rowcol2(cell): + """Convert an Excel cell reference string in A1 notation + to numeric row/col notation. + + Returns: row, col + + """ + m = _re_cell_ex.match(cell) + if not m: + raise Exception("Error in cell format") + col_abs, col, row_abs, row = m.groups() + # Convert base26 column string to number + # All your Base are belong to us. + row = int(row) - 1 + col = col_by_name(col.upper()) + return row, col + + +def rowcol_to_cell(row, col, row_abs=False, col_abs=False): + """Convert numeric row/col notation to an Excel cell reference string in + A1 notation. + + """ + assert 0 <= row < MAX_ROW # MAX_ROW counts from 1 + assert 0 <= col < MAX_COL # MAX_COL counts from 1 + d = col // 26 + m = col % 26 + chr1 = "" # Most significant character in AA1 + if row_abs: + row_abs = '$' + else: + row_abs = '' + if col_abs: + col_abs = '$' + else: + col_abs = '' + if d > 0: + chr1 = chr(ord('A') + d - 1) + chr2 = chr(ord('A') + m) + # Zero index to 1-index + return col_abs + chr1 + chr2 + row_abs + str(row + 1) + +def rowcol_pair_to_cellrange(row1, col1, row2, col2, + row1_abs=False, col1_abs=False, row2_abs=False, col2_abs=False): + """Convert two (row,column) pairs + into a cell range string in A1:B2 notation. + + Returns: cell range string + """ + assert row1 <= row2 + assert col1 <= col2 + return ( + rowcol_to_cell(row1, col1, row1_abs, col1_abs) + + ":" + + rowcol_to_cell(row2, col2, row2_abs, col2_abs) + ) + +def cellrange_to_rowcol_pair(cellrange): + """Convert cell range string in A1 notation to numeric row/col + pair. + + Returns: row1, col1, row2, col2 + + """ + cellrange = cellrange.upper() + # Convert a row range: '1:3' + res = _re_row_range.match(cellrange) + if res: + row1 = int(res.group(1)) - 1 + col1 = 0 + row2 = int(res.group(2)) - 1 + col2 = -1 + return row1, col1, row2, col2 + # Convert a column range: 'A:A' or 'B:G'. + # A range such as A:A is equivalent to A1:A16384, so add rows as required + res = _re_col_range.match(cellrange) + if res: + col1 = col_by_name(res.group(1).upper()) + row1 = 0 + col2 = col_by_name(res.group(2).upper()) + row2 = -1 + return row1, col1, row2, col2 + # Convert a cell range: 'A1:B7' + res = _re_cell_range.match(cellrange) + if res: + row1, col1 = cell_to_rowcol2(res.group(1)) + row2, col2 = cell_to_rowcol2(res.group(2)) + return row1, col1, row2, col2 + # Convert a cell reference: 'A1' or 'AD2000' + res = _re_cell_ref.match(cellrange) + if res: + row1, col1 = cell_to_rowcol2(res.group(1)) + return row1, col1, row1, col1 + raise Exception("Unknown cell reference %s" % (cellrange)) + + +def cell_to_packed_rowcol(cell): + """ pack row and column into the required 4 byte format """ + row, col, row_abs, col_abs = cell_to_rowcol(cell) + if col >= MAX_COL: + raise Exception("Column %s greater than IV in formula" % cell) + if row >= MAX_ROW: # this for BIFF8. for BIFF7 available 2^14 + raise Exception("Row %s greater than %d in formula" % (cell, MAX_ROW)) + col |= int(not row_abs) << 15 + col |= int(not col_abs) << 14 + return row, col + +# === sheetname functions === + +def valid_sheet_name(sheet_name): + if sheet_name == u"" or sheet_name[0] == u"'" or len(sheet_name) > 31: + return False + for c in sheet_name: + if c in u"[]:\\?/*\x00": + return False + return True + +def quote_sheet_name(unquoted_sheet_name): + if not valid_sheet_name(unquoted_sheet_name): + raise Exception( + 'attempt to quote an invalid worksheet name %r' % unquoted_sheet_name) + return u"'" + unquoted_sheet_name.replace(u"'", u"''") + u"'" diff --git a/.venv/lib/python3.9/site-packages/xlwt/Workbook.py b/.venv/lib/python3.9/site-packages/xlwt/Workbook.py new file mode 100644 index 00000000..4209ea06 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Workbook.py @@ -0,0 +1,712 @@ +# -*- coding: windows-1252 -*- +# Record Order in BIFF8 +# Workbook Globals Substream +# BOF Type = workbook globals +# Interface Header +# MMS +# Interface End +# WRITEACCESS +# CODEPAGE +# DSF +# TABID +# FNGROUPCOUNT +# Workbook Protection Block +# WINDOWPROTECT +# PROTECT +# PASSWORD +# PROT4REV +# PROT4REVPASS +# BACKUP +# HIDEOBJ +# WINDOW1 +# DATEMODE +# PRECISION +# REFRESHALL +# BOOKBOOL +# FONT + +# FORMAT * +# XF + +# STYLE + +# ? PALETTE +# USESELFS +# +# BOUNDSHEET + +# +# COUNTRY +# ? Link Table +# SST +# ExtSST +# EOF + +from . import BIFFRecords +from . import Style +from .compat import unicode_type, int_types, basestring + +class Workbook(object): + """ + This is a class representing a workbook and all its contents. When creating + Excel files with xlwt, you will normally start by instantiating an + object of this class. + """ + + ################################################################# + ## Constructor + ################################################################# + def __init__(self, encoding='ascii', style_compression=0): + self.encoding = encoding + self.__owner = 'None' + self.__country_code = None # 0x07 is Russia :-) + self.__wnd_protect = 0 + self.__obj_protect = 0 + self.__protect = 0 + self.__backup_on_save = 0 + # for WINDOW1 record + self.__hpos_twips = 0x01E0 + self.__vpos_twips = 0x005A + self.__width_twips = 0x3FCF + self.__height_twips = 0x2A4E + self.__custom_palette_b8 = None + + self.__active_sheet = 0 + self.__first_tab_index = 0 + self.__selected_tabs = 0x01 + self.__tab_width_twips = 0x0258 + + self.__wnd_hidden = 0 + self.__wnd_mini = 0 + self.__hscroll_visible = 1 + self.__vscroll_visible = 1 + self.__tabs_visible = 1 + + self.__styles = Style.StyleCollection(style_compression) + + self.__dates_1904 = 0 + self.__use_cell_values = 1 + + self.__sst = BIFFRecords.SharedStringTable(self.encoding) + + self.__worksheets = [] + self.__worksheet_idx_from_name = {} + self.__sheet_refs = {} + self._supbook_xref = {} + self._xcall_xref = {} + self._ownbook_supbookx = None + self._ownbook_supbook_ref = None + self._xcall_supbookx = None + self._xcall_supbook_ref = None + + + + ################################################################# + ## Properties, "getters", "setters" + ################################################################# + + def get_style_stats(self): + return self.__styles.stats[:] + + def set_owner(self, value): + self.__owner = value + + def get_owner(self): + return self.__owner + + owner = property(get_owner, set_owner) + + ################################################################# + + def set_country_code(self, value): + self.__country_code = value + + def get_country_code(self): + return self.__country_code + + country_code = property(get_country_code, set_country_code) + + ################################################################# + + def set_wnd_protect(self, value): + self.__wnd_protect = int(value) + + def get_wnd_protect(self): + return bool(self.__wnd_protect) + + wnd_protect = property(get_wnd_protect, set_wnd_protect) + + ################################################################# + + def set_obj_protect(self, value): + self.__obj_protect = int(value) + + def get_obj_protect(self): + return bool(self.__obj_protect) + + obj_protect = property(get_obj_protect, set_obj_protect) + + ################################################################# + + def set_protect(self, value): + self.__protect = int(value) + + def get_protect(self): + return bool(self.__protect) + + protect = property(get_protect, set_protect) + + ################################################################# + + def set_backup_on_save(self, value): + self.__backup_on_save = int(value) + + def get_backup_on_save(self): + return bool(self.__backup_on_save) + + backup_on_save = property(get_backup_on_save, set_backup_on_save) + + ################################################################# + + def set_hpos(self, value): + self.__hpos_twips = value & 0xFFFF + + def get_hpos(self): + return self.__hpos_twips + + hpos = property(get_hpos, set_hpos) + + ################################################################# + + def set_vpos(self, value): + self.__vpos_twips = value & 0xFFFF + + def get_vpos(self): + return self.__vpos_twips + + vpos = property(get_vpos, set_vpos) + + ################################################################# + + def set_width(self, value): + self.__width_twips = value & 0xFFFF + + def get_width(self): + return self.__width_twips + + width = property(get_width, set_width) + + ################################################################# + + def set_height(self, value): + self.__height_twips = value & 0xFFFF + + def get_height(self): + return self.__height_twips + + height = property(get_height, set_height) + + ################################################################# + + def set_active_sheet(self, value): + self.__active_sheet = value & 0xFFFF + self.__first_tab_index = self.__active_sheet + + def get_active_sheet(self): + return self.__active_sheet + + active_sheet = property(get_active_sheet, set_active_sheet) + + ################################################################# + + def set_tab_width(self, value): + self.__tab_width_twips = value & 0xFFFF + + def get_tab_width(self): + return self.__tab_width_twips + + tab_width = property(get_tab_width, set_tab_width) + + ################################################################# + + def set_wnd_visible(self, value): + self.__wnd_hidden = int(not value) + + def get_wnd_visible(self): + return not bool(self.__wnd_hidden) + + wnd_visible = property(get_wnd_visible, set_wnd_visible) + + ################################################################# + + def set_wnd_mini(self, value): + self.__wnd_mini = int(value) + + def get_wnd_mini(self): + return bool(self.__wnd_mini) + + wnd_mini = property(get_wnd_mini, set_wnd_mini) + + ################################################################# + + def set_hscroll_visible(self, value): + self.__hscroll_visible = int(value) + + def get_hscroll_visible(self): + return bool(self.__hscroll_visible) + + hscroll_visible = property(get_hscroll_visible, set_hscroll_visible) + + ################################################################# + + def set_vscroll_visible(self, value): + self.__vscroll_visible = int(value) + + def get_vscroll_visible(self): + return bool(self.__vscroll_visible) + + vscroll_visible = property(get_vscroll_visible, set_vscroll_visible) + + ################################################################# + + def set_tabs_visible(self, value): + self.__tabs_visible = int(value) + + def get_tabs_visible(self): + return bool(self.__tabs_visible) + + tabs_visible = property(get_tabs_visible, set_tabs_visible) + + ################################################################# + + def set_dates_1904(self, value): + self.__dates_1904 = int(value) + + def get_dates_1904(self): + return bool(self.__dates_1904) + + dates_1904 = property(get_dates_1904, set_dates_1904) + + ################################################################# + + def set_use_cell_values(self, value): + self.__use_cell_values = int(value) + + def get_use_cell_values(self): + return bool(self.__use_cell_values) + + use_cell_values = property(get_use_cell_values, set_use_cell_values) + + ################################################################# + + def get_default_style(self): + return self.__styles.default_style + + default_style = property(get_default_style) + + ################################################################# + + def set_colour_RGB(self, colour_index, red, green, blue): + if not(8 <= colour_index <= 63): + raise Exception("set_colour_RGB: colour_index (%d) not in range(8, 64)" % + colour_index) + if min(red, green, blue) < 0 or max(red, green, blue) > 255: + raise Exception("set_colour_RGB: colour values (%d,%d,%d) must be in range(0, 256)" + % (red, green, blue)) + if self.__custom_palette_b8 is None: + self.__custom_palette_b8 = list(Style.excel_default_palette_b8) + # User-defined Palette starts at colour index 8, + # so subtract 8 from colour_index when placing in palette + palette_index = colour_index - 8 + self.__custom_palette_b8[palette_index] = red << 24 | green << 16 | blue << 8 + + ################################################################## + ## Methods + ################################################################## + + def add_style(self, style): + return self.__styles.add(style) + + def add_font(self, font): + return self.__styles.add_font(font) + + def add_str(self, s): + return self.__sst.add_str(s) + + def del_str(self, sst_idx): + self.__sst.del_str(sst_idx) + + def str_index(self, s): + return self.__sst.str_index(s) + + def add_rt(self, rt): + return self.__sst.add_rt(rt) + + def rt_index(self, rt): + return self.__sst.rt_index(rt) + + def add_sheet(self, sheetname, cell_overwrite_ok=False): + """ + This method is used to create Worksheets in a Workbook. + + :param sheetname: + + The name to use for this sheet, as it will appear in the + tabs at the bottom of the Excel application. + + :param cell_overwrite_ok: + + If ``True``, cells in the added worksheet will not raise an + exception if written to more than once. + + :return: + + The :class:`~xlwt.Worksheet.Worksheet` that was added. + + """ + from . import Utils + from .Worksheet import Worksheet + if not isinstance(sheetname, unicode_type): + sheetname = sheetname.decode(self.encoding) + if not Utils.valid_sheet_name(sheetname): + raise Exception("invalid worksheet name %r" % sheetname) + lower_name = sheetname.lower() + if lower_name in self.__worksheet_idx_from_name: + raise Exception("duplicate worksheet name %r" % sheetname) + self.__worksheet_idx_from_name[lower_name] = len(self.__worksheets) + self.__worksheets.append(Worksheet(sheetname, self, cell_overwrite_ok)) + return self.__worksheets[-1] + + def get_sheet(self, sheet): + if isinstance(sheet, int_types): + return self.__worksheets[sheet] + elif isinstance(sheet, basestring): + sheetnum = self.sheet_index(sheet) + return self.__worksheets[sheetnum] + else: + raise Exception("sheet must be integer or string") + + def sheet_index(self, sheetname): + try: + sheetnum = self.__worksheet_idx_from_name[sheetname.lower()] + except KeyError: + self.raise_bad_sheetname(sheetname) + + return sheetnum + + def raise_bad_sheetname(self, sheetname): + raise Exception("Formula: unknown sheet name %s" % sheetname) + + def convert_sheetindex(self, strg_ref, n_sheets): + idx = int(strg_ref) + if 0 <= idx < n_sheets: + return idx + msg = "Formula: sheet index (%s) >= number of sheets (%d)" % (strg_ref, n_sheets) + raise Exception(msg) + + def _get_supbook_index(self, tag): + if tag in self._supbook_xref: + return self._supbook_xref[tag] + self._supbook_xref[tag] = idx = len(self._supbook_xref) + return idx + + def setup_ownbook(self): + self._ownbook_supbookx = self._get_supbook_index(('ownbook', 0)) + self._ownbook_supbook_ref = None + reference = (self._ownbook_supbookx, 0xFFFE, 0xFFFE) + if reference in self.__sheet_refs: + raise Exception("can't happen") + self.__sheet_refs[reference] = self._ownbook_supbook_ref = len(self.__sheet_refs) + + def setup_xcall(self): + self._xcall_supbookx = self._get_supbook_index(('xcall', 0)) + self._xcall_supbook_ref = None + reference = (self._xcall_supbookx, 0xFFFE, 0xFFFE) + if reference in self.__sheet_refs: + raise Exception("can't happen") + self.__sheet_refs[reference] = self._xcall_supbook_ref = len(self.__sheet_refs) + + def add_sheet_reference(self, formula): + patches = [] + n_sheets = len(self.__worksheets) + sheet_refs, xcall_refs = formula.get_references() + + for ref0, ref1, offset in sheet_refs: + if not ref0.isdigit(): + try: + ref0n = self.__worksheet_idx_from_name[ref0.lower()] + except KeyError: + self.raise_bad_sheetname(ref0) + else: + ref0n = self.convert_sheetindex(ref0, n_sheets) + if ref1 == ref0: + ref1n = ref0n + elif not ref1.isdigit(): + try: + ref1n = self.__worksheet_idx_from_name[ref1.lower()] + except KeyError: + self.raise_bad_sheetname(ref1) + else: + ref1n = self.convert_sheetindex(ref1, n_sheets) + if ref1n < ref0n: + msg = "Formula: sheets out of order; %r:%r -> (%d, %d)" \ + % (ref0, ref1, ref0n, ref1n) + raise Exception(msg) + if self._ownbook_supbookx is None: + self.setup_ownbook() + reference = (self._ownbook_supbookx, ref0n, ref1n) + if reference in self.__sheet_refs: + patches.append((offset, self.__sheet_refs[reference])) + else: + nrefs = len(self.__sheet_refs) + if nrefs > 65535: + raise Exception('More than 65536 inter-sheet references') + self.__sheet_refs[reference] = nrefs + patches.append((offset, nrefs)) + + for funcname, offset in xcall_refs: + if self._ownbook_supbookx is None: + self.setup_ownbook() + if self._xcall_supbookx is None: + self.setup_xcall() + # print funcname, self._supbook_xref + patches.append((offset, self._xcall_supbook_ref)) + if not isinstance(funcname, unicode_type): + funcname = funcname.decode(self.encoding) + if funcname in self._xcall_xref: + idx = self._xcall_xref[funcname] + else: + self._xcall_xref[funcname] = idx = len(self._xcall_xref) + patches.append((offset + 2, idx + 1)) + + formula.patch_references(patches) + + ################################################################## + ## BIFF records generation + ################################################################## + + def __bof_rec(self): + return BIFFRecords.Biff8BOFRecord(BIFFRecords.Biff8BOFRecord.BOOK_GLOBAL).get() + + def __eof_rec(self): + return BIFFRecords.EOFRecord().get() + + def __intf_hdr_rec(self): + return BIFFRecords.InteraceHdrRecord().get() + + def __intf_end_rec(self): + return BIFFRecords.InteraceEndRecord().get() + + def __intf_mms_rec(self): + return BIFFRecords.MMSRecord().get() + + def __write_access_rec(self): + return BIFFRecords.WriteAccessRecord(self.__owner).get() + + def __wnd_protect_rec(self): + return BIFFRecords.WindowProtectRecord(self.__wnd_protect).get() + + def __obj_protect_rec(self): + return BIFFRecords.ObjectProtectRecord(self.__obj_protect).get() + + def __protect_rec(self): + return BIFFRecords.ProtectRecord(self.__protect).get() + + def __password_rec(self): + return BIFFRecords.PasswordRecord().get() + + def __prot4rev_rec(self): + return BIFFRecords.Prot4RevRecord().get() + + def __prot4rev_pass_rec(self): + return BIFFRecords.Prot4RevPassRecord().get() + + def __backup_rec(self): + return BIFFRecords.BackupRecord(self.__backup_on_save).get() + + def __hide_obj_rec(self): + return BIFFRecords.HideObjRecord().get() + + def __window1_rec(self): + flags = 0 + flags |= (self.__wnd_hidden) << 0 + flags |= (self.__wnd_mini) << 1 + flags |= (self.__hscroll_visible) << 3 + flags |= (self.__vscroll_visible) << 4 + flags |= (self.__tabs_visible) << 5 + + return BIFFRecords.Window1Record(self.__hpos_twips, self.__vpos_twips, + self.__width_twips, self.__height_twips, + flags, + self.__active_sheet, self.__first_tab_index, + self.__selected_tabs, self.__tab_width_twips).get() + + def __codepage_rec(self): + return BIFFRecords.CodepageBiff8Record().get() + + def __country_rec(self): + if not self.__country_code: + return b'' + return BIFFRecords.CountryRecord(self.__country_code, self.__country_code).get() + + def __dsf_rec(self): + return BIFFRecords.DSFRecord().get() + + def __tabid_rec(self): + return BIFFRecords.TabIDRecord(len(self.__worksheets)).get() + + def __fngroupcount_rec(self): + return BIFFRecords.FnGroupCountRecord().get() + + def __datemode_rec(self): + return BIFFRecords.DateModeRecord(self.__dates_1904).get() + + def __precision_rec(self): + return BIFFRecords.PrecisionRecord(self.__use_cell_values).get() + + def __refresh_all_rec(self): + return BIFFRecords.RefreshAllRecord().get() + + def __bookbool_rec(self): + return BIFFRecords.BookBoolRecord().get() + + def __all_fonts_num_formats_xf_styles_rec(self): + return self.__styles.get_biff_data() + + def __palette_rec(self): + if self.__custom_palette_b8 is None: + return b'' + info = BIFFRecords.PaletteRecord(self.__custom_palette_b8).get() + return info + + def __useselfs_rec(self): + return BIFFRecords.UseSelfsRecord().get() + + def __boundsheets_rec(self, data_len_before, data_len_after, sheet_biff_lens): + # ................................. + # BOUNDSEHEET0 + # BOUNDSEHEET1 + # BOUNDSEHEET2 + # .................................. + # WORKSHEET0 + # WORKSHEET1 + # WORKSHEET2 + boundsheets_len = 0 + for sheet in self.__worksheets: + boundsheets_len += len(BIFFRecords.BoundSheetRecord( + 0x00, sheet.visibility, sheet.name, self.encoding + ).get()) + + start = data_len_before + boundsheets_len + data_len_after + + result = b'' + for sheet_biff_len, sheet in zip(sheet_biff_lens, self.__worksheets): + result += BIFFRecords.BoundSheetRecord( + start, sheet.visibility, sheet.name, self.encoding + ).get() + start += sheet_biff_len + return result + + def __all_links_rec(self): + pieces = [] + temp = [(idx, tag) for tag, idx in self._supbook_xref.items()] + temp.sort() + for idx, tag in temp: + stype, snum = tag + if stype == 'ownbook': + rec = BIFFRecords.InternalReferenceSupBookRecord(len(self.__worksheets)).get() + pieces.append(rec) + elif stype == 'xcall': + rec = BIFFRecords.XcallSupBookRecord().get() + pieces.append(rec) + temp = [(idx, name) for name, idx in self._xcall_xref.items()] + temp.sort() + for idx, name in temp: + rec = BIFFRecords.ExternnameRecord( + options=0, index=0, name=name, fmla='\x02\x00\x1c\x17').get() + pieces.append(rec) + else: + raise Exception('unknown supbook stype %r' % stype) + if len(self.__sheet_refs) > 0: + # get references in index order + temp = [(idx, ref) for ref, idx in self.__sheet_refs.items()] + temp.sort() + temp = [ref for idx, ref in temp] + externsheet_record = BIFFRecords.ExternSheetRecord(temp).get() + pieces.append(externsheet_record) + return b''.join(pieces) + + def __sst_rec(self): + return self.__sst.get_biff_record() + + def __ext_sst_rec(self, abs_stream_pos): + return b'' + #return BIFFRecords.ExtSSTRecord(abs_stream_pos, self.sst_record.str_placement, + #self.sst_record.portions_len).get() + + def get_biff_data(self): + before = b'' + before += self.__bof_rec() + before += self.__intf_hdr_rec() + before += self.__intf_mms_rec() + before += self.__intf_end_rec() + before += self.__write_access_rec() + before += self.__codepage_rec() + before += self.__dsf_rec() + before += self.__tabid_rec() + before += self.__fngroupcount_rec() + before += self.__wnd_protect_rec() + before += self.__protect_rec() + before += self.__obj_protect_rec() + before += self.__password_rec() + before += self.__prot4rev_rec() + before += self.__prot4rev_pass_rec() + before += self.__backup_rec() + before += self.__hide_obj_rec() + before += self.__window1_rec() + before += self.__datemode_rec() + before += self.__precision_rec() + before += self.__refresh_all_rec() + before += self.__bookbool_rec() + before += self.__all_fonts_num_formats_xf_styles_rec() + before += self.__palette_rec() + before += self.__useselfs_rec() + + country = self.__country_rec() + all_links = self.__all_links_rec() + + shared_str_table = self.__sst_rec() + after = country + all_links + shared_str_table + + ext_sst = self.__ext_sst_rec(0) # need fake cause we need calc stream pos + eof = self.__eof_rec() + + self.__worksheets[self.__active_sheet].selected = True + sheets = b'' + sheet_biff_lens = [] + for sheet in self.__worksheets: + data = sheet.get_biff_data() + sheets += data + sheet_biff_lens.append(len(data)) + + bundlesheets = self.__boundsheets_rec(len(before), len(after)+len(ext_sst)+len(eof), sheet_biff_lens) + + sst_stream_pos = len(before) + len(bundlesheets) + len(country) + len(all_links) + ext_sst = self.__ext_sst_rec(sst_stream_pos) + + return before + bundlesheets + after + ext_sst + eof + sheets + + def save(self, filename_or_stream): + """ + This method is used to save the Workbook to a file in native Excel + format. + + :param filename_or_stream: + This can be a string containing a filename of + the file, in which case the excel file is saved to disk using the name + provided. It can also be a stream object with a write method, such as + a :class:`~io.StringIO`, in which case the data for the excel + file is written to the stream. + """ + from . import CompoundDoc + + doc = CompoundDoc.XlsDoc() + doc.save(filename_or_stream, self.get_biff_data()) + + diff --git a/.venv/lib/python3.9/site-packages/xlwt/Worksheet.py b/.venv/lib/python3.9/site-packages/xlwt/Worksheet.py new file mode 100644 index 00000000..c306f3c9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/Worksheet.py @@ -0,0 +1,1420 @@ +# -*- coding: windows-1252 -*- +# BOF +# UNCALCED +# INDEX +# Calculation Settings Block +# PRINTHEADERS +# PRINTGRIDLINES +# GRIDSET +# GUTS +# DEFAULTROWHEIGHT +# WSBOOL +# Page Settings Block +# Worksheet Protection Block +# DEFCOLWIDTH +# COLINFO +# SORT +# DIMENSIONS +# Row Blocks +# WINDOW2 +# SCL +# PANE +# SELECTION +# STANDARDWIDTH +# MERGEDCELLS +# LABELRANGES +# PHONETIC +# Conditional Formatting Table +# Hyperlink Table +# Data Validity Table +# SHEETLAYOUT (BIFF8X only) +# SHEETPROTECTION (BIFF8X only) +# RANGEPROTECTION (BIFF8X only) +# EOF + +from . import BIFFRecords +from . import Bitmap +from . import Style +from .Row import Row +from .Column import Column +from .compat import unicode, itervalues +import tempfile + +class Worksheet(object): + """ + This is a class + representing the contents of a sheet in a workbook. + + .. warning:: + + You don't normally create instances of this class yourself. + They are returned from calls to :meth:`~xlwt.Workbook.Workbook.add_sheet`. + """ + # a safe default value, 3 is always valid! + active_pane = 3 + + ################################################################# + ## Constructor + ################################################################# + def __init__(self, sheetname, parent_book, cell_overwrite_ok=False): + self.Row = Row + self.Column = Column + + self.__name = sheetname + self.__parent = parent_book + self._cell_overwrite_ok = cell_overwrite_ok + + self.__rows = {} + self.__cols = {} + self.__merged_ranges = [] + self.__bmp_rec = b'' + + self.__show_formulas = 0 + self.__show_grid = 1 + self.__show_headers = 1 + self.__panes_frozen = 0 + self.show_zero_values = 1 + self.__auto_colour_grid = 1 + self.__cols_right_to_left = 0 + self.__show_outline = 1 + self.__remove_splits = 0 + # Multiple sheets can be selected, but only one can be active + # (hold down Ctrl and click multiple tabs in the file in OOo) + self.__selected = 0 + # "sheet_visible" should really be called "sheet_active" + # and is 1 when this sheet is the sheet displayed when the file + # is open. More than likely only one sheet should ever be set as + # visible. + # The same sheet should be specified in Workbook.active_sheet + # (that way, both the WINDOW1 record in the book and the WINDOW2 + # records in each sheet will be in agreement) + # The visibility of the sheet is found in the "visibility" + # attribute obtained from the BOUNDSHEET record. + self.__sheet_visible = 0 + self.__page_preview = 0 + + self.__first_visible_row = 0 + self.__first_visible_col = 0 + self.__grid_colour = 0x40 + self.__preview_magn = 0 # use default (60%) + self.__normal_magn = 0 # use default (100%) + self.__scl_magn = None + self.explicit_magn_setting = False + + self.visibility = 0 # from/to BOUNDSHEET record. + + self.__vert_split_pos = None + self.__horz_split_pos = None + self.__vert_split_first_visible = None + self.__horz_split_first_visible = None + + # This is a caller-settable flag: + + self.split_position_units_are_twips = False + + # Default is False for backward compatibility with pyExcelerator + # and previous versions of xlwt. + # if panes_frozen: + # vert/horz_split_pos are taken as number of rows/cols + # else: # split + # if split_position_units_are_twips: + # vert/horz_split_pos are taken as number of twips + # else: + # vert/horz_split_pos are taken as + # number of rows(cols) * default row(col) height (width) (i.e. 12.75 (8.43) somethings) + # and converted to twips by approximate formulas + # Callers who are copying an existing file should use + # xlwt_worksheet.split_position_units_are_twips = True + # because that's what's actually in the file. + + # There are 20 twips to a point. There are 72 points to an inch. + + self.__row_gut_width = 0 + self.__col_gut_height = 0 + + self.__show_auto_page_breaks = 1 + self.__dialogue_sheet = 0 + self.__auto_style_outline = 0 + self.__outline_below = 0 + self.__outline_right = 0 + self.__fit_num_pages = 0 + self.__show_row_outline = 1 + self.__show_col_outline = 1 + self.__alt_expr_eval = 0 + self.__alt_formula_entries = 0 + + self.__row_default_height = 0x00FF + self.row_default_height_mismatch = 0 + self.row_default_hidden = 0 + self.row_default_space_above = 0 + self.row_default_space_below = 0 + + self.__col_default_width = 0x0008 + + self.__calc_mode = 1 + self.__calc_count = 0x0064 + self.__RC_ref_mode = 1 + self.__iterations_on = 0 + self.__delta = 0.001 + self.__save_recalc = 0 + + self.__print_headers = 0 + self.__print_grid = 0 + self.__grid_set = 1 + self.__vert_page_breaks = [] + self.__horz_page_breaks = [] + self.__header_str = '&P' + self.__footer_str = '&F' + self.__print_centered_vert = 0 + self.__print_centered_horz = 1 + self.__left_margin = 0.3 #0.5 + self.__right_margin = 0.3 #0.5 + self.__top_margin = 0.61 #1.0 + self.__bottom_margin = 0.37 #1.0 + self.__paper_size_code = 9 # A4 + self.__print_scaling = 100 + self.__start_page_number = 1 + self.__fit_width_to_pages = 1 + self.__fit_height_to_pages = 1 + self.__print_in_rows = 1 + self.__portrait = 1 + self.__print_not_colour = 0 + self.__print_draft = 0 + self.__print_notes = 0 + self.__print_notes_at_end = 0 + self.__print_omit_errors = 0 + self.__print_hres = 0x012C # 300 dpi + self.__print_vres = 0x012C # 300 dpi + self.__header_margin = 0.1 + self.__footer_margin = 0.1 + self.__copies_num = 1 + + self.__wnd_protect = 0 + self.__obj_protect = 0 + self.__protect = 0 + self.__scen_protect = 0 + self.__password = '' + + self.last_used_row = 0 + self.first_used_row = 65535 + self.last_used_col = 0 + self.first_used_col = 255 + self.row_tempfile = None + self.__flushed_rows = {} + self.__row_visible_levels = 0 + + ################################################################# + ## Properties, "getters", "setters" + ################################################################# + + def set_name(self, value): + self.__name = value + + def get_name(self): + return self.__name + + name = property(get_name, set_name) + + ################################################################# + + def get_parent(self): + return self.__parent + + parent = property(get_parent) + + ################################################################# + + def get_rows(self): + return self.__rows + + rows = property(get_rows) + + ################################################################# + + def get_cols(self): + return self.__cols + + cols = property(get_cols) + + ################################################################# + + def get_merged_ranges(self): + return self.__merged_ranges + + merged_ranges = property(get_merged_ranges) + + ################################################################# + + def get_bmp_rec(self): + return self.__bmp_rec + + bmp_rec = property(get_bmp_rec) + + ################################################################# + + def set_show_formulas(self, value): + self.__show_formulas = int(value) + + def get_show_formulas(self): + return bool(self.__show_formulas) + + show_formulas = property(get_show_formulas, set_show_formulas) + + ################################################################# + + def set_show_grid(self, value): + self.__show_grid = int(value) + + def get_show_grid(self): + return bool(self.__show_grid) + + show_grid = property(get_show_grid, set_show_grid) + + ################################################################# + + def set_show_headers(self, value): + self.__show_headers = int(value) + + def get_show_headers(self): + return bool(self.__show_headers) + + show_headers = property(get_show_headers, set_show_headers) + + ################################################################# + + def set_panes_frozen(self, value): + self.__panes_frozen = int(value) + + def get_panes_frozen(self): + return bool(self.__panes_frozen) + + panes_frozen = property(get_panes_frozen, set_panes_frozen) + + ################################################################# + + ### def set_show_empty_as_zero(self, value): + ### self.__show_empty_as_zero = int(value) + + ### def get_show_empty_as_zero(self): + ### return bool(self.__show_empty_as_zero) + + ### show_empty_as_zero = property(get_show_empty_as_zero, set_show_empty_as_zero) + + ################################################################# + + def set_auto_colour_grid(self, value): + self.__auto_colour_grid = int(value) + + def get_auto_colour_grid(self): + return bool(self.__auto_colour_grid) + + auto_colour_grid = property(get_auto_colour_grid, set_auto_colour_grid) + + ################################################################# + + def set_cols_right_to_left(self, value): + self.__cols_right_to_left = int(value) + + def get_cols_right_to_left(self): + return bool(self.__cols_right_to_left) + + cols_right_to_left = property(get_cols_right_to_left, set_cols_right_to_left) + + ################################################################# + + def set_show_outline(self, value): + self.__show_outline = int(value) + + def get_show_outline(self): + return bool(self.__show_outline) + + show_outline = property(get_show_outline, set_show_outline) + + ################################################################# + + def set_remove_splits(self, value): + self.__remove_splits = int(value) + + def get_remove_splits(self): + return bool(self.__remove_splits) + + remove_splits = property(get_remove_splits, set_remove_splits) + + ################################################################# + + def set_selected(self, value): + self.__selected = int(value) + + def get_selected(self): + return bool(self.__selected) + + selected = property(get_selected, set_selected) + + ################################################################# + + def set_sheet_visible(self, value): + self.__sheet_visible = int(value) + + def get_sheet_visible(self): + return bool(self.__sheet_visible) + + sheet_visible = property(get_sheet_visible, set_sheet_visible) + + ################################################################# + + def set_page_preview(self, value): + self.__page_preview = int(value) + + def get_page_preview(self): + return bool(self.__page_preview) + + page_preview = property(get_page_preview, set_page_preview) + + ################################################################# + + def set_first_visible_row(self, value): + self.__first_visible_row = value + + def get_first_visible_row(self): + return self.__first_visible_row + + first_visible_row = property(get_first_visible_row, set_first_visible_row) + + ################################################################# + + def set_first_visible_col(self, value): + self.__first_visible_col = value + + def get_first_visible_col(self): + return self.__first_visible_col + + first_visible_col = property(get_first_visible_col, set_first_visible_col) + + ################################################################# + + def set_grid_colour(self, value): + self.__grid_colour = value + + def get_grid_colour(self): + return self.__grid_colour + + grid_colour = property(get_grid_colour, set_grid_colour) + + ################################################################# + + def set_preview_magn(self, value): + self.__preview_magn = value + + def get_preview_magn(self): + return self.__preview_magn + + preview_magn = property(get_preview_magn, set_preview_magn) + + ################################################################# + + def set_normal_magn(self, value): + self.__normal_magn = value + + def get_normal_magn(self): + return self.__normal_magn + + normal_magn = property(get_normal_magn, set_normal_magn) + + ################################################################# + + def set_scl_magn(self, value): + self.__scl_magn = value + + def get_scl_magn(self): + return self.__scl_magn + + scl_magn = property(get_scl_magn, set_scl_magn) + + + ################################################################# + + def set_vert_split_pos(self, value): + self.__vert_split_pos = abs(value) + + def get_vert_split_pos(self): + return self.__vert_split_pos + + vert_split_pos = property(get_vert_split_pos, set_vert_split_pos) + + ################################################################# + + def set_horz_split_pos(self, value): + self.__horz_split_pos = abs(value) + + def get_horz_split_pos(self): + return self.__horz_split_pos + + horz_split_pos = property(get_horz_split_pos, set_horz_split_pos) + + ################################################################# + + def set_vert_split_first_visible(self, value): + self.__vert_split_first_visible = abs(value) + + def get_vert_split_first_visible(self): + return self.__vert_split_first_visible + + vert_split_first_visible = property(get_vert_split_first_visible, set_vert_split_first_visible) + + ################################################################# + + def set_horz_split_first_visible(self, value): + self.__horz_split_first_visible = abs(value) + + def get_horz_split_first_visible(self): + return self.__horz_split_first_visible + + horz_split_first_visible = property(get_horz_split_first_visible, set_horz_split_first_visible) + + ################################################################# + + #def set_row_gut_width(self, value): + # self.__row_gut_width = value + # + #def get_row_gut_width(self): + # return self.__row_gut_width + # + #row_gut_width = property(get_row_gut_width, set_row_gut_width) + # + ################################################################# + # + #def set_col_gut_height(self, value): + # self.__col_gut_height = value + # + #def get_col_gut_height(self): + # return self.__col_gut_height + # + #col_gut_height = property(get_col_gut_height, set_col_gut_height) + # + ################################################################# + + def set_show_auto_page_breaks(self, value): + self.__show_auto_page_breaks = int(value) + + def get_show_auto_page_breaks(self): + return bool(self.__show_auto_page_breaks) + + show_auto_page_breaks = property(get_show_auto_page_breaks, set_show_auto_page_breaks) + + ################################################################# + + def set_dialogue_sheet(self, value): + self.__dialogue_sheet = int(value) + + def get_dialogue_sheet(self): + return bool(self.__dialogue_sheet) + + dialogue_sheet = property(get_dialogue_sheet, set_dialogue_sheet) + + ################################################################# + + def set_auto_style_outline(self, value): + self.__auto_style_outline = int(value) + + def get_auto_style_outline(self): + return bool(self.__auto_style_outline) + + auto_style_outline = property(get_auto_style_outline, set_auto_style_outline) + + ################################################################# + + def set_outline_below(self, value): + self.__outline_below = int(value) + + def get_outline_below(self): + return bool(self.__outline_below) + + outline_below = property(get_outline_below, set_outline_below) + + ################################################################# + + def set_outline_right(self, value): + self.__outline_right = int(value) + + def get_outline_right(self): + return bool(self.__outline_right) + + outline_right = property(get_outline_right, set_outline_right) + + ################################################################# + + def set_fit_num_pages(self, value): + self.__fit_num_pages = value + + def get_fit_num_pages(self): + return self.__fit_num_pages + + fit_num_pages = property(get_fit_num_pages, set_fit_num_pages) + + ################################################################# + + def set_show_row_outline(self, value): + self.__show_row_outline = int(value) + + def get_show_row_outline(self): + return bool(self.__show_row_outline) + + show_row_outline = property(get_show_row_outline, set_show_row_outline) + + ################################################################# + + def set_show_col_outline(self, value): + self.__show_col_outline = int(value) + + def get_show_col_outline(self): + return bool(self.__show_col_outline) + + show_col_outline = property(get_show_col_outline, set_show_col_outline) + + ################################################################# + + def set_alt_expr_eval(self, value): + self.__alt_expr_eval = int(value) + + def get_alt_expr_eval(self): + return bool(self.__alt_expr_eval) + + alt_expr_eval = property(get_alt_expr_eval, set_alt_expr_eval) + + ################################################################# + + def set_alt_formula_entries(self, value): + self.__alt_formula_entries = int(value) + + def get_alt_formula_entries(self): + return bool(self.__alt_formula_entries) + + alt_formula_entries = property(get_alt_formula_entries, set_alt_formula_entries) + + ################################################################# + + def set_row_default_height(self, value): + self.__row_default_height = value + + def get_row_default_height(self): + return self.__row_default_height + + row_default_height = property(get_row_default_height, set_row_default_height) + + ################################################################# + + def set_col_default_width(self, value): + self.__col_default_width = value + + def get_col_default_width(self): + return self.__col_default_width + + col_default_width = property(get_col_default_width, set_col_default_width) + + ################################################################# + + def set_calc_mode(self, value): + self.__calc_mode = value & 0x03 + + def get_calc_mode(self): + return self.__calc_mode + + calc_mode = property(get_calc_mode, set_calc_mode) + + ################################################################# + + def set_calc_count(self, value): + self.__calc_count = value + + def get_calc_count(self): + return self.__calc_count + + calc_count = property(get_calc_count, set_calc_count) + + ################################################################# + + def set_RC_ref_mode(self, value): + self.__RC_ref_mode = int(value) + + def get_RC_ref_mode(self): + return bool(self.__RC_ref_mode) + + RC_ref_mode = property(get_RC_ref_mode, set_RC_ref_mode) + + ################################################################# + + def set_iterations_on(self, value): + self.__iterations_on = int(value) + + def get_iterations_on(self): + return bool(self.__iterations_on) + + iterations_on = property(get_iterations_on, set_iterations_on) + + ################################################################# + + def set_delta(self, value): + self.__delta = value + + def get_delta(self): + return self.__delta + + delta = property(get_delta, set_delta) + + ################################################################# + + def set_save_recalc(self, value): + self.__save_recalc = int(value) + + def get_save_recalc(self): + return bool(self.__save_recalc) + + save_recalc = property(get_save_recalc, set_save_recalc) + + ################################################################# + + def set_print_headers(self, value): + self.__print_headers = int(value) + + def get_print_headers(self): + return bool(self.__print_headers) + + print_headers = property(get_print_headers, set_print_headers) + + ################################################################# + + def set_print_grid(self, value): + self.__print_grid = int(value) + + def get_print_grid(self): + return bool(self.__print_grid) + + print_grid = property(get_print_grid, set_print_grid) + + ################################################################# + # + #def set_grid_set(self, value): + # self.__grid_set = int(value) + # + #def get_grid_set(self): + # return bool(self.__grid_set) + # + #grid_set = property(get_grid_set, set_grid_set) + # + ################################################################# + + def set_vert_page_breaks(self, value): + self.__vert_page_breaks = value + + def get_vert_page_breaks(self): + return self.__vert_page_breaks + + vert_page_breaks = property(get_vert_page_breaks, set_vert_page_breaks) + + ################################################################# + + def set_horz_page_breaks(self, value): + self.__horz_page_breaks = value + + def get_horz_page_breaks(self): + return self.__horz_page_breaks + + horz_page_breaks = property(get_horz_page_breaks, set_horz_page_breaks) + + ################################################################# + + def set_header_str(self, value): + if isinstance(value, str): + value = unicode(value, self.__parent.encoding) + self.__header_str = value + + def get_header_str(self): + return self.__header_str + + header_str = property(get_header_str, set_header_str) + + ################################################################# + + def set_footer_str(self, value): + if isinstance(value, str): + value = unicode(value, self.__parent.encoding) + self.__footer_str = value + + def get_footer_str(self): + return self.__footer_str + + footer_str = property(get_footer_str, set_footer_str) + + ################################################################# + + def set_print_centered_vert(self, value): + self.__print_centered_vert = int(value) + + def get_print_centered_vert(self): + return bool(self.__print_centered_vert) + + print_centered_vert = property(get_print_centered_vert, set_print_centered_vert) + + ################################################################# + + def set_print_centered_horz(self, value): + self.__print_centered_horz = int(value) + + def get_print_centered_horz(self): + return bool(self.__print_centered_horz) + + print_centered_horz = property(get_print_centered_horz, set_print_centered_horz) + + ################################################################# + + def set_left_margin(self, value): + self.__left_margin = value + + def get_left_margin(self): + return self.__left_margin + + left_margin = property(get_left_margin, set_left_margin) + + ################################################################# + + def set_right_margin(self, value): + self.__right_margin = value + + def get_right_margin(self): + return self.__right_margin + + right_margin = property(get_right_margin, set_right_margin) + + ################################################################# + + def set_top_margin(self, value): + self.__top_margin = value + + def get_top_margin(self): + return self.__top_margin + + top_margin = property(get_top_margin, set_top_margin) + + ################################################################# + + def set_bottom_margin(self, value): + self.__bottom_margin = value + + def get_bottom_margin(self): + return self.__bottom_margin + + bottom_margin = property(get_bottom_margin, set_bottom_margin) + + ################################################################# + + def set_paper_size_code(self, value): + self.__paper_size_code = value + + def get_paper_size_code(self): + return self.__paper_size_code + + paper_size_code = property(get_paper_size_code, set_paper_size_code) + + ################################################################# + + def set_print_scaling(self, value): + self.__print_scaling = value + + def get_print_scaling(self): + return self.__print_scaling + + print_scaling = property(get_print_scaling, set_print_scaling) + + ################################################################# + + def set_start_page_number(self, value): + self.__start_page_number = value + + def get_start_page_number(self): + return self.__start_page_number + + start_page_number = property(get_start_page_number, set_start_page_number) + + ################################################################# + + def set_fit_width_to_pages(self, value): + self.__fit_width_to_pages = value + + def get_fit_width_to_pages(self): + return self.__fit_width_to_pages + + fit_width_to_pages = property(get_fit_width_to_pages, set_fit_width_to_pages) + + ################################################################# + + def set_fit_height_to_pages(self, value): + self.__fit_height_to_pages = value + + def get_fit_height_to_pages(self): + return self.__fit_height_to_pages + + fit_height_to_pages = property(get_fit_height_to_pages, set_fit_height_to_pages) + + ################################################################# + + def set_print_in_rows(self, value): + self.__print_in_rows = int(value) + + def get_print_in_rows(self): + return bool(self.__print_in_rows) + + print_in_rows = property(get_print_in_rows, set_print_in_rows) + + ################################################################# + + def set_portrait(self, value): + self.__portrait = int(value) + + def get_portrait(self): + return bool(self.__portrait) + + portrait = property(get_portrait, set_portrait) + + ################################################################# + + def set_print_colour(self, value): + self.__print_not_colour = int(not value) + + def get_print_colour(self): + return not bool(self.__print_not_colour) + + print_colour = property(get_print_colour, set_print_colour) + + ################################################################# + + def set_print_draft(self, value): + self.__print_draft = int(value) + + def get_print_draft(self): + return bool(self.__print_draft) + + print_draft = property(get_print_draft, set_print_draft) + + ################################################################# + + def set_print_notes(self, value): + self.__print_notes = int(value) + + def get_print_notes(self): + return bool(self.__print_notes) + + print_notes = property(get_print_notes, set_print_notes) + + ################################################################# + + def set_print_notes_at_end(self, value): + self.__print_notes_at_end = int(value) + + def get_print_notes_at_end(self): + return bool(self.__print_notes_at_end) + + print_notes_at_end = property(get_print_notes_at_end, set_print_notes_at_end) + + ################################################################# + + def set_print_omit_errors(self, value): + self.__print_omit_errors = int(value) + + def get_print_omit_errors(self): + return bool(self.__print_omit_errors) + + print_omit_errors = property(get_print_omit_errors, set_print_omit_errors) + + ################################################################# + + def set_print_hres(self, value): + self.__print_hres = value + + def get_print_hres(self): + return self.__print_hres + + print_hres = property(get_print_hres, set_print_hres) + + ################################################################# + + def set_print_vres(self, value): + self.__print_vres = value + + def get_print_vres(self): + return self.__print_vres + + print_vres = property(get_print_vres, set_print_vres) + + ################################################################# + + def set_header_margin(self, value): + self.__header_margin = value + + def get_header_margin(self): + return self.__header_margin + + header_margin = property(get_header_margin, set_header_margin) + + ################################################################# + + def set_footer_margin(self, value): + self.__footer_margin = value + + def get_footer_margin(self): + return self.__footer_margin + + footer_margin = property(get_footer_margin, set_footer_margin) + + ################################################################# + + def set_copies_num(self, value): + self.__copies_num = value + + def get_copies_num(self): + return self.__copies_num + + copies_num = property(get_copies_num, set_copies_num) + + ################################################################## + + def set_wnd_protect(self, value): + self.__wnd_protect = int(value) + + def get_wnd_protect(self): + return bool(self.__wnd_protect) + + wnd_protect = property(get_wnd_protect, set_wnd_protect) + + ################################################################# + + def set_obj_protect(self, value): + self.__obj_protect = int(value) + + def get_obj_protect(self): + return bool(self.__obj_protect) + + obj_protect = property(get_obj_protect, set_obj_protect) + + ################################################################# + + def set_protect(self, value): + self.__protect = int(value) + + def get_protect(self): + return bool(self.__protect) + + protect = property(get_protect, set_protect) + + ################################################################# + + def set_scen_protect(self, value): + self.__scen_protect = int(value) + + def get_scen_protect(self): + return bool(self.__scen_protect) + + scen_protect = property(get_scen_protect, set_scen_protect) + + ################################################################# + + def set_password(self, value): + self.__password = value + + def get_password(self): + return self.__password + + password = property(get_password, set_password) + + ################################################################## + ## Methods + ################################################################## + + def get_parent(self): + return self.__parent + + def write(self, r, c, label="", style=Style.default_style): + """ + This method is used to write a cell to a :class:`Worksheet`. + + :param r: + + The zero-relative number of the row in the worksheet to which + the cell should be written. + + :param c: + + The zero-relative number of the column in the worksheet to which + the cell should be written. + + :param label: + + The data value to be written. + + An :class:`int`, :class:`long`, or + :class:`~decimal.Decimal` instance is converted to :class:`float`. + + A :class:`unicode` instance is written as is. A :class:`bytes` + instance is converted to :class:`unicode` using the + encoding, which defaults to ``ascii``, specified when the + :class:`Workbook` instance was created. + + A :class:`~datetime.datetime`, :class:`~datetime.date` or + :class:`~datetime.time` instance is converted into Excel date format + (a float representing the number of days since (typically) + ``1899-12-31T00:00:00``, under the pretence that + 1900 was a leap year). + + A :class:`bool` instance will show up as ``TRUE`` or ``FALSE`` in + Excel. + + ``None`` causes the cell to be blank: no data, only formatting. + + An :class:`xlwt.Formula` instance causes an Excel formula to be + written. + + :param style: + + A style, also known as an XF (extended format), is an + :class:`~xlwt.Style.XFStyle` object, which encapsulates the + formatting applied to the cell and its contents. + + :class:`~xlwt.Style.XFStyle` objects are best set up using the + :func:`~xlwt.Style.easyxf` function. They may also be set up by + setting attributes in :class:`Alignment`, :class:`Borders`, + :class:`Pattern`, :class:`Font` and :class:`Protection` objects then + setting those objects and a format string as attributes of an + :class:`~xlwt.Style.XFStyle` object. + """ + self.row(r).write(c, label, style) + + def write_rich_text(self, r, c, rich_text_list, style=Style.default_style): + self.row(r).set_cell_rich_text(c, rich_text_list, style) + + def merge(self, r1, r2, c1, c2, style=Style.default_style): + # Stand-alone merge of previously written cells. + # Problems: (1) style to be used should be existing style of + # the top-left cell, not an arg. + # (2) should ensure that any previous data value in + # non-top-left cells is nobbled. + # Note: if a cell is set by a data record then later + # is referenced by a [MUL]BLANK record, Excel will blank + # out the cell on the screen, but OOo & Gnu will not + # blank it out. Need to do something better than writing + # multiple records. In the meantime, avoid this method and use + # write_merge() instead. + if c2 > c1: + self.row(r1).write_blanks(c1 + 1, c2, style) + for r in range(r1+1, r2+1): + self.row(r).write_blanks(c1, c2, style) + self.__merged_ranges.append((r1, r2, c1, c2)) + + def write_merge(self, r1, r2, c1, c2, label="", style=Style.default_style): + assert 0 <= c1 <= c2 <= 255 + assert 0 <= r1 <= r2 <= 65535 + self.write(r1, c1, label, style) + if c2 > c1: + self.row(r1).write_blanks(c1 + 1, c2, style) # skip (r1, c1) + for r in range(r1+1, r2+1): + self.row(r).write_blanks(c1, c2, style) + self.__merged_ranges.append((r1, r2, c1, c2)) + + def insert_bitmap(self, filename, row, col, x = 0, y = 0, scale_x = 1, scale_y = 1): + bmp = Bitmap.ImDataBmpRecord(filename) + obj = Bitmap.ObjBmpRecord(row, col, self, bmp, x, y, scale_x, scale_y) + + self.__bmp_rec += obj.get() + bmp.get() + + def insert_bitmap_data(self, data, row, col, x = 0, y = 0, scale_x = 1, scale_y = 1): + bmp = Bitmap.ImRawDataBmpRecord(data) + obj = Bitmap.ObjBmpRecord(row, col, self, bmp, x, y, scale_x, scale_y) + + self.__bmp_rec += obj.get() + bmp.get() + + def col(self, indx): + if indx not in self.__cols: + self.__cols[indx] = self.Column(indx, self) + return self.__cols[indx] + + def row(self, indx): + if indx not in self.__rows: + if indx in self.__flushed_rows: + raise Exception("Attempt to reuse row index %d of sheet %r after flushing" % (indx, self.__name)) + self.__rows[indx] = self.Row(indx, self) + if indx > self.last_used_row: + self.last_used_row = indx + if indx < self.first_used_row: + self.first_used_row = indx + return self.__rows[indx] + + def row_height(self, row): # in pixels + if row in self.__rows: + return self.__rows[row].get_height_in_pixels() + else: + return 17 + + def col_width(self, col): # in pixels + if col in self.__cols: + return self.__cols[col].width_in_pixels() + else: + return 64 + + + ################################################################## + ## BIFF records generation + ################################################################## + + def __bof_rec(self): + return BIFFRecords.Biff8BOFRecord(BIFFRecords.Biff8BOFRecord.WORKSHEET).get() + + def __update_row_visible_levels(self): + if self.__rows: + temp = max(self.__rows[r].level for r in self.__rows) + 1 + self.__row_visible_levels = max(temp, self.__row_visible_levels) + + def __guts_rec(self): + self.__update_row_visible_levels() + col_visible_levels = 0 + if len(self.__cols) != 0: + col_visible_levels = max(self.__cols[c].level for c in self.__cols) + 1 + return BIFFRecords.GutsRecord( + self.__row_gut_width, self.__col_gut_height, self.__row_visible_levels, col_visible_levels).get() + + def __defaultrowheight_rec(self): + options = 0x0000 + options |= (self.row_default_height_mismatch & 1) << 0 + options |= (self.row_default_hidden & 1) << 1 + options |= (self.row_default_space_above & 1) << 2 + options |= (self.row_default_space_below & 1) << 3 + defht = self.__row_default_height + return BIFFRecords.DefaultRowHeightRecord(options, defht).get() + + def __wsbool_rec(self): + options = 0x00 + options |= (self.__show_auto_page_breaks & 0x01) << 0 + options |= (self.__dialogue_sheet & 0x01) << 4 + options |= (self.__auto_style_outline & 0x01) << 5 + options |= (self.__outline_below & 0x01) << 6 + options |= (self.__outline_right & 0x01) << 7 + options |= (self.__fit_num_pages & 0x01) << 8 + options |= (self.__show_row_outline & 0x01) << 10 + options |= (self.__show_col_outline & 0x01) << 11 + options |= (self.__alt_expr_eval & 0x01) << 14 + options |= (self.__alt_formula_entries & 0x01) << 15 + + return BIFFRecords.WSBoolRecord(options).get() + + def __eof_rec(self): + return BIFFRecords.EOFRecord().get() + + def __colinfo_rec(self): + result = b'' + for col in self.__cols: + result += self.__cols[col].get_biff_record() + return result + + def __dimensions_rec(self): + return BIFFRecords.DimensionsRecord( + self.first_used_row, self.last_used_row, + self.first_used_col, self.last_used_col + ).get() + + def __window2_rec(self): + # Appends SCL record. + options = 0 + options |= (self.__show_formulas & 0x01) << 0 + options |= (self.__show_grid & 0x01) << 1 + options |= (self.__show_headers & 0x01) << 2 + options |= (self.__panes_frozen & 0x01) << 3 + options |= (self.show_zero_values & 0x01) << 4 + options |= (self.__auto_colour_grid & 0x01) << 5 + options |= (self.__cols_right_to_left & 0x01) << 6 + options |= (self.__show_outline & 0x01) << 7 + options |= (self.__remove_splits & 0x01) << 8 + options |= (self.__selected & 0x01) << 9 + options |= (self.__sheet_visible & 0x01) << 10 + options |= (self.__page_preview & 0x01) << 11 + if self.explicit_magn_setting: + # Experimentation: caller can set the scl magn. + # None -> no SCL record written + # Otherwise 10 <= scl_magn <= 400 or scl_magn == 0 + # Note: value 0 means use 100 for normal view, 60 for page break preview + # BREAKING NEWS: Excel interprets scl_magn = 0 very literally, your + # sheet appears like a tiny dot on the screen + scl_magn = self.__scl_magn + else: + if self.__page_preview: + scl_magn = self.__preview_magn + magn_default = 60 + else: + scl_magn = self.__normal_magn + magn_default = 100 + if scl_magn == magn_default or scl_magn == 0: + # Emulate what we think MS does + scl_magn = None # don't write an SCL record + return BIFFRecords.Window2Record( + options, self.__first_visible_row, self.__first_visible_col, + self.__grid_colour, + self.__preview_magn, self.__normal_magn, scl_magn).get() + + def __panes_rec(self): + if self.__vert_split_pos is None and self.__horz_split_pos is None: + return b"" + + if self.__vert_split_pos is None: + self.__vert_split_pos = 0 + if self.__horz_split_pos is None: + self.__horz_split_pos = 0 + + if self.__panes_frozen: + if self.__vert_split_first_visible is None: + self.__vert_split_first_visible = self.__vert_split_pos + if self.__horz_split_first_visible is None: + self.__horz_split_first_visible = self.__horz_split_pos + + # when frozen, the active pane has to be specifically set: + if self.__vert_split_pos > 0 and self.__horz_split_pos > 0: + active_pane = 0 + elif self.__vert_split_pos > 0 and self.__horz_split_pos == 0: + active_pane = 1 + elif self.__vert_split_pos == 0 and self.__horz_split_pos > 0: + active_pane = 2 + else: + active_pane = 3 + else: + if self.__vert_split_first_visible is None: + self.__vert_split_first_visible = 0 + if self.__horz_split_first_visible is None: + self.__horz_split_first_visible = 0 + if not self.split_position_units_are_twips: + # inspired by pyXLWriter + if self.__horz_split_pos > 0: + self.__horz_split_pos = 20 * self.__horz_split_pos + 255 + if self.__vert_split_pos > 0: + self.__vert_split_pos = 113.879 * self.__vert_split_pos + 390 + + # when split, the active pain can be set as required: + active_pane = self.active_pane + + result = BIFFRecords.PanesRecord(*map(int, ( + self.__vert_split_pos, + self.__horz_split_pos, + self.__horz_split_first_visible, + self.__vert_split_first_visible, + active_pane + ))).get() + + return result + + def __row_blocks_rec(self): + result = [] + for row in itervalues(self.__rows): + result.append(row.get_row_biff_data()) + result.append(row.get_cells_biff_data()) + return b''.join(result) + + def __merged_rec(self): + return BIFFRecords.MergedCellsRecord(self.__merged_ranges).get() + + def __bitmaps_rec(self): + return self.__bmp_rec + + def __calc_settings_rec(self): + result = b'' + result += BIFFRecords.CalcModeRecord(self.__calc_mode & 0x01).get() + result += BIFFRecords.CalcCountRecord(self.__calc_count & 0xFFFF).get() + result += BIFFRecords.RefModeRecord(self.__RC_ref_mode & 0x01).get() + result += BIFFRecords.IterationRecord(self.__iterations_on & 0x01).get() + result += BIFFRecords.DeltaRecord(self.__delta).get() + result += BIFFRecords.SaveRecalcRecord(self.__save_recalc & 0x01).get() + return result + + def __print_settings_rec(self): + result = b'' + result += BIFFRecords.PrintHeadersRecord(self.__print_headers).get() + result += BIFFRecords.PrintGridLinesRecord(self.__print_grid).get() + result += BIFFRecords.GridSetRecord(self.__grid_set).get() + result += BIFFRecords.HorizontalPageBreaksRecord(self.__horz_page_breaks).get() + result += BIFFRecords.VerticalPageBreaksRecord(self.__vert_page_breaks).get() + result += BIFFRecords.HeaderRecord(self.__header_str).get() + result += BIFFRecords.FooterRecord(self.__footer_str).get() + result += BIFFRecords.HCenterRecord(self.__print_centered_horz).get() + result += BIFFRecords.VCenterRecord(self.__print_centered_vert).get() + result += BIFFRecords.LeftMarginRecord(self.__left_margin).get() + result += BIFFRecords.RightMarginRecord(self.__right_margin).get() + result += BIFFRecords.TopMarginRecord(self.__top_margin).get() + result += BIFFRecords.BottomMarginRecord(self.__bottom_margin).get() + + setup_page_options = (self.__print_in_rows & 0x01) << 0 + setup_page_options |= (self.__portrait & 0x01) << 1 + setup_page_options |= (0x00 & 0x01) << 2 + setup_page_options |= (self.__print_not_colour & 0x01) << 3 + setup_page_options |= (self.__print_draft & 0x01) << 4 + setup_page_options |= (self.__print_notes & 0x01) << 5 + setup_page_options |= (0x00 & 0x01) << 6 + setup_page_options |= (0x01 & 0x01) << 7 + setup_page_options |= (self.__print_notes_at_end & 0x01) << 9 + setup_page_options |= (self.__print_omit_errors & 0x03) << 10 + + result += BIFFRecords.SetupPageRecord(self.__paper_size_code, + self.__print_scaling, + self.__start_page_number, + self.__fit_width_to_pages, + self.__fit_height_to_pages, + setup_page_options, + self.__print_hres, + self.__print_vres, + self.__header_margin, + self.__footer_margin, + self.__copies_num).get() + return result + + def __protection_rec(self): + result = b'' + result += BIFFRecords.ProtectRecord(self.__protect).get() + result += BIFFRecords.ScenProtectRecord(self.__scen_protect).get() + result += BIFFRecords.WindowProtectRecord(self.__wnd_protect).get() + result += BIFFRecords.ObjectProtectRecord(self.__obj_protect).get() + result += BIFFRecords.PasswordRecord(self.__password).get() + return result + + def get_biff_data(self): + result = [ + self.__bof_rec(), + self.__calc_settings_rec(), + self.__guts_rec(), + self.__defaultrowheight_rec(), + self.__wsbool_rec(), + self.__colinfo_rec(), + self.__dimensions_rec(), + self.__print_settings_rec(), + self.__protection_rec(), + ] + if self.row_tempfile: + self.row_tempfile.flush() + self.row_tempfile.seek(0) + result.append(self.row_tempfile.read()) + self.row_tempfile.seek(0, 2) # to EOF + # Above seek() is necessary to avoid a spurious IOError + # with Errno 0 if the caller continues on writing rows + # and flushing row data after the save(). + # See https://bugs.python.org/issue3207 + result.extend([ + self.__row_blocks_rec(), + self.__merged_rec(), + self.__bitmaps_rec(), + self.__window2_rec(), + self.__panes_rec(), + self.__eof_rec(), + ]) + return b''.join(result) + + def flush_row_data(self): + if self.row_tempfile is None: + self.row_tempfile = tempfile.TemporaryFile() + self.row_tempfile.write(self.__row_blocks_rec()) + for rowx in self.__rows: + self.__flushed_rows[rowx] = 1 + self.__update_row_visible_levels() + self.__rows = {} + + diff --git a/.venv/lib/python3.9/site-packages/xlwt/__init__.py b/.venv/lib/python3.9/site-packages/xlwt/__init__.py new file mode 100644 index 00000000..1a2d64b8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/__init__.py @@ -0,0 +1,9 @@ +__VERSION__ = '1.3.0' + +from .Workbook import Workbook +from .Worksheet import Worksheet +from .Row import Row +from .Column import Column +from .Formatting import Font, Alignment, Borders, Pattern, Protection +from .Style import XFStyle, easyxf, easyfont, add_palette_colour +from .ExcelFormula import * diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/BIFFRecords.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/BIFFRecords.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd21689b6fa59e67c22c1cf9c6ea8f350d6eb0de GIT binary patch literal 101444 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 B$T$E1 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Bitmap.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Bitmap.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34bd47d40863933270b31a0ea2866f54df5d7dbc GIT binary patch literal 7148 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 B!@mFk literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Cell.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Cell.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2908c00fc79b960bc4ec33280faa1c6e1e161b18 GIT binary patch literal 6145 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Column.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Column.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4f176107240709b1652f7a7b002537b497f031e GIT binary patch literal 1954 zcmb7FPj4GV6rb6DUa##Wl#){Z8lj3TDzQYBNJuD+2n2d@3zazRWwr5)<8?N>W@hXp zmN|fX<A%hkm*ki;--8b@;)aCeufV0eH*2S<OApLye)HzNH#2YEd-FDFGy(!G`u%D0 zw@b)hIJw>&nCwEAO#nt1&B!s0C?#JLW-<E-VK%p>c4RZ>F$vun1+|ds#$J&<$h*aN zyi*9)0p<(MhS@H3xevfeL>Y-JMw#`5z)r9ij1F^|2cye9?s8{pMHN<IK5%?iWdV#; zR%3M-1J;1ESF{I*4v+H=?!IV9W8Z@=M*xaUsG?I#G#Q;x2ZTpdyvM8~D(~hNB?@je zq3dKye+K!4#P-AjbSBohdqIHmm;+d0E}##4khp3vFjzBKH`p+^!U$OBwP$cRnLPs- zf7~&KHq)H()8@t<5jOKeHIp1T3VKr{`61u<bZa}Dt-^xz*BW?rRR^g|bE%TN!?g{n zI`}@xhWslb3ZcE&Y^N*nAQ3!QQE<rBoYujdh))i5H9qamb={Mcsh;+FDPug>ZpKGE z(>|D&B?HN|Ka^a=k}K^WaH-;Ms<b!Efy+W$JCbMJ$N}xss9qi#%O2<Il8ALM`=5uc zW6si~^-Ut!mm(eU)}ynYD)QFjPOm7k*5-)kqgIw4v<7FYSLF9LKWoWU@lOUx=O{Vk zvUQrBs8$))&B2-WV=z2baqPkN@+|;Do3u_n_yX!#I4-{N>U`Z|O~*q0c|-atFScRw z|MSAc)oBZSBIz>-?D#IY0ur!ky!z_6Zf|Yf+YUoVRA8m>5vl+XL9;Nl#2d(4K|nv2 za0^wc5Q|a^1rpsrj0eVR^V?o2bnq!I8q(O!Gr(07(ucIXr0tN3+c3YR<)O>xVGbFq z-UcOUJV>w3x)Q$HwKwKxox@|gz{g^t+XU_w+M*6aR81I*r8>*FXdt`1zIIF2vO#!1 zBWZ%}vO!nEmoAS%GM9f72t^c_(uaz36ROxPs9{rUO5v3{?Zde8$L(+R)BA^ayq|vC z`{mF3Cca=R#!0)P80M@D%{#zYj?b#`6(ngqNKbhtKLnl9NNc=4kI<6J3#OujF)NIz zBOowNjP8P&JLE@Gkowfj=?;0g;J`&d!@kCW5(*sm8pkm@Udd5Vy!zg}S77w9)b*=V z?>xA`a#d*yOc!(a85%W1t7hnf83rw^2`o$k!z*4#SVMRX0o4WmRZ2HuOvOzAZ9~2+ zqFX5jDlKyPk^?cP4$^Kn7UtLS5iFE=BB3kvM)^H=<&ttctYL}BlVcvoQ1jyBf(<jI z>v8<UFv&`Zz#I@*J;b{J+8>Bwz=b+14SpX5A0TWXJWoJF;WHFA0Q@b9#5d|wf6aG& vJFtA)cZ{qZI#KP52mAZw|4$m9Xs<Zv^Nuna|Fy05KQ9g5!Tn9z)UAI3P-wMa literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/CompoundDoc.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/CompoundDoc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41e183fcd56d56d3ef3bd0873a540778721978de GIT binary patch literal 4513 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 H3qt+BP<SEK literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormula.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormula.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ce766760be1f4c33f37aacd4cc82bbbc4e01619 GIT binary patch literal 1905 zcmZuxOK%%D5awegt!3FzQYTK**P@qZQPeWrOA8o5f~08<fojApf@~WsHcQG{Wwoo2 zv?DL%9-8!5^kVhqbN|g=d-Ac@oH|3TWHk-}&J1UUoCiNLT3c%p7{T9vM!gy#|KMVI zIIy@2Q-2FY5J6KiqyZhWfKeit;3*F{=$+IV)`A)(KM+w9^|wUSW&I5g8iMZ=zj01s zyU)t2-;ZONKFrndFpc&irKKvbEbAW2u~g^aOu9ZT*Y9<Idi(_KBa`G=jQZulU~w0w z-UT8_Km`fd0c80Xg{&aEw<K_cBWl1QE71U67p_<V-VjZ(3fvVf$Z)M}R9X2>z{5}n zZWxYif<X(GeJM?-WKZL297kztS21QVAJ{AG=6#s@2uN{fLMJ37hMuwsozfF#_^C5t zQ`FWbWKcV;PYER`4H}?xt+WDC^C`)89AZ|FN0gj!N{U;sw<YL=ZjveeojxVc;66_H zZ#-+x_%_)F4b;NA#YP-uUmDLA;Pqzh_Mphsu&NSDQXEnYYPRDF>nQ8hjkGVksunb= zGlKS_6H>`6mb%=ya9-GS&|dWD1AYseR)9GYT&t3@;TN~<JEfy#+AFCpYboNQr1FfY zHf(wKb7v?;5_KL$N<2`>k?iap_f4L4_Tzq@r=6`MnH_b~<V9z6Z2Ecj&DM7vof!G` zD2iW32U2&&>1)$jhThibxP(hjvc!a83xepYKnP{DPMfp^vq{(KIxE^$x?6MS7|}f+ zC}TG*3h)@XQ=GKN_Q@IT`b;56)GClOs?fuhG$U7FS*{<<z*e>H2I#Z}8ZEBRWB=df z5)SLI$7b*lgb95>p6~w3PS^>@YzFX7ChQM(lNff&0aJX+Hf^2*I$;82LC`wWh4htU zamBmu*zTiq%*1!p3b2x^QC8NqQHQZ99qcVwu%w_dicH)GeAM$^PfJtsM2z1rv~8X6 zB~_(<3|lq6BLWRCZp@2)-|bSvvFWy4tQ$+*C+Z^*eX1J3)CLj+&Qg~hBgf`*(9L_> znukNfr4Wcz8{!iX3^}EO8U~^Gz<EQbm9%N!U-56#tHzJEcwFo!g+zWo&y38BUbt+d z){dQuTi3kjnZAU_o2L2eBs*9<V-cCiyR8d<al(9`=jmd0L{*75^L?_{^-TU!W?rOJ zbbPV#mz}S4@f^1tMQRKA?cDyfQR;j5gh=q$jndgMy<B;EB;%x)#IxgcbCH6&4qfqU z>LxIC3rNXRnOTr1#P5f0z#6^)<Lm<5Vs(B2fU2&)g0C@)hB6GxW*820ahReEAMmTg zD6K3j@ROyv(P60YxvFbOK1K2w5*z#C;;n>lOd(KQm>SXQQkT0en2yV>#dk|$|3v#q o+Ny$Dl$li79}7VJVjyE<eV1^xIKFguXZhaY7z2Z~sY_e@Ke7PC)c^nh literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaLexer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18f4069c34ee7438d7bdfb0abc1c83512798a3b0 GIT binary patch literal 3823 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelFormulaParser.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3783a865c31dbcb75d5d08775c9e7c8c8e265351 GIT binary patch literal 11494 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelMagic.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/ExcelMagic.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..877e858b5096575d8c55c16074a8b9a73692c87f GIT binary patch literal 21432 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Formatting.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Formatting.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed4b73c91b658522b7d85e5e0e51055b2dc6f194 GIT binary patch literal 7541 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Row.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Row.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f82cf70389ad80c4b0c1bdda7380b93b0dbc1f21 GIT binary patch literal 9436 zcmcgyPjDPpTJQhU^GBn<mSovUCW)PR5=U~J>}Iogjd2`j6S9Je1GO|2+O2s#@{D?V z#;;qpEJm;!8OVjL-2)UXdyqIl`9Kv_P@FhWTbw8^RB?blaDj_1TqrJERGHuRy`Is` zNZzm)M%7=xKi_+=-}k=%Pr=kwNyE?kt3L_;`3+6`cdCs3EL3jbN&W~xXhM&)j_&EY zCJbRlM#uC_E?X#Dp2cN5vOA9FbP8Uf<9cqV=oLF9uhc1f<xa(`bf&y1#&a;H>QzzJ zL?N2?W-zB4&35LzIqok;^PL56p>xJN(^>QuJ4@b@u6>}1k|;mcL^)jEH@&l>a7U|G zj_`F_UC#=)!bYnTL`S-+mv4S}`}QYcqbo&%Zu?I9C<^OtR=AVO_rfU3iZ`Pm-r{0u zeY>*}s^;?TuIy|_0e4iswH+O!m2Y;tQ5eK(`1?|J)sSU>Gfe#kCQbZ}R<r4gAPsy> zf^{qJ@1P@}a~%!V?QWdbt*o>WBw>=uR=k-xJ2Hqj!>rhfQ$Kyw3lkL6P~zz%4cg%7 z&qU=0p5!vN8fu;{G|$+?0sX1&nRGahHO~^3u<^EqBMNvs!WBil3yOrED=K0N^`fYX zX}n8fM$F<}7IR`A?}}IuXYig9i((1ys#q3h@tzhd;vC*H;=Fhc?^*G@cmeOZaK63J z)J09a_*nPO<h7T?%cw2pwF}}RYD;<T74a%+%X#gRsH1k4YT~t~Azl}kQD4b>-w>;) zoy%)$;tFc#^V*x@Dr(Q=wYS9Es68*fCcY}(LFt8X$*YCWdoPNwfLY$n%ul)x>$9Hi z`z^8KxxU{EWEiJjc~J5nZg>S>9i}(s`<+(oH@Xp{Q#IHbR!hFW)AU=h2zR_i-@g|m zz6hJac9b5s&!IhRZQe_P=l5DWVU%Q!YRTqCN91kOId-R&bb_>TFSDcYK^SE&z99;F zNr=%cA;LH-C%vE%`oTu`K{%*vgi-flgZN|cqcT13;Yof7kZOHB)%Oiq6MA2_(7C0{ z=Y_GQCzoSG*HZ9LUtiSr_0Q4Y*L<^Y09t+Hz&_Maa#{on7_Ip(!=kWIE^)amY?Ld) z0i5END#K}pGYn@L&M};4xF9r~PGMb=?6#gh)wJEqviq>cw5>f1lG;UixfXZRS`cA} zp{Q*<s;%xsNv#QC4br1&j60eofU#QrOjc?otvE@8xDm=ZG-ah<4WjLkWi~7N-~#4= ztfGoJ7nDejRXLj;?EzP0W!1o9p3SPt2zLydjyWW&Dy#vQWGkvZdhWx%#k_BVTjUvh zy<8-)L|~b~Spq8r&Jj2dP&YFx38SWGfp9xsMeV{*?uB9cM3a|+@{^C(I-zI<Yu^l{ zxFuT;!nI$1bT943Yj+y=pxM?|AB6FPwWzhR)_auR>&9=bzPpw{2fYDK+X^<rWNjyU zn680%tG!1Vgr^m^Qr~|Wb0@C=XnIYr=nno$45@@aBY)>@yA_&XFt}mR!2D<9O=gF= z^#%%ky{)zNJtH;x#=b7}bK0JnnyJ-?o?Fm_c}^475_olA-?RGQ!nVC-$!k>dZRRO^ zeG}8*_x24{_`6T@g7)E!%^&_T{r>gm>rQ4hp_ZB0nca-KL7JIR#+fa<+p&<ZVTQUb zU&kx66S5?pojz>!63^*%ftPqDgqx|X01PUWrSAu#y`7}F4){766E?cKt5>XLkZE^b z5n~LmL1HlY4QTQK8j>#n`uZ*Hv)=+uG@{9#2?;WDIle%0EU&~c#<2^zHMfl9TB<{$ zja;I&wgF0+pp?0#$xo;R38%UR%589+`YljWlafll&2<O;+~Yg<EvBud<W;_akJ^0= z3TnF(=OTOQqO2=<rIr*=jV;`zt6%wQy<U)SVu*Z^05Ojw=8>-gc-3Z0f?nH6C`iwG zQ&BKd9UOrb$ju}(0X?fl&}KfLq(78$PS*`O#ew2}9}6WUJsOU|&3TqM_W0Rd89SD- z;bR<Iz%2AI#~z>{h+A35whd_6kBHK3i!|(>8N0&7d09gY8yMG9y$uR~PusKZ_fW>E zQ8|8v+7FymKd=Q^apw?v`Ow&d_C9XGS?YajS=+PwcDsPb6@_!!lD6mc4f&%!tUgYS z-p)JJ=0M99*KgO&Ru3O7O?)o{G;E>asDg$gI?hj9(Ewo+RZUy|?;A%yhj+awYj|ab zNHYs=PAD&+3ikoU#DDYMD{p5mmD5%y%nFTeXQLH|g!w4R3JJWi7~WWc1xb=P;1EUI zD~G*q<DQ>x_o6Vfxsur;jMBh!b|PMe6xg_Eh2&U~s}=ep{S@6v4M5W^=pYwb$Kq1O zs2Iz76)iIcwGRYtZ1kAGk?_&ON9v$}BZPT+qQiS*Mesyd-#`9I*#a4lNn_u}_=HuQ zzPn63bsEwbM-eg|3@`r@FMj}jI)vkm577?{DwEW*<zz{GVyvrUw6l3-MaI+#A5m5e zgz#B)$xE13)&c5<+R0ngFtMFtg6%MVjNXI@p+Q2b`tB6%W7zwDp`E;m!Jr;5OdFeA ztU_cNn8FkY1DeeYXg26Pu^iEhX+??^k_Q^?>vuG@q>-6X7|V;a<Z0_p)4IxWZ$L$) zJ0nZ8W}fIjvHB^DGT1CDwbK?Y)1<lQ&>vZFda&2L?`K);)o1NcEv9^u@0_}r>Kj|k z1R~^L(e)ouAQ2Gy4Q-Eo!nQ7q8?e6DwO^tZ*c4%*ZeQ2-V5d=sbw=Ice$`@eOJStN z<`#FP#o-of&?1VMU5!T6g89mW9<1OpZe^sU$SqT<B`qD4fmOjXg{KNFX&l-I(}(0| zR@Ylke;W)zyOhrX9MN9c_dK$wN+$P+82*S9LF*~J%cB{plB0D*BkD6Fe%XXe#%>x( zmtyy!hbC!muZTldQK=;1J~n-s3AU)4ERbF`*CX9t+Umwooq1tWZf1#<B%Ik{*!dWf zCiI;otaC<HpUeMT^(H0wbob1F)yBq-MGmxo`eNeXqzORLKwEpD$#3HgJC&@G211Fw zhrZr47s)$=E#ue$xPARBELrOGar#2rH_1u~ZCmFV3I}fAX01jX1Kev!2H<qe_1$-V zAw}TZOKWMjhUi8<q(HbvV*2$O8~Qltgx|O*Yxw$|Z(I~Lcnb7}g@Lnxzz$Z5MgC=c zwl~KD?hhyOyIYw>Gw=aqMGDZF%vk}{4AN98n^u4Uf&!FvhtDJ{VigtbAsWNC_}z_m zW_FTIZ>lH52l*-+ZTV|hb@HIfh(<!Gbj=?SMaiS5BXV^^uRgU57iF?f6@8cWzzBYW zj$!a4-lT_*<!u!D8kvW_c}T$<#8c=?a106V8*mh7;35zP3<Y7_f1Zde-^V~G|GRie z3Y&7r%Cm`*Z>rZ-`5V;z8G#AgFr9xLQXH&24}p_gh>E_L+t>-5F$F_Po5YWIK~_OP zr92+0?^|%`N!1(}hmgQDXRv*P+2h1SM4mf%nC&+~kk1xqu+Yw8T5ubDq#qdfEetkT z=GS>Ubt5xYuVfbGSSI&Zx)!yP6y8hk`k!Os|H7l~zCMr{WiN&jw|Xt=Hi9U*j>2FR z=sAxu%!P={x-ykhR40+ojjmT>CgK#&<ofd@))UM88HVOGULZea=^39hMjkRx^e~-C zqDj;S#!*)qi25c!phzFU(f41VH=*Owa>GCDIl-1>fO(-?C@|BR%4<2mY2^Nkae!#! zkfJs10Ota2p|u_IVevK&3eeK}zD116Y<d$D)m?cTuR-j5nnTa>Dn9_0;%M5a67GU> zdgPNlcC(1TGy4JM0wj4onLBVSCP^?w9u6N-8t$Zj3Ead5Aekje-JK=+kKv4|Ph!fU z2_;XL34-mB8#h4^wC;h&#|UEXvqT@0p}=I2Elf4JUp8ONT9)$XB0{3=N%Gw$cUB#K z^#)}?{{|@ITbmo(8Y7)J>>@_-VG9&s2!^SozA=_mx<E;#k2EBcNDGZH1dbX#eC%T^ z(Km)nVe~C9gQ=JShJj5qhnol?h$$wi5l|bkp%OH8=w3<1lyvl7k>g1!KAS#sL;66! z@$Qqq#hiKMeu0m_V(c!CQE7;Jf>OjaOsQ)qOj6-X#ln~rhtqd{7jsKmX>{k3_#ks) zPHsPYzthA0D$KvfsBwm1S9k*J#6E}OPg46c?^F9}!asS=cd^3pZU;fnSV)&!G)02S zAS5N5{cJ)Ui#*}~1CgNuFc5i1UwAqp_8gj$f^FD@nEy!s45{%kDqlhkQi2Agf1n*~ zC`Keu5zy;>n{2r-e%~G_#ep2)Dxn0k)9{;7u$kmqa)MCVJnKKo0s1m*hDdcIA9@#G zBuRc1uPFD13FQ?ipNh*5MWcs=bD9R38S+Ao|4+co<wL1QD9D8|`N$DZaLrJU+`trH zLZ=f(BZa|0^6ylGF+mEo>oV<#G-YOOAmb^wsD60}C~KD;Z;nuK-SaPK9hZmkt>tJZ zw)X!g_{60r1m9Z->4u72SlluBC^fo5S-n#a_1G-%xPLpb>v6NdNMm~*WBV^46<>Co z$m2rUBL^nI?wsF6whGY&RDqtl5P;&)GbkN<2JvE!K@lEfC{y`;J)YwkE12;Qa~z8P z)GWCojsKHzs_Aq((=^CQmOsvMXHVe%m~rRY^XXiB0neGH3H<*7{vv%3;u-a=$0KdU zjPVx`TWwqWKJHd>oQbcEn>_bYdwJgg)+booI5<000Yrh76W?={$DB*gH<4S!s1$QQ z2O1$~*tZTT>pPPo)0n<M)KnSYJf>pP`IDtziNl><*hq1M0@W4N=&liWEA`r`=Cnp^ zMS-vUh`?O}L*GiasmiX$E|plc=&D6tAz%<-gY+(yD0iq5eio$$CFOWz6~HUp&Q%WO zURW(<7WvVct<r({y@+g4f)Jh<QfyHV%=ha+&n()&AV4!=)^T4J;1-Pa7jDHUQ?LA3 z+-y>&Ft>#hB5!kq`5%}up^qOZeDd=geetPdt-$E7Ab?&$bVH?*PC3O2N;3$ucPXe} z<vGR#f_y--3qRE<rmGFQ(x4+vuBNsi-9J1&#XNMtLq7<~zCBDjrtxcNkjQCh@;d~G zHu7@<-vy}WUg18<S@oEZL}jJuC&+Q^01c5TiWpJwRqT7h0UDyNkJ&=9gF{=mNJ;^b z=(Z*oz}(~qb1)!~z~1DfH6qr(-YbA6g_K0?YyH9=(gb_OIOR4gwTVvhM^qC?FEG{y z*9_Dg(D3uLAWWza?%yXxQV{6d*PDY&5bnXe^aVxL3t=uocnZQI{DJWFU0Lfp2c^E- zFE*8RrUZtDRyEdamq`uW2NtV^3dDAhaH5b#JDBb}gOG+57h%kxhf-43v5#N3bMMv! zy>`z%LryP4R@LFpj>G^p3{hfw%mH*Eddvo%OMU>|aAi)=>xH<{S|fzJ1cnmrIfR8! zm4v!pQlSJfjr=_V98dfXO8Do1{F<9*G^Dx;2r@lU(~h$f5-|RdIdbVO5Ienwx#$X} zf<(QAcL~seSe@5vsLdHOsB_{T@0W6a`{Y!0<mF$Y%ScM;_<n|4`A%1CL+*Xt?)vw) zgDCGQ`hF61(*$#@(uQsm;CPC)(JiWelfZ`rev!b(1inS!+XU_s2ncKvxJQ5l9RDVf zQ$!`$yVTJmaGyXz-~oY01b&l1pTO@D_=3Q90lW&U5MPBH-n6O>NjKuBFoDTgkqZE% zMiUA#ahtW^F1clQ+Ff)l*TJLyX5ETwy9L**I{0((6lMxsK3^@A&RROpQ(wuD^WuM^ zVfCG9oU&9qwBAF?OYsWpd{*u3M3QtX?O&nN6ao4qrSnPqdnMBG$gIH+k;IvqL%Ee8 tRa`qu$ZR?0xeq=%Oca08X?xDxYwCcnldL3UpDN&?ihhYNcq;m``M+QLbo~GT literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Style.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Style.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7fbcc6675f4f62d8c02a7e34526ed74d146501c GIT binary patch literal 16125 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 a_p<%S`+oLU@{T#A-7_!a4#_?9ss9J2*t-${ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/UnicodeUtils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/UnicodeUtils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b288d0d00d4707ea16c1cfd935fdde0170fc0d80 GIT binary patch literal 3785 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Utils.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..558a75fd35bee6abb3e29d25006b14b2e119787c GIT binary patch literal 4180 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 E0ZWS8_y7O^ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Workbook.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Workbook.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1a6629da57e814ca241e9b19c4fae8a12fcf51b GIT binary patch literal 22785 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Worksheet.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/Worksheet.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa164de148688ae75258827a2ae961b083b1a5c7 GIT binary patch literal 41855 zcmchA2YejG_5U@Uda*3`!hs7G*m9xS#<(|%3%0><U{0r-m2~pyPM$kiwjxtvLQ6tO zNC<%xnG`|_A&rFe1|dBmAtaYdNHe679zw$Z``+x`&Ft;k(*5)K$)2+By`6dUojx}^ z6K`y+^WiW0;d|qwpYZvfp$Gjhgom{-*<M(}C;Ta2zd!2tlRp4|AR1tQ5dL5^$o^0& z)E|n5*k6^Z>aU7cu|J#&_g6=&`)i^#c&-*TsoH4m7N4jUbvOD%omF=<7_ApIyM68T z$Nh+|-Jh>tf8plMyR1Y;iYy+(>yx?u_yBxWyK{$A7CgaSnIYnA$fO4Q)9rzL^<X-g z$OtQ6m&{r6U_3QwWsf5?-}VIA`d=||0f1onqJH6v282Hv6oF{y4!;-_AyIXsKUyWi zqWVT(G%RXFE!=9W#;Uf$M*~r)F6u$41Eub$AL=)VMm#s*xdG3OqDeI4xe3orcy1Og zVgjC9@Z5su38Ga@#B(d2Tk$+mw24W0ZWEKm6u6T>pJcTmrpaQem<GxeF<s1nI~AO% z))a82iJ4*+DAUDkaSGfS;LNb5gELb^#Hpan5~qndaA$)v+nNQ=DPpde2TDZD7pKEL z6`WJ82so#S1)?34IpPd)CfvE;%(do#GfylOi$Iw#I>chQr-O64H6NS>Vu@G^O1oGl z&VqXeIA>Vx;G8MW7UzJnP@F5ygS!ZvMb<)aI>d6Z0+hvKr8pn%5^$DSi@{kcR*4Hh zSteGCHE_=Y=PYX(IA@EsVjU>wi1lIv+;hP>*E$EB^TbB636$kxv)BT61vo3L<>0Io zTg8Q-oG&gC7sFiz&MNDCa4ry+h;5*(7Td)RxNE>!W32{ft=K6p1!bMsC3eGI56*gP z9XK1r9&s5c8^z^fFWgPwY_c|jvsqjrt^{R^h>EM=ZUtwnwFR6D#noaTC>M!qL=5i5 z;9P871kNR*Q^Y~pCb~od?sjmtTid|dA%w6%*(th358O+^xzyST&Mwg_lA!DsePTb{ zJ>cxIc7t=7NQr(>E*EK$fx8!+z1HR6Tp<R;0Z^_Ks77$3;6$w}!MRH0#2_eFi-Tea z?mlq#SyzK|jW{F@gAx<N;$?6<!RfSO;Kap<I08zSxK>;THvvw<>H<fI>%|SASmNd4 zDBNyvx-AQw9&w|11t`7ZCUG;|BsfW{7o0wEi?|h({o*$9O1LR-Qr3QO`o*int3gSN z+r?|(X28i<X>bO_9pbg191yP)cfyt6Nb3MNS@C-D22gV1F7ZaVgWwEWIdBe&yTzM8 z84~x1d*L1e=a4l7&SCLp@fJ{q#aqSO;Jyr;ms!K$jEGTj43s0{KJj+A*Mf7cbp)L2 z#5=@0LAhSMOWY6l25@e$t_SDk;@#o_P>zcCi1)(15u6*Xqu{(kyidFzl$*qZ;sbDR z2Ipq$CU9;M4~Y+ga;x}|_%Phtz`4!36`WU!kBE<g@+$GL_!!(*gY#<ZRp8t%J}w>s z<u&3H;*)Ui0Ot<tHQ>Bfd`f&8l-G&Rh|j{k6P!D(*MakT@j3B%P~ITEAifCqE^zL$ z-T=-U#h1jRpxiAU6JLh=CUD+l-3`t?;&Jg6Q0^696<>q<W^mqY-3!iJ#Mi|)KzXb9 zruY`zw}JCE>#g97if@bWfO1ScA-)UuK5*`{j)C)b@jdY*DDM#87f-=`Cpho4-T}_L z#1F&|LAhV##gE{=8=QAr_k;6*__6p2DDM$J6+eUfUU1%Py$784iJyz7L3zLUg?I+; zgWx=9y&s$ph+m3df%1^}wfGI(4}$YS>mhJHBz`N7gYseVtauLYN5J`r^<i*6Dt;$^ z56Z*h58{t-KL*antcSt*xcHNJ9+XGKpT%F`egd3NSdW17N%2?lH&8w${x1Fj_tW5f z+WHhYpAr8Q{{rQ+;sx<<xSs>(bJl0U`Mh{h{0EdTi2sV0;C>ODFIry!=S%2wd@z1c zAB7u$3BvyvBp$OKg~XRZp`NJ<)W_k5VXEQ(3M9T_Jr0Slf>Hxh3+mV4*1^=n|8+=w z-TE3Nz5z-DOe3h@gxdtu4F9(v@h$6{koYzzEie;6{SMq#n2GQ|0f{H9??B?aptQkE z0`+@vC&NsE|4B$ZX?+h8-v?zX%rsD+f;$~%2K+yO#1E{eAn`*`X2Q$@H4k?-%qj5y z2ogWC@{ss3C=r-bLH!Bb(_rSn|5Hf()cOe|eg?{1n0cW79PWIW)8T&_5>Hz{hr};H zSpd@x>N9Z9fH@QXUqa%S)-#a!6(|c~7J>R}xE(Ny;r|UJeq;R_62Apy3CvPZkHcLC za~Ay1LgHELI3%6}<!qR9K>Z!ub79Vd|M!siz4bdt`~j5ZYODa?3hR%3f9j_ET?unO z#u(2t#gC^|Fc*OG=OSe_C~H9Z3*6G1Yw@%W)W5<#2(uAp6Q2JDcL-)P%oaTV9qv|` z3!%?H;9dlCG5r68dkM@o`2PiWJIoIFUx2$4=2H0o4R;sJZunn>y9een`2Pd<a+tmF z{}=8RFjvC=65J@vRX3s>M`6U(F#Gt4(sm6@jGrj&oiK5J3c}X~li(*R;R43urz-fm zVS4x}3|}uylAo&K>x0?PPc`tRVEXx~7QQr0hM%Y&48R=Vr+WA#OqQP-;LGjyEjWZx zXTElCM()q{T2^lOA9WG@?deTsBlsVWBvSEgmTA%&kXF`8=aT83NUqn4Br@q79<z~5 zHw5|hNHPuY5JKz9WcGK|@rxZDk)gOu<F%D56(th6GBX$vne^$oNIE0?<Ehl)NJ3ii zoP`jwxp+EZaj-ekgH?ze&J4<|mFn)`FiA!0a3n6Rh_rHpGHr=Sx6JfM5(q0B$z>ud z`>kB>%Fb&KrG|1Hls?v|=!pvvW0gBqfX9OvC2vm9@VM|t{s7DdnC#W?<$NQ)m@nst z?H?gKFcN?r90|e>C5c<bcDRT9)q-Sd1j*D2lBr|6o+%CNZ)CfPDb4I}VgCfSTiKq- zb{pH1*q$sx2yF`cr?NdwP>j<>73>*InaPw{Y|m!<6t*KQb1KW6#`YYJcP{(qu|1#d z)7f6Yc00?Q!S<O<S;+Px5k`z1>|f0G61JDJy-ZYtdltWSwy1&s9QL2f-19^&D9hQu zg6)-TpU*O@7{d$LUM=b%xkl8(UduA;SZ2Lw0A&N)8$~1hn?w`r&7v9h7LIW%OJ2zK zMQmTp_9bj@<IuLVy@TzYY+uUuF42N;cC)=lOo0C~wlC**_p;0tY+uQCl<liTD<rRG z|2{Dh{%hDDW2`zk7vfw_x|p62KFt5xw*Q3+o~$K{rh>=U!rFZNCGysgC1CZu_`vf| zZ0p*&W;if^C(P!av-t13YkFSs+@&9%{fcMS^gOub>EX5qpIURAf=Vt#SUvZ=iT)m0 zllL8e2?4eXSp}X9!^*eBinTu$OUL`Ie5+0wKpjixqOGxnl}g1j2Q4`yG0Ti)_M30Y z%uv>(Br>UNewrRuza@Jt5tH$BkCn|&)LC8q12JhOO!;hYW+>K;24WDcP=1mw&#WGq z6#1!T<X$T-ESWVq1M#$#jiHSgw$k|)etp=InV6a_=V$7n#0PVk7}Q6T6XQ5$>s(4# zOeTAJb1{getnQo{6NjA{%%zfP%S?~7`T<}pJCI7|viUYWP&92;B4-Kn1#Y2Y2b0-k zSIRPD8Ho2-F|^1Blh#mvmL61hQf707mtx456S0w){1p9dO0S)EM}#V_Sbw}HZN8$K zaK$hal}IV-B<m2;oJi*QF_yJ*)Qd#xIAuwoaX3F+R|2Ihr(lW=WU?l&HzS8N-dvCO z3>5N)loo<1HJfvWqD<zJnRE=xD!FV76(E)yN)9**Gk}ft4CZ1(Ns;R{RRpNa>b0np z<WJRA<kG<fjB~IHz21ID3Pm!W%JdAPpiq~bHwquWnWcrGm}?QUiLov#g^V|9UxCF% z*JO1gXVQcHF;XdO)&<s=(prels42{)<QBL_jPX=1hMbo%3$-kNiVg#@?3x`z`y!Kw z*GMQuDXi}JAOvk3XOz=o{mE>9JeTOrw;IeO))G)kby{{Hp0Hx^F4RCbh62o}NR+OE z3M!Y4CI~}~M692d-6rcPV{#%hn9iBCWY-2XaNX(!10`Csi&J^a#xm*r1U)QarE+mI zm~0#^2wF-+Zsy^DOr{Ac)%F=GDHgY6M*ZZvj>>4}I9EQsa2h$zl}G0qF{$Wd*_<>% z>CR-3N~RmdKxvjh3BYQGh*8vLIdKS3>?R)6{-RZpJ;}7$Ua1z^<`_!mG6RJ-jEv~Y z<Z_w*0&9kz5d-l7Qa?Ftp>0Ht8}BI{v&a%OYGz}Lu`F6Lh70wp%aUdxr+UXFi+U1O z`3!i7uL^%jz$NNrnwoh>k1~*vIT=qnGFR!E&g2ShywUh8u8?u`X)X-B=B%d0@X)!& zQQOnP1VHgK{YZ!<Wd^;nkrj&Fi;0CB{UFhe^xEai&Q*hD7csXCPh<wrzEBHirfMiH z(1jvV2}g1>U42D{F-$2)n}ZIN-=ert!3HpS!fZ(78!>>%#Rju9`a!$gr23uWv&N<l zS!dI81GQs0tAC(7=^O@hrv}kWaHuW~s6BGPAmyNy%C?8{A(}!(Yq?{gv0k)ZjqhSK zJBm&y8{<9TlY<x}zOZX?zlCmT@z%H$8)fpKwRq>@-drZVcz2>VlSwV^IB2C0E>0!8 z77rZG^=8s%b*xy7PSje2Mt47TDvN0zwzxRb>=-zluZhKy=wM^9^ASV#aacZo*dGqS z6zq~qH1#hSuG9F67O>y)heP3sW!!>HA%v1NQB-^d-uT?oyCok&7;Y^kw2;UIO(hF| z<S$Mw>DMTpPWosf@PL2&1Af^C+5-WZgOjh~Au8vCvJMip2JJQv*Pp;smIF89EwB?b zmni&^pA1iFY4kth>qDIK5WMm*EJRIlmPOWMNA@I`E|C@3V~z~*Orm&>xI|V*p{Zi| zV=cP25~d}AF-O?$my^9BtfBCz0ewb`PRqgqdpwa%Ibmd|)Y)gfBO~^BBAe<J*#uJE zm~}m`#X7@8)Q}r<^vKC+UeVW6^!Ci|pIXF|%!q9X?;6sCcd_{5KgvrlR7<$T;+M}L z#yoO5#Xn3IwN+XjnLw3*hp2@~B+aG^<yT97nOs@&joL~JL4DAsq(=ztXG$sJR-`!} z3`Ou0vYR3Vm?2jIDDoykVFp=lpfnZ}OT)ifO^+$1Y%yfU35L9-7{419jm0!l{0;8$ z8!{DRFw+ZzmSXzs@$y7Xjp;5h*kTnS!CJT>(NavM9sFc>Eu3N%<1y=mc<3u6v)nZy z*mSXq@L(-G@z8^hxNAXp=wcP)af%lnZIn>wD$r6*o7rX=af()i3v1(sOB*HF8O7P| z+PH~Uj8DW1pUH#|^-|_K*!h}H5fh<u;fgR~jodJrObIWKWVO3S?!p!0bgCClrFO<% zAK9eo<JQjDVijS-TDW0TYG>@=x4LUVsBq^~(1J&mNE`i;zt{<ScF3ylISsO|eGhlY z#wzFzO%+FnY{*rF2<zd72=~WE_`BToAV>_kip$6xFH9P^FS3_^uhi6-Y}TN{3UNh9 zaO`eKG;pTdv0v>TyDn03`saG3-yWo4rL(YRO)2yoq&eges`|q_rSeHrs<J_v8kevh zZb;Z;G%fsYcRf@!(c~(|WFFLXttZ@W@T!r$!r!l{;%GN?v5LwDYvG0nw;Ou!1MXT7 zAi9`09_Cb+2l1Fc%=dy}5>*XjiJqouLz)(+n02H~whR!w>puH^D=FcI!X!#wS-`{Y zO1R377mw4&fd{(08{AbX#}2Y%$K%*I@Ib&fx+_tR2Pr}QNa2tCWOzx!y?iVH+qIfB zQyO^@8=>OIx>ssSOfyS{CN*{p-a!$&m5F9bmlovh?h!kr#+&YTuXL;CLHj-K4O;xp z=0Ou1d&=+kO1Wwiv@q{>kD8Khw_W_3-+0r0hF97-&$VA1-=@Xy$a76<>?wbbSIRlh zwIJ_vkC+m!NsTw%XL_Yu4HN7iR3Fgdb`BG4k+G-v{az_nV*@+9_qj(&$;}p`^sLvO z<HdY1-j$V$yb|3?72NzW_#>JgGtD|~3XT~7EDf>(ZWX<i(r1SIF?R*Z1--N06At+h zA<=O{IJ94{@k8}z+_fRxOu_NM!3uc7VTSv8cLfLzQ?Oz<7JI=l(@{E%PWo|8ulb(k z#CT;q5V2Bj5Y2QH1S5v8xhr*|S1N{W$q8Z8^2X5c33m-0<;i$uJg~7+p0If-^`yH} zjxuH{HU73p&0Wv-f^8aMz|&)1is1pT+P(27O}#m0QFE0q1I!rzc+6W^FE`|-5e%;J z{KZ`_Px(rU8m~2<<AoWw-{y9f7c`|D?YAjd5lXCy8%o@en-ToST@!-G6s#DRbG>lk z^)oI;##WvX3PNGx>sUW47p@2&*2oPXUOy|3q}p90!l+!hVw}$N!im=g?A0i>rKw3% z$gy5z$W??6>*0nDuMHUCPjJ_RfHCAM#$@@JFyT!uQ{8o;_Nt%@%T<I4>v2*{IQ$vz zdgz!aJs?;7BR`oF%>Pz^?K=PC;$iGvJI!4Mg2Iq1g92@V`?|mvDRR&B8-dPqkDStP z$W>e>R(b)!zgKV{Ks!b3Okb#}<M_S8DOv`Hks}^G5^Ljz4F6u?jAF67HUy4Sv|@Z{ zBZ+H4DdB^Kf&xD0x@%PI<~7lZ@L_FEjE@$@3U_Ua_-LXP<Fm>OAFfEoZq2otI*xYM zkgEt8*24`MZfA|~H@NFTuo!X`V{(BPCR3;&P#lhJsuyV*oobHuT%;?)hjntpXG$U6 z4xP5S>*OKrjg|ZQ+iEX}co;)1PGOg<+N-!n)5bB3DHkpS$g?H>CaK|u3XfgNL*DDI z27yv8JYGzwyK-%6c+HwBYXKA9-+7Iui(}2YOm^&ue8&qBUb8L>IPR_l0a7OGjR!4W zjNpUKwe-aiZ&_;(Y?t+*Vkv*47c9K%q_|_cSJT9?>!c_%b{M|r1p{w2DF(RTJyK47 zVJo+HvfZo3Ca?51QR~4`^Hy-F#XZ$*G)h8aPw`V;DQ+sJhNB&HkJc3OPBUdSI{)&0 zS8A%f*(>q9Myu_2AJNostkG&xWy!}|rD37RAcS>rYZQ37Rtx<)cO3`{O{!unws>K| zU8}Lh|0YcnN7rh|RfGrY;f4oytw#8_y6ZuR7;@g2xYvoTUQpCho#0Oz?$Dy1XjTX2 zmnGe^Q}`)G=9b=CN(Tpar+Z{YzIXagQvNUWN+_3q?L&=wGz}c(Uy~|JG~POC`DY#6 z($D2z3;iwbIuHt)RK-|a<b?&7e|ww1`ZnbqnkJ6&Z^%`I2kYU62bX^%{QKSYAVdtg ziZQv^3lr`fR2=jp;)9wZj?SSZR1q4ifg2j!Pm}`xpt}YoC`e6j9NgQ1ZC=Ue*%<$* zqAj9+K~upoTXl+-LE+gt{DxF;OFvIho#8&}t^%Rp6df-X+fRrEuX}4)eBE6UYCfH! zW5?pz6Jp^E_gn5N5Ef3+GAx3KiN?p-(_qo~A?H6DkW+I3`pTg%*cUn)IHEqFuPW{3 z4&(5uY~kbod=Oud$!gM=J`>2-SZREAg|DmR4x+hjYvwv-e;$OFzfbyyA?)+l`J4R1 zj(SxR95aCUnUEuQD^6UUN@4y;15b;X!Q(cNK7{)p!6tuCi2Q-9B3|;3^th8OPpC>? z<o)8rP?ZG70~JT^2~{b~-*`h+5-NiVFR2oCk{<d}F9>*Yui8K3<HJ8_>NqC%F4ARy z6!oGO(PPBQI=LaklY5s~o_E)&)HoX{s2HnV<H3sBfPcE{MXi)06(n5|R;<%lv7%W1 z?XHsxR;&}{SK*KRWVq#W@6mRH?OLOFz^8qQ8bA|R_#wwJ;M1haAo1+S=;ImJevC(X zT98%l5mWj#Dep4kp6)$f>E?|{yryq|HQS)Y@7Rc>i;X?y|Mp5bZ$#3=Y<7>DlCF!5 zH|>{srJXyR(x=3ewdl1rpK3!%Xzc0!uUERct0{#z%{^L5bxCNvX};Vm%{-VlK9i1U z@jC|dhTPaw?nf<fE$cjjHv*mG9yul6kQ;CM_j;v&A~$a4x8P@JO3X6nvW~CD$BqFG z5_QL5BBjj{>>_ssTqMVf!xdgQXp=hov-h(#B^>2Ibq2QF*fFT~!a$qJ*@2$t9yuex zry>+0A7^`irC0iSk+<yI{?(cqjz!)w*|FnL=Y<0=@|Fd>&Rq$@qD*$Y<ss^Y2mcU2 zzj2y}8En;bar_WaE<AQb8odzV9|FolzSvz2f}~t{yqH|&g$WNaDSXEP4!bmM979YO z>9M2I;)M#2D_w%V%w3TJEDp83aXHBt{Aw?Nv_`}}z~X96BiBa6mMa6r7`J(hyIBvn zv82|X*x|?A^(eI`1wB05lapYw&kGaY<y$zpqgzwMvCFq0QV|jyyBiX`;kOWU(mi$t z!9H5VyG*nofi&}P@RfbDt$sVW#tQ*^Z#$i3gCC`^seeGz!!e|^rx`5fjYNaeg}>Nt zD1AGwJ_lkw+_11Wx|j6Gy6aIY6iQuhOiofJVqS>w<U%EtPi7g`6md*0G%0U9PB1rR z9o*31d4?AH5qBL*V6dhYW6?PVEch^*8{PFN%|ThJA}m;klVHK2-|VhK2@9n|rENvr z3k&XaRi1DR_W3)vYw9>U-E!fIkYSD7km3HfJd)SCYvcgPR=?sh)8&N|57v}c+Brmb zYf3o=Yh|((p~U*Qp~U01vN-N_*QW%Mrn@&r?(_GA7c|-r5p)LAeVPuAIjqu`gv!9c zxI-<P&~mGOpv<XWBNFY`2MY0>?$MT#&e3|-ANMqiF{GK!mwKOj<fRsbgvOrcxnoE( zh4=yYXiI74Xvdjm%PY-1)Uwy7)d5)#Yw<gVT87-%Q+~Qv%6WWc1p0`3<dk?rZoK8Z z`-JJY*OOKHKj*FjWxF9a_Vk}|!t@(~e$hR0O1~jD-t_l)rJw70=@hiDYVkYjc}ZyO zDPQE3a<1p45WnFbEhW1o<elb|jGubF63^o&?Wni!X=*sePnuL&@~JLvfXSX<{KPuA zwfQ`L(n5dAT?c|fld2et<O#9RPPhB1yB<^xG^vWPU>!WM&_aLOT?fKKld2etJ})fl zRU6O8|NU0eK|7sFlc)#-j@u0bJ{ikCOz=7PxGDX%M8#>}@0E6*Wo5N<3;(Ps;h1IV zVilplTDYOWb1XghzqxBcQ0QXbc({)RQeH5q>9&1H<BMA4&gr%-QkHzQeC}hP<z8)~ znq%AHz2qJxXZ^UNypB@o0<*jxjG9pK7b)3_=R_5(!XNp`Y=Fs5g*D<E#pL~%f7Fi? z?t>$tV?o?rh2zfJ1M#QvE3<tBzkN>t9kibjbX~w8&bOqq_xXhI2rdu6k>T_lkF4ZF z{8x4s*T-?}ele7lIP5RpACbk|0V4E=1O6j)LiZwRrEom%K`Ww8P^Iez==uTd;ic;a z$U9WHzJMd|O(uGaVIv@tU`_Cy!C*>6y6^&yFvr~m<vJwBrvs+^xPj|`uSP14<A}>u z5%F9+!lyw}LS55=^sY-6;l}Dk=fdJsDwFQPV@B%e6msTTVI`8diJ@boayyGRF;LFp zt_Ruzqq4W~N_Q#~&*>RSkr!jL?<6zBv4M}o>3WF{O}gtaj+X7z6)Wro@r=G0!65dH z@Rbz$>xCU73nN2RAR_j5Y`z<!vooGeB$J&0BRhcPb|;arp<atilO9MRrBrO`gj6jt zLr8+UvO;LZ$Uxv)<d>C8_FEkVkCquGv(qzI3PDgfTJ%PqAe%FhO@~m(BNS7Fz7B}z z^n@&kM>w}|TZZEnk5Z))@x$2&piEei1-ZimNo-6`9d6e{>Fiv(e8q}IOP4J=Yw4aP zOIGrKLUAyS1NXV2A#_wM1d;1CHCVc0$r9WHLU)R!toT6Wuoai>UdYjDidtfal4wWh zE1}5X02Q6i&ON&>+tf**!M}OkCA;Y%nbvh>ILz=nJGW=j79<mKv{Wbwg@!>@rmj>x zy?-SxBq0#vs3&RMN}`~o)9THzv`WS|jBHl76lhA@k;Kz>&e)%9sG6&5>e(3ax1nY$ zur9*Hg^_qFn~CgCXNGX$3NTGauGkz|U>ySXg04d00?@vY>Q3B%rBI^Py(qisAo-3f zHnR&jyZUexa-qf`8{-36T)lvrNm%JRmtcv+2XM5%Y8Z-u#?$Bza5GHdiWn2SG3Wrb zakWdAmCZ#k2BU0ut>i1a2h)kdB`+#5Ry=$7P<Lm9ICQ9bhq@`IKaSgC2s5;rc8Fbv z^%Q}~H`~N>IE=n)kkX9arJ(A%RI(@Ck7LTU8oV9{JL8&(&V_m~g%@@r980D(@y)m{ zrxUp$ih=G_M^8h^0`!ctkzWP1(8XpgJA)`s1?rK?1e6H;#@WNgprkw9$zudmQ0xR5 z-%(7+b~;)W9c;deyYlubbrP{(=KYC$757g0DlUnRBUj}rjC<;fV~ul$n~Obu2M1Nb zxI<gArvnC~{82fdR4vi4<6P`RIa@(_0gbxo%)e+;krzuPfzVpw3}R@b9S_^g8ew;c z9m@Km-u_{2V(5@c@UB9HM1Qp9j-EHbWNFE1Bs3D-ACeJm1QitiDMYWLQ50>0<Y!#E zImLH0u+g_~>Ji+bgYj0F$6FX}DaixNXtU&K2%9E(4Avbw=Eo=uXgq-LZIB!vQcZy5 z8<d9Jer5Aje7%aKVO&0p%83@Cy;|j8KrYP(<g$Dq0XMNsfx?(Mkg4LcfHx5yG@|x3 z)cB|RC*z-<pDdFg1PXsO@D$Pb6drH`QZVET`u6+f?2%BxCj$HZ*|}*PfqRkf<v?S^ zZ*$g<1PVS8gv{20j6xG5(JG*X%@H=rhd{^MzzqnA5ThJsl!HK-o&#jtDB~_7NLMRq zgus#qZUf4p(W-Kk+pD&#%XnzrK~5x#K@W2oXXNDuDpecFBDf`;?$}<VPSD;=)GcJu zX^ZkgveuBrCD9G^dX<d~>t2GMenj*6=i*<BznB5q>{OF4T^p33Oep+O=8@szxfY(B z4cL(y_~`m7TuU`lLq%>R3}|YO;7X<tirq-<Xy_O?WKZ#(hmCQuU}>*zl?)=RX!C{r z8_`p)?;lXt)#e*^boCXV_*m$C5ObOKI#v8?==gd%bzT+QAV$x5|DnA9aK4&0XjrjB z1<&Dp5QjqNgXoZ)m9`P%Lb@HMi?79v6HZ(qvCVgCU|9Q@%#iy(M5Xn@{$24Q0~mP; zWjr5~y$!ws9w8cMsDebtV9K3jT}l?6Mk#lbwTG<B$Z}NCRwI2e3aZBehIYJ0xp3JW z8ZWDIG9Mn^sqH)Hmorrr8}S{(HD-OFui_>C5&tx;S)PJ7_%bQ`$ogm~nHGl}8cRxJ zzpi4df4H&KJ)ksr>(r&|bOmeYKj@S5b7)5SDcBvDjNh7HjveOQWa|3TuJbB1%3Db? zMpfO{7vAra98L_4FQtppgAW;pm#;&|-9M0{ahkL+PP8YQ!a5Y0Cus7?mnTH#$p~)A zz^IpRU_b|qMs_mcAYtWeY4|xn;}1T{UtPD=PwX^Vbj+s{9%{-Z!9&w?+O=NiZ^gel z+$TUrhYCG&8C1pP$q3%&B>Di*)I#D${l}0%n!{5yMnaEIfLwckb&m4q5cY-->}?oG z9-`|a)cpghfI3QF9eqKoUO1v>X;WZWJEy-S^#84{Y*gw}QHwUKZAN99axHRbT8na} zwj~{PD5`yUR+LE~6N-Q2r-qJOPs$Z)J#*Mc&=)`_5+p#?IrJg=yc1)N^~vt;<?DCY zw|7Tt_wLwr@$RjgHtmsA?cLU9qO~z>RqUn<eQqc9C>)=ESW~UYH&i1z$g_G`IvVH} z6k@p~=fBz4jUH75Zmc27MpTKTxKMS9?+8A^81aJ^I2yPGHy3g#2yHJ5l+t?^Om;e~ z5#PSnBlHs2k3Rq9xIE$jziOYq&!q<gLb_fAL&iYI5;X)XtwJ3{F!|b5Jyx17f?9ni zl$Nw1V|aO~=Eq`#18Bc+tq}^9d1Xn*s#FGDLw0pXF$mQQ2UXO;{`eu8MR;~$&4;M+ zs;b3GQ2fyuCy3#-rBV_0&-4$^8<SpTxKSTO*k$R%hg&e5I|i(TpI~RuktzFw^4wgI zGbn&NZYhH>{NoJ5AO(^bhJY4^sfMY+z$P?WOQozZ6e){!oVmsA^ff4oKr2Qil7`A! zu_Ul$CusK+v*~ml4qSJJ6Thv5XRg%c=f#RIoB;HE5CZ|p>cmL}u0C~J5WE^m$YrDn zl|#;4u0D1SM_Z|~nx({<-*qv%evMVVi?WwSa6Sy7LiiGBCfP6PtyD?az?ZPa%c}Nw z!9%&J8decli>KAT9mqANpu&L?&?o_>1T{*KDIuGJLFbV`u4*)l_XEcQqt$voa}hsI zpzwF~7*ehw|5~!vk+q(z)nqYplQ*i1(spHrw(^y8Hi|sh2&%zXb%wj6Y_tk1VZAv= z?-QLID;!vWN@({4$mzF+itvTFJEATLosqV&#QjABN>Y;G+yIk(4Ax1Z)PYk_7J#G9 zTR~Z+fg56Q!y2WEDb*S!%#<39Qq7cFje^@+k&$&8rIso6iju1vt>^hh17}x*Jwred z_8kwAMXTt?sVz88^CH<yJ{o`@uP2Mj;c=q1H%e-><vC=XOV)X0EhmdlJ6cITK0t=g zO1Xd@IjbA??p}|@F+GFrZkkT3cIIkBEH;#-#Wbq5_d`Qo-S?piP(C{T1%|a>J@gli zyri^}CY+aQYaNX(MgyhpFsM|m-BjoaPuTrgs{4DGw4;EX-Ji`jlrZWhjaRuqafi2W z@)7l&Atry*&|e?J?B$66K-~z2J~VMAmC=-_aRmL)(uGc&;K6E6yIohp*r0)X2YJNm zgtRe6`x2As?hL*32x-kTE?`>cZ}aOj5JS??pFpHi!XVCshv#MJ52$d%;CkP_DzyIL z5~$S*)Rv7{2To_{wm`cK@PLwsmIDY#X2|j!ZnU7b76YIJd{$3Djlbr<!`iQZPGKVr zc+po2!)aKL6rHqEkEea4YOX4^Ud>gd)~mUy)Os~nm0GXns#5FKTvckln%9W{D!yIK zxlPA4&7d^cO@XhkK|Twe8jl3hXTz<RFT!p*5)h3?f@xe&+}8|$(~%&}srTci@9cCe zKKRKeas4vd`j*iN94;;g?!%?PeQoqUy@&R{dUO&uD9u`fA}y-W)NaTg^7WFHB&&}s zYDOfD;pKjMOu;(NZ3V3wv`<!d5zQbzl`lDqtl4CpLRN&VQ^`7wtT|-OC2Jm83&}c@ ztah@_Ad6d{#=V%>Wrmj7MUQHpJe{lsWN~*BQp+FI8sUyI`()r~1Kk>1>|x9ydK6V^ zNQFx!@=+A5EVYTgfNL|w%~rU&rg%?W$lnG@s(vv4F)N@Ju2FcY0ikNkg_jm^kP(;} z4XPQFU-@XZRHUnWu<2QG)xFhJOat;^*jOIH+dD}flT5C}OzEOGK%pv)Ap)kMBScX? zfe)e6%Zr@5Y)(MlC4%70wmAX$Q1P9IL<pRasDdp(Rq;N?H%XUb{XRc)GlHT>MNP%{ zPr8G;&)?<~)mR;)d(3UEP)ytCzU)39c(u%<w1zP(t?sMI)#j*(bL!zzxL9H9t~wgH z-8Wi)48?mSfMIH#YF<l=QKd3`ybWke>HvGbeDQg=t~+$~+T=~Zy}dylzT81<E~Jae zw}h;vu=4eBEaT(KQ@Zd+6~g+Rbd|Fzf_Y2;2GovnZLlMA2w4u0<tS?PG5h8q6tw54 zplwAQKD67RfIZPhv8uyR5hmD%k&bO}Wnf_<UISl?zro)U3<qdPss082-B2^Et<7oi zlodQ0Byho_UcQG${tWC<|3=@w2=!jHGDq`ETJW`>Lh9*2lkn4G<uMdZnIZhblFBj9 zhIsuk%G>Q|CdC~atqVo@t1((J&z6Y!P+umQwtGB<6|X=z07HktVPP7!p9j=4p}krZ zM#`J;uf=b68K$_Ac<5Ui2U0s#==NNv(-XGwb>SOGZ@a<aRaVc=4Wk`iJjHF8R$>n6 zX^$t2aZO32OgGYxGlu$RD)06LU*3pkZJxv!@yrrUms*HqZ02imYAcb3j1ej6S1b*$ z$Ajua3$Td5T9g4Q2{eYmISRD4g^_^>meHwNbI<3Hrz*|8or6x*x!yr*0Iks(C#rSG zuGRz(FNC)@shZfd0q16I!+p4R_G}~p%ARO5aW>E;$Tn*eSkcB^R<|a;nP|-yViAq6 zKei>e5Vd|I-F$3QsE$TkcH`n>5D>0Sz7XUG?7^>FU7v{Y7oF>6|AfXl)J{1>C6C6y zQ|*6kaEG-|H(lN{a*a9<YRR~$`~b-{bIpM&=a_@Wx#gg7PC00vx#W=JafE}`g4~?o zpz+wlL8H!Ll7luGv?&f6EhSEK(58bnL!<fnW>WbPn7&b$0PZY)RwF-9L=bak#hJ|< zHLX*WQ<$R0dx{caiW=`}Z;R?;D5tW78ttifP7@($phkOWOHpU$K##dF^I+z~oDQ>q zwQGm(445-v7Q!q#R$r=ZT8G9Zp9Z^*@!0X}iB=qiwRfm`(YlkqI^C*{JF-jbMI^~} zqm8*+aH7s7$zn$)F9tu_NYvd{q0(GJwCP(jGC7PNkK?JGxJh?CUl?x(x}5~3UXI^G zXtzcgdmFJElpgjQJD9YYuj95!JBie|bpv0*ZPRu!?Q(;5DbZRk!F}7?__}RdbT^SF z?4q0Q4f-CUH}A;|7}U#%+Pa=^+BO)MgCVabYadzsT^4_i)JczVvbtbJn*q<@043Fq ze=o7FAd5fO<!@B^qs#=+1X&hY-DLH^()&FAF?n!6U0-CckT}N-ZRpF@SHx<I05>FO zQA0v`N)1Vy*S|m$MyoaUL>sd!@?rQ;95fVtp}*JF4{OIBxkYKTLDX}EV7LuR!SjFD z1{Fpg4fdSC-o{{OM;~-i<$S_!>@PK$`@xJ(RFepk)5{$EeuxbyCMQXprk(b7NIMov zv80QjoSB93%0O{j1&sr)qKu-}+RP|(%woP$26P?XW}x2(4}M_6;x7Fx#x~YH;71n? z8-q01RcwdZ#<CtLHOG8Cwdlan70Vpiqjj`A%)WYo!5sYlQR;qcM;q;7ANj!(SR7<7 zn)uOX4y}dl32e8rJ(2A;#DYBpSauGMPEtce>bkkv;<;o4v~QoBH!d`{e|#`6Z7=IK z^Tw^_g|*duL|^Q+H`p1=hvud3rKKqI8g^|xB|qH=(paj<&(?Y6RnmOd%u;y(Y0p>j zZ*_DDy0xDN%lQymQ;`p=jZ<a-<~Y_&NofperbHVHnhb3onxAAOgXS_+PR(qw7ro>` zD&j8XJkLj)7DxDk3OUW0e8xTGd}I1QLiIGI!Gy)%R29OMrugrLaE(4&E-r@AoT01^ z6dRjM5DQAGnk~2GFj=9}7h~EeW}s9HxI|;1iV02A5&ZN4c`S6V@*O~DOH18;Uy|NY z$A8h<5tg_)hiZzq+&AvQ4>=hbmxniF*MQn{K@&zb(~(d@l8%L61wzO!=P%CrCeCkl z+Aq>{wA2*Pud6MLN{w+gnEAr?cK_z~kg7pkjS!)|3&fSWpb-LeuRU#Yh{f{w9il%Y z2C=gtmdE7ez+gOObE>hlmPuf4#WU3*3U-97YhmSUP<98<rytITc(=s$L=3AN(d7;F zK&j2jm$MhOCo7M_8*MBU%XqrS%F0&|t=hg^Ti%Q(c`J+YdF%Fe#H_rH7_THoEi>q- zKlv&mzM6;)OjMs`W##Qee+|?18+hd%M1L*OTliiGD1}eA`HD38I^x|)Jo@Pdg`I{9 zIGu)1B$71ik#8Wbipf5_UfxCYHxj+kxPVjMO^i3e!t#=RZ>7A4X!o)(|2kjzeMr8U z7;hm)1D~&FUr8q4M)XmlPcW{Hp|T?HBj(#(m{4E7lbG)!W<4Qgf21zoP2>lN+@N23 zCf`eR>NcYF+O1&nK_Y*EEc&fSevo`0BB>@mX)uc|mH7H2la(JP&PSL-CsxHuoR1Rc zVd6~H-Z4@mKSnYiXBq8nQ|1wp`2@*K;;iQUW3bfiHu94s@F@~#GVeu^pC-;{h|^kr z8;JZYaX&}g#=<oe*wqevKTnJ=7z{2G@=L^clo@nh1-2&hz$_~tBgU7B(ZV`X+gvCp zj}z}JB_1-V$oneszDB%8<Ft7Bbz*#j7!zF%ZI|C7_P2@MN>SNeo4w&dK0(~?5_bYc zR^alc1o<Q}zfa6s?nco7Sn>x%{2>wRn5fQ#&J*=VM6K73AeKKS@=u7|Xq>|<e@2X- z!^-0~JWP+}(?s|MtZ2P<hO2yr$iF0Vqv|CKwf9%V_%$)=6oc#fZ;5)Gs4ZNO^t^wL zc)w#F7a*PYC*nO%ygC&DooXomOw_+H)&7=|sDCBu--tR<#f+gL=KYr7lQreviTw{Z zHpMFcMeG-dU9X*DCSN4-e~8?mpYSDLB0BmQC{{WHOJW%1lR=_38izy4Dq@6*(d;-` zN!Adnmfx|*8^}KF^^x_&XduRfvQu?r6ET~K*-FLOJQGJwAZ{yhC%PQUBIzc2*+y(T z^X#)m<RoIy4e`;M!Vw*E3elz#4XsY;s0ula=+lW_ujqUngPcL+nHrhT4Un^lJe$a^ zN?JcoK%PR}2yrJUuJ$3iJdK!hh-sH7`czxaBkFvON*^=J1w?HpYNK6V`gtBH&m_h| zW*DEp$_`>ICPrQ915vqzs7r}juYD^iml63aCS%)RVHP0ICh|E%uC|AS@?0XFN2CV* zt1`KQ=qq8N2ezjhv>^-n7OJ?~@=SZ2<vOx1BI{zZE+^{>vUZTgOR037vfM}3VX|nr zB40+<2w6wSdL>!cl64(fw~}=mSv=IanS8g9Mc2*B>&d!-tXGls8nOndte32V^hnc{ zGhwo=0sPA057mXL8m{wAtqIly>q4;IjN+JfQ}GRxPVbMOQswbhNw4vTS(zs4Ei-k{ z4%jHWlGL3<lZIde!vCMz;kw}D^IC(gt&{6o!Y$#3s)qW8Fia4>nugkj=B8j%82{!s zEp4iXKiE{)w4kZB=@c#>_}!(I$_h#dT1B#`q^o_3VfgalM5ccro>Pa)))4R2C~x^N l>K3Nx9EMizxCBz+%h#+@L*CVM@Wd1E2FyLwKWt+T{6B{iCx`$5 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/__init__.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b99e999347a2f56339041204c77e0b3508335a1f GIT binary patch literal 563 zcmYk3&2H2%5Xa+uC!1_3Cl0(oDv^MCLP8bLb_JKRbP*t5EH`m?qxHwi@oqP~3Qxe9 zxABz|ufPdUxM0h_`Ol0zV~uH9=AK*l=XZLBJnt7c|1aM;uiY7pL+4?yL!Hl&Ko}v= z0r!QE{Yi&B5CIM*9q~|vIGl9MBN5|RBsdW%PDO?@k>h;IC443dT!<2vVvgtH1fNXt zlt239b=k_R@^8_L`n-Pr+da>#kWIfRy}eUv>vRTs*f3_C6Wpo?XW~o6_d-^Y4Hrrp zo4w)vS_<Z5@>c1N>A@yTY7EoT=1Z*%YfZ0Y724$MVr53oY|Q8|9`9`m_8zJ{rd>yN zlrwjaw2G^}cD>NqO?8TAgnYifUA_NsLx_ff=>QM{A_tr=loqaIFV{MTXaYz9834Y( zMpJ7&gFpc&0dogjTpwG;p~#-oAD%vi4B*elMzF4@%{!{QE8QPhvm7@@$!67Vl;TZ& zVDixL{;t`LW~1bJ{jwQ)!=CMEyQOP3G)Mkm8bW&68$#;cXwz3hb$iY(VBQ1t5B&oD J+fUIG^cT-Jkw^dl literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/antlr.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/antlr.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d6ba3a92772bb3aaa71165b93b98fb72e3a0169 GIT binary patch literal 75711 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 Dy0j8uo9L0fs; z2MhgJ1H1nup}?Kc@P2gkoP~ky{!dPp1S9A;VwLa>prHPlK}dSJ6Z-!nUYupFqy~EV zjN+4VT#?^3P+$EMzFb^a^<@iJQb~J)%<%n4SR?RQYTlGUoY0%ZdRd-5K7}oYAlugg zu%Pc{x%Xbgqv&7wE+l}IoqhZFu<Tlg<nCFRzglDE?bk}Oq##j(3MSRry6Pp<<@x(5 zrBxYAcF>KgO*HqQJLGVMZSPW630hJ^bgJ;Wk2YrKG*D~^r#^ybF{I%x26lW4u`c1Q zD2;Ftgishv!kdXlpj*dyf+bh~Cz^Z66Rf~DT0D{0Rf?yPU%(T`@VI@8yf(*)&<F=M z=#Mm!qc9tfqlvU{WUN(@$0@i^U=$<<WIg&3S(owh*isP6AO6-GGh=h|7yDu+wK|br z?;;r}nzRItFo%y%$Q(!<{CLl6KQ58sqD8@JXm==dhSKU=c<uZ8<iWF8%j0EERnNw& zy=TnzyKbiWVPx$~WZRKFz$u!%g=B#cWZB5Jes_>o|Bfd~^zeFNYS}-p6o&g+?p?5u z*kEyAfJFR*h*ThP2wez7tpLs~K!no_=jdv?CJl9-&ba7vInc+R6I2qlv&#bac+HG7 zyDYHfW~p4ml7HD70R0+C>y;iEo1WJjs~Iv^a&_^zd>*gkROSX^x@<qjvgsD4-CPEu zE1v8!>MvSoOsOJ;V{2=2m9mJ0M?q`ozs}jUx`3}qoradHN0Yis&geQ2vGBdbGNDgU zeEWVPDZ&f;wsy%*a27oR6qCCrHeY<NQKY5UN08P&7+AEi#g|ne-nWrP{R#pvUzetM zNzY=c1WHP}4-q}xc(_QfxgrTU%&<h*B7zo&o*FHL{#sEnHyYD{H0!KJO>}+XUaTqi zEgN12ex~ti&d$x(Pkw~hvDc%v7?GX<;h3zYT7#xT?uePi(4gB@7$>Q)(B+p2m1Tqh z+Fk_{z?4+&DRPGZHBOk3?o-@wx1s9bq<AsxR-vK?dl{lz-Ir7cx;oqRuBr^N6mi%! zYu%_QXii<vyDSJJU9KwdF4otwN`8%H*Xpe9Yq{tcJ?^UZIE@T-CH7qyNv0I`5;0(< zsgN~Q2cMM~5Tb=e*u8xd>3)!?^x+1xO?p<j0Ha0@MvaW(`*B>6AFFH_*R>40?DdKJ z#_E&f@aTq?)>665dq49gLZT5t>dC~A-FIm{{dGv<b-I(-r%t&Kj@3{3jqodYVHGz} za6ZeD3Y)_>G_sn|LMiORh>QQ@J-*hs?ZQo<x2<Ax)OLT}5amr4e!=ji<#>o%99Hf{ zSZ}XY%{FXPn1KC!Pho7PsrO_GeTL0?J@FZagCizr(a*`w1lW<xr-?e%mJPjvN%i}r ziqDE<LZkexT^z@VFd%tISgE@0xUmFiT5a`7L__CA9XBfBY3$tL_qRD+t-`=kqWnyC z<~ZzCKZFd?iEY6jFVmQf8`MSq{|QSW@*ntfU361#Frhmno5n#G&~B8*-IXLyi-*D{ z(}}mAAuvjFX)-0nXXA)Qex%7TuDXlhrPm30K!ZIo3ngICvlVvjIwO_Xrm+PzMwPh1 z!7!TKMTnAyBTWvrLol!z*2_IMOZ022Af*<~P`J)3!ojM_X6W^i#1GD5D`z-fxq^YW z1m;BJ&7&W5PS9aMBdNMc@|c*Us=y8Bjq1TYENI4PZj|qJy|$DwY`w@a`0<|Au^*S^ zJpywcEf_(OG8O~&6Qd+$gBInl6D?+lp3BzuTG2_GSLBB|T!PI{;>I^J!qA5*&{H}W zu$JIrS~r;b9eA4KM34Pcy)P-PdiO%Q{+OQ<dtYwFh%4{M%~laQZ)^r=$GH*!^pNfN z>c|E6^zN6*ozq=HrB4Idp^`SLFC&3^8GHdxwQoCHt>wqGoY4UUe&bEg#VXnBG6WLu zk$)mtX(h&T7AK26IQpM=oB>FrE~i)aH8E57LyK~Nonu5u0TC$ChVE>=pkGk;X(h-f z)VNGv9`Ge<{ZtOk#;^Pv7sUh`wDn#4zK&vxTUlJRuo1to58_3PVu~+{*Mfp(O7Seb zHWb7wEeQp_=(JGSb|ork6pV);tHA$Vp-P0ulB$H;*CJC9H$4_6_A2w%#%O(XEsQ}9 zP7(#)a%6ZPH>@9<wty|Q@Y#7K%CvsoQb%YMT2$=B&EjeYcXBdnhlsUMwB@W7Mncsd z6sl5yQbCCww9mMsoR>WbD)+NQG?~)G;%f-a>lZB>ziTN9t6QXAYwM%D_4POUL&Vvh zLS~IcS$ppVi}<>y+a8;=CJ6(H`$<G7*C=1>*shD~mpFZ`pHK?wDRs(5>Ga^)p=W5h z;z=Yi<*9s6J2!Hfz|*2gS?j6#wWPG_!`L)h<SOBd{KfbZoA5)Ln>@t>Bg7~x`;U(X zSe(XggXn}mjbcC+i|`#JhyFYsC=t0b=^4srs!v+FeTd)a9rY?5ScIcHXtjc$$9i9C zVe3fm^|)cZ7o8IJ*2`m!Sr#%`OJ?lL0lCZ_rpyY*cD=HtbG2-u$9`USDN>A;L`I~a z$e&e=u4Zr@11YX(h#|hAZq|n+f5lE@0C_?;<1B5T<wD1e8klCBWwL){kYQZWP4s$P zY!CXzG*)<HRu<Y;)~xEznH_-hSmMMzfYX&iwSYC>dLQ5pF8_tYY!`@uv7(;09cgI` z)B^Ss<SGQ%3#6A#TQ*`#*Yf@bdA_oRQ$MCB>kU~4;Wt*A$dG@$;z-V2tT?tYp4x&< z@t51EgenqI@PE7~6$GU{xPcnc6MCJ>BEf8Zl|KQ-MuW}0wo<~d8>L~2+h{{+&^EoN zkOeTk2Y+N@(#P7VH)9yf>e9pF%Q;NE^-zR<LBO5j#%T^j@P`MWPTnHK1Da}k1P}v7 z4Gke&C_vjUc;fWw{g#AT76EE}O52jG7hjVQNj}L!1e#=k0|zlO*&3sQOwT^p7QFFH zW)1nj0&apD`z}OKV@25%9cIE)xq82@E3lQ20kySB4k>WzyZ8h(DjT2UoFHs}%WZ6f zl<E4zrk?Q>OLipnLYDQ5MLf>A+(HH$ns%MbFXR^Ta23m)t}XBk7vKLw{KYn)onbbZ z<@M5I!UJCVu$MlrK7&W<dIn(|W`mV83pCM>kq~ScWMHyE57;2<^g{B`At)=ZZ;|>z zSFGdL$`rQ|5vif4c^5@VyfaT)wbCB}YMcm$(j%Cr8G$tBKq@}zcLNBa=jO#KH^k_V zFdZEfEO?<9<Bq0x5-6JjHc+OUNv2pR^<^E!cjI^*`H?G!ab3-XVE%|Wd9;^D!pYSB zwbMD+B$Ps#@yVK^-A(0Lj2N*fdY&NOxKMK#t1Z#Ml0{AqiI`GE&Kzz;$T<2KC!ZNH zV+8i&oRJ6`_)2Mq3+(8;8)@it!Cjq)X$8mTnTYeqs|$JQ)5)g2nt?AA7P9PZPak7% z&OH1+w5a8RluGPo+gP-}20p5cJFlRsJb|n#EfjN#!qFLcAnRtlnW?X5>KHYc!vd*6 z-cCUb!|<EsNG6Lul>gvB(`o2~c?>sUzEZkI!6eK(WRn{K`Ej4z$S|w;q=h*sBlxR- z4sSy{gx<Hr@;t&hOfo-(ol9h4Z^10=Et-YB9cE!~$t>*cH1ktk#!0-}IEnWdC-Ei5 zNxauMi7(ZMKw>YGbv*nLdzr|gkJ!sZ4tuOD*M~e-2K3>M*waJ~b*v2P!yGFs^&yUx zRr>J8%4#0jh<w(#FL!Uj-|O_TjFq)GcJU6x*YV&*{4MK45i8g0!w@U$^&yCr8(ca3 zu(H9Ob9wAxy0TFpcUak^k2$Pt*2f!Gw&-IGD?|D?!^&2DjA7+QeSl$Qn?AU(@-ls3 zVP(5Ms<3jCs}CvMVGb$0SszhY*{P2vth`(wNm#i>A4OQXRUbiExlJEESh?LCI(P?; z5}bBtfZ?5P&7DP=c8Pb`U68p73##GFa@3sFfhtakzivbfk_C7zAsf^X*lt+Ra@pE_ zV-N3rUlw-fH~<JCd|43UR8fQ_p06?5+2|4@QFjA#Rid|GBi#$VjisW~rmzui+CR{2 zC^vHKxgx+G(oRduv_scTKvwKW-qQNmnd*M+qOGU)JiU+WembFf-Q#fYI$Eva>^>aw z=Pi#!4|BGEY`X3hr~SJ^W9)c+vRTD(W|%^lS9-BIZ4LB2KQ{aDSmPuBsIl?t0UXnI zQiQ7X0T}O$)lXJR47H^9mO<T+k<LRhw=NjTB15l;H{wEMRBH8oD594?^*E+k>Tz#0 zHsyD-v97^Q+$l%fHS|{bSTq{eXDB#XvHp+ufvQ8gQdkCx4!tR?Ec|)<q!Fz6LcE_? ziC)76t5MkEND*Nrf}>{19<%->1kgIxz~>4F&&dYo&?NNilxT(!&eR!jUKqUE)Ce{d zg#K{ep-6_JBmx<C&?m?=`~tpnnMMJ|^C_li6!~4`T?gJhNoRYop`X4J&)zNRJM8q$ ztqYl{F7+&SKSnM%ZKt@9!{2<PV<C^fg$3>_4Iez_<Ig{GpCd4IXskCnjpwN2IM*Xn zKU-J3L4UYXHeB?D>p=l8JBzc+^tKQ5yYT4XT~&XETk+q)0f?Mv7e_O35Y?Rvg|%4c zu`+NA(~^DEFf-?2kT-kj2|#r{XZ*P5)Z<G71dE?$)p0f=mF0Q>=*mzN{1X?ld2V=~ z&51eR+&k!$b?0L|!m`mS_c^+~c0kQqY=c_O=cFSSngDBG!_Ttm#X3W;#8xzR{3u?1 z&EmHKo8Q7D5spLFsGelQfw~`Wk~rJr$_WGTAKC#7lU!?gUrw+IrZKM<h1hRPt8ZAq z^+t1AQgc_BNWyDH;SQi+NUnYp<cU+9i&nX4*B5yIVj;3RMs_=0dpu|XV%1N{-tFPA z>HvxHb-eV4H;4Ej)L5whNbC4F3xyRi6kxcK!>C>$_#K4NZo`LfT5xUA$U2byGDP|z z2mW}^zH&b<ajJ%35gWMHL$%GEBEvW+cpc(v$ekSYFv1?_$xtUoiFYF7ZxbV2;B0K# z6vyeBw$@N1q;|sCMLVh3yy!sV_9cCU7ts)+yofA=AMbeyK`8pk0e<n*>Xvrk2jjW= zJ$24PV}s2G?LKHP4%izJohfUSFYI_TUL+Ak_Ub8G2<W{RO`9YFa%SE&a-n(+iLjAD ztg?R)cyzV{CoPnxgSIbVjNa<ucGTwLeY+X=SU@AqJ(jLL=oZ?lBW-}o$wEslfRV-x zfI;I%2EmW_yo9e>fI*f>MScY-k}6VWWkmbZ2DW|+{HhpJS;^WLH%4i6Y63s0VRfTO zlBaa(D=f$>lOSKg!<_y`P~c=Qv51Fx(8w~{c>gt$f=C%TOC1Eh>oJz|y1`Z<cB_b; zmNanfiQpGW*PbvfRilmphom}@Elk#0<cJ715KV+F>)ikGo&r~RAi-#-wzdOx0AF3d z2&ik4q3~6@y$vAYGfV&9V8M*@nUyReKr+CUmy~h?ZnVhKj%6~Xkg1oUH)AykSBBt8 zuvcZ?Y6V}wmr68`zrdZBo1T3fCkvVba%9e=$`mmP#IH6JY1X3*JYdFe_SlSy*%G@4 zB&uu;f-aqGnp_fw86fS=%ifSE`j$@IaaJTVrPX;Hi77FCzyQ1#edQYcXn}T@G?uoA znq8dgzyKDGSgYo1Jm(e%K9t8{329Wzwd&)DVO<1lYCBmo?*<xrCpBr6vWDPYeD?PU zcv5_Aa$F7bzIc30yBzqGc`K7GWhPO|cpWoir>atC+esh3#R?agX{bT>;2(A5-gGn4 zMFiipG?;*oMNg7WNt>;-gLB5J$v-4Y36ez?q}@eiq&~3l(XOV%g^x(AD~iT#OkIJu z^no1kvw>?8F~HFr{XDT*KZlVG)pb6r&-A^7@eS+y5PZhN$$XhWI*P}>Mz<w_eBIbl z4ksLr9-nMLlI$VY<eC)cgPf?OiKJ}LYaGceq+zq$Awx--OT+4Fhc-YxOLscUlSTAA z8BZJ01v#xZ4LAuTFJ<%;QBVS(3q%vE0zjM#{9@E)Hst_|p<4<K&20*z(?$xy3L_z< z(UfsRk&ZF1OfXVur1?^}_@m*_3D~ViS~X4jPBBRGbI%E(9|$Q726dWCqr8EtXh8^H zXoRR92Xy0X<DgGbutrGHMAkT_P7$#L?Zzt#Vp#nA$`CgpTEvdvCmJVx{2%WFAv27K zg&vF~LCnEA)LXc!(NsF@3sUX5r*I-Ao(g}9I?n0`Jw-~AcV$k|R6hpMH{;qFul$nW zc4Vw}qWWl9gf%?;B{DP(leIMapwemBp`65OW0*bkGH6=XFkF(!cs*ll#DI)f+9|1C z0rHRAm%W}i>CPK+DAJpn6cKcYN`Q{Y15gdZnsYr^8xB;`tI?=d$X&nONkcOe&O*gc zBM%U5KMBZDy$D@HMKO4LPOas5!VC;<V<G*D={06$pfO8s$NLuM3^3(9PXpM{wf#K0 z2Oc@74`?JtbZxiUX;)c<J<j1}2EdH^lQElApO|c@M~HP))GHUyCPHGO1B|pD$*~1N z5}tIZ--dt;coB0_^ub4qLDSaA;!F%-&WqxeZ@)3fs}=aV*@1(uc%u2P@}-PEh*QvJ z&PfP+*i>h3RyAZNfhn7^eNHo?U0dn?g9^~>fI+i{ndV*PX6LSQ5K)hl&<WJ`x;0Xd z>d)q4=tp$WNXiH^BlD1%w@3|ec2#Q-U}ZUpWXi!Yx4fs$;b8S8b>^p#7&DLekhV7v zM^PE1v5DRwZI60GVi)`>d~qg3nKRH)4Imiqx6nJ_=stt4xbg+|%T_($kCDz-=CQw0 zi;a)(zk|3KbwCy{`)$hXHz2Q&jz8Y>60S|K)IrasHf2CoobmWRy6P^xe$s-t&hBK; z&08w2Si&PTV1vQ88vIGz$c!|qBpBWd=y_#IZK{x5``ySj@)OxJI1-7K2Mq=;HJrk$ z?_*qBfa@bh^@M#F8Zx&oNna@GRn(!b+^L62!vL(^5#P(Z1qRPBcn^aE4Avv?mU1Ip z-zEW5AxyT^GR?wheRy1Ml$%Oc@Q3D|s8I2Gm;nwiH+g(F$wpbG=d63Mcaxs$!wfc5 zZk|BdjEoqfpGTOVlw|Kn9<IL`0VzmZIy8#4`i?Z<S;m;ZhmN7ANaa~zamTuPr+zPO z@A{53q#ig0isQ|z8E=*mFVv?o`Y6IJNsm5|f`_u5%f$E2<(4*zMt`W~q@iwOGNVny zw-RUcTfstJ)}0)0Xxq?V@C{4nQ+72Uh(0ERmMB%#lSuhal5#DQVcL}1ICb(2baD{X zf$Kiq0jQfdud}(cr8*<Lnb0)!8YJJ6<){GBL|v!Qm_5t^*9V9jbA#`1q(?UIBlU=~ zghGr`LHaq=R=@Og<Z$kqbPL{~QA(F^C%#D%OkSr<Slx#m=4y?fwUCH(2UePv;s@%M z)&}+VO?ne0^loVm6dF{n*q>!6XSpc@J7l6s=MKuZ=F&aoX)d8JZ>*Qc@4g#<P_KgY z_YiX_19dQ8uH{tHrv3$k=!aMkJ#6S{F@>5mS1O1$&+5f-)Cj3N_^8W1C75%PeM&IJ zsr8AayW?fh%0yO0%b?5kCYCG8rCw2RduYD#k+1+ocq~U$mF<3_7M>tqh>&JOCsxuo zXdnsWLa#7CH(SF-<iBFodxZJS`xa6KUD5ymp{b`hAW7(!phw-zVoIkeekvPVOD!G{ zA14Y#TO2$hJ(6)!V&VKB@7Y+Su)Vk;4q8}gzA{#HYjFIVNR`w8WR~Mo76Br#_1tcR z(1aV5Uj?x1$AwA2-{Nhb;WjsVp%@0r7#9J*9GJ+w_us%n?+67hI;tZ`pO;~EFH@r( z6^y=*2}Sw3$hUT@QR4uQT_8jg`3Dw|O+*`WgxX(Wbe<#^Jw=AENj1P1zd;(1A31rz z*}bkT2VLl3;9L@wNyZnxJzH!=E+~!+wQ)vDMs4zA;*`pmjIhj_%ugdZScS;z^HcWo z(D-)PdP@!bSJfJm)1%{<TmB+(*W80KLPUPIq;0_(-)ctM9>>~WVPr}Ims~v2PP;bd zIZgJrkj|sXLz_H^{*d;}tK_vcBi`JXMUc~J{v<3)8LP?ct|%dOOJM@e6x}vmr?DnQ zzQJFNFl5;xHh*C;;&V<p^=6h}f`Mo@G+ug3)!1acIyy4@$Rw4Sz0<+IKAGvUo8u)a zxYdcV`RT@=vlQCk!6{^%gB_78CRGG`d?DN9h;s6StO~dQBw^1Mu;A2punf|><hbU) z#`Y)er@01$c~MuW*1_3U91`g@0>$GtEDSE3MM2MHu`$kCd{Yb@1|&DLMNam~tZb2k zzuY3HAuvRwilWkyL@Uj)Zy^VWw0Rlto&XVzdzbn?l9qZ3=JMw;-u)XUh;;$onB|A{ z-B9RnNJp)*n5lS&f8n>MH_ZWMOX;;S3w$JpsEaU(%u5949k>CJ;b@~<Dg_riZ0LK^ zKA|t8%SJp44+~gGK+ddh;nQXZ!}<5K9QznBUv)16yU1Q{LON?I2zo>CXqgrEDM~%h zl9rHy#$@eG5N*gUd^pa6_&;{`0q<*zpV}c@lHrNM<QD0lU`>XjDT+ns5ERrwJIK*` z%w6TN%Zz0r7d>BkE>T6LU2v9FjQZG$g)Pi0jB495_BjyCas_{>+~dWp>?*3RXQf9A zEc5xsiAaBz2mWR)M6c-d6u{;Pxo~Db@(D&=^oXUG=<b+@Xr(an(5T2zkx83~^glD8 zkBK_W;6VmF7h1iN!GGr4cQKF>KFVjWW*`;H)~G6sN=?6xcaJd`V;~iOfJu%sDqWBe zOYG5NeiTFG4ByT&pud)?GmyT?<sI9JI?I4VWyLY73>(#VF-rAaJ;gv&sqf|8e_|ja zOZqx#a?)qKl`r1L;28!#z<?~K{tJV@WzJ!Je3j7w26P;ICpwY(9Aa{mtkcztmi7wE z)t{TPS$pToz5k>jTjO&Rx4itz7IUSn-0Aht{lyiMo?E)<eEPV#8`3W?6$JV?07rh* zHHLACdspaW89zA%>Stc<0s0Yv%8!F4QAWfcFE6?&jMd@zK3>7P;j;BIwkZsWr}tXf z3rOl;8H7X@`jQR%$>+LXFzbPBj{zTGvVR)O!;35_$JbCTx`xeQ)c?kZNtSTgr)3o} z)AEN~Dd-Jftnvo9anZ?$y3D-70?M6~lGV`1yr_;~v52V{L?S;{%rLGxHOR1L!(;!d zSROLSb8uG5@(?TrG0Q`WvN(i?0P2MyC3EOsr#bYm3y1nG!!q7<rMudVJ(X9B18o|+ zD(hXAdtEx!uaVWY&k+U1Qma=nAXd>1gHKQzO>_zfbQr4|=q468s)N1qge>i4&j=$x znkE6Y%YRRMX6!xO^wcBz{X+a*4(5ZC{%N!_V^hW!c<96Fx>uiu6JN>&RTeOQgq75J zDt&rde8yx)G<btjtfZPZVfEQj9QCfMr{^bORV$m-?%T@Kv$Yf3%2@QyMK5LlbhPHc zVLMu$#K!LB$$Gij!>vxn<f~mAk9G*j#I;nha;>#*WpckTK4}Fr#aLqu3N3pG^+SAj zJ?}zx3EIO=<dpSzN$XA@u96A48bP1E#deZ^zUVZU=z=9T4k8qE>~BB>jU7GSdFXso znSL1Sv4q2EWH}B$?_e%$w{-5}BB^eJmXyJQ3!F8EyDabEP?^JyF!=#!A9z;K@qrZI zoyHSzP|@1K*Gt2%a8}soIdW4oyRfmo0HL%K5lw;D;m*tMyYJXhRp6~N#to&U56x2Z zZEw2af`)yh6D=K_2h4H`4Z{+;CSr@<o`5cxK)a^Btkth0!^A=1U3f7<m=riL$%M3r zX_-LeIp5*OHbVOE$5np}ffy(;H5Uk6>an0ar|Z~Cux;Z`s8q1+FQj1$0ViXP0iNQF zw2V3|Z=i6QH+jC%Dj5xFpnPuRP;<>VhCSrvc}!9BRv$ZGb;+v6q|)*n=IhX)_Fu`D zWIfHeQd18j>htYK`D!~8#rT#Jy00W{_*|@#T!Vnx=*hu^hTgH+ZE8esv=E^c>BdWF zMOJ~#0H3}B@<Wf=9kcBJ@I&{El3UCW%FBRBVwiZP$7hv0GSA%{PFR(-)PN7)Mo?gN z1t^fGGz!CDZOj%qdx*-X@feVWX^Rg&bYS$sy+<GLDRYrTS!kt<R{)M&_&P;o$PrxO z81Scgc`Pzv$+i&i&JQ92P60yDO=W6b?%>|c+0-ex8)LcEL>L>?lWjR^*N~pFE(KB& zd;jM2RM+V}{+DRMm|KD-JRwGJx;rD?LME5O^1AFpVB+1F(6^s0Nj)e}Wz_RmKn?Br zM_KCkF$h~Wi=bDr=yh3&^zj_*u0}(whpm_Er_@g{(e(^s+{6KME#M|{5?VJm0qud^ zE1T|jnk7@6m?wykF4F9z9))xZPFIS%O$geudr?|O<#z3P87Sv*Onw4rf2YMpK3pV? z@DIf-VG9dy!c9<RtR!#{ZQOo?hfg|2O~V0uXEX_=Qy?Avagy+q&?|jLt-*^pc5JRs z5j$UdBBh!ghBgqU7zd<}@qfz>#y^ZtEBTT8_U$`-P+ef&KgNKl>(yeEcG_qW)~^^c zdYF!MQ6eqEs*^kE#n?oKh}Rn2_y-DNRqwK57qr{~YPzK*S*Mhd#uTxnA>WXYtU%w4 zP5ZEMB!M5#%`ge0Kc1~kpDp8z#!0t4If2<)uASoZ<MS9WV@onscB{wdPn^J%B3f<+ znR^`^Wsh>~3BSAVXEDMO5P7c~d&^c&j7>uq7_C0>HWovxfDVS&+2@_-)d%p^wIYqJ zjwwC7t|Qn2cXCGWji%mD_&=3^L`msnz~BcGF!&i_0AwRS%CBKu^=Sm5`WPP=ywIHi z<GaAPgIFDe<2lgztTKClw%Is35Ud-3m}qC5f~RXYLkd`|34)f0Mx*yI((|f$y+_Kk z_(*+%!6zAfn8BwQ<Xh<`{QKUJU}ot7Y~?bWsPRiymXKN41C$XFv?7R#5Y74qSA7J) zpAeI$8ARDgHemfRVxrro#-ynaFE)7Sonr~u|M5N$BU*gfb0;Fc*bS-QVvq!ugWXF6 zOQLh$$S$}s7Dgl&(*>Q)Er?KZY2%<ap0mQtKGfP^!fEgv&1y7}oEAk}chiN$9in9* z{uN{_8y%UhTRq1IF_L{A&ptrhaRGPi{|vV@8-EzFrdt{wX(nQqUjem1tfn>pBv1+a z92>{gpR-66;SoPUds?j*a2KRwrYTiSN$ft+rRwt$rn0`)kV`xv;jPG_?e4!HCT5|& zV3Q%{L>Z~U@)fZ%IH^Bq1Ko2DAN%Mcz>l@mK)|sS_aXb(-bo-2t!To#|BHYVYDHWU ze}#~7N&Ul(I8y(KZixZ(GCm5Ri+qtOImnmTc?`-!O7%w!VjTF#c=ib!0~p-;Y|^(m zc_F}IpWq;;guIC34P@e5{V5SlqSF`g;x~w;?w6=&>jMmIOsxY9S}Kh6l_tXqqnwK# zHH3I}BIh4B)N3C%gmZ_6pq@x`eik2Fg85U7CZWr*+UJOF4{oeuhtk<^M#YoAQGQ$^ zlEDRCic*eYg9V6-57xd&p!^teFw(fO8@!CzMuwKetfX22`el5g4fhmD>VrT?n=pTl zcVfaMr)Y7<;wjOVLS~XXQH6dMU%ZES3?dm!*fa!YjIj^KK>WkG4vMM9ks$IT=EJzI z!qDdpJS@6T%VzV)H~8^BASF8&S!l~91N*+M8=K9^g$hX(x(VNYnPt-A32kIuGWcuj zX@LokP?>TRhQ{3JFu>|50EA&cW;4Hzd(D4s@#BlXAbydqcTF1{r5A3uB3@u5)$4~q z%|G>N0Fz!;@k#qSq+b93pHAkkf^KFVndYuVe5qLt-)C0CFLU*3_<pk*ez{o<KVVkF zuQ03O2hD2um1Z^kDzh4XwOI|n#;k_F&a8%CYgWUrGppgtW;OivW;OhJvl{*eSqq=S zs`!KMA>g;stcBlX*1~T#YvH$;weUk`E&Nuq7XC)F7Ji#q3;!~+7Jj>{*TUaq*23>F zYvFG;YvFgAweT-DYvFG(YvFHoPe7MF>E$(}M5HD;J%j5ZTykW&ZaWfUMKaY`+58#< zYTVPL#)+eJ{TBE?@hy&NpvRf^qN1mn_(k0>!)}ZV^?NwJm}To7Y94^N-j8d__Z~gw zm9VGbp)qd6ebyUjaJ|AY%qSh3#XIqidl1`dub4bh!#0Tb)nr6;fFqy%W7E@h?OQ_r z)LsF!_e4CT?#3%w3A`iTA+o9eBiUSgoB8@Q|8mYz=5aYIwM5gbeS-&tc3Rpm60rn1 z<E?}eCS6)fQBwt=L|F#OUN)fHCT>yT0wKtT&CajhOV8RV?C4>O!HoI{G2>{@>%)$x z58`Nyu@hBVhO0H4U1PQhO}BV9ehgn^iD4igR~j5!b9pI{TZ(Ko03SE?%fvjCThbN% zDxMW1XbpyFMZbS3uo?)kdXS4Z7DesXfLd1swd>-jMKXD5@e>|f6u*xHzuv|1i)8Xr z;I{_!J6fF?gSlY*VQg+XIgaH@i(>gKu<Vauc|%CWwpqOtxL!wG&(<0cP*;oWCxGl= z1lbK1vTgHvDX{GYw#T?7$D-8yBoJH^L2yNgVC3^iPl2zXOc`NE1wFP|x%wQQC5byx zILicR1Y3^**KX9#{%AigGw*o;n*2p9?YtJxD(%x_c_pcH#4aj0yuQ(hml0hVZU_kj z=PF;kwWe#SE*eS&>20{-Xe~e*r$TadpMC<jsx7z|70`C8%4$9Q8Dhyf2O*k~#M;j) zhI@Y$C)nFzGTt0w4?mgJhR#VTTksEvz5yr%b2$URRpxTw*GE69gw68V>1wa#PZ94= zGbk{KA?$BcD~@b85>a%&U@1&L!(l4Q5|XAp_p$p4EZ!Q<)5IbMak=p;5`}@LQp}Xj z&)rPWh7*nIyge~td-z10-75zEX7i2@v%pC><k5RWmAS<N+HK0J-&_lnUuXw~tEmCS z=ux*@!1^P=OoH|>Ij~^Kg3SOHU$DN*g47p6y7F4}7zNeq+muW^Z|=6BSmg;9XtywP zpa@Y;8N;k!TN>7AD~F1M&u{`PG_0$JMT-O6@j}j^zU#N+gb7Bk%UAD4V7cFKLHC=4 z{l0z0#R>YEHU#y^D$=DBUj2{-&?0S^;Hn{F%cR@{_K-?EKicb@tV=6=aMrEfXTkKD z#(LdSnJ9^ttZh4;wF#B^1$=ebg4AKyGr`9A!T``@@=y|hRy$(Aj$LF&X<l>*HcX;U z5dEj|Dki|hAale5(P_$%Xf8xLO{O~pi0*F019_Vs1W<glQ{re1_ptY$_g8@C0*iz! z0M6^qs6mv-ze>zQ_w!DC&OXSyn6NFG1<G(hc@<G+WzoCQNe?`q&oq}B)WcLQ^&AvH zdo*5PFN&Fzi}+XHu4i_Mwp9e69TXuSDFP<{r57a^sJ7*pY~>rzF9#a^>%wwi(M)1F zkgBB|K~K6Hz=NKYtA$NZ%C1YEXW$=%sR!FTxe+^HWk*ou7;3b*`IO<bqbEfJ4<F21 zhduJ|vw*K74L;Aiy}bMN)<P?KD~_|Y+LFoHR(j+xY<Afgv=la|v>4Yp>RPz2Mx1_+ zK>}-u8|8FZ7XeTCV{=(qj1|-^$OY|=yvlaB=y;fOkKS@sotZsTjSYzthn;_b^mU>Y zjD~%6A~BDb0=s^lv#yavmHaONxyK{O`PyJo7B2;EEA5OUgW5#u{VyQ+9T5b5dq>-R zUUDoWL)%0wzX&Y9^NLtT@_8w+T&HQ!c4(W3>mLHw^ATJ(nWlS%++GTdA)453u5qmF z0p+J7C~uCU{2w)3mbSi3;~W|CCQ|l~faY5wXs(K&8Ob3+O?(u3Dn5Y-Jrg1EF%b>7 znT%S4t~fIQfBY42$U-*_4@UF}Z@`EVBSg5<$mu2U;ZI*cUx&@2;kzg)akl_p{)PMm zzHStzif;C8a09|l6nKNp8R-kDpJvP|s@zDk8&hYEu_w$b`etTheOL3QdARt_;UL7S zTdtp-IX*jG9&5m7v{vQf#4Y2pl@}dC%e%_!H`dp~w`4!4fyJw$4oFusjB;t;`F*@Q zz<{$tdPf25v5o_=mAVa2%x2cwH;R^5bqJZM9dgIu9tKg(LY(ElkJP8Jk1lvL&vGfS zUN})quX38c9;w&D%aZ>{J+My>M(D)E)B)B+sP(e!(b4-3_=_HEu$;Q)S!4xgvj=1g z7VvMZH#@C}tmpTAuRa4Xx_q$$(eeL>Ss*=)!{Q7$O<(<vF6U<#=KpvfC>Pp^LN5f2 zEpAdx67@S6#2|BW?|p<U*`}NRQ1k_?gIE8Z`5$csBAaK0gVCR`!1+al))Xd))KjF0 zy}*{ew!ceKFyzO0ibG+qM`tvGK?N=8>RbXg+`|#Tm`3Ik-f!V!CKtJfp`D~XMpTc; zIWdBxQG6Mo%`mT0toaDV$p2?Z^wSpn<wlBx@L7x&@FdE9T;hXzb!to(vJ3`XLuO%K zhs&804#DDl@l_!9K_V8YGXCh(&Zj+MGOm&7cKAL8<C-I^jsy;o1vnqJ=-{_r01He9 z!l5WMf4mQfz=1_<NRQP)oY64l^m3Rcnva*!TZP>3OP)vih=t4bVV$%|eXV48Ey<9e z*9!ZJ>Y&}GK4xKIafX$mWiTF*2EIdKxGr9uC+Igacp6lT*E&}h{i+4juZ9Lq$a;}{ zOIgVv!i!v|CvGU{(!vGsbn?Dv{89aVu`WA%%4iyQ0-BeTyw#hCQmk=snbWhxM)M$C zWPCfZ!RJc2aKx*j3tkZ4;vORm30y8ayY+rIz~yfVp9m#l!nIbv9%6#cOZ<_BV1gCv zEv~siMchlWI5<QCtzaRI5yt>}>hx%;Rz{3VYVi!?K=`(!x9;LI*i<DZ=}-W@&NFC5 zmvUZK&CWKw-1y1KX;-AHob<I{q7;E6Sfq1w@;Ex}6CS3_&WzQ@tMc~P?7^CQNiWUt zO5^&lT}V3hk1TqW{$3|05AaSt{B4$=J&bs~V;+>}#`W;*8n_La24W>|PmZlZ4y=fF zC^V-ql!r$d{(>YRi#CJnf`<jpdPU?dc&M)vQNi|~ac7F6{*_PVRlE$`S`LLc0)l;k zWzbd#>>a}M0zx&xN;WcdTcWGXFA)YOJQb%G6({l(XUo*H3_iht6Dist@+sbl<@+~y z_nY#9!S65-P3bY-jWPHvgWqNFKN*OLLJTb8e<MS78Lfv)2szSJ9MLI`niK~m>JJ%E ze^%5^trdmh!1E<O`!WOeIg0XKFMIq;-u)E=wtf|^E&Sh%ev?5+Q@NworG6eS{sA_| z`UGO|3R!}sfzf|l<i`bo@dbl^yZ{fO4qVZ%dho<g;V&&@PPt;Yz#Ip-$d3(;Kk-%Y z{Z8Zr2DJA#7O|uZi>BanfzyVr%^z4!1L%aG#8R*0@ZnRM$s+=n`XkM(O9=j-5#SJJ z*r&`UFEBUpAC7j(n7tWOaE8G8BQ@OMx-P7I(JdP;^2Qq&HXw1AP;+4ly^T9Md7K$= z0?p({(??SN832;Lh#Y@1Oi!y9G!ks;BTcLQcO`uH*U|6v#z3->e22d-JP~fEi`5v~ zM|~)(SDxurHxC4qYB5c1GsF3a=N5LRoZ&hi>C<pH0HglWG#t$v8m_(!@~VF)H*VtH z?=zsE2li{NHbzg0Uzuj(f8?vZ2>itbtNGw=etHl=rP!cP33}M1(**vKGJ>NIdhb!X zahn`&i~}Ejti8e-HHphQHM!$v5qO$u_5~$x?-~-QEWRSE{f?uq7zc>ySBSHiR-fiw z*aD^T@kIe+1z&4l8p6Kf6sp!(!(oE4Ib)0x0u~9>&W2vH0C$Ce6U^vAA9yt|Mc^gD z<csb$Fpou40K#K?ya!jEY=RTt(W&SIw6Oa@pJ|bFTuSLSDV@xxkrWyudCDKE{SxkF zuYx$~zXfc1i487c5;8|~Nk|7_$4LK2l!Bq=yAUx4g<wc7O%;ZJaDXg%;)bs-cW2z< zW^lp-dPW)o7BIw@J^XS!BxEY@X1M?q%boV2sb<|Q=D}YFT@U9lNPcoCk2(V(Aa@sv zFcjo!IA;x~HKeeZ5oQ8^P<#4bJSqClKtmnYqQ+)t`L!qJ;RpmJ;`O?5JKM@$wl+UK z-RwHN7rwGXW!b7ojiF3fA1WjKx8h#+B*ISO$cQ#&TC7wu%>gh<Z^0kWsbYEvB=vFv zOV&)OpTvVW-Kn4rknU^nncl&QWBx1}WxtI3(L5bF!pf=((as_)!WMW98msziqVhKk z+G(hpiBzDWS}pM<P*T;`2>I{Zfu~w}V+bB<sU?Jb&9uz{=OSA|O@nN$mEceTXuDMa z#qd`tC@IE@PS^FO$Icg*!aJ02TY72IeHfRFSp8laC!9FwnbAX|m(6;sj;U%@ce9Z` z0?t5@ZWsAS2Y+V$3Wu3b;T@W?uw9bmq*S<tBqKs_sk@;)%s`Bfs3D}a7&6p?(#YoJ zes;yQV!VCX-+^pe%l^W&6LU^fr#UC8%bXL{ZI-!XwbIpk#)9@6yi!Pq2(2knNh<LM z-G&?b_&|IbPf$6_Fe6@}*mF?c;81qo%Tw1zQK%xlOQpy?JP1n9BGO61D?vRsZU|z; zkztrGhr_-*)&;U6?<VDGY2XSP?Xby%AHt#pRTwD~`G?njT;g&<3yMGW`dfIDI5z%m zyg2ArH8^Lf1x7ntTs;u6lqInzM+f&>Kn7cY_zoMwzbF8Iyyqna5^D$wWH}uunfw1A zELn_-9C<uo!CMi9Ct4ZZ0<OMj?c5Axd^2K!iWMyYRgd(Y<iDWrd@~~G6JZv|h6sal z2jh5sP<)Hi*m4XzF=Auu+qi`MLhhpUp4g-TyK?(qsmCzJ%;SxNKJ&3Q>1c3&6UL`F z%6lr0spdDerlZ<`Jq8cql%Y^Xm1)(3z){aH%Yg2O7{y2+P%M6q4@Mcps)B>NSCT$@ z#&wXZ`*AWOFDwlTB;$6O+iXvx?Z_J2sts~gH<ml&FV{TChTmH`WzG>cwF*raQ;uWs zkru=S-3GBE_I@KI&M?%_8!nFZERwyBSnD2eFxvdu&cg;(N+O7v3hsPU*sbbq88bV9 z4T|%6b6@qx8kJxILH!9I7@uUP`a~Qj?xphvi_-=y>b08=VDllbNfTlXXr#^?%M~}& zFZyPE^e=+TBY+c!eHPEgE!Z}fFK)+=HegXv0}Du7)4JlYexeOLXriHLZP9`vr3GJr zD>z^bVSa_h`}2e{{u!Q4S}<2y^wltZ`+Iw!{s5@OlCS=>9atJKrY*3Ry3k^;N7CTO z`=GEiVM*1(q3xQ?qO%rco1u)B$NfWF8YLBfVW=axX*G>f?bmyQVi_e=eA(*-`@3K# zjO=;Dxd#dD1XDSv{P|N^9wEu+wz|%Ospn9{%jqrZ+wylvvBZ~4mi8_?Wfio2Q>;9~ zzckp&B%B`kQD?ejc5R_bu0&MDq?BlfB!#vGa<4M%dht9Kj6d%<ec#3s!cbEA3NUf> zf4q;8#olQYg~oUXa!NM7;g%pm2#j~c-6uPR^E5#SoTlj$LdcqTi(=W8t*SYS`8(oY zM3WMls1DwdDyqagl0!x`n0;K79GYUC{3b>Rs@`WqIz*`e?<QLb@ip-l{va{-<Er0_ z;3c&YtA8K?{)Isdaa#4hJ4BoZ0Y{9vHgEq&8FbaZ5wXh&*wNhhc48MZ)7n@?Ott-q zrdsN1+R_@DR@uMm{nyw(=)Kk`J+(cQ!sXfwpbog=E~2^~l?MYTjzKs7I$^H#@A<cY zcioEV+pAGD>n!MfWKDQ3bsB1nQA;numr_lhIEj5OybRVy#cCDHr}%Paw%&LzQNjfk zSJ$jsLcn$#WLF)Gfh!vDFm#4tzZa+v@U(T=f*-aCXb=pY^O^c~KG!<LIX;IjaV~v2 z1^ozSmM*;MO3h+J*RIrgST7d1uXPr{hjLyXyHW8t(Zcx5Tmuq9oKEdY3uw^Rq=lkr zf===o{1Gwa73RiNV{&X-E$5>cPcH?KA7#0<P{Q^bFv#`8RL5Pe2hxh~$MId{hi9o_ zT=mZ(_<wIy=5D~q_=@WltAj}=_+mu2Q@sMkYR_-1|FEkUdM1rWILi76DNAZ$(9;El zA{<o7L%zU|_h>^}o+aeIkqs<XZ~q6+J{Hg2pXq7Q9iq8|5rw&Qo*x9{c<#MG=~u1X zInLa!`S8W&?Ui&sbhvxg0?`wK;Hwu6W@!ipb`kRbrz~j8qtHx>7Y)>ujUD-KSkSDp z@zsk4W6Eaq1(xl%EofHRtQ1!O>Pt)E1b={#Z2;RCZ+L}?$(p-&dipaK$WD^eJbwXz zDuw#|@g!J1CQnm8ziWZ)vq1XQM>h9=ypOPb&>`poB{kVTu>J2@FqRSq#A_&R7ncfG z0b&_I{DB2RiV>CIi^E_yqW(Jy*S;7B@b-%q45=N!So{b&3Z90~1{kL;5JnP}IMDs0 zr0nT#vuG6v?oTn_wo-E+-hL?|^Td<-BK3O}nNwl>vXyzasb6!(=8MGLTgG~nJ>?#n z&@=AU+E*=bD-5`Jnrmq>iM}s5&lx9?V}q+^^{*^=B6h-8*90c1&-n%j^fy-i(mXI~ zwVDUGJNWTFT0=f}N%g~XWD8QL-(e8z-Tn#R{hb9#S_vRodd@YK<{b$z-ic>lw_r#s z0Ssv+FQ6QGd=oFny8+{$l3*k?6~dGu4b8IDCuMHSr`aW(|B2^`E%Hb3;$JN=qG~i| zF(-@(sFo%fz`+*=j!~6WnPo_5^In&q8~f&OTA+H#)Aq}k792aUZ?^--mm}DZb}Q*} zBLiC5^&~g-BSkevEoU&mU<HFg2CEpXW<ZgzD3mpI#fZ0$xv(bG8os!W0eN4ME%i3n ztXM^|>uvC^XOi^{LPnN5nR2E&`HL*XPTatC#vb5Y;U0Q;MysRvH1Y$m)G)641_W2u z!J*d{J+k=7s}3I)$4HbV`0+l2OW04F@VHNJ#Ha<OfCbUyP2drEQRo0h>`7$*%6<!8 zsQShMGcJJn9i=!eh8?@jw|g{6lK`?kbP^zG0S$YZ4epxzyZ0cEB;J>!n|c-lgT_39 zN!cVeB#Qd%rzFO>0f^_BGtD|+42*phpN%6L`LTe*xa$9gAgpYE&u5tigLy}sbYS3d zix>bE+zz*dzeTsx?ZV#<x7+Q(-;%q;?Zw|tGY!^drop<+G+2+B23umL!FsU|^ctjJ z>Ru-s#PpdBVwRZ=V)|XZLCkWqLCk>JAZCTxAZE~P5VO*35VOi`5VN|vrn>rky0Y4A z6my-~C}yqMC}v%CU8QU`i@DzAzQK2Tg?%_a9JPFCq`jD-c{IB3iji)Y9Bi*V`*=-t zgPe&q%^}<Bkb~iz&}#sO+{>!)e{3?03-_AYgRPh^fq_&u-gR)|Ddr1ComwxSsSSFz zMb7~mtT0hPD}&m^fSCC+2YZllq8*W<Z5^@H(|2531~+~T+(T@GfuP;iBG|4+?wq^R z+dIR8!OfZ-*}yqHg@b5L<1{m1_$4B-(ZWg$n@L?ZMdg+dEAITl#RZsDT<y~^K|LB5 zSpgvY6>tT!8`Fp^aMeauom|Mw_Ag{Fz>p$mFy!C@iK!XwWmQ|dkb&vs7Q7cvP^{~~ z8O7myJcp?TlUMSWs{>(h$z05&v3Ko-9M^@I(^_T2*Szi@ZUu1NU9f-fdfnMaXVobj zt#(L{eWb_m*I)c3i0tJXaJ{NFDqVAWuqkV{Fs{*_q-qXn)M*Bx9FV?YGxFa-vM~m` z6Z|l^fp-^9&OycEunwHBIOr2Fjz)_P#3~kM+=3fyEL5a8)v5$20)^s~Gs`1%IISg< zTc=pbVratg=J-a-s&*!U{X~?qN_(<me`4Jn+nEM}%G6!T0)WzQ;cc*P!H4(|RGNMi zAAe|NO44kl^L~I(k$@t;FA6cyQm{DGV`?jb-PjHyHHBAN5LdQB^oN^*6Y;{2_W|HC zBBVN}g=W`Aje$M^)DS+woPp>lQhrfIl1NXz<<XGzrF8%EaY9fPYT<?epEx%NUYpwB zwGGhJ%NQi#LiOn};$p1K<W)>!G-G5UK1pOEg9d2PQh9<~#;5CDG^h#55W}KmHcy{5 z!5?fTzFgdbdsuYgl-zPWCDpnU7IGP$bO_(~C4L<ynJBImr5u2$?IA*52F43@6Q9P~ zHWkc90y+aRbbLZVJOn@9^ODxgCP!iIcVubZTO{d3WTNjRO#EeoS?<GvaI(f}R|`sY zv0O=js576p00o7jln;IATl(psP?Y>KX1}sA<7o>{P?vynWrN057zL>Ezoi|3q;Xnn z37$e0?bXzMc>DG?0A4b)#`jwQmRfb*#<YRT)2zj2>}yMgz0}Qkn^fmsk6z>H+cw^5 z!3?TDnxsW0MF68OhBs+wOBcZHED!IppaopLxb<OCV8YpJS=3mw<tXnx7O<db{{aSw zAF<#Boc#if5NcgQK{!bLv33B+*`$yEV{;5RTU#I~na{yP&F2f!3BHDfGgWnbwqEse z+#kK}tuS$IZrDFtL%cqTP5foIizsnr;n*Yk`*`}FAgV-z+C_7{&%~fcn-9jB*8T<f zGnc@Ysa?22JmgN4FFNY{j76Y;tteaQ=`P#4w1}{PtuUrH+LceDGOHm;T+&wXa~7yz zVBy<NB2d^T@e=93A%J2Xv*xHWHCawCLTSg^27>{5NwGsvJS&<dOt<Mdjd~>mbu`ZH zO!EswOn2zi*x98w`+fTR2#T&n3j04INJ0ye2XF&FY&2+Dj?JaO@Sl<q1(0*$ib17w zg+><3IHJhQ(J@j89>zl-gQip7qHfVQLnwnNM`CcX3Z-rb@|Db~NjF|9(%|DPm7dnr z%bbgH0t^rEZ7m%YmZA0WG5oE_Mzb-8hDwY|ViTf5^5V@)JDE$8W$8A&@Ve@aF>I7K zQ*E4_b@dvjfB@R;)Yjs(0pAa>_#8y)1Ib`q=)h$9GF&`ljO&NGi=1xfKU$&uP{$d| z77JONMvA<~kxk^sdgrhH2!g;M(I;z%u&n-#9WYH`)ey`E;&Fn%IdceA-W)<zFo#eT z%^_4B<`Ak<uy$yf*Rg+gW@eT<n?{e80!Gq2$boe-iCGmO%>j-<Toi<@Z4lxx6NGsQ zaTqF&Yz#cLu#c*G8@?6i7P#PlnfG@hYi;3@=?}5#-oqC=7~IT&=0Gz+CTDUdErt0U zX|IRp`ebN&*f#ru;)Lb|yFu}umo%uD&ZqtxVJArzwmgsUNEK*&OG7AERFPsphSEw( zoCJny(wEwUSyWMqfl=>6UWsMdhZkRI3uJL+DFy^(k?Kk+3km#}7MNubk|n52WOrU< zh5DNi$k>`g77SLfX3YtlWOq^M1;@DPwW!?bPgaz%E0vTy2z^BEaQ2RR3XF+$tNvjL zh(Ubv*m+rn=5P@Tm>7qlUnt34C-aeRndX_uQZKPfb7n)^YKM782GQesl2>ywDIoNT z?C|?haGxynii=b7{BtCjUO~cP6%9^Yzy;%z(+``49IOSw8j^C6nJ2QUa5=kS&;jPM zdRLNh3>!)iQ_|hExk|2q(Zy&SLzekl#E{j*H7<5LXs1wRkW|p1Pan|`@93TY5gY&; zcB|`<MsLl?HsWKG7uVhvbXX4#dQ^iB#-LWSBsyp>Ja?xn-s5Z{lvs^PWyvgr)U4F; z-!fgO(|nat(P=2bewT#k#Uq@oRT2SDao%6diF_N!i^z|)F^sFe4M8Bx!N(BWi!1?! zMYiOv4C?#Q$;QchCtY}Rit1F4C^Q5?YD)uJj~gD{wUCM~$^?^x;s`^X6YLp^<oDw{ z^-~NsGYCsB1t;aYNOB5-6c!dj;;wIt-3ySwA0W`D8N?v}G@dObWR?;VcuRYzkjR=w zdna&IZ-hjPO8=iB*d*9Lix>SC?4hfGy|0QhkD^ock@{hu-daCAaGcw^P~bJV*;*K* zIJ-@^8os>%vnqRCotA<OF?|BeQ^*>9t<<S-w6v;WJXMz%usBNUnM7@0+5C0|i;YVv zyH8I3YHny>{7@W?z;e5l+26>3Os~6q0Vx;=YGEzQoiN!hz%3CjReyLc%K5B{PusVu zTaff>TU9k*Dc~x)9n!iAP<<XAn`~ti0MwF;t=u2KgUaECArMesr#Syd21%U5_HqM@ zq9rCLI}Eh;Z6g4?9v{;pg=qtXRPDn%ppiN)?!^ANe1e0FNRklYW&~S^h;F!8H;lc4 zeN<X@V2HFC;uGMs3K1@`;vhseMc`jBEH_68#rR!FA5zAMaWB)DnTs`RFFQUvcXqp9 z*(ve$#n6G1>#URQd>z4t0xmFavev1Vc41gVd^l2rYbTbLG%#Nw&obtj!;L-`94;(H ztwSxPGkUY|+KqJ_r1E-*KpILl&eAiGoW3=5IbT&7gzPGu%Avw7Ayxaa)HAGLe@r)m zwz!oa$65lbMBPnF$%x}lCb=sLBy1C;=z2n;!i(`EPbSV6bJ?`wo0fJ#Dh=bRKY<`< z7pq(j8`9RWVYNBEHdoEzEL*e)^hO0Ucqp2|Lx&kWl+56v)9rJYAzhc-?=HvRZg;?4 zfxkWOpt}-(msC3|y)wnTMrD9p>#%~2VL!2>N}wAScM^jrL+ydHZIu8gcaKkx)$39} zUWbm%jLrE>m22?a`*_8$-R&c}f_BGwYqK^jx#>~Z6mZ>xtNt$34b)FLd%c0tOsIsq z*ai((w%H#jp%{5X8O>q=cb8tkZMIX8RRWOVPcCHZIL;B%VtQ%~qjw%^E|X}@h5Q8^ zYmQCOpuTch1^1#^$U}wozkWX^=iZO6F_mV%`EmUX)LlC*4&hawmvo}cnzWzOX+_EP zbL}|qJd?((Y33Om77T?Ks_(f1;1$^8)c;-j7{V*)Jdk#vZ7wzDqH6d$)4+xe$Z<En zZ*(FKj1WI8`9bx@Y%Qh@k$M8D^XBc8y!A1Y9Mn6I7F)+zsh;suQCIYj5|$MbiZjCc zr1dT@uMd0IUUh*g>GeP%K<)dBezdQNH1}8VV{u0r=G|Knc%{iY<{E_OuzHTOI(nUb zpto1Dn1$S_<$=u9%5W|w8TNwl50L@c&(qe%6IiLzJ5cDkZ$m!Q1LdBx%5VhFP_dWg z;u-GdAhXSxKBVMx%IYj)z3E;|RpQjmOqY{KtV!1}pQUxlBgRHx5Zbr&#JS$(Cs4u% zq^cknrQ*Yh^K7V+=k)qPQbz4Zj(W(m2X}r0>=07Xh!<~!T(j^d(g2sxfYYwf(pOn{ zLrphTv!AUzBI@yZ+o3u}o<t(4)Ag2w^(~n`LmWsAsEs!4ShA~zjk&T{w1KUD2}r-w zg15mcE8vON&T&cPxR{n|BeEhONjV6{hyni)-WuYA6(I^^boW+r?2~vd&AZxQ*7k!4 zX6#e~(M?4F)2yj!vkl~eGledeTRKV`MF-33^VdcWBcc~2%GyY?itBHfLSDsM4@bc7 zA+B0{Lm{Br<ny{LyBsGr{*U)mY=mZPaa5S<U2+Z(7cOnX#y5DfkiI+hXzKJ4UrL`o zvLjXF6fL$*gyh>L&4CA+Q`f#%WDYD_{BAeO3d!R%A1Ia0?<j2hlM`plV`Wsq9DD%F z$E%IUtJNBwG)Vn2*Q1ulYAzmZ0s2E_X(`)-9C*@|Fp`L>XcO~Q4><(l0J$wnB5#FV zXQl~}v3`VP`~NCCo7gsrD31GMYi~&iLqTZ-Rb!kYR0u^Nger(aT0j&EZ3v%2h$_?u zLftqX+d+*|MGzqYy>LPZaVX-7C<4lrD;FdrZax80ZyZ1z;lTg**8AbuIbh3w&+g97 zynQqK=FOX#H^j{a5;BFL?<OWJ>D4Q2R5NVcC4r$LPZ&GbXRM#ykZ<T{1-FJT*d(m* zq6G8K22gSG{wPRuCidkwt|kw!c^=}+8!PKDDrHx+%~GZFX1yX(EBGZeJ|3yqz-r~~ zdXhq;**w=>%kbYcf!RvCE*~>B#jJGvg-WC_BsWnU8lcu*Oc#iu9mkrDb;WE}s@mSA z;g2MJn`&B6Dhw+plQ?v%21;j?Vt8DUt|lcBmQ)G|c-t&q!dnaQP`={|oeCmKaG(H` zPAbHepwwOD!8YUeHkn52t!mx1k<$pC(9J(V1c^xYF;zLvQuqAde9F3q@rjXnJ*hl? z&g8{p;!Iv6=a3Xd)g$^@`!M`m<j07?$ox(!zh8FG&qf;9a{1Koe#_{Fp7uH8ZtO7* zk=_1rLdYL~f)EAiVWj5?J=wlo)(g+Oop~kg<+LtAQ8A7|7-`6M7laG44)r^+G80XP zQ#KM1nr?hnkp@1xJGY>p-JU4!LuMkqOeJ$n@xn@bMLUrVv|Xfu@HI^q37RzL5yf}t zREBmV8>al8;V0^HKkZaqCgJ!KXJQ37oJqfd@)L@;BQJTogy?IT+>q{ZZgz3EO#q)B zOfF^nQ@ZdQDME_+M@7$>0ru|E6@JE+9~Ib?g#p&E-kNzj+j83=r$q<p+v1(VWZR_F zP%ZXVgJ9ZHOZ&@29@OXr$RG0JW>Fsg&5Epa<oY2=rxvHvZnR$AEYbxa)j%DHi^1xB z>J)s+z0p}y(6ui-bLCbiHET}$#1q@j2bg!5rN_U`va{pX*Q6^!U>54om-S4Z>CchQ zX3DJ%eA&33&8x;fpyiHxkgM&;nbABqV8!SKo#mCCJE*Zg{JHv{a26HP=A;$SOy$@p z(rBgGa;y!xO>>pTs@XuQ;?i>cB4wao@+m6B&uR_BJ#iVx@TF;-mua}w-MUy<4Yl%I z7VCEze^sB*_&^(?Y0HNt7%J=?-q8Sf>c%aVIH`xWAbQcSDJOpn!006Djoz+BR?#kg z8aC^$o!Pl&)!zCW#`NG5iX1(-9+5ShdAm07U{pIQmp#}~1tiZ@fZGKs5&O;i`_(s- z2}kQvWs-=m_KThK#g?3b#ZtX!k|vrnPr=DPVO@=9M1HTNR;%0S^a`s(G$W*LNol6d zVU?+aX08p;OhF_xBLHHmM03tp#dtaQ7S-mFW4rJ2Go~<zq@vX?6OB}|K4(2-`Xyjc zYq6&~1by_jnZ8c<$Ic#pNgG+5dA+M$7VFv+R`<r<xX(7vuPt{x9QKgwQAnc7{u!Z7 z;3MENU{lOLCv-`S^EVMPd2T(vDu*+`?Z6@6ZeR{L0W1N}0v%urcn5e7cpvxx_z<`x z=#`qUy(Nd_et|P}a=nuf){S2NIsXL*p8y{N>G;BN4yiGj!3eX^e#f~VfM0<>fWLsh zfq#H8ve^er19t)U0Q0~rMER1?SHKnE8{k{uUm#EM6abqVeIp?g88d_!kd7?K*j>%Y zWDS;y)yNkMFZYh7F;m=$Xzqn`=bN=|qYh!Y_V<GE*|0EGR@IEZ#bB=ulb2=J3SG%f zy8~`U|1H;cWp}-E3YXou3*9Mt%{WsEOYULRDJnO|2ZZC%x8(M_Tix^SUg4PA=N{1i z(!{(w=(civa})brO*!p#k1FM@+`2ohl$T{syV)(;nFrlNZc0>a<!*Chr68OZb=Um| Da>dfa literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/__pycache__/compat.cpython-39.pyc b/.venv/lib/python3.9/site-packages/xlwt/__pycache__/compat.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9893c51374bce55fe9ace2bf556c64519dbe75cd GIT binary patch literal 842 zcmb7CJ#X7E5Is^KmK7`M8gwbT8ECB`Q-Pu=&<>f>0G`Svkj1mDM5YXqb}gV=`!BjQ zqv!q`uATB1I`t^INYQ3!DexVSe7g5|QjujT&=B9g6i+z7cc1(|Xb)b~G&=$blGPvp z;hIS}1<7wB!IA5846Z{FVJrqT6C8>ZkLFB_@JM7bkl|+#$1;*J#S`@mqYy+y+=RHP z=VCVJ#LkF4IIxqRE!h;t!#h%ki@SiZWPW`Ze(g8k&@>|g3m@StyM<5m+MZpExeF_c z+C_vbvuBRUJ?Mm(Ki=gllvR<R7h1m6)fV&1c4-@xPs?S~)cItKYMa;9EMK>F*{HM0 zi`-C|=j)=pDHdq*UH!r4WwTlrcCv2W&?|L1nYZIx2Tc3^{<~-GZ!_jPq~PLhQE$-b zn3B$O4?};Jda?fegz{f%z6XD)c_&?m{Jp6^a0^rDm3n^<DRjn9?@Rw*Rqx}$s{Y{d zebw<J7ns(#(H6C-8g*5vdE<i1_h&AewHA$wC6<juH`u7ETLi{xF*;;dwq2u3XN5u9 zuGGRsx=;&r@ve_Sr7V3JWjm&EVcn=jcQ@U6^ey?bx5>*@BR4g^^1qjPN|5kl9<rE) P-9wQlJPngBBc$vn&U2}& literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.9/site-packages/xlwt/antlr.py b/.venv/lib/python3.9/site-packages/xlwt/antlr.py new file mode 100644 index 00000000..b9d93f0d --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/antlr.py @@ -0,0 +1,2864 @@ +from __future__ import print_function +## This file is part of PyANTLR. See LICENSE.txt for license +## details..........Copyright (C) Wolfgang Haefelinger, 2004. + +## This file was copied for use with xlwt from the 2.7.7 ANTLR distribution. Yes, it +## says 2.7.5 below. The 2.7.5 distribution version didn't have a +## version in it. + +## Here is the contents of the ANTLR 2.7.7 LICENSE.txt referred to above. + +# SOFTWARE RIGHTS +# +# ANTLR 1989-2006 Developed by Terence Parr +# Partially supported by University of San Francisco & jGuru.com +# +# We reserve no legal rights to the ANTLR--it is fully in the +# public domain. An individual or company may do whatever +# they wish with source code distributed with ANTLR or the +# code generated by ANTLR, including the incorporation of +# ANTLR, or its output, into commerical software. +# +# We encourage users to develop software with ANTLR. However, +# we do ask that credit is given to us for developing +# ANTLR. By "credit", we mean that if you use ANTLR or +# incorporate any source code into one of your programs +# (commercial product, research project, or otherwise) that +# you acknowledge this fact somewhere in the documentation, +# research report, etc... If you like ANTLR and have +# developed a nice tool with the output, please mention that +# you developed it using ANTLR. In addition, we ask that the +# headers remain intact in our source code. As long as these +# guidelines are kept, we expect to continue enhancing this +# system and expect to make other tools available as they are +# completed. +# +# The primary ANTLR guy: +# +# Terence Parr +# parrt@cs.usfca.edu +# parrt@antlr.org + +## End of contents of the ANTLR 2.7.7 LICENSE.txt ######################## + +## get sys module +import sys + +from .compat import long, basestring, int_types, xrange + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### global symbols ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +### ANTLR Standard Tokens +SKIP = -1 +INVALID_TYPE = 0 +EOF_TYPE = 1 +EOF = 1 +NULL_TREE_LOOKAHEAD = 3 +MIN_USER_TYPE = 4 + +### ANTLR's EOF Symbol +EOF_CHAR = '' + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### general functions ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +## Version should be automatically derived from configure.in. For now, +## we need to bump it ourselfs. Don't remove the <version> tags. +## <version> +def version(): + r = { + 'major' : '2', + 'minor' : '7', + 'micro' : '5', + 'patch' : '' , + 'version': '2.7.5' + } + return r +## </version> + +def error(fmt,*args): + if fmt: + print("error: ", fmt % tuple(args)) + +def ifelse(cond,_then,_else): + if cond : + r = _then + else: + r = _else + return r + +def is_string_type(x): + # return (isinstance(x,str) or isinstance(x,unicode)) + # Simplify; xlwt doesn't support Python < 2.3 + return isinstance(basestring) + +def assert_string_type(x): + assert is_string_type(x) + pass + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### ANTLR Exceptions ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class ANTLRException(Exception): + + def __init__(self, *args): + Exception.__init__(self, *args) + + +class RecognitionException(ANTLRException): + + def __init__(self, *args): + ANTLRException.__init__(self, *args) + self.fileName = None + self.line = -1 + self.column = -1 + if len(args) >= 2: + self.fileName = args[1] + if len(args) >= 3: + self.line = args[2] + if len(args) >= 4: + self.column = args[3] + + def __str__(self): + buf = [''] + if self.fileName: + buf.append(self.fileName + ":") + if self.line != -1: + if not self.fileName: + buf.append("line ") + buf.append(str(self.line)) + if self.column != -1: + buf.append(":" + str(self.column)) + buf.append(":") + buf.append(" ") + return str('').join(buf) + + __repr__ = __str__ + + +class NoViableAltException(RecognitionException): + + def __init__(self, *args): + RecognitionException.__init__(self, *args) + self.token = None + self.node = None + if isinstance(args[0],AST): + self.node = args[0] + elif isinstance(args[0],Token): + self.token = args[0] + else: + raise TypeError("NoViableAltException requires Token or AST argument") + + def __str__(self): + if self.token: + line = self.token.getLine() + col = self.token.getColumn() + text = self.token.getText() + return "unexpected symbol at line %s (column %s): \"%s\"" % (line,col,text) + if self.node == ASTNULL: + return "unexpected end of subtree" + assert self.node + ### hackish, we assume that an AST contains method getText + return "unexpected node: %s" % (self.node.getText()) + + __repr__ = __str__ + + +class NoViableAltForCharException(RecognitionException): + + def __init__(self, *args): + self.foundChar = None + if len(args) == 2: + self.foundChar = args[0] + scanner = args[1] + RecognitionException.__init__(self, "NoViableAlt", + scanner.getFilename(), + scanner.getLine(), + scanner.getColumn()) + elif len(args) == 4: + self.foundChar = args[0] + fileName = args[1] + line = args[2] + column = args[3] + RecognitionException.__init__(self, "NoViableAlt", + fileName, line, column) + else: + RecognitionException.__init__(self, "NoViableAlt", + '', -1, -1) + + def __str__(self): + mesg = "unexpected char: " + if self.foundChar >= ' ' and self.foundChar <= '~': + mesg += "'" + self.foundChar + "'" + elif self.foundChar: + mesg += "0x" + hex(ord(self.foundChar)).upper()[2:] + else: + mesg += "<None>" + return mesg + + __repr__ = __str__ + + +class SemanticException(RecognitionException): + + def __init__(self, *args): + RecognitionException.__init__(self, *args) + + +class MismatchedCharException(RecognitionException): + + NONE = 0 + CHAR = 1 + NOT_CHAR = 2 + RANGE = 3 + NOT_RANGE = 4 + SET = 5 + NOT_SET = 6 + + def __init__(self, *args): + self.args = args + if len(args) == 5: + # Expected range / not range + if args[3]: + self.mismatchType = MismatchedCharException.NOT_RANGE + else: + self.mismatchType = MismatchedCharException.RANGE + self.foundChar = args[0] + self.expecting = args[1] + self.upper = args[2] + self.scanner = args[4] + RecognitionException.__init__(self, "Mismatched char range", + self.scanner.getFilename(), + self.scanner.getLine(), + self.scanner.getColumn()) + elif len(args) == 4 and is_string_type(args[1]): + # Expected char / not char + if args[2]: + self.mismatchType = MismatchedCharException.NOT_CHAR + else: + self.mismatchType = MismatchedCharException.CHAR + self.foundChar = args[0] + self.expecting = args[1] + self.scanner = args[3] + RecognitionException.__init__(self, "Mismatched char", + self.scanner.getFilename(), + self.scanner.getLine(), + self.scanner.getColumn()) + elif len(args) == 4 and isinstance(args[1], BitSet): + # Expected BitSet / not BitSet + if args[2]: + self.mismatchType = MismatchedCharException.NOT_SET + else: + self.mismatchType = MismatchedCharException.SET + self.foundChar = args[0] + self.set = args[1] + self.scanner = args[3] + RecognitionException.__init__(self, "Mismatched char set", + self.scanner.getFilename(), + self.scanner.getLine(), + self.scanner.getColumn()) + else: + self.mismatchType = MismatchedCharException.NONE + RecognitionException.__init__(self, "Mismatched char") + + ## Append a char to the msg buffer. If special, + # then show escaped version + # + def appendCharName(self, sb, c): + if not c or c == 65535: + # 65535 = (char) -1 = EOF + sb.append("'<EOF>'") + elif c == '\n': + sb.append("'\\n'") + elif c == '\r': + sb.append("'\\r'"); + elif c == '\t': + sb.append("'\\t'") + else: + sb.append('\'' + c + '\'') + + ## + # Returns an error message with line number/column information + # + def __str__(self): + sb = [''] + sb.append(RecognitionException.__str__(self)) + + if self.mismatchType == MismatchedCharException.CHAR: + sb.append("expecting ") + self.appendCharName(sb, self.expecting) + sb.append(", found ") + self.appendCharName(sb, self.foundChar) + elif self.mismatchType == MismatchedCharException.NOT_CHAR: + sb.append("expecting anything but '") + self.appendCharName(sb, self.expecting) + sb.append("'; got it anyway") + elif self.mismatchType in [MismatchedCharException.RANGE, MismatchedCharException.NOT_RANGE]: + sb.append("expecting char ") + if self.mismatchType == MismatchedCharException.NOT_RANGE: + sb.append("NOT ") + sb.append("in range: ") + self.appendCharName(sb, self.expecting) + sb.append("..") + self.appendCharName(sb, self.upper) + sb.append(", found ") + self.appendCharName(sb, self.foundChar) + elif self.mismatchType in [MismatchedCharException.SET, MismatchedCharException.NOT_SET]: + sb.append("expecting ") + if self.mismatchType == MismatchedCharException.NOT_SET: + sb.append("NOT ") + sb.append("one of (") + for i in range(len(self.set)): + self.appendCharName(sb, self.set[i]) + sb.append("), found ") + self.appendCharName(sb, self.foundChar) + + return str().join(sb).strip() + + __repr__ = __str__ + + +class MismatchedTokenException(RecognitionException): + + NONE = 0 + TOKEN = 1 + NOT_TOKEN = 2 + RANGE = 3 + NOT_RANGE = 4 + SET = 5 + NOT_SET = 6 + + def __init__(self, *args): + self.args = args + self.tokenNames = [] + self.token = None + self.tokenText = '' + self.node = None + if len(args) == 6: + # Expected range / not range + if args[3]: + self.mismatchType = MismatchedTokenException.NOT_RANGE + else: + self.mismatchType = MismatchedTokenException.RANGE + self.tokenNames = args[0] + self.expecting = args[2] + self.upper = args[3] + self.fileName = args[5] + + elif len(args) == 4 and isinstance(args[2], int): + # Expected token / not token + if args[3]: + self.mismatchType = MismatchedTokenException.NOT_TOKEN + else: + self.mismatchType = MismatchedTokenException.TOKEN + self.tokenNames = args[0] + self.expecting = args[2] + + elif len(args) == 4 and isinstance(args[2], BitSet): + # Expected BitSet / not BitSet + if args[3]: + self.mismatchType = MismatchedTokenException.NOT_SET + else: + self.mismatchType = MismatchedTokenException.SET + self.tokenNames = args[0] + self.set = args[2] + + else: + self.mismatchType = MismatchedTokenException.NONE + RecognitionException.__init__(self, "Mismatched Token: expecting any AST node", "<AST>", -1, -1) + + if len(args) >= 2: + if isinstance(args[1],Token): + self.token = args[1] + self.tokenText = self.token.getText() + RecognitionException.__init__(self, "Mismatched Token", + self.fileName, + self.token.getLine(), + self.token.getColumn()) + elif isinstance(args[1],AST): + self.node = args[1] + self.tokenText = str(self.node) + RecognitionException.__init__(self, "Mismatched Token", + "<AST>", + self.node.getLine(), + self.node.getColumn()) + else: + self.tokenText = "<empty tree>" + RecognitionException.__init__(self, "Mismatched Token", + "<AST>", -1, -1) + + def appendTokenName(self, sb, tokenType): + if tokenType == INVALID_TYPE: + sb.append("<Set of tokens>") + elif tokenType < 0 or tokenType >= len(self.tokenNames): + sb.append("<" + str(tokenType) + ">") + else: + sb.append(self.tokenNames[tokenType]) + + ## + # Returns an error message with line number/column information + # + def __str__(self): + sb = [''] + sb.append(RecognitionException.__str__(self)) + + if self.mismatchType == MismatchedTokenException.TOKEN: + sb.append("expecting ") + self.appendTokenName(sb, self.expecting) + sb.append(", found " + self.tokenText) + elif self.mismatchType == MismatchedTokenException.NOT_TOKEN: + sb.append("expecting anything but '") + self.appendTokenName(sb, self.expecting) + sb.append("'; got it anyway") + elif self.mismatchType in [MismatchedTokenException.RANGE, MismatchedTokenException.NOT_RANGE]: + sb.append("expecting token ") + if self.mismatchType == MismatchedTokenException.NOT_RANGE: + sb.append("NOT ") + sb.append("in range: ") + self.appendTokenName(sb, self.expecting) + sb.append("..") + self.appendTokenName(sb, self.upper) + sb.append(", found " + self.tokenText) + elif self.mismatchType in [MismatchedTokenException.SET, MismatchedTokenException.NOT_SET]: + sb.append("expecting ") + if self.mismatchType == MismatchedTokenException.NOT_SET: + sb.append("NOT ") + sb.append("one of (") + for i in range(len(self.set)): + self.appendTokenName(sb, self.set[i]) + sb.append("), found " + self.tokenText) + + return str().join(sb).strip() + + __repr__ = __str__ + + +class TokenStreamException(ANTLRException): + + def __init__(self, *args): + ANTLRException.__init__(self, *args) + + +# Wraps an Exception in a TokenStreamException +class TokenStreamIOException(TokenStreamException): + + def __init__(self, *args): + if args and isinstance(args[0], Exception): + io = args[0] + TokenStreamException.__init__(self, str(io)) + self.io = io + else: + TokenStreamException.__init__(self, *args) + self.io = self + + +# Wraps a RecognitionException in a TokenStreamException +class TokenStreamRecognitionException(TokenStreamException): + + def __init__(self, *args): + if args and isinstance(args[0], RecognitionException): + recog = args[0] + TokenStreamException.__init__(self, str(recog)) + self.recog = recog + else: + raise TypeError("TokenStreamRecognitionException requires RecognitionException argument") + + def __str__(self): + return str(self.recog) + + __repr__ = __str__ + + +class TokenStreamRetryException(TokenStreamException): + + def __init__(self, *args): + TokenStreamException.__init__(self, *args) + + +class CharStreamException(ANTLRException): + + def __init__(self, *args): + ANTLRException.__init__(self, *args) + + +# Wraps an Exception in a CharStreamException +class CharStreamIOException(CharStreamException): + + def __init__(self, *args): + if args and isinstance(args[0], Exception): + io = args[0] + CharStreamException.__init__(self, str(io)) + self.io = io + else: + CharStreamException.__init__(self, *args) + self.io = self + + +class TryAgain(Exception): + pass + + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### Token ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class Token(object): + SKIP = -1 + INVALID_TYPE = 0 + EOF_TYPE = 1 + EOF = 1 + NULL_TREE_LOOKAHEAD = 3 + MIN_USER_TYPE = 4 + + def __init__(self,**argv): + try: + self.type = argv['type'] + except: + self.type = INVALID_TYPE + try: + self.text = argv['text'] + except: + self.text = "<no text>" + + def isEOF(self): + return (self.type == EOF_TYPE) + + def getColumn(self): + return 0 + + def getLine(self): + return 0 + + def getFilename(self): + return None + + def setFilename(self,name): + return self + + def getText(self): + return "<no text>" + + def setText(self,text): + if is_string_type(text): + pass + else: + raise TypeError("Token.setText requires string argument") + return self + + def setColumn(self,column): + return self + + def setLine(self,line): + return self + + def getType(self): + return self.type + + def setType(self,type): + if isinstance(type,int): + self.type = type + else: + raise TypeError("Token.setType requires integer argument") + return self + + def toString(self): + ## not optimal + type_ = self.type + if type_ == 3: + tval = 'NULL_TREE_LOOKAHEAD' + elif type_ == 1: + tval = 'EOF_TYPE' + elif type_ == 0: + tval = 'INVALID_TYPE' + elif type_ == -1: + tval = 'SKIP' + else: + tval = type_ + return '["%s",<%s>]' % (self.getText(),tval) + + __str__ = toString + __repr__ = toString + +### static attribute .. +Token.badToken = Token( type=INVALID_TYPE, text="<no text>") + +if __name__ == "__main__": + print("testing ..") + T = Token.badToken + print(T) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CommonToken ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class CommonToken(Token): + + def __init__(self,**argv): + Token.__init__(self,**argv) + self.line = 0 + self.col = 0 + try: + self.line = argv['line'] + except: + pass + try: + self.col = argv['col'] + except: + pass + + def getLine(self): + return self.line + + def getText(self): + return self.text + + def getColumn(self): + return self.col + + def setLine(self,line): + self.line = line + return self + + def setText(self,text): + self.text = text + return self + + def setColumn(self,col): + self.col = col + return self + + def toString(self): + ## not optimal + type_ = self.type + if type_ == 3: + tval = 'NULL_TREE_LOOKAHEAD' + elif type_ == 1: + tval = 'EOF_TYPE' + elif type_ == 0: + tval = 'INVALID_TYPE' + elif type_ == -1: + tval = 'SKIP' + else: + tval = type_ + d = { + 'text' : self.text, + 'type' : tval, + 'line' : self.line, + 'colm' : self.col + } + + fmt = '["%(text)s",<%(type)s>,line=%(line)s,col=%(colm)s]' + return fmt % d + + __str__ = toString + __repr__ = toString + + +if __name__ == '__main__' : + T = CommonToken() + print(T) + T = CommonToken(col=15,line=1,text="some text", type=5) + print(T) + T = CommonToken() + T.setLine(1).setColumn(15).setText("some text").setType(5) + print(T) + print(T.getLine()) + print(T.getColumn()) + print(T.getText()) + print(T.getType()) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CommonHiddenStreamToken ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class CommonHiddenStreamToken(CommonToken): + def __init__(self,*args): + CommonToken.__init__(self,*args) + self.hiddenBefore = None + self.hiddenAfter = None + + def getHiddenAfter(self): + return self.hiddenAfter + + def getHiddenBefore(self): + return self.hiddenBefore + + def setHiddenAfter(self,t): + self.hiddenAfter = t + + def setHiddenBefore(self, t): + self.hiddenBefore = t + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### Queue ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +## Shall be a circular buffer on tokens .. +class Queue(object): + + def __init__(self): + self.buffer = [] # empty list + + def append(self,item): + self.buffer.append(item) + + def elementAt(self,index): + return self.buffer[index] + + def reset(self): + self.buffer = [] + + def removeFirst(self): + self.buffer.pop(0) + + def length(self): + return len(self.buffer) + + def __str__(self): + return str(self.buffer) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### InputBuffer ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class InputBuffer(object): + def __init__(self): + self.nMarkers = 0 + self.markerOffset = 0 + self.numToConsume = 0 + self.queue = Queue() + + def __str__(self): + return "(%s,%s,%s,%s)" % ( + self.nMarkers, + self.markerOffset, + self.numToConsume, + self.queue) + + def __repr__(self): + return str(self) + + def commit(self): + self.nMarkers -= 1 + + def consume(self) : + self.numToConsume += 1 + + ## probably better to return a list of items + ## because of unicode. Or return a unicode + ## string .. + def getLAChars(self) : + i = self.markerOffset + n = self.queue.length() + s = '' + while i<n: + s += self.queue.elementAt(i) + return s + + ## probably better to return a list of items + ## because of unicode chars + def getMarkedChars(self) : + s = '' + i = 0 + n = self.markerOffset + while i<n: + s += self.queue.elementAt(i) + return s + + def isMarked(self) : + return self.nMarkers != 0 + + def fill(self,k): + ### abstract method + raise NotImplementedError() + + def LA(self,k) : + self.fill(k) + return self.queue.elementAt(self.markerOffset + k - 1) + + def mark(self) : + self.syncConsume() + self.nMarkers += 1 + return self.markerOffset + + def rewind(self,mark) : + self.syncConsume() + self.markerOffset = mark + self.nMarkers -= 1 + + def reset(self) : + self.nMarkers = 0 + self.markerOffset = 0 + self.numToConsume = 0 + self.queue.reset() + + def syncConsume(self) : + while self.numToConsume > 0: + if self.nMarkers > 0: + # guess mode -- leave leading characters and bump offset. + self.markerOffset += 1 + else: + # normal mode -- remove first character + self.queue.removeFirst() + self.numToConsume -= 1 + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CharBuffer ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class CharBuffer(InputBuffer): + def __init__(self,reader): + ##assert isinstance(reader,file) + super(CharBuffer,self).__init__() + ## a reader is supposed to be anything that has + ## a method 'read(int)'. + self.input = reader + + def __str__(self): + base = super(CharBuffer,self).__str__() + return "CharBuffer{%s,%s" % (base,str(input)) + + def fill(self,amount): + try: + self.syncConsume() + while self.queue.length() < (amount + self.markerOffset) : + ## retrieve just one char - what happend at end + ## of input? + c = self.input.read(1) + ### python's behaviour is to return the empty string on + ### EOF, ie. no exception whatsoever is thrown. An empty + ### python string has the nice feature that it is of + ### type 'str' and "not ''" would return true. Contrary, + ### one can't do this: '' in 'abc'. This should return + ### false, but all we get is then a TypeError as an + ### empty string is not a character. + + ### Let's assure then that we have either seen a + ### character or an empty string (EOF). + assert len(c) == 0 or len(c) == 1 + + ### And it shall be of type string (ASCII or UNICODE). + assert is_string_type(c) + + ### Just append EOF char to buffer. Note that buffer may + ### contain then just more than one EOF char .. + + ### use unicode chars instead of ASCII .. + self.queue.append(c) + except Exception as e: + raise CharStreamIOException(e) + ##except: # (mk) Cannot happen ... + ##error ("unexpected exception caught ..") + ##assert 0 + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### LexerSharedInputState ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class LexerSharedInputState(object): + def __init__(self,ibuf): + assert isinstance(ibuf,InputBuffer) + self.input = ibuf + self.column = 1 + self.line = 1 + self.tokenStartColumn = 1 + self.tokenStartLine = 1 + self.guessing = 0 + self.filename = None + + def reset(self): + self.column = 1 + self.line = 1 + self.tokenStartColumn = 1 + self.tokenStartLine = 1 + self.guessing = 0 + self.filename = None + self.input.reset() + + def LA(self,k): + return self.input.LA(k) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TokenStream ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TokenStream(object): + def nextToken(self): + pass + + def __iter__(self): + return TokenStreamIterator(self) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TokenStreamIterator ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TokenStreamIterator(object): + def __init__(self,inst): + if isinstance(inst,TokenStream): + self.inst = inst + return + raise TypeError("TokenStreamIterator requires TokenStream object") + + def next(self): + assert self.inst + item = self.inst.nextToken() + if not item or item.isEOF(): + raise StopIteration() + return item + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TokenStreamSelector ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TokenStreamSelector(TokenStream): + + def __init__(self): + self._input = None + self._stmap = {} + self._stack = [] + + def addInputStream(self,stream,key): + self._stmap[key] = stream + + def getCurrentStream(self): + return self._input + + def getStream(self,sname): + try: + stream = self._stmap[sname] + except: + raise ValueError("TokenStream " + sname + " not found"); + return stream; + + def nextToken(self): + while 1: + try: + return self._input.nextToken() + except TokenStreamRetryException: + ### just retry "forever" + pass + + def pop(self): + stream = self._stack.pop(); + self.select(stream); + return stream; + + def push(self,arg): + self._stack.append(self._input); + self.select(arg) + + def retry(self): + raise TokenStreamRetryException() + + def select(self,arg): + if isinstance(arg,TokenStream): + self._input = arg + return + if is_string_type(arg): + self._input = self.getStream(arg) + return + raise TypeError("TokenStreamSelector.select requires " + + "TokenStream or string argument") + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TokenStreamBasicFilter ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TokenStreamBasicFilter(TokenStream): + + def __init__(self,input): + + self.input = input; + self.discardMask = BitSet() + + def discard(self,arg): + if isinstance(arg,int): + self.discardMask.add(arg) + return + if isinstance(arg,BitSet): + self.discardMark = arg + return + raise TypeError("TokenStreamBasicFilter.discard requires" + + "integer or BitSet argument") + + def nextToken(self): + tok = self.input.nextToken() + while tok and self.discardMask.member(tok.getType()): + tok = self.input.nextToken() + return tok + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TokenStreamHiddenTokenFilter ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TokenStreamHiddenTokenFilter(TokenStreamBasicFilter): + + def __init__(self,input): + TokenStreamBasicFilter.__init__(self,input) + self.hideMask = BitSet() + self.nextMonitoredToken = None + self.lastHiddenToken = None + self.firstHidden = None + + def consume(self): + self.nextMonitoredToken = self.input.nextToken() + + def consumeFirst(self): + self.consume() + + p = None; + while self.hideMask.member(self.LA(1).getType()) or \ + self.discardMask.member(self.LA(1).getType()): + if self.hideMask.member(self.LA(1).getType()): + if not p: + p = self.LA(1) + else: + p.setHiddenAfter(self.LA(1)) + self.LA(1).setHiddenBefore(p) + p = self.LA(1) + self.lastHiddenToken = p + if not self.firstHidden: + self.firstHidden = p + self.consume() + + def getDiscardMask(self): + return self.discardMask + + def getHiddenAfter(self,t): + return t.getHiddenAfter() + + def getHiddenBefore(self,t): + return t.getHiddenBefore() + + def getHideMask(self): + return self.hideMask + + def getInitialHiddenToken(self): + return self.firstHidden + + def hide(self,m): + if isinstance(m,int): + self.hideMask.add(m) + return + if isinstance(m.BitMask): + self.hideMask = m + return + + def LA(self,i): + return self.nextMonitoredToken + + def nextToken(self): + if not self.LA(1): + self.consumeFirst() + + monitored = self.LA(1) + + monitored.setHiddenBefore(self.lastHiddenToken) + self.lastHiddenToken = None + + self.consume() + p = monitored + + while self.hideMask.member(self.LA(1).getType()) or \ + self.discardMask.member(self.LA(1).getType()): + if self.hideMask.member(self.LA(1).getType()): + p.setHiddenAfter(self.LA(1)) + if p != monitored: + self.LA(1).setHiddenBefore(p) + p = self.lastHiddenToken = self.LA(1) + self.consume() + return monitored + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### StringBuffer ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class StringBuffer: + def __init__(self,string=None): + if string: + self.text = list(string) + else: + self.text = [] + + def setLength(self,sz): + if not sz : + self.text = [] + return + assert sz>0 + if sz >= self.length(): + return + ### just reset to empty buffer + self.text = self.text[0:sz] + + def length(self): + return len(self.text) + + def append(self,c): + self.text.append(c) + + ### return buffer as string. Arg 'a' is used as index + ## into the buffer and 2nd argument shall be the length. + ## If 2nd args is absent, we return chars till end of + ## buffer starting with 'a'. + def getString(self,a=None,length=None): + if not a : + a = 0 + assert a>=0 + if a>= len(self.text) : + return "" + + if not length: + ## no second argument + L = self.text[a:] + else: + assert (a+length) <= len(self.text) + b = a + length + L = self.text[a:b] + s = "" + for x in L : s += x + return s + + toString = getString ## alias + + def __str__(self): + return str(self.text) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### Reader ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +## When reading Japanese chars, it happens that a stream returns a +## 'char' of length 2. This looks like a bug in the appropriate +## codecs - but I'm rather unsure about this. Anyway, if this is +## the case, I'm going to split this string into a list of chars +## and put them on hold, ie. on a buffer. Next time when called +## we read from buffer until buffer is empty. +## wh: nov, 25th -> problem does not appear in Python 2.4.0.c1. + +class Reader(object): + def __init__(self,stream): + self.cin = stream + self.buf = [] + + def read(self,num): + assert num==1 + + if len(self.buf): + return self.buf.pop() + + ## Read a char - this may return a string. + ## Is this a bug in codecs/Python? + c = self.cin.read(1) + + if not c or len(c)==1: + return c + + L = list(c) + L.reverse() + for x in L: + self.buf.append(x) + + ## read one char .. + return self.read(1) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CharScanner ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class CharScanner(TokenStream): + ## class members + NO_CHAR = 0 + EOF_CHAR = '' ### EOF shall be the empty string. + + def __init__(self, *argv, **kwargs): + super(CharScanner, self).__init__() + self.saveConsumedInput = True + self.tokenClass = None + self.caseSensitive = True + self.caseSensitiveLiterals = True + self.literals = None + self.tabsize = 8 + self._returnToken = None + self.commitToPath = False + self.traceDepth = 0 + self.text = StringBuffer() + self.hashString = hash(self) + self.setTokenObjectClass(CommonToken) + self.setInput(*argv) + + def __iter__(self): + return CharScannerIterator(self) + + def setInput(self,*argv): + ## case 1: + ## if there's no arg we default to read from + ## standard input + if not argv: + import sys + self.setInput(sys.stdin) + return + + ## get 1st argument + arg1 = argv[0] + + ## case 2: + ## if arg1 is a string, we assume it's a file name + ## and open a stream using 2nd argument as open + ## mode. If there's no 2nd argument we fall back to + ## mode '+rb'. + if is_string_type(arg1): + f = open(arg1,"rb") + self.setInput(f) + self.setFilename(arg1) + return + + ## case 3: + ## if arg1 is a file we wrap it by a char buffer ( + ## some additional checks?? No, can't do this in + ## general). + if isinstance(arg1,file): + self.setInput(CharBuffer(arg1)) + return + + ## case 4: + ## if arg1 is of type SharedLexerInputState we use + ## argument as is. + if isinstance(arg1,LexerSharedInputState): + self.inputState = arg1 + return + + ## case 5: + ## check whether argument type is of type input + ## buffer. If so create a SharedLexerInputState and + ## go ahead. + if isinstance(arg1,InputBuffer): + self.setInput(LexerSharedInputState(arg1)) + return + + ## case 6: + ## check whether argument type has a method read(int) + ## If so create CharBuffer ... + try: + if arg1.read: + rd = Reader(arg1) + cb = CharBuffer(rd) + ss = LexerSharedInputState(cb) + self.inputState = ss + return + except: + pass + + ## case 7: + ## raise wrong argument exception + raise TypeError(argv) + + def setTabSize(self,size) : + self.tabsize = size + + def getTabSize(self) : + return self.tabsize + + def setCaseSensitive(self,t) : + self.caseSensitive = t + + def setCommitToPath(self,commit) : + self.commitToPath = commit + + def setFilename(self,f) : + self.inputState.filename = f + + def setLine(self,line) : + self.inputState.line = line + + def setText(self,s) : + self.resetText() + self.text.append(s) + + def getCaseSensitive(self) : + return self.caseSensitive + + def getCaseSensitiveLiterals(self) : + return self.caseSensitiveLiterals + + def getColumn(self) : + return self.inputState.column + + def setColumn(self,c) : + self.inputState.column = c + + def getCommitToPath(self) : + return self.commitToPath + + def getFilename(self) : + return self.inputState.filename + + def getInputBuffer(self) : + return self.inputState.input + + def getInputState(self) : + return self.inputState + + def setInputState(self,state) : + assert isinstance(state,LexerSharedInputState) + self.inputState = state + + def getLine(self) : + return self.inputState.line + + def getText(self) : + return str(self.text) + + def getTokenObject(self) : + return self._returnToken + + def LA(self,i) : + c = self.inputState.input.LA(i) + if not self.caseSensitive: + ### E0006 + c = c.__class__.lower(c) + return c + + def makeToken(self,type) : + try: + ## dynamically load a class + assert self.tokenClass + tok = self.tokenClass() + tok.setType(type) + tok.setColumn(self.inputState.tokenStartColumn) + tok.setLine(self.inputState.tokenStartLine) + return tok + except: + self.panic("unable to create new token") + return Token.badToken + + def mark(self) : + return self.inputState.input.mark() + + def _match_bitset(self,b) : + if b.member(self.LA(1)): + self.consume() + else: + raise MismatchedCharException(self.LA(1), b, False, self) + + def _match_string(self,s) : + for c in s: + if self.LA(1) == c: + self.consume() + else: + raise MismatchedCharException(self.LA(1), c, False, self) + + def match(self,item): + if is_string_type(item): + return self._match_string(item) + else: + return self._match_bitset(item) + + def matchNot(self,c) : + if self.LA(1) != c: + self.consume() + else: + raise MismatchedCharException(self.LA(1), c, True, self) + + def matchRange(self,c1,c2) : + if self.LA(1) < c1 or self.LA(1) > c2 : + raise MismatchedCharException(self.LA(1), c1, c2, False, self) + else: + self.consume() + + def newline(self) : + self.inputState.line += 1 + self.inputState.column = 1 + + def tab(self) : + c = self.getColumn() + nc = ( ((c-1)/self.tabsize) + 1) * self.tabsize + 1 + self.setColumn(nc) + + def panic(self,s='') : + print("CharScanner: panic: " + s) + sys.exit(1) + + def reportError(self,s) : + if not self.getFilename(): + print("error: " + str(s)) + else: + print(self.getFilename() + ": error: " + str(s)) + + def reportWarning(self,s) : + if not self.getFilename(): + print("warning: " + str(s)) + else: + print(self.getFilename() + ": warning: " + str(s)) + + def resetText(self) : + self.text.setLength(0) + self.inputState.tokenStartColumn = self.inputState.column + self.inputState.tokenStartLine = self.inputState.line + + def rewind(self,pos) : + self.inputState.input.rewind(pos) + + def setTokenObjectClass(self,cl): + self.tokenClass = cl + + def testForLiteral(self,token): + if not token: + return + assert isinstance(token,Token) + + _type = token.getType() + + ## special tokens can't be literals + if _type in [SKIP,INVALID_TYPE,EOF_TYPE,NULL_TREE_LOOKAHEAD] : + return + + _text = token.getText() + if not _text: + return + + assert is_string_type(_text) + _type = self.testLiteralsTable(_text,_type) + token.setType(_type) + return _type + + def testLiteralsTable(self,*args): + if is_string_type(args[0]): + s = args[0] + i = args[1] + else: + s = self.text.getString() + i = args[0] + + ## check whether integer has been given + if not isinstance(i,int): + assert isinstance(i,int) + + ## check whether we have a dict + assert isinstance(self.literals,dict) + try: + ## E0010 + if not self.caseSensitiveLiterals: + s = s.__class__.lower(s) + i = self.literals[s] + except: + pass + return i + + def toLower(self,c): + return c.__class__.lower() + + def traceIndent(self): + print(' ' * self.traceDepth) + + def traceIn(self,rname): + self.traceDepth += 1 + self.traceIndent() + print("> lexer %s c== %s" % (rname,self.LA(1))) + + def traceOut(self,rname): + self.traceIndent() + print("< lexer %s c== %s" % (rname,self.LA(1))) + self.traceDepth -= 1 + + def uponEOF(self): + pass + + def append(self,c): + if self.saveConsumedInput : + self.text.append(c) + + def commit(self): + self.inputState.input.commit() + + def consume(self): + if not self.inputState.guessing: + c = self.LA(1) + if self.caseSensitive: + self.append(c) + else: + # use input.LA(), not LA(), to get original case + # CharScanner.LA() would toLower it. + c = self.inputState.input.LA(1) + self.append(c) + + if c and c in "\t": + self.tab() + else: + self.inputState.column += 1 + self.inputState.input.consume() + + ## Consume chars until one matches the given char + def consumeUntil_char(self,c): + while self.LA(1) != EOF_CHAR and self.LA(1) != c: + self.consume() + + ## Consume chars until one matches the given set + def consumeUntil_bitset(self,bitset): + while self.LA(1) != EOF_CHAR and not self.set.member(self.LA(1)): + self.consume() + + ### If symbol seen is EOF then generate and set token, otherwise + ### throw exception. + def default(self,la1): + if not la1 : + self.uponEOF() + self._returnToken = self.makeToken(EOF_TYPE) + else: + self.raise_NoViableAlt(la1) + + def filterdefault(self,la1,*args): + if not la1: + self.uponEOF() + self._returnToken = self.makeToken(EOF_TYPE) + return + + if not args: + self.consume() + raise TryAgain() + else: + ### apply filter object + self.commit(); + try: + func=args[0] + func(*args[1:]) + except RecognitionException as e: + ## catastrophic failure + self.reportError(e); + self.consume(); + raise TryAgain() + + def raise_NoViableAlt(self,la1=None): + if not la1: la1 = self.LA(1) + fname = self.getFilename() + line = self.getLine() + col = self.getColumn() + raise NoViableAltForCharException(la1,fname,line,col) + + def set_return_token(self,_create,_token,_ttype,_offset): + if _create and not _token and (not _ttype == SKIP): + string = self.text.getString(_offset) + _token = self.makeToken(_ttype) + _token.setText(string) + self._returnToken = _token + return _token + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CharScannerIterator ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class CharScannerIterator: + + def __init__(self,inst): + if isinstance(inst,CharScanner): + self.inst = inst + return + raise TypeError("CharScannerIterator requires CharScanner object") + + def next(self): + assert self.inst + item = self.inst.nextToken() + if not item or item.isEOF(): + raise StopIteration() + return item + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### BitSet ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +### I'm assuming here that a long is 64bits. It appears however, that +### a long is of any size. That means we can use a single long as the +### bitset (!), ie. Python would do almost all the work (TBD). + +class BitSet(object): + BITS = 64 + NIBBLE = 4 + LOG_BITS = 6 + MOD_MASK = BITS -1 + + def __init__(self,data=None): + if not data: + BitSet.__init__(self,[long(0)]) + return + if isinstance(data,int): + BitSet.__init__(self,[long(data)]) + return + if isinstance(data,long): + BitSet.__init__(self,[data]) + return + if not isinstance(data,list): + raise TypeError("BitSet requires integer, long, or " + + "list argument") + for x in data: + if not isinstance(x, int_types): + raise TypeError(self,"List argument item is " + + "not a long: %s" % (x)) + self.data = data + + def __str__(self): + bits = len(self.data) * BitSet.BITS + s = "" + for i in xrange(0,bits): + if self.at(i): + s += "1" + else: + s += "o" + if not ((i+1) % 10): + s += '|%s|' % (i+1) + return s + + def __repr__(self): + return str(self) + + def member(self,item): + if not item: + return False + + if isinstance(item,int): + return self.at(item) + + if not is_string_type(item): + raise TypeError(self,"char or unichar expected: %s" % (item)) + + ## char is a (unicode) string with at most lenght 1, ie. + ## a char. + + if len(item) != 1: + raise TypeError(self,"char expected: %s" % (item)) + + ### handle ASCII/UNICODE char + num = ord(item) + + ### check whether position num is in bitset + return self.at(num) + + def wordNumber(self,bit): + return bit >> BitSet.LOG_BITS + + def bitMask(self,bit): + pos = bit & BitSet.MOD_MASK ## bit mod BITS + return (1 << pos) + + def set(self,bit,on=True): + # grow bitset as required (use with care!) + i = self.wordNumber(bit) + mask = self.bitMask(bit) + if i>=len(self.data): + d = i - len(self.data) + 1 + for x in xrange(0,d): + self.data.append(0) + assert len(self.data) == i+1 + if on: + self.data[i] |= mask + else: + self.data[i] &= (~mask) + + ### make add an alias for set + add = set + + def off(self,bit,off=True): + self.set(bit,not off) + + def at(self,bit): + i = self.wordNumber(bit) + v = self.data[i] + m = self.bitMask(bit) + return v & m + + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### some further funcs ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +def illegalarg_ex(func): + raise ValueError( + "%s is only valid if parser is built for debugging" % + (func.func_name)) + +def runtime_ex(func): + raise RuntimeError( + "%s is only valid if parser is built for debugging" % + (func.func_name)) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TokenBuffer ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TokenBuffer(object): + def __init__(self,stream): + self.input = stream + self.nMarkers = 0 + self.markerOffset = 0 + self.numToConsume = 0 + self.queue = Queue() + + def reset(self) : + self.nMarkers = 0 + self.markerOffset = 0 + self.numToConsume = 0 + self.queue.reset() + + def consume(self) : + self.numToConsume += 1 + + def fill(self, amount): + self.syncConsume() + while self.queue.length() < (amount + self.markerOffset): + self.queue.append(self.input.nextToken()) + + def getInput(self): + return self.input + + def LA(self,k) : + self.fill(k) + return self.queue.elementAt(self.markerOffset + k - 1).type + + def LT(self,k) : + self.fill(k) + return self.queue.elementAt(self.markerOffset + k - 1) + + def mark(self) : + self.syncConsume() + self.nMarkers += 1 + return self.markerOffset + + def rewind(self,mark) : + self.syncConsume() + self.markerOffset = mark + self.nMarkers -= 1 + + def syncConsume(self) : + while self.numToConsume > 0: + if self.nMarkers > 0: + # guess mode -- leave leading characters and bump offset. + self.markerOffset += 1 + else: + # normal mode -- remove first character + self.queue.removeFirst() + self.numToConsume -= 1 + + def __str__(self): + return "(%s,%s,%s,%s,%s)" % ( + self.input, + self.nMarkers, + self.markerOffset, + self.numToConsume, + self.queue) + + def __repr__(self): + return str(self) + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### ParserSharedInputState ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class ParserSharedInputState(object): + + def __init__(self): + self.input = None + self.reset() + + def reset(self): + self.guessing = 0 + self.filename = None + if self.input: + self.input.reset() + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### Parser ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class Parser(object): + + def __init__(self, *args, **kwargs): + self.tokenNames = None + self.returnAST = None + self.astFactory = None + self.tokenTypeToASTClassMap = {} + self.ignoreInvalidDebugCalls = False + self.traceDepth = 0 + if not args: + self.inputState = ParserSharedInputState() + return + arg0 = args[0] + assert isinstance(arg0,ParserSharedInputState) + self.inputState = arg0 + return + + def getTokenTypeToASTClassMap(self): + return self.tokenTypeToASTClassMap + + + def addMessageListener(self, l): + if not self.ignoreInvalidDebugCalls: + illegalarg_ex(self.addMessageListener) + + def addParserListener(self,l) : + if (not self.ignoreInvalidDebugCalls) : + illegalarg_ex(self.addParserListener) + + def addParserMatchListener(self, l) : + if (not self.ignoreInvalidDebugCalls) : + illegalarg_ex(self.addParserMatchListener) + + def addParserTokenListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + illegalarg_ex(self.addParserTokenListener) + + def addSemanticPredicateListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + illegalarg_ex(self.addSemanticPredicateListener) + + def addSyntacticPredicateListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + illegalarg_ex(self.addSyntacticPredicateListener) + + def addTraceListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + illegalarg_ex(self.addTraceListener) + + def consume(self): + raise NotImplementedError() + + def _consumeUntil_type(self,tokenType): + while self.LA(1) != EOF_TYPE and self.LA(1) != tokenType: + self.consume() + + def _consumeUntil_bitset(self, set): + while self.LA(1) != EOF_TYPE and not set.member(self.LA(1)): + self.consume() + + def consumeUntil(self,arg): + if isinstance(arg,int): + self._consumeUntil_type(arg) + else: + self._consumeUntil_bitset(arg) + + def defaultDebuggingSetup(self): + pass + + def getAST(self) : + return self.returnAST + + def getASTFactory(self) : + return self.astFactory + + def getFilename(self) : + return self.inputState.filename + + def getInputState(self) : + return self.inputState + + def setInputState(self, state) : + self.inputState = state + + def getTokenName(self,num) : + return self.tokenNames[num] + + def getTokenNames(self) : + return self.tokenNames + + def isDebugMode(self) : + return self.false + + def LA(self, i): + raise NotImplementedError() + + def LT(self, i): + raise NotImplementedError() + + def mark(self): + return self.inputState.input.mark() + + def _match_int(self,t): + if (self.LA(1) != t): + raise MismatchedTokenException( + self.tokenNames, self.LT(1), t, False, self.getFilename()) + else: + self.consume() + + def _match_set(self, b): + if (not b.member(self.LA(1))): + raise MismatchedTokenException( + self.tokenNames,self.LT(1), b, False, self.getFilename()) + else: + self.consume() + + def match(self,set) : + if isinstance(set,int): + self._match_int(set) + return + if isinstance(set,BitSet): + self._match_set(set) + return + raise TypeError("Parser.match requires integer ot BitSet argument") + + def matchNot(self,t): + if self.LA(1) == t: + raise MismatchedTokenException( + self.tokenNames, self.LT(1), t, True, self.getFilename()) + else: + self.consume() + + def removeMessageListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeMessageListener) + + def removeParserListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeParserListener) + + def removeParserMatchListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeParserMatchListener) + + def removeParserTokenListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeParserTokenListener) + + def removeSemanticPredicateListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeSemanticPredicateListener) + + def removeSyntacticPredicateListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeSyntacticPredicateListener) + + def removeTraceListener(self, l) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.removeTraceListener) + + def reportError(self,x) : + fmt = "syntax error:" + f = self.getFilename() + if f: + fmt = ("%s:" % f) + fmt + if isinstance(x,Token): + line = x.getColumn() + col = x.getLine() + text = x.getText() + fmt = fmt + 'unexpected symbol at line %s (column %s) : "%s"' + print(fmt % (line,col,text), file=sys.stderr) + else: + print(fmt,str(x), file=sys.stderr) + + def reportWarning(self,s): + f = self.getFilename() + if f: + print("%s:warning: %s" % (f,str(s))) + else: + print("warning: %s" % (str(s))) + + def rewind(self, pos) : + self.inputState.input.rewind(pos) + + def setASTFactory(self, f) : + self.astFactory = f + + def setASTNodeClass(self, cl) : + self.astFactory.setASTNodeType(cl) + + def setASTNodeType(self, nodeType) : + self.setASTNodeClass(nodeType) + + def setDebugMode(self, debugMode) : + if (not self.ignoreInvalidDebugCalls): + runtime_ex(self.setDebugMode) + + def setFilename(self, f) : + self.inputState.filename = f + + def setIgnoreInvalidDebugCalls(self, value) : + self.ignoreInvalidDebugCalls = value + + def setTokenBuffer(self, t) : + self.inputState.input = t + + def traceIndent(self): + print(" " * self.traceDepth) + + def traceIn(self,rname): + self.traceDepth += 1 + self.trace("> ", rname) + + def traceOut(self,rname): + self.trace("< ", rname) + self.traceDepth -= 1 + + ### wh: moved from ASTFactory to Parser + def addASTChild(self,currentAST, child): + if not child: + return + if not currentAST.root: + currentAST.root = child + elif not currentAST.child: + currentAST.root.setFirstChild(child) + else: + currentAST.child.setNextSibling(child) + currentAST.child = child + currentAST.advanceChildToEnd() + + ### wh: moved from ASTFactory to Parser + def makeASTRoot(self,currentAST,root) : + if root: + ### Add the current root as a child of new root + root.addChild(currentAST.root) + ### The new current child is the last sibling of the old root + currentAST.child = currentAST.root + currentAST.advanceChildToEnd() + ### Set the new root + currentAST.root = root + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### LLkParser ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class LLkParser(Parser): + + def __init__(self, *args, **kwargs): + try: + arg1 = args[0] + except: + arg1 = 1 + + if isinstance(arg1,int): + super(LLkParser,self).__init__() + self.k = arg1 + return + + if isinstance(arg1,ParserSharedInputState): + super(LLkParser,self).__init__(arg1) + self.set_k(1,*args) + return + + if isinstance(arg1,TokenBuffer): + super(LLkParser,self).__init__() + self.setTokenBuffer(arg1) + self.set_k(1,*args) + return + + if isinstance(arg1,TokenStream): + super(LLkParser,self).__init__() + tokenBuf = TokenBuffer(arg1) + self.setTokenBuffer(tokenBuf) + self.set_k(1,*args) + return + + ### unknown argument + raise TypeError("LLkParser requires integer, " + + "ParserSharedInputStream or TokenStream argument") + + def consume(self): + self.inputState.input.consume() + + def LA(self,i): + return self.inputState.input.LA(i) + + def LT(self,i): + return self.inputState.input.LT(i) + + def set_k(self,index,*args): + try: + self.k = args[index] + except: + self.k = 1 + + def trace(self,ee,rname): + print(type(self)) + self.traceIndent() + guess = "" + if self.inputState.guessing > 0: + guess = " [guessing]" + print((ee + rname + guess)) + for i in xrange(1,self.k+1): + if i != 1: + print(", ") + if self.LT(i) : + v = self.LT(i).getText() + else: + v = "null" + print("LA(%s) == %s" % (i,v)) + print("\n") + + def traceIn(self,rname): + self.traceDepth += 1; + self.trace("> ", rname); + + def traceOut(self,rname): + self.trace("< ", rname); + self.traceDepth -= 1; + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TreeParserSharedInputState ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TreeParserSharedInputState(object): + def __init__(self): + self.guessing = 0 + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### TreeParser ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class TreeParser(object): + + def __init__(self, *args, **kwargs): + self.inputState = TreeParserSharedInputState() + self._retTree = None + self.tokenNames = [] + self.returnAST = None + self.astFactory = ASTFactory() + self.traceDepth = 0 + + def getAST(self): + return self.returnAST + + def getASTFactory(self): + return self.astFactory + + def getTokenName(self,num) : + return self.tokenNames[num] + + def getTokenNames(self): + return self.tokenNames + + def match(self,t,set) : + assert isinstance(set,int) or isinstance(set,BitSet) + if not t or t == ASTNULL: + raise MismatchedTokenException(self.getTokenNames(), t,set, False) + + if isinstance(set,int) and t.getType() != set: + raise MismatchedTokenException(self.getTokenNames(), t,set, False) + + if isinstance(set,BitSet) and not set.member(t.getType): + raise MismatchedTokenException(self.getTokenNames(), t,set, False) + + def matchNot(self,t, ttype) : + if not t or (t == ASTNULL) or (t.getType() == ttype): + raise MismatchedTokenException(self.getTokenNames(), t, ttype, True) + + def reportError(self,ex): + print("error:",ex, file=sys.stderr) + + def reportWarning(self, s): + print("warning:",s) + + def setASTFactory(self,f): + self.astFactory = f + + def setASTNodeType(self,nodeType): + self.setASTNodeClass(nodeType) + + def setASTNodeClass(self,nodeType): + self.astFactory.setASTNodeType(nodeType) + + def traceIndent(self): + print(" " * self.traceDepth) + + def traceIn(self,rname,t): + self.traceDepth += 1 + self.traceIndent() + print(("> " + rname + "(" + + ifelse(t,str(t),"null") + ")" + + ifelse(self.inputState.guessing>0,"[guessing]",""))) + + def traceOut(self,rname,t): + self.traceIndent() + print(("< " + rname + "(" + + ifelse(t,str(t),"null") + ")" + + ifelse(self.inputState.guessing>0,"[guessing]",""))) + self.traceDepth -= 1 + + ### wh: moved from ASTFactory to TreeParser + def addASTChild(self,currentAST, child): + if not child: + return + if not currentAST.root: + currentAST.root = child + elif not currentAST.child: + currentAST.root.setFirstChild(child) + else: + currentAST.child.setNextSibling(child) + currentAST.child = child + currentAST.advanceChildToEnd() + + ### wh: moved from ASTFactory to TreeParser + def makeASTRoot(self,currentAST,root): + if root: + ### Add the current root as a child of new root + root.addChild(currentAST.root) + ### The new current child is the last sibling of the old root + currentAST.child = currentAST.root + currentAST.advanceChildToEnd() + ### Set the new root + currentAST.root = root + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### funcs to work on trees ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +def rightmost(ast): + if ast: + while(ast.right): + ast = ast.right + return ast + +def cmptree(s,t,partial): + while(s and t): + ### as a quick optimization, check roots first. + if not s.equals(t): + return False + + ### if roots match, do full list match test on children. + if not cmptree(s.getFirstChild(),t.getFirstChild(),partial): + return False + + s = s.getNextSibling() + t = t.getNextSibling() + + r = ifelse(partial,not t,not s and not t) + return r + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### AST ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class AST(object): + def __init__(self): + pass + + def addChild(self, c): + pass + + def equals(self, t): + return False + + def equalsList(self, t): + return False + + def equalsListPartial(self, t): + return False + + def equalsTree(self, t): + return False + + def equalsTreePartial(self, t): + return False + + def findAll(self, tree): + return None + + def findAllPartial(self, subtree): + return None + + def getFirstChild(self): + return self + + def getNextSibling(self): + return self + + def getText(self): + return "" + + def getType(self): + return INVALID_TYPE + + def getLine(self): + return 0 + + def getColumn(self): + return 0 + + def getNumberOfChildren(self): + return 0 + + def initialize(self, t): + pass + + def setFirstChild(self, c): + pass + + def setNextSibling(self, n): + pass + + def setText(self, text): + pass + + def setType(self, ttype): + pass + + def toString(self): + self.getText() + + __str__ = toString + + def toStringList(self): + return self.getText() + + def toStringTree(self): + return self.getText() + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### ASTNULLType ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +### There is only one instance of this class **/ +class ASTNULLType(AST): + def __init__(self): + AST.__init__(self) + pass + + def getText(self): + return "<ASTNULL>" + + def getType(self): + return NULL_TREE_LOOKAHEAD + + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### BaseAST ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class BaseAST(AST): + + verboseStringConversion = False + tokenNames = None + + def __init__(self): + self.down = None ## kid + self.right = None ## sibling + + def addChild(self,node): + if node: + t = rightmost(self.down) + if t: + t.right = node + else: + assert not self.down + self.down = node + + def getNumberOfChildren(self): + t = self.down + n = 0 + while t: + n += 1 + t = t.right + return n + + def doWorkForFindAll(self,v,target,partialMatch): + sibling = self + + while sibling: + c1 = partialMatch and sibling.equalsTreePartial(target) + if c1: + v.append(sibling) + else: + c2 = not partialMatch and sibling.equalsTree(target) + if c2: + v.append(sibling) + + ### regardless of match or not, check any children for matches + if sibling.getFirstChild(): + sibling.getFirstChild().doWorkForFindAll(v,target,partialMatch) + + sibling = sibling.getNextSibling() + + ### Is node t equal to 'self' in terms of token type and text? + def equals(self,t): + if not t: + return False + return self.getText() == t.getText() and self.getType() == t.getType() + + ### Is t an exact structural and equals() match of this tree. The + ### 'self' reference is considered the start of a sibling list. + ### + def equalsList(self, t): + return cmptree(self, t, partial=False) + + ### Is 't' a subtree of this list? + ### The siblings of the root are NOT ignored. + ### + def equalsListPartial(self,t): + return cmptree(self,t,partial=True) + + ### Is tree rooted at 'self' equal to 't'? The siblings + ### of 'self' are ignored. + ### + def equalsTree(self, t): + return self.equals(t) and \ + cmptree(self.getFirstChild(), t.getFirstChild(), partial=False) + + ### Is 't' a subtree of the tree rooted at 'self'? The siblings + ### of 'self' are ignored. + ### + def equalsTreePartial(self, t): + if not t: + return True + return self.equals(t) and cmptree( + self.getFirstChild(), t.getFirstChild(), partial=True) + + ### Walk the tree looking for all exact subtree matches. Return + ### an ASTEnumerator that lets the caller walk the list + ### of subtree roots found herein. + def findAll(self,target): + roots = [] + + ### the empty tree cannot result in an enumeration + if not target: + return None + # find all matches recursively + self.doWorkForFindAll(roots, target, False) + return roots + + ### Walk the tree looking for all subtrees. Return + ### an ASTEnumerator that lets the caller walk the list + ### of subtree roots found herein. + def findAllPartial(self,sub): + roots = [] + + ### the empty tree cannot result in an enumeration + if not sub: + return None + + self.doWorkForFindAll(roots, sub, True) ### find all matches recursively + return roots + + ### Get the first child of this node None if not children + def getFirstChild(self): + return self.down + + ### Get the next sibling in line after this one + def getNextSibling(self): + return self.right + + ### Get the token text for this node + def getText(self): + return "" + + ### Get the token type for this node + def getType(self): + return 0 + + def getLine(self): + return 0 + + def getColumn(self): + return 0 + + ### Remove all children */ + def removeChildren(self): + self.down = None + + def setFirstChild(self,c): + self.down = c + + def setNextSibling(self, n): + self.right = n + + ### Set the token text for this node + def setText(self, text): + pass + + ### Set the token type for this node + def setType(self, ttype): + pass + + ### static + def setVerboseStringConversion(verbose,names): + verboseStringConversion = verbose + tokenNames = names + setVerboseStringConversion = staticmethod(setVerboseStringConversion) + + ### Return an array of strings that maps token ID to it's text. + ## @since 2.7.3 + def getTokenNames(): + return tokenNames + + def toString(self): + return self.getText() + + ### return tree as lisp string - sibling included + def toStringList(self): + ts = self.toStringTree() + sib = self.getNextSibling() + if sib: + ts += sib.toStringList() + return ts + + __str__ = toStringList + + ### return tree as string - siblings ignored + def toStringTree(self): + ts = "" + kid = self.getFirstChild() + if kid: + ts += " (" + ts += " " + self.toString() + if kid: + ts += kid.toStringList() + ts += " )" + return ts + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CommonAST ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +### Common AST node implementation +class CommonAST(BaseAST): + def __init__(self,token=None): + super(CommonAST,self).__init__() + self.ttype = INVALID_TYPE + self.text = "<no text>" + self.line = 0 + self.column= 0 + self.initialize(token) + #assert self.text + + ### Get the token text for this node + def getText(self): + return self.text + + ### Get the token type for this node + def getType(self): + return self.ttype + + ### Get the line for this node + def getLine(self): + return self.line + + ### Get the column for this node + def getColumn(self): + return self.column + + def initialize(self,*args): + if not args: + return + + arg0 = args[0] + + if isinstance(arg0,int): + arg1 = args[1] + self.setType(arg0) + self.setText(arg1) + return + + if isinstance(arg0,AST) or isinstance(arg0,Token): + self.setText(arg0.getText()) + self.setType(arg0.getType()) + self.line = arg0.getLine() + self.column = arg0.getColumn() + return + + ### Set the token text for this node + def setText(self,text_): + assert is_string_type(text_) + self.text = text_ + + ### Set the token type for this node + def setType(self,ttype_): + assert isinstance(ttype_,int) + self.ttype = ttype_ + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### CommonASTWithHiddenTokens ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class CommonASTWithHiddenTokens(CommonAST): + + def __init__(self,*args): + CommonAST.__init__(self,*args) + self.hiddenBefore = None + self.hiddenAfter = None + + def getHiddenAfter(self): + return self.hiddenAfter + + def getHiddenBefore(self): + return self.hiddenBefore + + def initialize(self,*args): + CommonAST.initialize(self,*args) + if args and isinstance(args[0],Token): + assert isinstance(args[0],CommonHiddenStreamToken) + self.hiddenBefore = args[0].getHiddenBefore() + self.hiddenAfter = args[0].getHiddenAfter() + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### ASTPair ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class ASTPair(object): + def __init__(self): + self.root = None ### current root of tree + self.child = None ### current child to which siblings are added + + ### Make sure that child is the last sibling */ + def advanceChildToEnd(self): + if self.child: + while self.child.getNextSibling(): + self.child = self.child.getNextSibling() + + ### Copy an ASTPair. Don't call it clone() because we want type-safety */ + def copy(self): + tmp = ASTPair() + tmp.root = self.root + tmp.child = self.child + return tmp + + def toString(self): + r = ifelse(not root,"null",self.root.getText()) + c = ifelse(not child,"null",self.child.getText()) + return "[%s,%s]" % (r,c) + + __str__ = toString + __repr__ = toString + + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### ASTFactory ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class ASTFactory(object): + def __init__(self,table=None): + self._class = None + self._classmap = ifelse(table,table,None) + + def create(self,*args): + if not args: + return self.create(INVALID_TYPE) + + arg0 = args[0] + arg1 = None + arg2 = None + + try: + arg1 = args[1] + arg2 = args[2] + except: + pass + + # ctor(int) + if isinstance(arg0,int) and not arg2: + ### get class for 'self' type + c = self.getASTNodeType(arg0) + t = self.create(c) + if t: + t.initialize(arg0, ifelse(arg1,arg1,"")) + return t + + # ctor(int,something) + if isinstance(arg0,int) and arg2: + t = self.create(arg2) + if t: + t.initialize(arg0,arg1) + return t + + # ctor(AST) + if isinstance(arg0,AST): + t = self.create(arg0.getType()) + if t: + t.initialize(arg0) + return t + + # ctor(token) + if isinstance(arg0,Token) and not arg1: + ttype = arg0.getType() + assert isinstance(ttype,int) + t = self.create(ttype) + if t: + t.initialize(arg0) + return t + + # ctor(token,class) + if isinstance(arg0,Token) and arg1: + assert isinstance(arg1,type) + assert issubclass(arg1,AST) + # this creates instance of 'arg1' using 'arg0' as + # argument. Wow, that's magic! + t = arg1(arg0) + assert t and isinstance(t,AST) + return t + + # ctor(class) + if isinstance(arg0,type): + ### next statement creates instance of type (!) + t = arg0() + assert isinstance(t,AST) + return t + + + def setASTNodeClass(self,className=None): + if not className: + return + assert isinstance(className,type) + assert issubclass(className,AST) + self._class = className + + ### kind of misnomer - use setASTNodeClass instead. + setASTNodeType = setASTNodeClass + + def getASTNodeClass(self): + return self._class + + + + def getTokenTypeToASTClassMap(self): + return self._classmap + + def setTokenTypeToASTClassMap(self,amap): + self._classmap = amap + + def error(self, e): + import sys + print(e, file=sys.stderr) + + def setTokenTypeASTNodeType(self, tokenType, className): + """ + Specify a mapping between a token type and a (AST) class. + """ + if not self._classmap: + self._classmap = {} + + if not className: + try: + del self._classmap[tokenType] + except: + pass + else: + ### here we should also perform actions to ensure that + ### a. class can be loaded + ### b. class is a subclass of AST + ### + assert isinstance(className,type) + assert issubclass(className,AST) ## a & b + ### enter the class + self._classmap[tokenType] = className + + def getASTNodeType(self,tokenType): + """ + For a given token type return the AST node type. First we + lookup a mapping table, second we try _class + and finally we resolve to "antlr.CommonAST". + """ + + # first + if self._classmap: + try: + c = self._classmap[tokenType] + if c: + return c + except: + pass + # second + if self._class: + return self._class + + # default + return CommonAST + + ### methods that have been moved to file scope - just listed + ### here to be somewhat consistent with original API + def dup(self,t): + return dup(t,self) + + def dupList(self,t): + return dupList(t,self) + + def dupTree(self,t): + return dupTree(t,self) + + ### methods moved to other classes + ### 1. makeASTRoot -> Parser + ### 2. addASTChild -> Parser + + ### non-standard: create alias for longish method name + maptype = setTokenTypeASTNodeType + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### ASTVisitor ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +class ASTVisitor(object): + def __init__(self,*args): + pass + + def visit(self,ast): + pass + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### +### static methods and variables ### +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx### + +ASTNULL = ASTNULLType() + +### wh: moved from ASTFactory as there's nothing ASTFactory-specific +### in this method. +def make(*nodes): + if not nodes: + return None + + for i in xrange(0,len(nodes)): + node = nodes[i] + if node: + assert isinstance(node,AST) + + root = nodes[0] + tail = None + if root: + root.setFirstChild(None) + + for i in xrange(1,len(nodes)): + if not nodes[i]: + continue + if not root: + root = tail = nodes[i] + elif not tail: + root.setFirstChild(nodes[i]) + tail = root.getFirstChild() + else: + tail.setNextSibling(nodes[i]) + tail = tail.getNextSibling() + + ### Chase tail to last sibling + while tail.getNextSibling(): + tail = tail.getNextSibling() + return root + +def dup(t,factory): + if not t: + return None + + if factory: + dup_t = factory.create(t.__class__) + else: + raise TypeError("dup function requires ASTFactory argument") + dup_t.initialize(t) + return dup_t + +def dupList(t,factory): + result = dupTree(t,factory) + nt = result + while t: + ## for each sibling of the root + t = t.getNextSibling() + nt.setNextSibling(dupTree(t,factory)) + nt = nt.getNextSibling() + return result + +def dupTree(t,factory): + result = dup(t,factory) + if t: + result.setFirstChild(dupList(t.getFirstChild(),factory)) + return result + +###xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +### $Id$ + +# Local Variables: *** +# mode: python *** +# py-indent-offset: 4 *** +# End: *** diff --git a/.venv/lib/python3.9/site-packages/xlwt/compat.py b/.venv/lib/python3.9/site-packages/xlwt/compat.py new file mode 100644 index 00000000..4f77a574 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/compat.py @@ -0,0 +1,28 @@ +import sys + +PY3 = sys.version_info[0] >= 3 + +if PY3: + unicode = bytes.decode + unicode_type = str + basestring = str + xrange = range + int_types = (int,) + long = int + + def iteritems(d): + return iter(d.items()) + def itervalues(d): + return iter(d.values()) +else: + # Python 2 + unicode = unicode_type = unicode + basestring = basestring + xrange = xrange + int_types = (int, long) + long = long + + def iteritems(d): + return d.iteritems() + def itervalues(d): + return d.itervalues() diff --git a/.venv/lib/python3.9/site-packages/xlwt/excel-formula.g b/.venv/lib/python3.9/site-packages/xlwt/excel-formula.g new file mode 100644 index 00000000..d98d9b90 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/xlwt/excel-formula.g @@ -0,0 +1,374 @@ +header { + import struct + import Utils + from UnicodeUtils import upack1 + from ExcelMagic import * + + _RVAdelta = {"R": 0, "V": 0x20, "A": 0x40} + _RVAdeltaRef = {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20} + _RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0} + + + class FormulaParseException(Exception): + """ + An exception indicating that a Formula could not be successfully parsed. + """ +} + +header "ExcelFormulaParser.__init__" { + self.rpn = "" + self.sheet_references = [] + self.xcall_references = [] +} + +options { + language = "Python"; +} + +class ExcelFormulaParser extends Parser; +options { + k = 2; + defaultErrorHandler = false; + buildAST = false; +} + + +tokens { + TRUE_CONST; + FALSE_CONST; + STR_CONST; + NUM_CONST; + INT_CONST; + + FUNC_IF; + FUNC_CHOOSE; + NAME; + QUOTENAME; + + EQ; + NE; + GT; + LT; + GE; + LE; + + ADD; + SUB; + MUL; + DIV; + + POWER; + PERCENT; + + LP; + RP; + + LB; + RB; + + COLON; + COMMA; + SEMICOLON; + REF2D; + REF2D_R1C1; + BANG; +} + +formula + : expr["V"] + ; + +expr[arg_type] + : // {print "\n**expr %s" % arg_type} + prec0_expr[arg_type] + ( + ( + EQ { op = struct.pack('B', ptgEQ) } + | NE { op = struct.pack('B', ptgNE) } + | GT { op = struct.pack('B', ptgGT) } + | LT { op = struct.pack('B', ptgLT) } + | GE { op = struct.pack('B', ptgGE) } + | LE { op = struct.pack('B', ptgLE) } + ) + prec0_expr[arg_type] { self.rpn += op } + )* + ; + +prec0_expr[arg_type] + : prec1_expr[arg_type] + ( + ( + CONCAT { op = struct.pack('B', ptgConcat) } + ) + prec1_expr[arg_type] { self.rpn += op } + )* + ; + +prec1_expr[arg_type] + : // {print "**prec1_expr1 %s" % arg_type} + prec2_expr[arg_type] + // {print "**prec1_expr2 %s" % arg_type} + ( + ( + ADD { op = struct.pack('B', ptgAdd) } + | SUB { op = struct.pack('B', ptgSub) } + ) + // {print "**prec1_expr3 %s" % arg_type} + prec2_expr[arg_type] + { self.rpn += op; + // print "**prec1_expr4 %s" % arg_type + } + )* + ; + + +prec2_expr[arg_type] + : prec3_expr[arg_type] + ( + ( + MUL { op = struct.pack('B', ptgMul) } + | DIV { op = struct.pack('B', ptgDiv) } + ) + prec3_expr[arg_type] { self.rpn += op } + )* + ; + +prec3_expr[arg_type] + : prec4_expr[arg_type] + ( + ( + POWER { op = struct.pack('B', ptgPower) } + ) + prec4_expr[arg_type] { self.rpn += op } + )* + ; + +prec4_expr[arg_type] + : prec5_expr[arg_type] + ( + PERCENT { self.rpn += struct.pack('B', ptgPercent) } + )? + ; + +prec5_expr[arg_type] + : primary[arg_type] + | SUB primary[arg_type] { self.rpn += struct.pack('B', ptgUminus) } + ; + +primary[arg_type] + : TRUE_CONST + { + self.rpn += struct.pack("2B", ptgBool, 1) + } + | FALSE_CONST + { + self.rpn += struct.pack("2B", ptgBool, 0) + } + | str_tok:STR_CONST + { + self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\"")) + } + | int_tok:INT_CONST + { + // print "**int_const", int_tok.text + int_value = int(int_tok.text) + if int_value <= 65535: + self.rpn += struct.pack("<BH", ptgInt, int_value) + else: + self.rpn += struct.pack("<Bd", ptgNum, float(int_value)) + } + | num_tok:NUM_CONST + { + self.rpn += struct.pack("<Bd", ptgNum, float(num_tok.text)) + } + | ref2d_tok:REF2D + { + // print "**ref2d %s %s" % (ref2d_tok.text, arg_type) + r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text) + ptg = ptgRefR + _RVAdeltaRef[arg_type] + self.rpn += struct.pack("<B2H", ptg, r, c) + } + | ref2d1_tok:REF2D COLON ref2d2_tok:REF2D + { + r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text) + r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text) + ptg = ptgAreaR + _RVAdeltaArea[arg_type] + self.rpn += struct.pack("<B4H", ptg, r1, r2, c1, c2) + } + | sheet1 = sheet + { + sheet2 = sheet1 + } + ( COLON sheet2 = sheet )? BANG ref3d_ref2d: REF2D + { + ptg = ptgRef3dR + _RVAdeltaRef[arg_type] + rpn_ref2d = "" + r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text) + rpn_ref2d = struct.pack("<3H", 0x0000, r1, c1) + } + ( COLON ref3d_ref2d2: REF2D + { + ptg = ptgArea3dR + _RVAdeltaArea[arg_type] + r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text) + rpn_ref2d = struct.pack("<5H", 0x0000, r1, r2, c1, c2) + } + )? + { + self.rpn += struct.pack("<B", ptg) + self.sheet_references.append((sheet1, sheet2, len(self.rpn))) + self.rpn += rpn_ref2d + } + | FUNC_IF + LP expr["V"] (SEMICOLON | COMMA) + { + self.rpn += struct.pack("<BBH", ptgAttr, 0x02, 0) // tAttrIf + pos0 = len(self.rpn) - 2 + } + expr[arg_type] (SEMICOLON | COMMA) + { + self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 0) // tAttrSkip + pos1 = len(self.rpn) - 2 + self.rpn = self.rpn[:pos0] + struct.pack("<H", pos1-pos0) + self.rpn[pos0+2:] + } + expr[arg_type] RP + { + self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 3) // tAttrSkip + self.rpn += struct.pack("<BBH", ptgFuncVarR, 3, 1) // 3 = nargs, 1 = IF func + pos2 = len(self.rpn) + self.rpn = self.rpn[:pos1] + struct.pack("<H", pos2-(pos1+2)-1) + self.rpn[pos1+2:] + } + | FUNC_CHOOSE + { + arg_type = "R" + rpn_chunks = [] + } + LP expr["V"] // first argument (the selector) + { + rpn_start = len(self.rpn) + ref_markers = [len(self.sheet_references)] + } + ( + (SEMICOLON | COMMA) + { mark = len(self.rpn) } + ( + expr[arg_type] + | { self.rpn += struct.pack("B", ptgMissArg) } + ) + { + rpn_chunks.append(self.rpn[mark:]) + ref_markers.append(len(self.sheet_references)) + } + )* + RP + { + self.rpn = self.rpn[:rpn_start] + nc = len(rpn_chunks) + chunklens = [len(chunk) for chunk in rpn_chunks] + skiplens = [0] * nc + skiplens[-1] = 3 + for ic in xrange(nc-1, 0, -1): + skiplens[ic-1] = skiplens[ic] + chunklens[ic] + 4 + jump_pos = [2 * nc + 2] + for ic in xrange(nc): + jump_pos.append(jump_pos[-1] + chunklens[ic] + 4) + chunk_shift = 2 * nc + 6 // size of tAttrChoose + for ic in xrange(nc): + for refx in xrange(ref_markers[ic], ref_markers[ic+1]): + ref = self.sheet_references[refx] + self.sheet_references[refx] = (ref[0], ref[1], ref[2] + chunk_shift) + chunk_shift += 4 // size of tAttrSkip + choose_rpn = [] + choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x04, nc)) // 0x04 is tAttrChoose + choose_rpn.append(struct.pack("<%dH" % (nc+1), *jump_pos)) + for ic in xrange(nc): + choose_rpn.append(rpn_chunks[ic]) + choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x08, skiplens[ic])) // 0x08 is tAttrSkip + choose_rpn.append(struct.pack("<BBH", ptgFuncVarV, nc+1, 100)) // 100 is CHOOSE fn + self.rpn += "".join(choose_rpn) + } + | name_tok:NAME + { + raise Exception("[formula] found unexpected NAME token (%r)" % name_tok.txt) + // #### TODO: handle references to defined names here + } + | func_tok:NAME + { + func_toku = func_tok.text.upper() + if func_toku in all_funcs_by_name: + (opcode, + min_argc, + max_argc, + func_type, + arg_type_str) = all_funcs_by_name[func_toku] + arg_type_list = list(arg_type_str) + else: + raise Exception("[formula] unknown function (%s)" % func_tok.text) + // print "**func_tok1 %s %s" % (func_toku, func_type) + xcall = opcode < 0 + if xcall: + // The name of the add-in function is passed as the 1st arg + // of the hidden XCALL function + self.xcall_references.append((func_toku, len(self.rpn) + 1)) + self.rpn += struct.pack("<BHHH", + ptgNameXR, + 0xadde, // ##PATCHME## index to REF entry in EXTERNSHEET record + 0xefbe, // ##PATCHME## one-based index to EXTERNNAME record + 0x0000) // unused + } + LP arg_count = expr_list[arg_type_list, min_argc, max_argc] RP + { + if arg_count > max_argc or arg_count < min_argc: + raise Exception, "%d parameters for function: %s" % (arg_count, func_tok.text) + if xcall: + func_ptg = ptgFuncVarR + _RVAdelta[func_type] + self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) // 255 is magic XCALL function + elif min_argc == max_argc: + func_ptg = ptgFuncR + _RVAdelta[func_type] + self.rpn += struct.pack("<BH", func_ptg, opcode) + elif arg_count == 1 and func_tok.text.upper() == "SUM": + self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) // tAttrSum + else: + func_ptg = ptgFuncVarR + _RVAdelta[func_type] + self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode) + } + | LP expr[arg_type] RP + { + self.rpn += struct.pack("B", ptgParen) + } + ; + +expr_list[arg_type_list, min_argc, max_argc] returns [arg_cnt] + { + arg_cnt = 0 + arg_type = arg_type_list[arg_cnt] + // print "**expr_list1[%d] req=%s" % (arg_cnt, arg_type) + } + : expr[arg_type] { arg_cnt += 1 } + ( + { + if arg_cnt < len(arg_type_list): + arg_type = arg_type_list[arg_cnt] + else: + arg_type = arg_type_list[-1] + if arg_type == "+": + arg_type = arg_type_list[-2] + // print "**expr_list2[%d] req=%s" % (arg_cnt, arg_type) + } + (SEMICOLON | COMMA) + ( + expr[arg_type] + | { self.rpn += struct.pack("B", ptgMissArg) } + ) + { arg_cnt += 1 } + )* + | + ; + +sheet returns[ref] + : sheet_ref_name: NAME + { ref = sheet_ref_name.text } + | sheet_ref_int: INT_CONST + { ref = sheet_ref_int.text } + | sheet_ref_quote: QUOTENAME + { ref = sheet_ref_quote.text[1:-1].replace("''", "'") } + ;