From bab77ca8bcbd67db39c5650ec12695b7ae1f5307 Mon Sep 17 00:00:00 2001 From: youqiang Date: Tue, 15 May 2018 09:11:39 +0800 Subject: [PATCH] the revised initial version --- AUTHORS.rst | 75 + LICENCE.rst | 23 + MANIFEST.in | 10 + README.rst | 60 + bitbucket-pipelines.yml | 9 + doc/Makefile | 130 + doc/_static/.placeholder | 0 doc/changes.rst | 1224 +++++ doc/charts/area.png | Bin 0 -> 37749 bytes doc/charts/area.py | 37 + doc/charts/area.rst | 29 + doc/charts/area3D.png | Bin 0 -> 39610 bytes doc/charts/area3d.py | 38 + doc/charts/bar.png | Bin 0 -> 104913 bytes doc/charts/bar.py | 65 + doc/charts/bar.rst | 41 + doc/charts/bar3D.png | Bin 0 -> 28272 bytes doc/charts/bar3d.py | 29 + doc/charts/bubble.png | Bin 0 -> 59465 bytes doc/charts/bubble.py | 46 + doc/charts/bubble.rst | 13 + doc/charts/chart_layout.png | Bin 0 -> 79037 bytes doc/charts/chart_layout.py | 78 + doc/charts/chart_layout.rst | 71 + doc/charts/chart_layout_default.png | Bin 0 -> 11790 bytes doc/charts/chartsheet.png | Bin 0 -> 41321 bytes doc/charts/chartsheet.py | 27 + doc/charts/chartsheet.rst | 11 + doc/charts/doughnut.png | Bin 0 -> 94413 bytes doc/charts/doughnut.py | 55 + doc/charts/doughnut.rst | 12 + doc/charts/gauge.png | Bin 0 -> 27943 bytes doc/charts/gauge.py | 57 + doc/charts/gauge.rst | 15 + doc/charts/introduction.rst | 95 + doc/charts/limits_and_scaling.rst | 60 + doc/charts/limits_and_scaling_log.png | Bin 0 -> 57320 bytes doc/charts/limits_and_scaling_log.py | 67 + doc/charts/limits_and_scaling_minmax.png | Bin 0 -> 22311 bytes doc/charts/limits_and_scaling_minmax.py | 42 + doc/charts/limits_and_scaling_orientation.png | Bin 0 -> 75148 bytes doc/charts/limits_and_scaling_orientation.py | 60 + doc/charts/line.png | Bin 0 -> 125459 bytes doc/charts/line.py | 81 + doc/charts/line.rst | 33 + doc/charts/line3D.png | Bin 0 -> 43079 bytes doc/charts/line3D.py | 38 + doc/charts/pattern.png | Bin 0 -> 29891 bytes doc/charts/pattern.py | 45 + doc/charts/pattern.rst | 12 + doc/charts/pie.png | Bin 0 -> 38189 bytes doc/charts/pie.py | 68 + doc/charts/pie.rst | 45 + doc/charts/pie3D.png | Bin 0 -> 40590 bytes doc/charts/pie3D.py | 32 + doc/charts/projected-pie.png | Bin 0 -> 60325 bytes doc/charts/radar.png | Bin 0 -> 67116 bytes doc/charts/radar.py | 41 + doc/charts/radar.rst | 18 + doc/charts/scatter.png | Bin 0 -> 38199 bytes doc/charts/scatter.py | 38 + doc/charts/scatter.rst | 22 + doc/charts/secondary.png | Bin 0 -> 21534 bytes doc/charts/secondary.py | 43 + doc/charts/secondary.rst | 13 + doc/charts/stock.png | Bin 0 -> 92652 bytes doc/charts/stock.py | 102 + doc/charts/stock.rst | 44 + doc/charts/surface.png | Bin 0 -> 143344 bytes doc/charts/surface.py | 62 + doc/charts/surface.rst | 18 + doc/comments.rst | 95 + doc/conf.py | 311 ++ doc/defined_names.rst | 27 + doc/development.rst | 199 + doc/example.py | 18 + doc/filters.png | Bin 0 -> 25958 bytes doc/filters.py | 31 + doc/filters.rst | 19 + doc/format_merged_cells.py | 59 + doc/formatting.rst | 189 + doc/formula.rst | 91 + doc/index.rst | 317 ++ doc/logo.png | Bin 0 -> 5505 bytes doc/make.bat | 155 + doc/optimized.rst | 103 + doc/pandas.rst | 93 + doc/print_settings.rst | 76 + doc/protection.rst | 62 + doc/styles.rst | 301 ++ doc/table.png | Bin 0 -> 20324 bytes doc/table.py | 26 + doc/tutorial.rst | 308 ++ doc/usage.rst | 164 + doc/validation.rst | 113 + doc/windows-development.rst | 82 + doc/worksheet_properties.rst | 57 + doc/worksheet_tables.rst | 26 + openpyxl/.constants.json | 8 + openpyxl/__init__.py | 30 + openpyxl/cell/__init__.py | 5 + openpyxl/cell/cell.py | 382 ++ openpyxl/cell/interface.py | 60 + openpyxl/cell/read_only.py | 155 + openpyxl/cell/tests/__init__.py | 0 openpyxl/cell/tests/test_cell.py | 418 ++ openpyxl/cell/tests/test_read_only.py | 158 + openpyxl/cell/tests/test_text.py | 184 + openpyxl/cell/text.py | 186 + openpyxl/chart/_3d.py | 103 + openpyxl/chart/__init__.py | 20 + openpyxl/chart/_chart.py | 178 + openpyxl/chart/area_chart.py | 107 + openpyxl/chart/axis.py | 402 ++ openpyxl/chart/bar_chart.py | 145 + openpyxl/chart/bubble_chart.py | 68 + openpyxl/chart/chartspace.py | 268 + openpyxl/chart/data_source.py | 188 + openpyxl/chart/descriptors.py | 44 + openpyxl/chart/error_bar.py | 60 + openpyxl/chart/label.py | 130 + openpyxl/chart/layout.py | 75 + openpyxl/chart/legend.py | 73 + openpyxl/chart/line_chart.py | 132 + openpyxl/chart/marker.py | 91 + openpyxl/chart/packaging.rst | 8 + openpyxl/chart/picture.py | 36 + openpyxl/chart/pie_chart.py | 177 + openpyxl/chart/plotarea.py | 169 + openpyxl/chart/print_settings.py | 58 + openpyxl/chart/radar_chart.py | 53 + openpyxl/chart/reader.py | 80 + openpyxl/chart/reference.py | 132 + openpyxl/chart/scatter_chart.py | 54 + openpyxl/chart/series.py | 197 + openpyxl/chart/series_factory.py | 42 + openpyxl/chart/shapes.py | 90 + openpyxl/chart/stock_chart.py | 55 + openpyxl/chart/surface_chart.py | 120 + openpyxl/chart/tests/__init__.py | 0 openpyxl/chart/tests/conftest.py | 12 + openpyxl/chart/tests/data/chart1.xml | 1198 +++++ openpyxl/chart/tests/data/plotarea.xml | 187 + openpyxl/chart/tests/data/sample.xlsx | Bin 0 -> 37202 bytes .../tests/data/scatterchart_plot_area.xml | 147 + openpyxl/chart/tests/test_area_chart.py | 158 + openpyxl/chart/tests/test_axis.py | 364 ++ openpyxl/chart/tests/test_bar_chart.py | 188 + openpyxl/chart/tests/test_bubble_chart.py | 39 + openpyxl/chart/tests/test_chart.py | 134 + openpyxl/chart/tests/test_chartspace.py | 289 ++ openpyxl/chart/tests/test_data_source.py | 154 + openpyxl/chart/tests/test_error_bar.py | 42 + openpyxl/chart/tests/test_label.py | 80 + openpyxl/chart/tests/test_layout.py | 75 + openpyxl/chart/tests/test_legend.py | 69 + openpyxl/chart/tests/test_line_chart.py | 81 + openpyxl/chart/tests/test_marker.py | 86 + openpyxl/chart/tests/test_picture.py | 33 + openpyxl/chart/tests/test_pie_chart.py | 183 + openpyxl/chart/tests/test_plotarea.py | 148 + openpyxl/chart/tests/test_print.py | 61 + openpyxl/chart/tests/test_radar_chart.py | 44 + openpyxl/chart/tests/test_reader.py | 43 + openpyxl/chart/tests/test_reference.py | 102 + openpyxl/chart/tests/test_scatter_chart.py | 39 + openpyxl/chart/tests/test_series.py | 344 ++ openpyxl/chart/tests/test_series_factory.py | 145 + openpyxl/chart/tests/test_shapes.py | 49 + openpyxl/chart/tests/test_stock_chart.py | 92 + openpyxl/chart/tests/test_surface_chart.py | 165 + openpyxl/chart/tests/test_text.py | 41 + openpyxl/chart/tests/test_title.py | 76 + openpyxl/chart/tests/test_trendline.py | 64 + openpyxl/chart/tests/test_updown_bars.py | 37 + openpyxl/chart/text.py | 66 + openpyxl/chart/title.py | 74 + openpyxl/chart/trendline.py | 96 + openpyxl/chart/updown_bars.py | 32 + openpyxl/chartsheet/__init__.py | 4 + openpyxl/chartsheet/chartsheet.py | 110 + openpyxl/chartsheet/custom.py | 62 + openpyxl/chartsheet/properties.py | 29 + openpyxl/chartsheet/protection.py | 42 + openpyxl/chartsheet/publish.py | 59 + openpyxl/chartsheet/relation.py | 98 + openpyxl/chartsheet/tests/__init__.py | 0 openpyxl/chartsheet/tests/test_chartsheet.py | 88 + openpyxl/chartsheet/tests/test_custom.py | 94 + openpyxl/chartsheet/tests/test_properties.py | 43 + openpyxl/chartsheet/tests/test_protection.py | 58 + openpyxl/chartsheet/tests/test_publish.py | 81 + openpyxl/chartsheet/tests/test_relation.py | 58 + openpyxl/chartsheet/tests/test_views.py | 63 + openpyxl/chartsheet/views.py | 52 + openpyxl/comments/__init__.py | 5 + openpyxl/comments/author.py | 22 + openpyxl/comments/comment_sheet.py | 232 + openpyxl/comments/comments.py | 63 + openpyxl/comments/shape_writer.py | 117 + openpyxl/comments/tests/__init__.py | 0 openpyxl/comments/tests/conftest.py | 16 + openpyxl/comments/tests/data/comments.xlsx | Bin 0 -> 13202 bytes openpyxl/comments/tests/data/comments1.xml | 33 + openpyxl/comments/tests/data/comments2.xml | 81 + .../comments/tests/data/commentsDrawing1.vml | 54 + openpyxl/comments/tests/data/comments_out.xml | 25 + .../comments/tests/data/control+comments.vml | 86 + .../tests/data/google_docs_comments.xml | 14 + .../comments/tests/data/size+comments.vml | 22 + openpyxl/comments/tests/test_author.py | 41 + openpyxl/comments/tests/test_comment.py | 48 + .../comments/tests/test_comment_reader.py | 42 + openpyxl/comments/tests/test_comment_sheet.py | 106 + openpyxl/comments/tests/test_shape_writer.py | 118 + openpyxl/compat/__init__.py | 81 + openpyxl/compat/abc.py | 9 + openpyxl/compat/accumulate.py | 21 + openpyxl/compat/numbers.py | 31 + openpyxl/compat/singleton.py | 41 + openpyxl/compat/strings.py | 50 + openpyxl/compat/tests/__init__.py | 0 openpyxl/compat/tests/test_compat.py | 96 + openpyxl/conftest.py | 55 + openpyxl/descriptors/__init__.py | 58 + openpyxl/descriptors/base.py | 270 + openpyxl/descriptors/excel.py | 114 + openpyxl/descriptors/namespace.py | 13 + openpyxl/descriptors/nested.py | 131 + openpyxl/descriptors/sequence.py | 128 + openpyxl/descriptors/serialisable.py | 232 + openpyxl/descriptors/slots.py | 18 + openpyxl/descriptors/tests/__init__.py | 2 + openpyxl/descriptors/tests/test_base.py | 397 ++ openpyxl/descriptors/tests/test_excel.py | 211 + openpyxl/descriptors/tests/test_namespace.py | 31 + openpyxl/descriptors/tests/test_nested.py | 363 ++ openpyxl/descriptors/tests/test_sequence.py | 378 ++ .../descriptors/tests/test_serialisable.py | 209 + openpyxl/develop/__init__.py | 0 openpyxl/develop/classify.py | 328 ++ openpyxl/develop/stub.py | 53 + openpyxl/develop/tests/__init__.py | 0 openpyxl/develop/tests/data/defined_name.xsd | 24 + openpyxl/develop/tests/test_classify.py | 142 + openpyxl/drawing/__init__.py | 5 + openpyxl/drawing/colors.py | 438 ++ openpyxl/drawing/drawing.py | 103 + openpyxl/drawing/effect.py | 404 ++ openpyxl/drawing/fill.py | 392 ++ openpyxl/drawing/graphic.py | 599 +++ openpyxl/drawing/image.py | 80 + openpyxl/drawing/line.py | 164 + openpyxl/drawing/shape.py | 416 ++ openpyxl/drawing/shapes.py | 544 ++ openpyxl/drawing/spreadsheet_drawing.py | 375 ++ openpyxl/drawing/tests/__init__.py | 0 openpyxl/drawing/tests/conftest.py | 10 + openpyxl/drawing/tests/data/plain.png | Bin 0 -> 493 bytes .../data/spreadsheet_drawing_with_chart.xml | 25 + .../drawing/tests/data/text_box_drawing.xml | 64 + openpyxl/drawing/tests/test_color.py | 179 + openpyxl/drawing/tests/test_descriptors.py | 14 + openpyxl/drawing/tests/test_drawing.py | 106 + openpyxl/drawing/tests/test_effect.py | 37 + openpyxl/drawing/tests/test_fill.py | 243 + openpyxl/drawing/tests/test_graphic.py | 537 ++ openpyxl/drawing/tests/test_image.py | 45 + openpyxl/drawing/tests/test_line.py | 134 + openpyxl/drawing/tests/test_shape.py | 187 + openpyxl/drawing/tests/test_shapes.py | 142 + .../drawing/tests/test_spreadsheet_drawing.py | 364 ++ openpyxl/drawing/tests/test_text.py | 197 + openpyxl/drawing/text.py | 711 +++ openpyxl/formatting/__init__.py | 4 + openpyxl/formatting/formatting.py | 115 + openpyxl/formatting/rule.py | 294 ++ openpyxl/formatting/tests/__init__.py | 0 openpyxl/formatting/tests/conftest.py | 23 + .../tests/data/conditional-formatting.xlsx | Bin 0 -> 50266 bytes openpyxl/formatting/tests/data/worksheet.xml | 233 + openpyxl/formatting/tests/test_formatting.py | 295 ++ openpyxl/formatting/tests/test_rule.py | 366 ++ openpyxl/formula/__init__.py | 4 + openpyxl/formula/excel_tokenizer_parser.txt | 61 + openpyxl/formula/tests/__init__.py | 0 openpyxl/formula/tests/test_tokenizer.py | 591 +++ openpyxl/formula/tests/test_translate.py | 235 + openpyxl/formula/tokenizer.py | 437 ++ openpyxl/formula/translate.py | 165 + openpyxl/packaging/__init__.py | 3 + openpyxl/packaging/core.py | 118 + openpyxl/packaging/extended.py | 138 + openpyxl/packaging/interface.py | 57 + openpyxl/packaging/manifest.py | 208 + openpyxl/packaging/relationship.py | 166 + openpyxl/packaging/tests/__init__.py | 0 openpyxl/packaging/tests/conftest.py | 10 + openpyxl/packaging/tests/data/bug137.xlsx | Bin 0 -> 10004 bytes openpyxl/packaging/tests/data/core.xml | 21 + openpyxl/packaging/tests/data/hyperlink.xlsx | Bin 0 -> 5181 bytes openpyxl/packaging/tests/data/manifest.xml | 15 + openpyxl/packaging/tests/data/pivot.xlsx | Bin 0 -> 14504 bytes .../packaging/tests/data/print_settings.xlsx | Bin 0 -> 8464 bytes openpyxl/packaging/tests/data/sample.xlsm | Bin 0 -> 8352 bytes .../packaging/tests/data/workbook_1904.xml | 12 + .../packaging/tests/data/workbook_links.xml | 16 + .../tests/data/workbook_missing_id.xml | 12 + .../tests/data/workbook_security.xlsx | Bin 0 -> 21086 bytes openpyxl/packaging/tests/test_core.py | 84 + openpyxl/packaging/tests/test_extended.py | 69 + openpyxl/packaging/tests/test_interface.py | 15 + openpyxl/packaging/tests/test_manifest.py | 289 ++ openpyxl/packaging/tests/test_relationship.py | 111 + openpyxl/packaging/tests/test_workbook.py | 161 + openpyxl/packaging/workbook.py | 129 + openpyxl/pivot/__init__.py | 2 + openpyxl/pivot/cache.py | 1112 +++++ openpyxl/pivot/fields.py | 322 ++ openpyxl/pivot/record.py | 111 + openpyxl/pivot/table.py | 1174 +++++ openpyxl/pivot/tests/__init__.py | 0 openpyxl/pivot/tests/conftest.py | 16 + .../pivot/tests/data/pivotCacheDefinition.xml | 51 + .../pivot/tests/data/pivotCacheRecords.xml | 139 + openpyxl/pivot/tests/data/pivotTable.xml | 103 + openpyxl/pivot/tests/test_cache.py | 199 + openpyxl/pivot/tests/test_fields.py | 195 + openpyxl/pivot/tests/test_record.py | 113 + openpyxl/pivot/tests/test_table.py | 378 ++ openpyxl/reader/__init__.py | 1 + openpyxl/reader/excel.py | 297 ++ openpyxl/reader/strings.py | 28 + openpyxl/reader/tests/__init__.py | 0 openpyxl/reader/tests/conftest.py | 12 + .../tests/data/Table1-XmlFromAccess.xml | 85 + openpyxl/reader/tests/data/bug137.xlsx | Bin 0 -> 10004 bytes .../tests/data/complex-styles-worksheet.xml | 410 ++ .../reader/tests/data/complex-styles.xlsx | Bin 0 -> 46700 bytes .../tests/data/contains_chartsheets.xlsx | Bin 0 -> 14649 bytes .../tests/data/empty_with_no_properties.xlsx | Bin 0 -> 9476 bytes .../extended_conditional_formatting_sheet.xml | 153 + .../tests/data/frozen_view_worksheet.xml | 20 + .../reader/tests/data/hidden_rows_cols.xml | 31 + openpyxl/reader/tests/data/hidden_sheets.xml | 19 + openpyxl/reader/tests/data/jasper_sheet.xml | 2324 +++++++++ .../reader/tests/data/legacy_drawing.xlsm | Bin 0 -> 12452 bytes .../tests/data/legacy_drawing_worksheet.xml | 284 ++ openpyxl/reader/tests/data/null_file.xlsx | 0 .../reader/tests/data/protected_sheet.xml | 65 + .../reader/tests/data/shared-strings-rich.xml | 33 + .../tests/data/sharedStrings-emptystring.xml | 2 + openpyxl/reader/tests/data/sharedStrings.xml | 2 + openpyxl/reader/tests/data/sharedStrings2.xml | 8 + .../reader/tests/data/worksheet_formulae.xml | 163 + .../data/worksheet_without_coordinates.xml | 55 + openpyxl/reader/tests/test_excel.py | 164 + openpyxl/reader/tests/test_strings.py | 31 + openpyxl/reader/tests/test_style.py | 65 + openpyxl/reader/tests/test_worksheet.py | 742 +++ openpyxl/reader/worksheet.py | 338 ++ openpyxl/styles/__init__.py | 12 + openpyxl/styles/alignment.py | 73 + openpyxl/styles/borders.py | 114 + openpyxl/styles/builtins.py | 1398 ++++++ openpyxl/styles/cell_style.py | 203 + openpyxl/styles/colors.py | 193 + openpyxl/styles/differential.py | 96 + openpyxl/styles/fills.py | 224 + openpyxl/styles/fonts.py | 114 + openpyxl/styles/named_styles.py | 292 ++ openpyxl/styles/numbers.py | 188 + openpyxl/styles/protection.py | 18 + openpyxl/styles/proxy.py | 63 + openpyxl/styles/styleable.py | 141 + openpyxl/styles/stylesheet.py | 230 + openpyxl/styles/table.py | 92 + openpyxl/styles/tests/__init__.py | 1 + openpyxl/styles/tests/conftest.py | 23 + .../styles/tests/data/alignment_styles.xml | 47 + .../builtins_as_custom_number_formats.xml | 74 + openpyxl/styles/tests/data/complex-styles.xml | 257 + openpyxl/styles/tests/data/dxf_style.xml | 2069 ++++++++ .../tests/data/empty-workbook-styles.xml | 53 + .../styles/tests/data/no_number_format.xml | 47 + .../styles/tests/data/none_value_styles.xml | 50 + openpyxl/styles/tests/data/rgb_colors.xml | 2024 ++++++++ openpyxl/styles/tests/data/simple-styles.xml | 48 + .../tests/data/styles_number_formats.xml | 112 + .../data/worksheet_unprotected_style.xml | 76 + openpyxl/styles/tests/test_alignments.py | 27 + openpyxl/styles/tests/test_borders.py | 80 + openpyxl/styles/tests/test_cell_style.py | 198 + openpyxl/styles/tests/test_colors.py | 81 + openpyxl/styles/tests/test_differential.py | 52 + openpyxl/styles/tests/test_fills.py | 289 ++ openpyxl/styles/tests/test_fonts.py | 77 + openpyxl/styles/tests/test_named_style.py | 239 + openpyxl/styles/tests/test_number_style.py | 115 + openpyxl/styles/tests/test_protection.py | 19 + openpyxl/styles/tests/test_proxy.py | 67 + openpyxl/styles/tests/test_styleable.py | 131 + openpyxl/styles/tests/test_stylesheet.py | 335 ++ openpyxl/styles/tests/test_table.py | 86 + openpyxl/tests/__init__.py | 6 + openpyxl/tests/conftest.py | 32 + openpyxl/tests/data/Thumbs.db | Bin 0 -> 3072 bytes .../tests/data/genuine/empty-with-styles.xlsx | Bin 0 -> 7511 bytes openpyxl/tests/data/genuine/empty.xlsx | Bin 0 -> 7842 bytes openpyxl/tests/data/genuine/empty_libre.xlsx | Bin 0 -> 8107 bytes .../data/genuine/empty_no_dimensions.xlsx | Bin 0 -> 10300 bytes openpyxl/tests/data/genuine/guess_types.xlsx | Bin 0 -> 30133 bytes .../tests/data/genuine/libreoffice_nrt.xlsx | Bin 0 -> 4534 bytes openpyxl/tests/data/genuine/sample.xlsx | Bin 0 -> 11395 bytes openpyxl/tests/data/reader/bigfoot.xlsx | Bin 0 -> 410964 bytes .../tests/data/reader/bug328_hyperlinks.xml | 53 + .../tests/data/reader/bug393-worksheet.xml | 64 + openpyxl/tests/data/reader/empty_rows.xml | 43 + openpyxl/tests/data/reader/merged-ranges.xml | 2 + .../reader/nonstandard_workbook_name.xlsx | Bin 0 -> 6399 bytes openpyxl/tests/data/reader/null_archive.xlsx | Bin 0 -> 126 bytes openpyxl/tests/data/reader/sheet2.xml | 341 ++ .../data/reader/sheet2_invalid_dimension.xml | 341 ++ .../tests/data/reader/sheet2_no_dimension.xml | 340 ++ openpyxl/tests/data/reader/sheet2_no_span.xml | 340 ++ openpyxl/tests/data/reader/vba+comments.xlsm | Bin 0 -> 22554 bytes .../tests/data/reader/vba-comments-saved.xlsm | Bin 0 -> 6302 bytes openpyxl/tests/data/reader/vba-test.xlsm | Bin 0 -> 52938 bytes .../tests/data/writer/Content_types_vba.xml | 20 + openpyxl/tests/data/writer/font.xml | 10 + openpyxl/tests/data/writer/styles.xml | 41 + openpyxl/tests/helper.py | 21 + .../tests/long/dump_writer_performance.py | 19 + openpyxl/tests/long/long_dump_thread.py | 22 + openpyxl/tests/notes_on_namespaces.rst | 13 + openpyxl/tests/schema.py | 49 + openpyxl/tests/schemas/dml-chart.xsd | 1499 ++++++ openpyxl/tests/schemas/dml-chartDrawing.xsd | 146 + openpyxl/tests/schemas/dml-compatibility.xsd | 14 + openpyxl/tests/schemas/dml-diagram.xsd | 1091 ++++ openpyxl/tests/schemas/dml-lockedCanvas.xsd | 11 + openpyxl/tests/schemas/dml-main.xsd | 3048 ++++++++++++ openpyxl/tests/schemas/dml-picture.xsd | 23 + .../tests/schemas/dml-spreadsheetDrawing.xsd | 185 + .../schemas/dml-wordprocessingDrawing.xsd | 185 + openpyxl/tests/schemas/opc-coreProperties.xsd | 50 + openpyxl/tests/schemas/opc-relationships.xsd | 33 + openpyxl/tests/schemas/pml.xsd | 1676 +++++++ .../shared-additionalCharacteristics.xsd | 28 + .../tests/schemas/shared-bibliography.xsd | 144 + .../schemas/shared-commonSimpleTypes.xsd | 166 + .../shared-customXmlDataProperties.xsd | 25 + .../shared-customXmlSchemaProperties.xsd | 18 + .../shared-documentPropertiesCustom.xsd | 59 + .../shared-documentPropertiesExtended.xsd | 56 + .../shared-documentPropertiesVariantTypes.xsd | 195 + openpyxl/tests/schemas/shared-math.xsd | 582 +++ .../schemas/shared-relationshipReference.xsd | 25 + openpyxl/tests/schemas/sml.xsd | 4430 +++++++++++++++++ openpyxl/tests/schemas/vml-main.xsd | 569 +++ openpyxl/tests/schemas/vml-officeDrawing.xsd | 509 ++ .../tests/schemas/vml-presentationDrawing.xsd | 12 + .../tests/schemas/vml-spreadsheetDrawing.xsd | 108 + .../schemas/vml-wordprocessingDrawing.xsd | 96 + openpyxl/tests/schemas/wml.xsd | 3644 ++++++++++++++ openpyxl/tests/test_backend.py | 59 + openpyxl/tests/test_iter.py | 449 ++ openpyxl/tests/test_read.py | 46 + openpyxl/tests/test_vba.py | 121 + openpyxl/utils/__init__.py | 19 + openpyxl/utils/bound_dictionary.py | 27 + openpyxl/utils/cell.py | 215 + openpyxl/utils/dataframe.py | 84 + openpyxl/utils/datetime.py | 146 + openpyxl/utils/escape.py | 44 + openpyxl/utils/exceptions.py | 35 + openpyxl/utils/formulas.py | 9 + openpyxl/utils/indexed_list.py | 49 + openpyxl/utils/protection.py | 23 + openpyxl/utils/tests/__init__.py | 0 openpyxl/utils/tests/test_bound_dictionary.py | 39 + openpyxl/utils/tests/test_cell.py | 174 + openpyxl/utils/tests/test_dataframe.py | 75 + openpyxl/utils/tests/test_datetime.py | 164 + openpyxl/utils/tests/test_escape.py | 10 + openpyxl/utils/tests/test_indexed_list.py | 54 + openpyxl/utils/tests/test_protection.py | 9 + openpyxl/utils/tests/test_units.py | 212 + openpyxl/utils/units.py | 108 + openpyxl/workbook/__init__.py | 5 + openpyxl/workbook/child.py | 169 + openpyxl/workbook/defined_name.py | 261 + openpyxl/workbook/external_link/__init__.py | 4 + openpyxl/workbook/external_link/external.py | 192 + .../workbook/external_link/tests/__init__.py | 0 .../workbook/external_link/tests/conftest.py | 12 + .../external_link/tests/data/OLELink.xml | 8 + .../tests/data/[Content_Types].xml | 15 + .../external_link/tests/data/book1.xlsx | Bin 0 -> 10044 bytes .../external_link/tests/data/book2.xlsx | Bin 0 -> 8578 bytes .../tests/data/externalLink1.xml | 69 + .../tests/data/externalLink1.xml.rels | 4 + .../external_link/tests/data/merge_range.xlsx | Bin 0 -> 8742 bytes .../external_link/tests/data/workbook.xml | 16 + .../tests/data/workbook.xml.rels | 10 + .../tests/data/workbook_external_range.xml | 23 + .../tests/data/workbook_namedrange.xml | 16 + .../external_link/tests/test_external.py | 165 + openpyxl/workbook/external_reference.py | 19 + openpyxl/workbook/function_group.py | 37 + openpyxl/workbook/parser.py | 188 + openpyxl/workbook/properties.py | 152 + openpyxl/workbook/protection.py | 164 + openpyxl/workbook/smart_tags.py | 57 + openpyxl/workbook/tests/__init__.py | 0 openpyxl/workbook/tests/conftest.py | 12 + .../tests/data/broken_print_titles.xml | 9 + openpyxl/workbook/tests/data/workbook.xml | 8 + .../tests/data/workbook_russian_code_name.xml | 13 + openpyxl/workbook/tests/test_child.py | 99 + openpyxl/workbook/tests/test_defined_name.py | 333 ++ .../workbook/tests/test_external_reference.py | 34 + .../workbook/tests/test_function_group.py | 59 + openpyxl/workbook/tests/test_parser.py | 47 + openpyxl/workbook/tests/test_pivot.py | 33 + openpyxl/workbook/tests/test_properties.py | 86 + openpyxl/workbook/tests/test_protection.py | 80 + openpyxl/workbook/tests/test_smart_tags.py | 86 + openpyxl/workbook/tests/test_views.py | 83 + openpyxl/workbook/tests/test_web.py | 93 + openpyxl/workbook/tests/test_workbook.py | 236 + openpyxl/workbook/views.py | 156 + openpyxl/workbook/web.py | 99 + openpyxl/workbook/workbook.py | 403 ++ openpyxl/worksheet/__init__.py | 4 + openpyxl/worksheet/cell_range.py | 449 ++ openpyxl/worksheet/copier.py | 69 + openpyxl/worksheet/datavalidation.py | 208 + openpyxl/worksheet/dimensions.py | 292 ++ openpyxl/worksheet/drawing.py | 15 + openpyxl/worksheet/filters.py | 372 ++ openpyxl/worksheet/header_footer.py | 274 + openpyxl/worksheet/hyperlink.py | 65 + openpyxl/worksheet/merge.py | 43 + openpyxl/worksheet/page.py | 167 + openpyxl/worksheet/pagebreak.py | 82 + openpyxl/worksheet/properties.py | 98 + openpyxl/worksheet/protection.py | 123 + openpyxl/worksheet/read_only.py | 267 + openpyxl/worksheet/related.py | 18 + openpyxl/worksheet/table.py | 365 ++ openpyxl/worksheet/tests/__init__.py | 0 openpyxl/worksheet/tests/conftest.py | 12 + openpyxl/worksheet/tests/data/copy_test.xlsx | Bin 0 -> 4916 bytes openpyxl/worksheet/tests/data/sheetPr2.xml | 4 + .../tests/data/sheet_inline_strings.xml | 62 + openpyxl/worksheet/tests/test_cell_range.py | 304 ++ .../worksheet/tests/test_datavalidation.py | 189 + openpyxl/worksheet/tests/test_dimensions.py | 191 + openpyxl/worksheet/tests/test_filters.py | 401 ++ openpyxl/worksheet/tests/test_header.py | 187 + openpyxl/worksheet/tests/test_hyperlink.py | 68 + openpyxl/worksheet/tests/test_page.py | 123 + openpyxl/worksheet/tests/test_pagebreak.py | 53 + openpyxl/worksheet/tests/test_properties.py | 54 + openpyxl/worksheet/tests/test_protection.py | 70 + openpyxl/worksheet/tests/test_read_only.py | 28 + openpyxl/worksheet/tests/test_related.py | 17 + openpyxl/worksheet/tests/test_table.py | 220 + openpyxl/worksheet/tests/test_views.py | 85 + openpyxl/worksheet/tests/test_worksheet.py | 714 +++ .../worksheet/tests/test_worksheet_copy.py | 141 + openpyxl/worksheet/tests/test_write_only.py | 451 ++ openpyxl/worksheet/views.py | 150 + openpyxl/worksheet/worksheet.py | 957 ++++ openpyxl/worksheet/write_only.py | 263 + openpyxl/writer/__init__.py | 2 + openpyxl/writer/etree_worksheet.py | 159 + openpyxl/writer/excel.py | 314 ++ openpyxl/writer/strings.py | 28 + openpyxl/writer/tests/__init__.py | 0 openpyxl/writer/tests/conftest.py | 10 + .../writer/tests/data/[Content_Types].xml | 16 + openpyxl/writer/tests/data/empty.xlsm | Bin 0 -> 8446 bytes openpyxl/writer/tests/data/empty.xlsx | Bin 0 -> 11395 bytes openpyxl/writer/tests/data/empty.xltm | Bin 0 -> 8450 bytes openpyxl/writer/tests/data/empty.xltx | Bin 0 -> 8431 bytes openpyxl/writer/tests/data/plain.png | Bin 0 -> 493 bytes openpyxl/writer/tests/data/sharedStrings.xml | 9 + openpyxl/writer/tests/data/theme1.xml | 281 ++ openpyxl/writer/tests/data/vba+comments.xlsm | Bin 0 -> 22554 bytes openpyxl/writer/tests/data/workbook.xml | 13 + openpyxl/writer/tests/data/workbook.xml.rels | 6 + .../tests/data/workbook_auto_filter.xml | 15 + .../writer/tests/data/workbook_protection.xml | 13 + .../writer/tests/data/workbook_vba.xml.rels | 8 + openpyxl/writer/tests/test_excel.py | 180 + openpyxl/writer/tests/test_strings.py | 31 + openpyxl/writer/tests/test_template.py | 75 + openpyxl/writer/tests/test_theme.py | 15 + openpyxl/writer/tests/test_workbook.py | 271 + openpyxl/writer/tests/test_worksheet.py | 583 +++ openpyxl/writer/theme.py | 295 ++ openpyxl/writer/workbook.py | 184 + openpyxl/writer/worksheet.py | 195 + openpyxl/xml/__init__.py | 27 + openpyxl/xml/constants.py | 115 + openpyxl/xml/functions.py | 106 + openpyxl/xml/tests/__init__.py | 0 openpyxl/xml/tests/test_functions.py | 43 + pytest.ini | 14 + requirements.txt | 7 + setup.cfg | 5 + setup.py | 70 + shippable.yml | 22 + tox.ini | 125 + 616 files changed, 89235 insertions(+) create mode 100644 AUTHORS.rst create mode 100644 LICENCE.rst create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100644 bitbucket-pipelines.yml create mode 100644 doc/Makefile create mode 100644 doc/_static/.placeholder create mode 100644 doc/changes.rst create mode 100644 doc/charts/area.png create mode 100644 doc/charts/area.py create mode 100644 doc/charts/area.rst create mode 100644 doc/charts/area3D.png create mode 100644 doc/charts/area3d.py create mode 100644 doc/charts/bar.png create mode 100644 doc/charts/bar.py create mode 100644 doc/charts/bar.rst create mode 100644 doc/charts/bar3D.png create mode 100644 doc/charts/bar3d.py create mode 100644 doc/charts/bubble.png create mode 100644 doc/charts/bubble.py create mode 100644 doc/charts/bubble.rst create mode 100644 doc/charts/chart_layout.png create mode 100644 doc/charts/chart_layout.py create mode 100644 doc/charts/chart_layout.rst create mode 100644 doc/charts/chart_layout_default.png create mode 100644 doc/charts/chartsheet.png create mode 100644 doc/charts/chartsheet.py create mode 100644 doc/charts/chartsheet.rst create mode 100644 doc/charts/doughnut.png create mode 100644 doc/charts/doughnut.py create mode 100644 doc/charts/doughnut.rst create mode 100644 doc/charts/gauge.png create mode 100644 doc/charts/gauge.py create mode 100644 doc/charts/gauge.rst create mode 100644 doc/charts/introduction.rst create mode 100644 doc/charts/limits_and_scaling.rst create mode 100644 doc/charts/limits_and_scaling_log.png create mode 100644 doc/charts/limits_and_scaling_log.py create mode 100644 doc/charts/limits_and_scaling_minmax.png create mode 100644 doc/charts/limits_and_scaling_minmax.py create mode 100644 doc/charts/limits_and_scaling_orientation.png create mode 100644 doc/charts/limits_and_scaling_orientation.py create mode 100644 doc/charts/line.png create mode 100644 doc/charts/line.py create mode 100644 doc/charts/line.rst create mode 100644 doc/charts/line3D.png create mode 100644 doc/charts/line3D.py create mode 100644 doc/charts/pattern.png create mode 100644 doc/charts/pattern.py create mode 100644 doc/charts/pattern.rst create mode 100644 doc/charts/pie.png create mode 100644 doc/charts/pie.py create mode 100644 doc/charts/pie.rst create mode 100644 doc/charts/pie3D.png create mode 100644 doc/charts/pie3D.py create mode 100644 doc/charts/projected-pie.png create mode 100644 doc/charts/radar.png create mode 100644 doc/charts/radar.py create mode 100644 doc/charts/radar.rst create mode 100644 doc/charts/scatter.png create mode 100644 doc/charts/scatter.py create mode 100644 doc/charts/scatter.rst create mode 100644 doc/charts/secondary.png create mode 100644 doc/charts/secondary.py create mode 100644 doc/charts/secondary.rst create mode 100644 doc/charts/stock.png create mode 100644 doc/charts/stock.py create mode 100644 doc/charts/stock.rst create mode 100644 doc/charts/surface.png create mode 100644 doc/charts/surface.py create mode 100644 doc/charts/surface.rst create mode 100644 doc/comments.rst create mode 100644 doc/conf.py create mode 100644 doc/defined_names.rst create mode 100644 doc/development.rst create mode 100644 doc/example.py create mode 100644 doc/filters.png create mode 100644 doc/filters.py create mode 100644 doc/filters.rst create mode 100644 doc/format_merged_cells.py create mode 100644 doc/formatting.rst create mode 100644 doc/formula.rst create mode 100644 doc/index.rst create mode 100644 doc/logo.png create mode 100644 doc/make.bat create mode 100644 doc/optimized.rst create mode 100644 doc/pandas.rst create mode 100644 doc/print_settings.rst create mode 100644 doc/protection.rst create mode 100644 doc/styles.rst create mode 100644 doc/table.png create mode 100644 doc/table.py create mode 100644 doc/tutorial.rst create mode 100644 doc/usage.rst create mode 100644 doc/validation.rst create mode 100644 doc/windows-development.rst create mode 100644 doc/worksheet_properties.rst create mode 100644 doc/worksheet_tables.rst create mode 100644 openpyxl/.constants.json create mode 100644 openpyxl/__init__.py create mode 100644 openpyxl/cell/__init__.py create mode 100644 openpyxl/cell/cell.py create mode 100644 openpyxl/cell/interface.py create mode 100644 openpyxl/cell/read_only.py create mode 100644 openpyxl/cell/tests/__init__.py create mode 100644 openpyxl/cell/tests/test_cell.py create mode 100644 openpyxl/cell/tests/test_read_only.py create mode 100644 openpyxl/cell/tests/test_text.py create mode 100644 openpyxl/cell/text.py create mode 100644 openpyxl/chart/_3d.py create mode 100644 openpyxl/chart/__init__.py create mode 100644 openpyxl/chart/_chart.py create mode 100644 openpyxl/chart/area_chart.py create mode 100644 openpyxl/chart/axis.py create mode 100644 openpyxl/chart/bar_chart.py create mode 100644 openpyxl/chart/bubble_chart.py create mode 100644 openpyxl/chart/chartspace.py create mode 100644 openpyxl/chart/data_source.py create mode 100644 openpyxl/chart/descriptors.py create mode 100644 openpyxl/chart/error_bar.py create mode 100644 openpyxl/chart/label.py create mode 100644 openpyxl/chart/layout.py create mode 100644 openpyxl/chart/legend.py create mode 100644 openpyxl/chart/line_chart.py create mode 100644 openpyxl/chart/marker.py create mode 100644 openpyxl/chart/packaging.rst create mode 100644 openpyxl/chart/picture.py create mode 100644 openpyxl/chart/pie_chart.py create mode 100644 openpyxl/chart/plotarea.py create mode 100644 openpyxl/chart/print_settings.py create mode 100644 openpyxl/chart/radar_chart.py create mode 100644 openpyxl/chart/reader.py create mode 100644 openpyxl/chart/reference.py create mode 100644 openpyxl/chart/scatter_chart.py create mode 100644 openpyxl/chart/series.py create mode 100644 openpyxl/chart/series_factory.py create mode 100644 openpyxl/chart/shapes.py create mode 100644 openpyxl/chart/stock_chart.py create mode 100644 openpyxl/chart/surface_chart.py create mode 100644 openpyxl/chart/tests/__init__.py create mode 100644 openpyxl/chart/tests/conftest.py create mode 100644 openpyxl/chart/tests/data/chart1.xml create mode 100644 openpyxl/chart/tests/data/plotarea.xml create mode 100644 openpyxl/chart/tests/data/sample.xlsx create mode 100644 openpyxl/chart/tests/data/scatterchart_plot_area.xml create mode 100644 openpyxl/chart/tests/test_area_chart.py create mode 100644 openpyxl/chart/tests/test_axis.py create mode 100644 openpyxl/chart/tests/test_bar_chart.py create mode 100644 openpyxl/chart/tests/test_bubble_chart.py create mode 100644 openpyxl/chart/tests/test_chart.py create mode 100644 openpyxl/chart/tests/test_chartspace.py create mode 100644 openpyxl/chart/tests/test_data_source.py create mode 100644 openpyxl/chart/tests/test_error_bar.py create mode 100644 openpyxl/chart/tests/test_label.py create mode 100644 openpyxl/chart/tests/test_layout.py create mode 100644 openpyxl/chart/tests/test_legend.py create mode 100644 openpyxl/chart/tests/test_line_chart.py create mode 100644 openpyxl/chart/tests/test_marker.py create mode 100644 openpyxl/chart/tests/test_picture.py create mode 100644 openpyxl/chart/tests/test_pie_chart.py create mode 100644 openpyxl/chart/tests/test_plotarea.py create mode 100644 openpyxl/chart/tests/test_print.py create mode 100644 openpyxl/chart/tests/test_radar_chart.py create mode 100644 openpyxl/chart/tests/test_reader.py create mode 100644 openpyxl/chart/tests/test_reference.py create mode 100644 openpyxl/chart/tests/test_scatter_chart.py create mode 100644 openpyxl/chart/tests/test_series.py create mode 100644 openpyxl/chart/tests/test_series_factory.py create mode 100644 openpyxl/chart/tests/test_shapes.py create mode 100644 openpyxl/chart/tests/test_stock_chart.py create mode 100644 openpyxl/chart/tests/test_surface_chart.py create mode 100644 openpyxl/chart/tests/test_text.py create mode 100644 openpyxl/chart/tests/test_title.py create mode 100644 openpyxl/chart/tests/test_trendline.py create mode 100644 openpyxl/chart/tests/test_updown_bars.py create mode 100644 openpyxl/chart/text.py create mode 100644 openpyxl/chart/title.py create mode 100644 openpyxl/chart/trendline.py create mode 100644 openpyxl/chart/updown_bars.py create mode 100644 openpyxl/chartsheet/__init__.py create mode 100644 openpyxl/chartsheet/chartsheet.py create mode 100644 openpyxl/chartsheet/custom.py create mode 100644 openpyxl/chartsheet/properties.py create mode 100644 openpyxl/chartsheet/protection.py create mode 100644 openpyxl/chartsheet/publish.py create mode 100644 openpyxl/chartsheet/relation.py create mode 100644 openpyxl/chartsheet/tests/__init__.py create mode 100644 openpyxl/chartsheet/tests/test_chartsheet.py create mode 100644 openpyxl/chartsheet/tests/test_custom.py create mode 100644 openpyxl/chartsheet/tests/test_properties.py create mode 100644 openpyxl/chartsheet/tests/test_protection.py create mode 100644 openpyxl/chartsheet/tests/test_publish.py create mode 100644 openpyxl/chartsheet/tests/test_relation.py create mode 100644 openpyxl/chartsheet/tests/test_views.py create mode 100644 openpyxl/chartsheet/views.py create mode 100644 openpyxl/comments/__init__.py create mode 100644 openpyxl/comments/author.py create mode 100644 openpyxl/comments/comment_sheet.py create mode 100644 openpyxl/comments/comments.py create mode 100644 openpyxl/comments/shape_writer.py create mode 100644 openpyxl/comments/tests/__init__.py create mode 100644 openpyxl/comments/tests/conftest.py create mode 100644 openpyxl/comments/tests/data/comments.xlsx create mode 100644 openpyxl/comments/tests/data/comments1.xml create mode 100644 openpyxl/comments/tests/data/comments2.xml create mode 100644 openpyxl/comments/tests/data/commentsDrawing1.vml create mode 100644 openpyxl/comments/tests/data/comments_out.xml create mode 100644 openpyxl/comments/tests/data/control+comments.vml create mode 100644 openpyxl/comments/tests/data/google_docs_comments.xml create mode 100644 openpyxl/comments/tests/data/size+comments.vml create mode 100644 openpyxl/comments/tests/test_author.py create mode 100644 openpyxl/comments/tests/test_comment.py create mode 100644 openpyxl/comments/tests/test_comment_reader.py create mode 100644 openpyxl/comments/tests/test_comment_sheet.py create mode 100644 openpyxl/comments/tests/test_shape_writer.py create mode 100644 openpyxl/compat/__init__.py create mode 100644 openpyxl/compat/abc.py create mode 100644 openpyxl/compat/accumulate.py create mode 100644 openpyxl/compat/numbers.py create mode 100644 openpyxl/compat/singleton.py create mode 100644 openpyxl/compat/strings.py create mode 100644 openpyxl/compat/tests/__init__.py create mode 100644 openpyxl/compat/tests/test_compat.py create mode 100644 openpyxl/conftest.py create mode 100644 openpyxl/descriptors/__init__.py create mode 100644 openpyxl/descriptors/base.py create mode 100644 openpyxl/descriptors/excel.py create mode 100644 openpyxl/descriptors/namespace.py create mode 100644 openpyxl/descriptors/nested.py create mode 100644 openpyxl/descriptors/sequence.py create mode 100644 openpyxl/descriptors/serialisable.py create mode 100644 openpyxl/descriptors/slots.py create mode 100644 openpyxl/descriptors/tests/__init__.py create mode 100644 openpyxl/descriptors/tests/test_base.py create mode 100644 openpyxl/descriptors/tests/test_excel.py create mode 100644 openpyxl/descriptors/tests/test_namespace.py create mode 100644 openpyxl/descriptors/tests/test_nested.py create mode 100644 openpyxl/descriptors/tests/test_sequence.py create mode 100644 openpyxl/descriptors/tests/test_serialisable.py create mode 100644 openpyxl/develop/__init__.py create mode 100644 openpyxl/develop/classify.py create mode 100644 openpyxl/develop/stub.py create mode 100644 openpyxl/develop/tests/__init__.py create mode 100644 openpyxl/develop/tests/data/defined_name.xsd create mode 100644 openpyxl/develop/tests/test_classify.py create mode 100644 openpyxl/drawing/__init__.py create mode 100644 openpyxl/drawing/colors.py create mode 100644 openpyxl/drawing/drawing.py create mode 100644 openpyxl/drawing/effect.py create mode 100644 openpyxl/drawing/fill.py create mode 100644 openpyxl/drawing/graphic.py create mode 100644 openpyxl/drawing/image.py create mode 100644 openpyxl/drawing/line.py create mode 100644 openpyxl/drawing/shape.py create mode 100644 openpyxl/drawing/shapes.py create mode 100644 openpyxl/drawing/spreadsheet_drawing.py create mode 100644 openpyxl/drawing/tests/__init__.py create mode 100644 openpyxl/drawing/tests/conftest.py create mode 100644 openpyxl/drawing/tests/data/plain.png create mode 100644 openpyxl/drawing/tests/data/spreadsheet_drawing_with_chart.xml create mode 100644 openpyxl/drawing/tests/data/text_box_drawing.xml create mode 100644 openpyxl/drawing/tests/test_color.py create mode 100644 openpyxl/drawing/tests/test_descriptors.py create mode 100644 openpyxl/drawing/tests/test_drawing.py create mode 100644 openpyxl/drawing/tests/test_effect.py create mode 100644 openpyxl/drawing/tests/test_fill.py create mode 100644 openpyxl/drawing/tests/test_graphic.py create mode 100644 openpyxl/drawing/tests/test_image.py create mode 100644 openpyxl/drawing/tests/test_line.py create mode 100644 openpyxl/drawing/tests/test_shape.py create mode 100644 openpyxl/drawing/tests/test_shapes.py create mode 100644 openpyxl/drawing/tests/test_spreadsheet_drawing.py create mode 100644 openpyxl/drawing/tests/test_text.py create mode 100644 openpyxl/drawing/text.py create mode 100644 openpyxl/formatting/__init__.py create mode 100644 openpyxl/formatting/formatting.py create mode 100644 openpyxl/formatting/rule.py create mode 100644 openpyxl/formatting/tests/__init__.py create mode 100644 openpyxl/formatting/tests/conftest.py create mode 100644 openpyxl/formatting/tests/data/conditional-formatting.xlsx create mode 100644 openpyxl/formatting/tests/data/worksheet.xml create mode 100644 openpyxl/formatting/tests/test_formatting.py create mode 100644 openpyxl/formatting/tests/test_rule.py create mode 100644 openpyxl/formula/__init__.py create mode 100644 openpyxl/formula/excel_tokenizer_parser.txt create mode 100644 openpyxl/formula/tests/__init__.py create mode 100644 openpyxl/formula/tests/test_tokenizer.py create mode 100644 openpyxl/formula/tests/test_translate.py create mode 100644 openpyxl/formula/tokenizer.py create mode 100644 openpyxl/formula/translate.py create mode 100644 openpyxl/packaging/__init__.py create mode 100644 openpyxl/packaging/core.py create mode 100644 openpyxl/packaging/extended.py create mode 100644 openpyxl/packaging/interface.py create mode 100644 openpyxl/packaging/manifest.py create mode 100644 openpyxl/packaging/relationship.py create mode 100644 openpyxl/packaging/tests/__init__.py create mode 100644 openpyxl/packaging/tests/conftest.py create mode 100644 openpyxl/packaging/tests/data/bug137.xlsx create mode 100644 openpyxl/packaging/tests/data/core.xml create mode 100644 openpyxl/packaging/tests/data/hyperlink.xlsx create mode 100644 openpyxl/packaging/tests/data/manifest.xml create mode 100644 openpyxl/packaging/tests/data/pivot.xlsx create mode 100644 openpyxl/packaging/tests/data/print_settings.xlsx create mode 100644 openpyxl/packaging/tests/data/sample.xlsm create mode 100644 openpyxl/packaging/tests/data/workbook_1904.xml create mode 100644 openpyxl/packaging/tests/data/workbook_links.xml create mode 100644 openpyxl/packaging/tests/data/workbook_missing_id.xml create mode 100644 openpyxl/packaging/tests/data/workbook_security.xlsx create mode 100644 openpyxl/packaging/tests/test_core.py create mode 100644 openpyxl/packaging/tests/test_extended.py create mode 100644 openpyxl/packaging/tests/test_interface.py create mode 100644 openpyxl/packaging/tests/test_manifest.py create mode 100644 openpyxl/packaging/tests/test_relationship.py create mode 100644 openpyxl/packaging/tests/test_workbook.py create mode 100644 openpyxl/packaging/workbook.py create mode 100644 openpyxl/pivot/__init__.py create mode 100644 openpyxl/pivot/cache.py create mode 100644 openpyxl/pivot/fields.py create mode 100644 openpyxl/pivot/record.py create mode 100644 openpyxl/pivot/table.py create mode 100644 openpyxl/pivot/tests/__init__.py create mode 100644 openpyxl/pivot/tests/conftest.py create mode 100644 openpyxl/pivot/tests/data/pivotCacheDefinition.xml create mode 100644 openpyxl/pivot/tests/data/pivotCacheRecords.xml create mode 100644 openpyxl/pivot/tests/data/pivotTable.xml create mode 100644 openpyxl/pivot/tests/test_cache.py create mode 100644 openpyxl/pivot/tests/test_fields.py create mode 100644 openpyxl/pivot/tests/test_record.py create mode 100644 openpyxl/pivot/tests/test_table.py create mode 100644 openpyxl/reader/__init__.py create mode 100644 openpyxl/reader/excel.py create mode 100644 openpyxl/reader/strings.py create mode 100644 openpyxl/reader/tests/__init__.py create mode 100644 openpyxl/reader/tests/conftest.py create mode 100644 openpyxl/reader/tests/data/Table1-XmlFromAccess.xml create mode 100644 openpyxl/reader/tests/data/bug137.xlsx create mode 100644 openpyxl/reader/tests/data/complex-styles-worksheet.xml create mode 100644 openpyxl/reader/tests/data/complex-styles.xlsx create mode 100644 openpyxl/reader/tests/data/contains_chartsheets.xlsx create mode 100644 openpyxl/reader/tests/data/empty_with_no_properties.xlsx create mode 100644 openpyxl/reader/tests/data/extended_conditional_formatting_sheet.xml create mode 100644 openpyxl/reader/tests/data/frozen_view_worksheet.xml create mode 100644 openpyxl/reader/tests/data/hidden_rows_cols.xml create mode 100644 openpyxl/reader/tests/data/hidden_sheets.xml create mode 100644 openpyxl/reader/tests/data/jasper_sheet.xml create mode 100644 openpyxl/reader/tests/data/legacy_drawing.xlsm create mode 100644 openpyxl/reader/tests/data/legacy_drawing_worksheet.xml create mode 100644 openpyxl/reader/tests/data/null_file.xlsx create mode 100644 openpyxl/reader/tests/data/protected_sheet.xml create mode 100644 openpyxl/reader/tests/data/shared-strings-rich.xml create mode 100644 openpyxl/reader/tests/data/sharedStrings-emptystring.xml create mode 100644 openpyxl/reader/tests/data/sharedStrings.xml create mode 100644 openpyxl/reader/tests/data/sharedStrings2.xml create mode 100644 openpyxl/reader/tests/data/worksheet_formulae.xml create mode 100644 openpyxl/reader/tests/data/worksheet_without_coordinates.xml create mode 100644 openpyxl/reader/tests/test_excel.py create mode 100644 openpyxl/reader/tests/test_strings.py create mode 100644 openpyxl/reader/tests/test_style.py create mode 100644 openpyxl/reader/tests/test_worksheet.py create mode 100644 openpyxl/reader/worksheet.py create mode 100644 openpyxl/styles/__init__.py create mode 100644 openpyxl/styles/alignment.py create mode 100644 openpyxl/styles/borders.py create mode 100644 openpyxl/styles/builtins.py create mode 100644 openpyxl/styles/cell_style.py create mode 100644 openpyxl/styles/colors.py create mode 100644 openpyxl/styles/differential.py create mode 100644 openpyxl/styles/fills.py create mode 100644 openpyxl/styles/fonts.py create mode 100644 openpyxl/styles/named_styles.py create mode 100644 openpyxl/styles/numbers.py create mode 100644 openpyxl/styles/protection.py create mode 100644 openpyxl/styles/proxy.py create mode 100644 openpyxl/styles/styleable.py create mode 100644 openpyxl/styles/stylesheet.py create mode 100644 openpyxl/styles/table.py create mode 100644 openpyxl/styles/tests/__init__.py create mode 100644 openpyxl/styles/tests/conftest.py create mode 100644 openpyxl/styles/tests/data/alignment_styles.xml create mode 100644 openpyxl/styles/tests/data/builtins_as_custom_number_formats.xml create mode 100644 openpyxl/styles/tests/data/complex-styles.xml create mode 100644 openpyxl/styles/tests/data/dxf_style.xml create mode 100644 openpyxl/styles/tests/data/empty-workbook-styles.xml create mode 100644 openpyxl/styles/tests/data/no_number_format.xml create mode 100644 openpyxl/styles/tests/data/none_value_styles.xml create mode 100755 openpyxl/styles/tests/data/rgb_colors.xml create mode 100644 openpyxl/styles/tests/data/simple-styles.xml create mode 100644 openpyxl/styles/tests/data/styles_number_formats.xml create mode 100644 openpyxl/styles/tests/data/worksheet_unprotected_style.xml create mode 100644 openpyxl/styles/tests/test_alignments.py create mode 100644 openpyxl/styles/tests/test_borders.py create mode 100644 openpyxl/styles/tests/test_cell_style.py create mode 100644 openpyxl/styles/tests/test_colors.py create mode 100644 openpyxl/styles/tests/test_differential.py create mode 100644 openpyxl/styles/tests/test_fills.py create mode 100644 openpyxl/styles/tests/test_fonts.py create mode 100644 openpyxl/styles/tests/test_named_style.py create mode 100644 openpyxl/styles/tests/test_number_style.py create mode 100644 openpyxl/styles/tests/test_protection.py create mode 100644 openpyxl/styles/tests/test_proxy.py create mode 100644 openpyxl/styles/tests/test_styleable.py create mode 100644 openpyxl/styles/tests/test_stylesheet.py create mode 100644 openpyxl/styles/tests/test_table.py create mode 100644 openpyxl/tests/__init__.py create mode 100644 openpyxl/tests/conftest.py create mode 100644 openpyxl/tests/data/Thumbs.db create mode 100644 openpyxl/tests/data/genuine/empty-with-styles.xlsx create mode 100644 openpyxl/tests/data/genuine/empty.xlsx create mode 100644 openpyxl/tests/data/genuine/empty_libre.xlsx create mode 100644 openpyxl/tests/data/genuine/empty_no_dimensions.xlsx create mode 100644 openpyxl/tests/data/genuine/guess_types.xlsx create mode 100644 openpyxl/tests/data/genuine/libreoffice_nrt.xlsx create mode 100644 openpyxl/tests/data/genuine/sample.xlsx create mode 100644 openpyxl/tests/data/reader/bigfoot.xlsx create mode 100644 openpyxl/tests/data/reader/bug328_hyperlinks.xml create mode 100644 openpyxl/tests/data/reader/bug393-worksheet.xml create mode 100644 openpyxl/tests/data/reader/empty_rows.xml create mode 100644 openpyxl/tests/data/reader/merged-ranges.xml create mode 100644 openpyxl/tests/data/reader/nonstandard_workbook_name.xlsx create mode 100644 openpyxl/tests/data/reader/null_archive.xlsx create mode 100644 openpyxl/tests/data/reader/sheet2.xml create mode 100644 openpyxl/tests/data/reader/sheet2_invalid_dimension.xml create mode 100644 openpyxl/tests/data/reader/sheet2_no_dimension.xml create mode 100644 openpyxl/tests/data/reader/sheet2_no_span.xml create mode 100644 openpyxl/tests/data/reader/vba+comments.xlsm create mode 100644 openpyxl/tests/data/reader/vba-comments-saved.xlsm create mode 100644 openpyxl/tests/data/reader/vba-test.xlsm create mode 100644 openpyxl/tests/data/writer/Content_types_vba.xml create mode 100644 openpyxl/tests/data/writer/font.xml create mode 100644 openpyxl/tests/data/writer/styles.xml create mode 100644 openpyxl/tests/helper.py create mode 100644 openpyxl/tests/long/dump_writer_performance.py create mode 100644 openpyxl/tests/long/long_dump_thread.py create mode 100644 openpyxl/tests/notes_on_namespaces.rst create mode 100644 openpyxl/tests/schema.py create mode 100755 openpyxl/tests/schemas/dml-chart.xsd create mode 100755 openpyxl/tests/schemas/dml-chartDrawing.xsd create mode 100755 openpyxl/tests/schemas/dml-compatibility.xsd create mode 100755 openpyxl/tests/schemas/dml-diagram.xsd create mode 100755 openpyxl/tests/schemas/dml-lockedCanvas.xsd create mode 100755 openpyxl/tests/schemas/dml-main.xsd create mode 100755 openpyxl/tests/schemas/dml-picture.xsd create mode 100755 openpyxl/tests/schemas/dml-spreadsheetDrawing.xsd create mode 100755 openpyxl/tests/schemas/dml-wordprocessingDrawing.xsd create mode 100755 openpyxl/tests/schemas/opc-coreProperties.xsd create mode 100755 openpyxl/tests/schemas/opc-relationships.xsd create mode 100755 openpyxl/tests/schemas/pml.xsd create mode 100755 openpyxl/tests/schemas/shared-additionalCharacteristics.xsd create mode 100755 openpyxl/tests/schemas/shared-bibliography.xsd create mode 100755 openpyxl/tests/schemas/shared-commonSimpleTypes.xsd create mode 100755 openpyxl/tests/schemas/shared-customXmlDataProperties.xsd create mode 100755 openpyxl/tests/schemas/shared-customXmlSchemaProperties.xsd create mode 100755 openpyxl/tests/schemas/shared-documentPropertiesCustom.xsd create mode 100755 openpyxl/tests/schemas/shared-documentPropertiesExtended.xsd create mode 100755 openpyxl/tests/schemas/shared-documentPropertiesVariantTypes.xsd create mode 100755 openpyxl/tests/schemas/shared-math.xsd create mode 100755 openpyxl/tests/schemas/shared-relationshipReference.xsd create mode 100755 openpyxl/tests/schemas/sml.xsd create mode 100755 openpyxl/tests/schemas/vml-main.xsd create mode 100755 openpyxl/tests/schemas/vml-officeDrawing.xsd create mode 100755 openpyxl/tests/schemas/vml-presentationDrawing.xsd create mode 100755 openpyxl/tests/schemas/vml-spreadsheetDrawing.xsd create mode 100755 openpyxl/tests/schemas/vml-wordprocessingDrawing.xsd create mode 100755 openpyxl/tests/schemas/wml.xsd create mode 100644 openpyxl/tests/test_backend.py create mode 100644 openpyxl/tests/test_iter.py create mode 100644 openpyxl/tests/test_read.py create mode 100644 openpyxl/tests/test_vba.py create mode 100644 openpyxl/utils/__init__.py create mode 100644 openpyxl/utils/bound_dictionary.py create mode 100644 openpyxl/utils/cell.py create mode 100644 openpyxl/utils/dataframe.py create mode 100644 openpyxl/utils/datetime.py create mode 100644 openpyxl/utils/escape.py create mode 100644 openpyxl/utils/exceptions.py create mode 100644 openpyxl/utils/formulas.py create mode 100644 openpyxl/utils/indexed_list.py create mode 100644 openpyxl/utils/protection.py create mode 100644 openpyxl/utils/tests/__init__.py create mode 100644 openpyxl/utils/tests/test_bound_dictionary.py create mode 100644 openpyxl/utils/tests/test_cell.py create mode 100644 openpyxl/utils/tests/test_dataframe.py create mode 100644 openpyxl/utils/tests/test_datetime.py create mode 100644 openpyxl/utils/tests/test_escape.py create mode 100644 openpyxl/utils/tests/test_indexed_list.py create mode 100644 openpyxl/utils/tests/test_protection.py create mode 100644 openpyxl/utils/tests/test_units.py create mode 100644 openpyxl/utils/units.py create mode 100644 openpyxl/workbook/__init__.py create mode 100644 openpyxl/workbook/child.py create mode 100644 openpyxl/workbook/defined_name.py create mode 100644 openpyxl/workbook/external_link/__init__.py create mode 100644 openpyxl/workbook/external_link/external.py create mode 100644 openpyxl/workbook/external_link/tests/__init__.py create mode 100644 openpyxl/workbook/external_link/tests/conftest.py create mode 100644 openpyxl/workbook/external_link/tests/data/OLELink.xml create mode 100644 openpyxl/workbook/external_link/tests/data/[Content_Types].xml create mode 100644 openpyxl/workbook/external_link/tests/data/book1.xlsx create mode 100644 openpyxl/workbook/external_link/tests/data/book2.xlsx create mode 100644 openpyxl/workbook/external_link/tests/data/externalLink1.xml create mode 100644 openpyxl/workbook/external_link/tests/data/externalLink1.xml.rels create mode 100644 openpyxl/workbook/external_link/tests/data/merge_range.xlsx create mode 100644 openpyxl/workbook/external_link/tests/data/workbook.xml create mode 100644 openpyxl/workbook/external_link/tests/data/workbook.xml.rels create mode 100644 openpyxl/workbook/external_link/tests/data/workbook_external_range.xml create mode 100644 openpyxl/workbook/external_link/tests/data/workbook_namedrange.xml create mode 100644 openpyxl/workbook/external_link/tests/test_external.py create mode 100644 openpyxl/workbook/external_reference.py create mode 100644 openpyxl/workbook/function_group.py create mode 100644 openpyxl/workbook/parser.py create mode 100644 openpyxl/workbook/properties.py create mode 100644 openpyxl/workbook/protection.py create mode 100644 openpyxl/workbook/smart_tags.py create mode 100644 openpyxl/workbook/tests/__init__.py create mode 100644 openpyxl/workbook/tests/conftest.py create mode 100644 openpyxl/workbook/tests/data/broken_print_titles.xml create mode 100644 openpyxl/workbook/tests/data/workbook.xml create mode 100644 openpyxl/workbook/tests/data/workbook_russian_code_name.xml create mode 100644 openpyxl/workbook/tests/test_child.py create mode 100644 openpyxl/workbook/tests/test_defined_name.py create mode 100644 openpyxl/workbook/tests/test_external_reference.py create mode 100644 openpyxl/workbook/tests/test_function_group.py create mode 100644 openpyxl/workbook/tests/test_parser.py create mode 100644 openpyxl/workbook/tests/test_pivot.py create mode 100644 openpyxl/workbook/tests/test_properties.py create mode 100644 openpyxl/workbook/tests/test_protection.py create mode 100644 openpyxl/workbook/tests/test_smart_tags.py create mode 100644 openpyxl/workbook/tests/test_views.py create mode 100644 openpyxl/workbook/tests/test_web.py create mode 100644 openpyxl/workbook/tests/test_workbook.py create mode 100644 openpyxl/workbook/views.py create mode 100644 openpyxl/workbook/web.py create mode 100644 openpyxl/workbook/workbook.py create mode 100644 openpyxl/worksheet/__init__.py create mode 100644 openpyxl/worksheet/cell_range.py create mode 100644 openpyxl/worksheet/copier.py create mode 100644 openpyxl/worksheet/datavalidation.py create mode 100644 openpyxl/worksheet/dimensions.py create mode 100644 openpyxl/worksheet/drawing.py create mode 100644 openpyxl/worksheet/filters.py create mode 100644 openpyxl/worksheet/header_footer.py create mode 100644 openpyxl/worksheet/hyperlink.py create mode 100644 openpyxl/worksheet/merge.py create mode 100644 openpyxl/worksheet/page.py create mode 100644 openpyxl/worksheet/pagebreak.py create mode 100644 openpyxl/worksheet/properties.py create mode 100644 openpyxl/worksheet/protection.py create mode 100644 openpyxl/worksheet/read_only.py create mode 100644 openpyxl/worksheet/related.py create mode 100644 openpyxl/worksheet/table.py create mode 100644 openpyxl/worksheet/tests/__init__.py create mode 100644 openpyxl/worksheet/tests/conftest.py create mode 100644 openpyxl/worksheet/tests/data/copy_test.xlsx create mode 100644 openpyxl/worksheet/tests/data/sheetPr2.xml create mode 100644 openpyxl/worksheet/tests/data/sheet_inline_strings.xml create mode 100644 openpyxl/worksheet/tests/test_cell_range.py create mode 100644 openpyxl/worksheet/tests/test_datavalidation.py create mode 100644 openpyxl/worksheet/tests/test_dimensions.py create mode 100644 openpyxl/worksheet/tests/test_filters.py create mode 100644 openpyxl/worksheet/tests/test_header.py create mode 100644 openpyxl/worksheet/tests/test_hyperlink.py create mode 100644 openpyxl/worksheet/tests/test_page.py create mode 100644 openpyxl/worksheet/tests/test_pagebreak.py create mode 100644 openpyxl/worksheet/tests/test_properties.py create mode 100644 openpyxl/worksheet/tests/test_protection.py create mode 100644 openpyxl/worksheet/tests/test_read_only.py create mode 100644 openpyxl/worksheet/tests/test_related.py create mode 100644 openpyxl/worksheet/tests/test_table.py create mode 100644 openpyxl/worksheet/tests/test_views.py create mode 100644 openpyxl/worksheet/tests/test_worksheet.py create mode 100644 openpyxl/worksheet/tests/test_worksheet_copy.py create mode 100644 openpyxl/worksheet/tests/test_write_only.py create mode 100644 openpyxl/worksheet/views.py create mode 100644 openpyxl/worksheet/worksheet.py create mode 100644 openpyxl/worksheet/write_only.py create mode 100644 openpyxl/writer/__init__.py create mode 100644 openpyxl/writer/etree_worksheet.py create mode 100644 openpyxl/writer/excel.py create mode 100644 openpyxl/writer/strings.py create mode 100644 openpyxl/writer/tests/__init__.py create mode 100644 openpyxl/writer/tests/conftest.py create mode 100644 openpyxl/writer/tests/data/[Content_Types].xml create mode 100644 openpyxl/writer/tests/data/empty.xlsm create mode 100644 openpyxl/writer/tests/data/empty.xlsx create mode 100644 openpyxl/writer/tests/data/empty.xltm create mode 100644 openpyxl/writer/tests/data/empty.xltx create mode 100644 openpyxl/writer/tests/data/plain.png create mode 100644 openpyxl/writer/tests/data/sharedStrings.xml create mode 100644 openpyxl/writer/tests/data/theme1.xml create mode 100644 openpyxl/writer/tests/data/vba+comments.xlsm create mode 100644 openpyxl/writer/tests/data/workbook.xml create mode 100644 openpyxl/writer/tests/data/workbook.xml.rels create mode 100644 openpyxl/writer/tests/data/workbook_auto_filter.xml create mode 100644 openpyxl/writer/tests/data/workbook_protection.xml create mode 100644 openpyxl/writer/tests/data/workbook_vba.xml.rels create mode 100644 openpyxl/writer/tests/test_excel.py create mode 100644 openpyxl/writer/tests/test_strings.py create mode 100644 openpyxl/writer/tests/test_template.py create mode 100644 openpyxl/writer/tests/test_theme.py create mode 100644 openpyxl/writer/tests/test_workbook.py create mode 100644 openpyxl/writer/tests/test_worksheet.py create mode 100644 openpyxl/writer/theme.py create mode 100644 openpyxl/writer/workbook.py create mode 100644 openpyxl/writer/worksheet.py create mode 100644 openpyxl/xml/__init__.py create mode 100644 openpyxl/xml/constants.py create mode 100644 openpyxl/xml/functions.py create mode 100644 openpyxl/xml/tests/__init__.py create mode 100644 openpyxl/xml/tests/test_functions.py create mode 100644 pytest.ini create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100755 setup.py create mode 100644 shippable.yml create mode 100644 tox.ini diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 0000000..1f2082e --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,75 @@ +This project was started by Eric Gazoni. In 2013 Charlie Clark became +co-maintainer of the project. + +It was initiallay *heavily* inspired by the PHPExcel library: +http://www.phpexcel.net/ + +Thanks to all those who participate in the project (in alphabetical order): + +* Stephane Bard +* Day Barr +* Stefan Behnel +* Bernt R. Brenna +* Sven Burk +* Max Bolingbroke +* Anders Chrigstrom +* ccoacley +* Maarten De Paepe +* Etienne Desautels +* Dmitriy Chernyshov +* Eric Chlebek +* Alexandre Fayolle +* Don Freeman +* Eric Gazoni +* Brice Gelineau +* Mark Gemmill +* Alex Gronholm +* Yaroslav Halchenko +* Fumito Hamamura +* Khchine Hamza +* Josh Haywood +* Jeff Holman +* Brent Hoover +* Eric Hurkman +* Jean Pierre Huart +* JarekPS +* Heikki Junes +* Chi Ho Kwok +* Yingjie Lan +* Detlef Lannert +* Laurent Laporte +* Nicholas Laver +* Greg Lehmann +* Adam Lofts +* Marko Loparic +* Samuel Loretan +* Amin Mirzaee +* Adam Morris +* aceMueller +* Gabi Nagy +* Thomas Nygards +* Felipe Ochoa +* Jun Omae +* Waldemar Osuch +* Jonathan Peirce +* Sergey Pikhovkin +* Ted Pollari +* Elias Rabel +* Rick Rankin +* ramn_se +* Philip Roche +* Wojciech Rola +* James Smagala +* Wolfgane Scherer +* Joseph Tate +* Gar Thompson +* Dieter Vandenbussche +* Paul Van Der Linden +* Gerald Van Huffelen +* Koert van der Veer +* Laurent Vasseur +* Kay Webber +* Shibukawa Yoshiki + +Project logo designed by Eric Gazoni, font by claudeserieux +(http://www.dafont.com/profile.php?user=337503) diff --git a/LICENCE.rst b/LICENCE.rst new file mode 100644 index 0000000..82213c5 --- /dev/null +++ b/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/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..04cdea9 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,10 @@ +prune openpyxl/tests +prune openpyxl/sample +prune openpyxl/benchmarks +prune openpyxl/develop +prune scratchpad + +recursive-exclude openpyxl test_*.py tests/*.py + +include *.rst +include openpyxl/.constants.json diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..4a739b6 --- /dev/null +++ b/README.rst @@ -0,0 +1,60 @@ +Introduction +============ +This is a fork from bitbucket of openpyxl package: https://bitbucket.org/openpyxl/openpyxl/src/default/ +I have repaired the image and chart loading bugs in this revised version. + +openpyxl +======== + +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. + + +Mailing List +============ + +Official 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") + + +Official documentation +====================== + +The documentation is at: https://openpyxl.readthedocs.io + +* installation methods +* code examples +* instructions for contributing + +Release notes: https://openpyxl.readthedocs.io/en/latest/changes.html + +Fix the image and chart loading bugs +====================================== + +Now you can add image or chart to the excel as you wish. They can be reserved when reopening the excel file. diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 0000000..50df006 --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1,9 @@ +image: openpyxl/openpyxl-ci + +pipelines: + default: + - step: + caches: + - pip + script: + - /tools/clean-launch.sh tox --skip-missing-interpreters -- -qrf \ No newline at end of file diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..c7e9d1f --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,130 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/openpyxl.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/openpyxl.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/openpyxl" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/openpyxl" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/_static/.placeholder b/doc/_static/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/doc/changes.rst b/doc/changes.rst new file mode 100644 index 0000000..6a4c896 --- /dev/null +++ b/doc/changes.rst @@ -0,0 +1,1224 @@ +2.5.3 (2018-04-18) +================== + + +Bugfixes +-------- + +* `#983 `_ Warning level too aggressive. +* `#1015 `_ Alignment and protection values not saved for named styles. +* `#1017 `_ Deleting elements from a legend doesn't work. +* `#1018 `_ Index names repeated for every row in dataframe. +* `#1020 `_ Worksheet protection not being stored. +* `#1023 `_ Exception raised when reading a tooltip. + + +2.5.2 (2018-04-06) +================== + + +Bugfixes +-------- + +* `#949 `_ High memory use when reading text-heavy files. +* `#970 `_ Copying merged cells copies references. +* `#978 `_ Cannot set comment size. +* `#985 `_ Exception when trying to save workbooks with no views. +* `#995 `_ Cannot delete last row or column. +* `#1002 `_ Cannot read Drawings containing embedded images. + + +Minor changes +------------- + +* Support for dataframes with multiple columns and multiple indices. + + +2.5.1 (2018-03-12) +================== + + +Bugfixes +-------- + +* `#934 `_ Headers and footers not included in write-only mode. +* `#960 `_ Deprecation warning raised when using ad-hoc access in read-only mode. +* `#964 `_ Not all cells removed when deleting multiple rows. +* `#966 `_ Cannot read 3d bar chart correctly. +* `#967 `_ Problems reading some charts. +* `#968 `_ Worksheets with SHA protection become corrupted after saving. +* `#974 `_ Problem when deleting ragged rows or columns. +* `#976 `_ GroupTransforms and GroupShapeProperties have incorrect descriptors +* Make sure that headers and footers in chartsheets are included in the file + + + +2.5.0 (2018-01-24) +================== + + +Minor changes +------------- + +* Correct definition for Connection Shapes. Related to # 958 + + +2.5.0-b2 (2018-01-19) +===================== + + +Bugfixes +-------- + +* `#915 `_ TableStyleInfo has no required attributes +* `#925 `_ Cannot read files with 3D drawings +* `#926 `_ Incorrect version check in installer +* Cell merging uses transposed parameters +* `#928 `_ ExtLst missing keyword for PivotFields +* `#932 `_ Inf causes problems for Excel +* `#952 `_ Cannot load table styles with custom names + + +Major Changes +------------- + +* You can now insert and delete rows and columns in worksheets + + +Minor Changes +------------- + +* pip now handles which Python versions can be used. + + +2.5.0-b1 (2017-10-19) +===================== + + +Bugfixes +-------- +* `#812 `_ Explicitly support for multiple cell ranges in conditonal formatting +* `#827 `_ Non-contiguous cell ranges in validators get merged +* `#837 `_ Empty data validators create invalid Excel files +* `#860 `_ Large validation ranges use lots of memory +* `#876 `_ Unicode in chart axes not handled correctly in Python 2 +* `#882 `_ ScatterCharts have defective axes +* `#885 `_ Charts with empty numVal elements cannot be read +* `#894 `_ Scaling options from existing files ignored +* `#895 `_ Charts with PivotSource cannot be read +* `#903 `_ Cannot read gradient fills +* `#904 `_ Quotes in number formats could be treated as datetimes + + +Major Changes +------------- + +`worksheet.cell()` no longer accepts a `coordinate` parameter. The syntax is now `ws.cell(row, column, value=None)` + + +Minor Changes +------------- + +Added CellRange and MultiCellRange types (thanks to Laurent LaPorte for the +suggestion) as a utility type for things like data validations, conditional +formatting and merged cells. + + +Deprecations +------------ + +ws.merged_cell_ranges has been deprecated because MultiCellRange provides sufficient functionality + + +2.5.0-a3 (2017-08-14) +===================== + + +Bugfixes +-------- +* `#848 `_ Reading workbooks with Pie Charts raises an exception +* `#857 `_ Pivot Tables without Worksheet Sources raise an exception + + +2.5.0-a2 (2017-06-25) +===================== + + +Major Changes +------------- + +* Read support for charts + + +Bugfixes +-------- +* `#833 `_ Cannot access chartsheets by title +* `#834 `_ Preserve workbook views +* `#841 `_ Incorrect classification of a datetime + + +2.5.0-a1 (2017-05-30) +===================== + + +Compatibility +------------- + +* Dropped support for Python 2.6 and 3.3. openpyxl will not run with Python 2.6 + + +Major Changes +------------- + +* Read/write support for pivot tables + + +Deprecations +------------ + +* Dropped the anchor method from images and additional constructor arguments + + +Bugfixes +-------- +* `#779 `_ Fails to recognise Chinese date format` +* `#828 `_ Include hidden cells in charts` + + +Pull requests +------------- +* `163 `_ Improved GradientFill + + +Minor changes +------------- + +* Remove deprecated methods from Cell +* Remove deprecated methods from Worksheet +* Added read/write support for the datetime type for cells + + +2.4.11 (2018-01-24) +=================== + +* #957 ``_ Relationship type for tables is borked + + +2.4.10 (2018-01-19) +=================== + +Bugfixes +-------- + +* #912 ``_ Copying objects uses shallow copy +* #921 ``_ API documentation not generated automatically +* #927 ``_ Exception raised when adding coloured borders together +* #931 ``_ Number formats not correctly deduplicated + + +Pull requests +------------- + +* 203 ``_ Correction to worksheet protection description +* 210 ``_ Some improvements to the API docs +* 211 ``_ Improved deprecation decorator +* 218 ``_ Fix problems with deepcopy + + +2.4.9 (2017-10-19) +================== + + +Bugfixes +-------- + +* `#809 `_ Incomplete documentation of `copy_worksheet` method +* `#811 `_ Scoped definedNames not removed when worksheet is deleted +* `#824 `_ Raise an exception if a chart is used in multiple sheets +* `#842 `_ Non-ASCII table column headings cause an exception in Python 2 +* `#846 `_ Conditional formats not supported in write-only mode +* `#849 `_ Conditional formats with no sqref cause an exception +* `#859 `_ Headers that start with a number conflict with font size +* `#902 `_ TableStyleElements don't always have a condtional format +* `#908 `_ Read-only mode sometimes returns too many cells + + + +Pull requests +------------- + +* `#179 `_ Cells kept in a set +* `#180 `_ Support for Workbook protection +* `#182 `_ Read support for page breaks +* `#183 `_ Improve documentation of `copy_worksheet` method +* `#198 `_ Fix for #908 + + +2.4.8 (2017-05-30) +================== + + +Bugfixes +-------- + +* AutoFilter.sortState being assignd to the ws.sortState +* `#766 `_ Sheetnames with apostrophes need additional escaping +* `#729 `_ Cannot open files created by Microsoft Dynamics +* `#819 `_ Negative percents not case correctly +* `#821 `_ Runtime imports can cause deadlock +* `#855 `_ Print area containing only columns leads to corrupt file + + +Minor changes +------------- +* Preserve any table styles + + +2.4.7 (2017-04-24) +================== + + +Bugfixes +-------- +* `#807 `_ Sample files being included by mistake in sdist + + +2.4.6 (2017-04-14) +================== + + +Bugfixes +-------- +* `#776 `_ Cannot apply formatting to plot area +* `#780 `_ Exception when element attributes are Python keywords +* `#781 `_ Exception raised when saving files with styled columns +* `#785 `_ Number formats for data labels are incorrect +* `#788 `_ Worksheet titles not quoted in defined names +* `#800 `_ Font underlines not read correctly + + +2.4.5 (2017-03-07) +================== + + +Bugfixes +-------- +* `#750 `_ Adding images keeps file handles open +* `#772 `_ Exception for column-only ranges +* `#773 `_ Cannot copy worksheets with non-ascii titles on Python 2 + + +Pull requests +------------- + +* `161 `_ Support for non-standard names for Workbook part. +* `162 `_ Documentation correction + + +2.4.4 (2017-02-23) +================== + + +Bugfixes +-------- + +* `#673 `_ Add close method to workbooks +* `#762 `_ openpyxl can create files with invalid style indices +* `#729 `_ Allow images in write-only mode +* `#744 `_ Rounded corners for charts +* `#747 `_ Use repr when handling non-convertible objects +* `#764 `_ Hashing function is incorrect +* `#765 `_ Named styles share underlying array + + +Minor Changes +------------- + +* Add roundtrip support for worksheet tables. + + +Pull requests +------------- + +* `160 `_ Don't init mimetypes more than once. + + +2.4.3 (unreleased) +================== +bad release + + +2.4.2 (2017-01-31) +================== + + +Bug fixes +--------- + +* `#727 `_ DeprecationWarning is incorrect +* `#734 `_ Exception raised if userName is missing +* `#739 `_ Always provide a date1904 attribute +* `#740 `_ Hashes should be stored as Base64 +* `#743 `_ Print titles broken on sheetnames with spaces +* `#748 `_ Workbook breaks when active sheet is removed +* `#754 `_ Incorrect descriptor for Filter values +* `#756 `_ Potential XXE vulerability +* `#758 `_ Cannot create files with page breaks and charts +* `#759 `_ Problems with worksheets with commas in their titles + + +Minor Changes +------------- + +* Add unicode support for sheet name incrementation. + + +2.4.1 (2016-11-23) +================== + + +Bug fixes +--------- + +* `#643 `_ Make checking for duplicate sheet titles case insensitive +* `#647 `_ Trouble handling LibreOffice files with named styles +* `#687 `_ Directly assigned new named styles always refer to "Normal" +* `#690 `_ Cannot parse print titles with multiple sheet names +* `#691 `_ Cannot work with macro files created by LibreOffice +* Prevent duplicate differential styles +* `#694 `_ Allow sheet titles longer than 31 characters +* `#697 `_ Cannot unset hyperlinks +* `#699 `_ Exception raised when format objects use cell references +* `#703 `_ Copy height and width when copying comments +* `#705 `_ Incorrect content type for VBA macros +* `#707 `_ IndexError raised in read-only mode when accessing individual cells +* `#711 `_ Files with external links become corrupted +* `#715 `_ Cannot read files containing macro sheets +* `#717 `_ Details from named styles not preserved when reading files +* `#722 `_ Remove broken Print Title and Print Area definitions + + +Minor changes +------------- + +* Add support for Python 3.6 +* Correct documentation for headers and footers + + +Deprecations +------------ + +Worksheet methods `get_named_range()` and `get_sqaured_range()` + + +Bug fixes +--------- + + +2.4.0 (2016-09-15) +================== + + +Bug fixes +--------- + +* `#652 `_ Exception raised when epoch is 1904 +* `#642 `_ Cannot handle unicode in headers and footers in Python 2 +* `#646 `_ Cannot handle unicode sheetnames in Python 2 +* `#658 `_ Chart styles, and axis units should not be 0 +* `#663 `_ Strings in external workbooks not unicode + + +Major changes +------------- + +* Add support for builtin styles and include one for Pandas + + +Minor changes +------------- + +* Add a `keep_links` option to `load_workbook`. External links contain cached + copies of the external workbooks. If these are big it can be advantageous to + be able to disable them. +* Provide an example for using cell ranges in DataValidation. +* PR 138 - add copy support to comments. + + +2.4.0-b1 (2016-06-08) +===================== + + +Minor changes +------------- + +* Add an the alias `hide_drop_down` to DataValidation for `showDropDown` because that is how Excel works. + + +Bug fixes +--------- + +* `#625 `_ Exception raises when inspecting EmptyCells in read-only mode +* `#547 `_ Functions for handling OOXML "escaped" ST_XStrings +* `#629 `_ Row Dimensions not supported in write-only mode +* `#530 `_ Problems when removing worksheets with charts +* `#630 `_ Cannot use SheetProtection in write-only mode + + +Features +-------- + +* Add write support for worksheet tables + + +2.4.0-a1 (2016-04-11) +===================== + + +Minor changes +------------- + +* Remove deprecated methods from DataValidation +* Remove deprecated methods from PrintPageSetup +* Convert AutoFilter to Serialisable and extend support for filters +* Add support for SortState +* Removed `use_iterators` keyword when loading workbooks. Use `read_only` instead. +* Removed `optimized_write` keyword for new workbooks. Use `write_only` instead. +* Improve print title support +* Add print area support +* New implementation of defined names +* New implementation of page headers and footers +* Add support for Python's NaN +* Added iter_cols method for worksheets +* ws.rows and ws.columns now always return generators and start at the top of the worksheet +* Add a `values` property for worksheets +* Default column width changed to 8 as per the specification + + +Deprecations +------------ + +* Cell anchor method +* Worksheet point_pos method +* Worksheet add_print_title method +* Worksheet HeaderFooter attribute, replaced by individual ones +* Flatten function for cells +* Workbook get_named_range, add_named_range, remove_named_range, get_sheet_names, get_sheet_by_name +* Comment text attribute +* Use of range strings deprecated for ws.iter_rows() +* Use of coordinates deprecated for ws.cell() +* Deprecate .copy() method for StyleProxy objects + + +Bug fixes +--------- + +* `#152 `_ Hyperlinks lost when reading files +* `#171 `_ Add function for copying worksheets +* `#386 `_ Cells with inline strings considered empty +* `#397 `_ Add support for ranges of rows and columns +* `#446 `_ Workbook with definedNames corrupted by openpyxl +* `#481 `_ "safe" reserved ranges are not read from workbooks +* `#501 `_ Discarding named ranges can lead to corrupt files +* `#574 `_ Exception raised when using the class method to parse Relationships +* `#579 `_ Crashes when reading defined names with no content +* `#597 `_ Cannot read worksheets without coordinates +* `#617 `_ Customised named styles not correctly preserved + + +2.3.5 (2016-04-11) +================== + + +Bug fixes +--------- + +* `#618 `_ Comments not written in write-only mode + + +2.3.4 (2016-03-16) +================== + + +Bug fixes +--------- + +* `#594 `_ Content types might be missing when keeping VBA +* `#599 `_ Cells with only one cell look empty +* `#607 `_ Serialise NaN as '' + + +Minor changes +------------- + +* Preserve the order of external references because formualae use numerical indices. +* Typo corrected in cell unit tests (PR 118) + + +2.3.3 (2016-01-18) +================== + + +Bug fixes +--------- + +* `#540 `_ Cannot read merged cells in read-only mode +* `#565 `_ Empty styled text blocks cannot be parsed +* `#569 `_ Issue warning rather than raise Exception raised for unparsable definedNames +* `#575 `_ Cannot open workbooks with embdedded OLE files +* `#584 `_ Exception when saving borders with attributes + + +Minor changes +------------- + +* `PR 103 `_ Documentation about chart scaling and axis limits +* Raise an exception when trying to copy cells from other workbooks. + + +2.3.2 (2015-12-07) +================== + + +Bug fixes +--------- + +* `#554 `_ Cannot add comments to a worksheet when preserving VBA +* `#561 `_ Exception when reading phonetic text +* `#562 `_ DARKBLUE is the same as RED +* `#563 `_ Minimum for row and column indexes not enforced + + +Minor changes +------------- + +* `PR 97 `_ One VML file per worksheet. +* `PR 96 `_ Correct descriptor for CharacterProperties.rtl +* `#498 `_ Metadata is not essential to use the package. + + +2.3.1 (2015-11-20) +================== + + +Bug fixes +--------- + +* `#534 `_ Exception when using columns property in read-only mode. +* `#536 `_ Incorrectly handle comments from Google Docs files. +* `#539 `_ Flexible value types for conditional formatting. +* `#542 `_ Missing content types for images. +* `#543 `_ Make sure images fit containers on all OSes. +* `#544 `_ Gracefully handle missing cell styles. +* `#546 `_ ExternalLink duplicated when editing a file with macros. +* `#548 `_ Exception with non-ASCII worksheet titles +* `#551 `_ Combine multiple LineCharts + + +Minor changes +------------- + +* `PR 88 `_ Fix page margins in parser. + + +2.3.0 (2015-10-20) +================== + + +Major changes +------------- + +* Support the creation of chartsheets + + +Bug fixes +--------- + +* `#532 `_ Problems when cells have no style in read-only mode. + + +Minor changes +------------- + +* PR 79 Make PlotArea editable in charts +* Use graphicalProperties as the alias for spPr + + +2.3.0-b2 (2015-09-04) +===================== + + +Bug fixes +--------- + +* `#488 `_ Support hashValue attribute for sheetProtection +* `#493 `_ Warn that unsupported extensions will be dropped +* `#494 `_ Cells with exponentials causes a ValueError +* `#497 `_ Scatter charts are broken +* `#499 `_ Inconsistent conversion of localised datetimes +* `#500 `_ Adding images leads to unreadable files +* `#509 `_ Improve handling of sheet names +* `#515 `_ Non-ascii titles have bad repr +* `#516 `_ Ignore unassigned worksheets + + +Minor changes +------------- + +* Worksheets are now iterable by row. +* Assign individual cell styles only if they are explicitly set. + + +2.3.0-b1 (2015-06-29) +===================== + + +Major changes +------------- + +* Shift to using (row, column) indexing for cells. Cells will at some point *lose* coordinates. +* New implementation of conditional formatting. Databars now partially preserved. +* et_xmlfile is now a standalone library. +* Complete rewrite of chart package +* Include a tokenizer for fomulae to be able to adjust cell references in them. PR 63 + + +Minor changes +------------- + +* Read-only and write-only worksheets renamed. +* Write-only workbooks support charts and images. +* `PR76 `_ Prevent comment images from conflicting with VBA + + +Bug fixes +--------- + +* `#81 `_ Support stacked bar charts +* `#88 `_ Charts break hyperlinks +* `#97 `_ Pie and combination charts +* `#99 `_ Quote worksheet names in chart references +* `#150 `_ Support additional chart options +* `#172 `_ Support surface charts +* `#381 `_ Preserve named styles +* `#470 `_ Adding more than 10 worksheets with the same name leads to duplicates sheet names and an invalid file + + +2.2.6 (unreleased) +================== + + +Bug fixes +--------- + +* `#502 `_ Unexpected keyword "mergeCell" +* `#503 `_ tostring missing in dump_worksheet +* `#506 `_ Non-ASCII formulae cannot be parsed +* `#508 `_ Cannot save files with coloured tabs +* Regex for ignoring named ranges is wrong (character class instead of prefix) + + +2.2.5 (2015-06-29) +================== + + +Bug fixes +--------- + +* `#463 `_ Unexpected keyword "mergeCell" +* `#484 `_ Unusual dimensions breaks read-only mode +* `#485 `_ Move return out of loop + + +2.2.4 (2015-06-17) +================== + + +Bug fixes +--------- + +* `#464 `_ Cannot use images when preserving macros +* `#465 `_ ws.cell() returns an empty cell on read-only workbooks +* `#467 `_ Cannot edit a file with ActiveX components +* `#471 `_ Sheet properties elements must be in order +* `#475 `_ Do not redefine class __slots__ in subclasses +* `#477 `_ Write-only support for SheetProtection +* `#478 `_ Write-only support for DataValidation +* Improved regex when checking for datetime formats + + +2.2.3 (2015-05-26) +================== + + +Bug fixes +--------- + +* `#451 `_ fitToPage setting ignored +* `#458 `_ Trailing spaces lost when saving files. +* `#459 `_ setup.py install fails with Python 3 +* `#462 `_ Vestigial rId conflicts when adding charts, images or comments +* `#455 `_ Enable Zip64 extensions for all versions of Python + + +2.2.2 (2015-04-28) +================== + + +Bug fixes +--------- + +* `#447 `_ Uppercase datetime number formats not recognised. +* `#453 `_ Borders broken in shared_styles. + + +2.2.1 (2015-03-31) +================== + + +Minor changes +------------- + +* `PR54 `_ Improved precision on times near midnight. +* `PR55 `_ Preserve macro buttons + + +Bug fixes +--------- + +* `#429 `_ Workbook fails to load because header and footers cannot be parsed. +* `#433 `_ File-like object with encoding=None +* `#434 `_ SyntaxError when writing page breaks. +* `#436 `_ Read-only mode duplicates empty rows. +* `#437 `_ Cell.offset raises an exception +* `#438 `_ Cells with pivotButton and quotePrefix styles cannot be read +* `#440 `_ Error when customised versions of builtin formats +* `#442 `_ Exception raised when a fill element contains no children +* `#444 `_ Styles cannot be copied + + +2.2.0 (2015-03-11) +================== + + +Bug fixes +--------- +* `#415 `_ Improved exception when passing in invalid in memory files. + + +2.2.0-b1 (2015-02-18) +===================== + + +Major changes +------------- +* Cell styles deprecated, use formatting objects (fonts, fills, borders, etc.) directly instead +* Charts will no longer try and calculate axes by default +* Support for template file types - PR21 +* Moved ancillary functions and classes into utils package - single place of reference +* `PR 34 `_ Fully support page setup +* Removed SAX-based XML Generator. Special thanks to Elias Rabel for implementing xmlfile for xml.etree +* Preserve sheet view definitions in existing files (frozen panes, zoom, etc.) + + +Bug fixes +--------- +* `#103 `_ Set the zoom of a sheet +* `#199 `_ Hide gridlines +* `#215 `_ Preserve sheet view setings +* `#262 `_ Set the zoom of a sheet +* `#392 `_ Worksheet header not read +* `#387 `_ Cannot read files without styles.xml +* `#410 `_ Exception when preserving whitespace in strings +* `#417 `_ Cannot create print titles +* `#420 `_ Rename confusing constants +* `#422 `_ Preserve color index in a workbook if it differs from the standard + + +Minor changes +------------- +* Use a 2-way cache for column index lookups +* Clean up tests in cells +* `PR 40 `_ Support frozen panes and autofilter in write-only mode +* Use ws.calculate_dimension(force=True) in read-only mode for unsized worksheets + + +2.1.5 (2015-02-18) +================== + + +Bug fixes +--------- +* `#403 `_ Cannot add comments in write-only mode +* `#401 `_ Creating cells in an empty row raises an exception +* `#408 `_ from_excel adjustment for Julian dates 1 < x < 60 +* `#409 `_ refersTo is an optional attribute + + +Minor changes +------------- +* Allow cells to be appended to standard worksheets for code compatibility with write-only mode. + + +2.1.4 (2014-12-16) +================== + + +Bug fixes +--------- + +* `#393 `_ IterableWorksheet skips empty cells in rows +* `#394 `_ Date format is applied to all columns (while only first column contains dates) +* `#395 `_ temporary files not cleaned properly +* `#396 `_ Cannot write "=" in Excel file +* `#398 `_ Cannot write empty rows in write-only mode with LXML installed + + +Minor changes +------------- +* Add relation namespace to root element for compatibility with iWork +* Serialize comments relation in LXML-backend + + +2.1.3 (2014-12-09) +================== + + +Minor changes +------------- +* `PR 31 `_ Correct tutorial +* `PR 32 `_ See #380 +* `PR 37 `_ Bind worksheet to ColumnDimension objects + + +Bug fixes +--------- +* `#379 `_ ws.append() doesn't set RowDimension Correctly +* `#380 `_ empty cells formatted as datetimes raise exceptions + + +2.1.2 (2014-10-23) +================== + + +Minor changes +------------- +* `PR 30 `_ Fix regex for positive exponentials +* `PR 28 `_ Fix for #328 + + +Bug fixes +--------- +* `#120 `_, `#168 `_ defined names with formulae raise exceptions, `#292 `_ +* `#328 `_ ValueError when reading cells with hyperlinks +* `#369 `_ IndexError when reading definedNames +* `#372 `_ number_format not consistently applied from styles + + +2.1.1 (2014-10-08) +================== + + +Minor changes +------------- +* PR 20 Support different workbook code names +* Allow auto_axis keyword for ScatterCharts + + +Bug fixes +--------- + +* `#332 `_ Fills lost in ConditionalFormatting +* `#360 `_ Support value="none" in attributes +* `#363 `_ Support undocumented value for textRotation +* `#364 `_ Preserve integers in read-only mode +* `#366 `_ Complete read support for DataValidation +* `#367 `_ Iterate over unsized worksheets + + +2.1.0 (2014-09-21) +================== + +Major changes +------------- +* "read_only" and "write_only" new flags for workbooks +* Support for reading and writing worksheet protection +* Support for reading hidden rows +* Cells now manage their styles directly +* ColumnDimension and RowDimension object manage their styles directly +* Use xmlfile for writing worksheets if available - around 3 times faster +* Datavalidation now part of the worksheet package + + +Minor changes +------------- +* Number formats are now just strings +* Strings can be used for RGB and aRGB colours for Fonts, Fills and Borders +* Create all style tags in a single pass +* Performance improvement when appending rows +* Cleaner conversion of Python to Excel values +* PR6 reserve formatting for empty rows +* standard worksheets can append from ranges and generators + + +Bug fixes +--------- +* `#153 `_ Cannot read visibility of sheets and rows +* `#181 `_ No content type for worksheets +* `241 `_ Cannot read sheets with inline strings +* `322 `_ 1-indexing for merged cells +* `339 `_ Correctly handle removal of cell protection +* `341 `_ Cells with formulae do not round-trip +* `347 `_ Read DataValidations +* `353 `_ Support Defined Named Ranges to external workbooks + + +2.0.5 (2014-08-08) +================== + + +Bug fixes +--------- +* `#348 `_ incorrect casting of boolean strings +* `#349 `_ roundtripping cells with formulae + + +2.0.4 (2014-06-25) +================== + +Minor changes +------------- +* Add a sample file illustrating colours + + +Bug fixes +--------- + +* `#331 `_ DARKYELLOW was incorrect +* Correctly handle extend attribute for fonts + + +2.0.3 (2014-05-22) +================== + +Minor changes +------------- + +* Updated docs + + +Bug fixes +--------- + +* `#319 `_ Cannot load Workbooks with vertAlign styling for fonts + + +2.0.2 (2014-05-13) +================== + +2.0.1 (2014-05-13) brown bag +============================= + +2.0.0 (2014-05-13) brown bag +============================= + + +Major changes +------------- + +* This is last release that will support Python 3.2 +* Cells are referenced with 1-indexing: A1 == cell(row=1, column=1) +* Use jdcal for more efficient and reliable conversion of datetimes +* Significant speed up when reading files +* Merged immutable styles +* Type inference is disabled by default +* RawCell renamed ReadOnlyCell +* ReadOnlyCell.internal_value and ReadOnlyCell.value now behave the same as Cell +* Provide no size information on unsized worksheets +* Lower memory footprint when reading files + + +Minor changes +------------- + +* All tests converted to pytest +* Pyflakes used for static code analysis +* Sample code in the documentation is automatically run +* Support GradientFills +* BaseColWidth set + + +Pull requests +------------- +* #70 Add filterColumn, sortCondition support to AutoFilter +* #80 Reorder worksheets parts +* #82 Update API for conditional formatting +* #87 Add support for writing Protection styles, others +* #89 Better handling of content types when preserving macros + + +Bug fixes +--------- +* `#46 `_ ColumnDimension style error +* `#86 `_ reader.worksheet.fast_parse sets booleans to integers +* `#98 `_ Auto sizing column widths does not work +* `#137 `_ Workbooks with chartsheets +* `#185 `_ Invalid PageMargins +* `#230 `_ Using \v in cells creates invalid files +* `#243 `_ - IndexError when loading workbook +* `#263 `_ - Forded conversion of line breaks +* `#267 `_ - Raise exceptions when passed invalid types +* `#270 `_ - Cannot open files which use non-standard sheet names or reference Ids +* `#269 `_ - Handling unsized worksheets in IterableWorksheet +* `#270 `_ - Handling Workbooks with non-standard references +* `#275 `_ - Handling auto filters where there are only custom filters +* `#277 `_ - Harmonise chart and cell coordinates +* `#280 `_- Explicit exception raising for invalid characters +* `#286 `_ - Optimized writer can not handle a datetime.time value +* `#296 `_ - Cell coordinates not consistent with documentation +* `#300 `_ - Missing column width causes load_workbook() exception +* `#304 `_ - Handling Workbooks with absolute paths for worksheets (from Sharepoint) + + +1.8.6 (2014-05-05) +================== + +Minor changes +------------- +Fixed typo for import Elementtree + +Bugfixes +-------- +* `#279 `_ Incorrect path for comments files on Windows + + +1.8.5 (2014-03-25) +================== + +Minor changes +------------- +* The '=' string is no longer interpreted as a formula +* When a client writes empty xml tags for cells (e.g. ), reader will not crash + + +1.8.4 (2014-02-25) +================== + +Bugfixes +-------- +* `#260 `_ better handling of undimensioned worksheets +* `#268 `_ non-ascii in formualae +* `#282 `_ correct implementation of register_namepsace for Python 2.6 + + +1.8.3 (2014-02-09) +================== + +Major changes +------------- +Always parse using cElementTree + +Minor changes +------------- +Slight improvements in memory use when parsing + +* `#256 `_ - error when trying to read comments with optimised reader +* `#260 `_ - unsized worksheets +* `#264 `_ - only numeric cells can be dates + + +1.8.2 (2014-01-17) +================== + +* `#247 `_ - iterable worksheets open too many files +* `#252 `_ - improved handling of lxml +* `#253 `_ - better handling of unique sheetnames + + +1.8.1 (2014-01-14) +================== + +* `#246 `_ + + +1.8.0 (2014-01-08) +================== + +Compatibility +------------- + +Support for Python 2.5 dropped. + +Major changes +------------- + +* Support conditional formatting +* Support lxml as backend +* Support reading and writing comments +* pytest as testrunner now required +* Improvements in charts: new types, more reliable + + +Minor changes +------------- + +* load_workbook now accepts data_only to allow extracting values only from + formulae. Default is false. +* Images can now be anchored to cells +* Docs updated +* Provisional benchmarking +* Added convenience methods for accessing worksheets and cells by key + + +1.7.0 (2013-10-31) +================== + + +Major changes +------------- + +Drops support for Python < 2.5 and last version to support Python 2.5 + + +Compatibility +------------- + +Tests run on Python 2.5, 2.6, 2.7, 3.2, 3.3 + + +Merged pull requests +-------------------- + +* 27 Include more metadata +* 41 Able to read files with chart sheets +* 45 Configurable Worksheet classes +* 3 Correct serialisation of Decimal +* 36 Preserve VBA macros when reading files +* 44 Handle empty oddheader and oddFooter tags +* 43 Fixed issue that the reader never set the active sheet +* 33 Reader set value and type explicitly and TYPE_ERROR checking +* 22 added page breaks, fixed formula serialization +* 39 Fix Python 2.6 compatibility +* 47 Improvements in styling + + +Known bugfixes +-------------- + +* `#109 `_ +* `#165 `_ +* `#209 `_ +* `#112 `_ +* `#166 `_ +* `#109 `_ +* `#223 `_ +* `#124 `_ +* `#157 `_ + + +Miscellaneous +------------- + +Performance improvements in optimised writer + +Docs updated diff --git a/doc/charts/area.png b/doc/charts/area.png new file mode 100644 index 0000000000000000000000000000000000000000..d44cf74a9505815bacfd2743a39827e467b0f705 GIT binary patch literal 37749 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%0Ab{6)OP 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 L>Znv-wF&<(c}9** literal 0 HcmV?d00001 diff --git a/doc/charts/bar3d.py b/doc/charts/bar3d.py new file mode 100644 index 0000000..0f959fc --- /dev/null +++ b/doc/charts/bar3d.py @@ -0,0 +1,29 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + Reference, + Series, + BarChart3D, +) + +wb = Workbook() +ws = wb.active + +rows = [ + (None, 2013, 2014), + ("Apples", 5, 4), + ("Oranges", 6, 2), + ("Pears", 8, 3) +] + +for row in rows: + ws.append(row) + +data = Reference(ws, min_col=2, min_row=1, max_col=3, max_row=4) +titles = Reference(ws, min_col=1, min_row=2, max_row=4) +chart = BarChart3D() +chart.title = "3D Bar Chart" +chart.add_data(data=data, titles_from_data=True) +chart.set_categories(titles) + +ws.add_chart(chart, "E5") +wb.save("bar3d.xlsx") diff --git a/doc/charts/bubble.png b/doc/charts/bubble.png new file mode 100644 index 0000000000000000000000000000000000000000..98583453c6b5413f86112590620f9d5d2258db45 GIT binary patch literal 59465 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

YH%9Awg(BIU~w- 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 1, then x = 1 - w. + +x is the horizontal position from the left +y is the vertical position the top +h is the height of the chart relative to its container +w is the width of the box + + +Mode +++++ + +In addition to the size and position the mode for the relevant attribute can +also be set to either `factor` or `edge`. Factor is the default: + +.. code:: + + layout.xMode = edge + + +Target +++++++ + +The layoutTarget can be set to ``outer`` or ``inner``. The default is ``outer``: + +.. code:: + + layout.layoutTarget = inner + + +Legend layout +------------- + +The position of the legend can be controlled either by setting its position: +``r``, ``l``, ``t``, ``b``, and ``tr``, for right, left, top, bottom and top +right respectively. The default is ``r``. + +.. code:: + + legend.position = 'tr' + +or applying a manual layout: + +.. code:: + + legend.layout = ManualLayout() + + +.. literalinclude:: chart_layout.py + + +This produces four charts illustrating various possibilities: + +.. image:: chart_layout.png + :alt: "Different chart and legend layouts" diff --git a/doc/charts/chart_layout_default.png b/doc/charts/chart_layout_default.png new file mode 100644 index 0000000000000000000000000000000000000000..7baaab6f155b3a0af7a83455ce23ee20bd65cbef GIT binary patch literal 11790 zcmdUVc{tSV_rC~b2_?HMk)4oa9ZQAmp^znevQ?I>GucHNAv=?bu`h#!7_u*+>|4sd zGlUt&%QfKQ4R;Az0)nEOpN6{4bUwngC0aR+5>Wg?lLuUx#4fzl}xSmyr0j14Vt<`34ec~f%L{qt4@bhl@w{00Y4|Pl-McWQ3WSt`be1r z^3zv`Vg*SUwA_2cXT;%CL9)VRjr~s7HG9;z9V-mJo_xveT>qqe4$?;tvh@l#r+o0q z=Ei|}Te!C0T(SeAvs06*@gh}Y3BOi(bgrjqG6#khIEa$)@F*FI1bC$%j6Oz0^yIA) zmhkaSPB8G`6rcs%9zOj1Zi1^u z2=w~;+L&UE0KQCl((nGbt>10)9gN8UesFm|&0|105fn9Ka<;Ot0)F;$qyi856nXq2 zOOD8x)5NKLvbTlQw;qpJIrN((RxLe31&%W|%X$tfLqQR+P>ot_$%P0Gy(YGT3F!|t zjYa-=?lJaDVY79tn+dLiFU3NuengujiX00;FTQH!XzKa+)&gvNrio_Z;o~+h8&|IZ z&Nq(?R%*E1X6;=f+A;&*7ins8a)D(RP~@nxi@Av$gKv4k=f)Mgv{bS6BTNe3hSJUE zb2_;9*?!fELWVu4>XiMn0qQP}E6VF6UW(Jq3%^s5PSuWXtudX%H=!!2P` z#a+3Jd5Z479SiU3u28^-ckOG^T)J4C!;eFiPH^F&u%OT5hBg6`Y3)u*x}VUC`I(J*u} zL-3Mx^-_hZ)-AhL#PfI0(1C?tIF{+F7@u!fZET}Gi$qH;7*yg{@{Ulq_+@&IL)L zTg1K9_Lw_0>7BZ*kJCuJprCV}gFQbGJ&}QLCJl!qL$wZARaIIB62j~HHpcjNh`^lS zyXOt1>rOdMj5Bt4Gif2Y9@k2@RAw|h{_+XAX(fP~#<@J6_%V&`zRP?-R)0Unjb`+w z*sRp2!1ucTm&sPnK^Tf9k%-;zJDK?bvZJ>14%%!ky(Wo#<6sd>@Dov5P>FsXYo=AV zvg6z<$Xnd<``yQ8(lbT7Y<0WF0eB9ki{rwEG#0@xQ@`%93ENl(?CO~V!eS|(m0eY? z73|qC)P{m62TcMUM%aH;E}ok7?VKE{Vh} zh8=7?8`?i9bC4k9=e2nfMe-1^bl#S6L)~(>guXhM!9P-4#nfaq2W*6dXHyaRNnP!HAvi4kR zK6%x;2B8Btl(0`u!&&XO#I&VXQ)eTdsoB)C;%a^K?yh_r!hAh3X+}lGlzs&lxt?|I zVswx0xdM5@8E_Nk_E(hp(6n-IZh6z}49m9Lw*Mb?w^G{)(8ZtynK> zT^?FbMGow&dwi8{?lO8eM@P^XGJo&+dP?cNl<&8v{kE)WE|NCUIESpU;y0*E z0{T|i%-DlwE~$HAXB`9J;yF9Dx4&SQtM}bigBS0~Uc22~9wnpaY+P;8BVcs1SY zCe4?km4@7C?L53F#DBTq3=QhMRFlb)E&sJ>58f*D)Y2DIwyg z;JVLoSSszO$C@Y!A9Jlp-Lnngyhey>CqH{rIQ_78e)9gG z5zlmLzG1bo>(XJYYYMr?YOoxGAw}u_#OW|1(w_MrM#QHPu$q+;Azkrzx)OCG zFbm|cU3kr=ClF5~VtJZ+KH<2>sti9(aE&ns&99Yoih6~=F(d|<8$Y$g&Ja7mn5!~U z%vO?2UQNSyY#{*I>kD91U(%pp!52Aha0+wg!>RebJ=tQ$zO{*(Azx}Bo+^vU^u(N+I>q$sv>{%Mv$kT5zVu!++ z7F1Zj@P#qG4QlG>m$e1UXa2F1P&6t;C!Z3xtV-pPNxgVRmFi~0^RrW}Xa3|F!J)fQ zUi}TJ*rdcb8lx!Q#CdkbnCt(WMP06A$5+Gq z2_7q#V*`~^|0zR(NZ6wr`CRv8n{?-wMe}+x3SNlc*D6~@1jAEa|8ZkybJ2^mQa)sh zKfX>_&8KT0{=yggV;b;d_=QYF?C`;7@24eURm$o>36{C#}G$ceoG_%q@u*; zNVi5)8rW*)_!vdF!Pg7Di;DC|7EK?eo2xE9I|CXT8uF=4^78Z?01W4KdH!UlC}=|$ z(Fy&K&*jUq^Zl_yBJ{)1K>$_MFeeqyt?a>(u0D%U{?cZsfGt+y(p-N+G5R5$@0(VL zxe5v|R0-?OK+m|nCxzM#iQ&9<@ZfFfS?Nj|7j+J6A}6cX8@I71!ibj2^ffpb5%nXq zpjF3W@#TooUPsRc{MIH2Hw5_vFEitY_uw|R@PbUouCjg!XR0?HF8#s?;@$2~aQT`C z=Y*N&7c~UUvtWnp@^MJaM*fIo58eP?A}(UNyY(5l?7t4hk4?`Gyrcff_rBzmgZK*j z85f}loYD|53u z>~PGfS%)^iMN4)X)oq#360HEM2xRfZ`cnmvJwWb0>ftA z3bZ$W@HEzp_ldnC^&BOEoFiP`OSJDMbP`f&kd$-*`mU2Dqx5~pMl#5+-^YbcX%f@m zg2{DJ`nmg?^q{qgw263_i*2}Bap9p$x zHHP7rzz(E9ZbJC_Kx>5oKng9X-c_8m|O-)sQFf(Vv2Qvx9K^8)k9RW zV_RJH_@iPIPoGn@i=ooJO?D&XgG$2QDttdrp!y6+zQ9tW zoT(+W?V|VoxIqVJod>4SlU6W5>By)6<>tEtsLR_KNQLtE;acAoXTtAnI9b`N{TceM zoOr4D>c}4)1rALLT&G9O3R2RmIHRKH^XAir1VB_Y3rAjulm~2E2wgKwJehtgarl7o z#YL~;b1*=%_0nW_Wd2_3FW<7%@L{BK5aYo*$tySq2PX*-z-Q*oOaTD z_Vk@)PQ$$?icLg=D%N)Q*(bVsc)q{cHLf8DvJ@jm88l1pnaEV)@)Tfl*OjWA=I_nq zM#2E)pN%t{%K*Jpx;ffaa!7<0g9biM54B5;2rXoz<`SkO`CW>UQ?1)=zO66LW)Y<% zU>1?LtUg7bfh)lWc)juKwQ%=umD-p2$G-%=I;+{FG$`oE9&&@D_m`KsbsgSDHuCx{ zy~G&=2;tVd_TUOGj0?O;eO7mSa1&r_}+;aNe<)h*8C3 z9?zw8eth>t&lZ1em-M#W-(f@~TH^2RU~gwQC!csld$N`~D}>;JB<&*er_0zQ^aL;I z+`(QdmeawSSz69-TaOM57%fXVlkT@|thF?x*p{6_E{Z;;@LE@xV6LS>%GLPK)UK^t zrIzt2qVMgj9JC_##p-$lyv*jRbxx33-EF<$+2Hxs81qI$>lZVZ>+JPYE>?CV=Bk>>daklm&EirKe{SqfzHNKOk+{hMO6nksC3CToy2Ge9# zX2zG;WC?>Q9yNR^-GL*-Kfsz)9dJM-8Tlc5iI8vf7Y(!!a@=V=`FtvQL5DF7@RqajLpkv}7?U31PclcjVS#>_ z`?-)CtoON>y>Rn*+&ZS3J@HAhwZz#A2G$Z%7YuC8k6e@0Qy6n{O4d<&SYQ<9&K{ED z%Npx>d2$a_>|j~DKfU4(!Ic{{J(Dt-5q_<~pb!MhY|*e3<6G(GTOLQHjo^ zZm(uHaX7#1<$sy+lJ?+1o2M;%uj&3;FVqT1mwrZqy?ZaMa)b3$qXTWNc+PLBL3Tn6 znzY2*Y1_|OxDT#&1s*PcLmwMLeV8Jul~HN3g|L`X*+SS%+$H%M&pSb8SP%^NDEV^x z4p(+j!fKhE_|#{i8F;8BuguBXHzIl;p5&yv&K=2qMQslr57C_QU_SDby$6i%^ulDwGs9>}Oq}IsSA^-*5mm1W z#umrS9taT*L`b&uKVj2OaZL=ynb{mEhjSKH9eJj!4B5@>QYgH0E-02c1yS|w)~mz& z`C_@@UrswD-*T{P(8I1g|%7(tV2S~i1qDSn)3)61N9y`pKdl)(|&T{sPpv96| zefBzdA34W8i@~VN{-QD*!j7|hVakU@m9`J#`S!?t=9Y6Id5cK-ccOCQP#%_h*b-p+@D*Ke;)ugrFPoKkCUe556EgC`r|XQ_CbQ3IKIN|(#>Ki}%X?aX zH>)1VJa;XJeVM~fshE#Je1e?19G5oVSIgH*m7nDLwH;be#*xaZ@T*QL=OjW8bTGMF zn~nJWbbV-HC;c$Nv~{0G2mUJ8I{GZ;Gfg$)$JSz`F7&hTb?%G3%RbEPL-fMxg7w5e zn?)MrnL5{YCSE9bo2j&3mdSTovRVFhoJc+Fxb430^eby^{n`xvN&P(@??*3equ#ve zb=-~-2;7Is_4Db4m&6rn=WL6SlozPJXpoI(qGAsbjC$32E=-mz_k1o#Vq2+p(~I@F zknfZERnU*V!=;fDTka~^gTkeK!}_Hd9Q{JMY@47tAPk z*(H?%(=h{E*X%VS7ahOk$(KW^x-X;uSuDsQ1NJC&uaKV^TSKDjRz~)rcDnu>`tzP8 zu5q$))p4>O1FObkW>jmnd^P@SLunsd9ec3BE1RH|vd&gq=)P~3!e*P%P9_vzvr=Zs z=9>Bi4BOM)O&ed-D9Z5mvg8@Mhek!q4|V-83d+~v1IpP9 zwzQrANTr?aV5=Rb)pIXB`mG5bp{PdYB6Hx^@$|c9ianJri`zX4=eCT1@6u)S$Q994 z$IantXM6AGj`h9k%EY_+N2w&CtX!l!EGsYBKit$`Ng7GVO6PIhZE)9Kz=2TxZ!jMn zOys_^cw{HZhY3TOYP>!xFggt)sXlN^*eY}<1=xpaEtxGXALx>w3vLDT`OS*0JM`3U z+|db~M(`=3mPZZ}@zG^e@7niZ?i4k>`z3#!@-M#Cs}V^|Wyr1=jr7O|v9gyXnZ%owGEVI8 zRa&;wC2!?mp784ukEu|8q`u@(=e^Ev4<1@f zbcv;l?TQgeBh-~MHQJrq;JvNcLAlVc;i0{qw1?2gOW=5FVf3kpy%VLQF0R?=QFQ@tNG7+Vm`Fx3-08X#%{ch5#b~q2Tz-et0e=f;&&d^-VgRK zO1cc(Wzl_fHY(l)WpGfLGaqu~vLCPvsRL9%(1#E_lap`e{Uc|}$vs&w_A$03yLpWr1_V<}4lK$e0?z{dud zEaeKg=V@Sz$$U&IB_GEmI)|944IV43czSn1++m_C*q%Bv71gmIDq`6q(FodKGf3$| zx~7`@9rCu{3;=tgV2|(_5etsj|2y^nhcOdvMsVd@k^tXyiH-Ol4 ztqLw-3T<>F;9Z#*G3)DBqa#@9WCmUdIoX+e}nnY|K~$Kn>!_bs@6TZOxz zZo77<)6$r1G$xf7;iGuLB}?Fbh;>0xh_lnjG~0+)wnklOCUFA;wd4dp}LhUX}IH`Q;Lx{kc56d$MgRkKZr@=(WDr zwRhKBrc4Wxg5Uz+&1a{k;Nc^v$C<8q^PcmoCkAJ0Z06?Aa!!o-pVboGRTgSFq zJ9{E*kdiJ~6#Rx$)g0;kgSb_v1_SK%bVTPomu?Urqee#VrNcZD zK*+CkLQI|sEr%vtfoh#{IIHj3-MZMLL3MBifT{%|Ig&fY+p6zW<3$bi-bMp!d zLU&-a2rO3PKM4Y`PBgTvlSbG7?N`rQBC3z|&tGn)z_utjC6~{m53>#5UwV83*J+}^ z*`KK8vhDJYpE6&SCk&hh;QpeA-SInONdx6W^sGq59EIjtUv2-5JMK-<21kJY>DPFK zh58Oj?6%5)eH0?XbnC%@Oj9q^KpDV8(y0qD$=1^$H*murmg#&L}+L!w$Qcj`zFx9`z>ZqwwB8p?B>}md&#>PE^=t@4PbCk2M z(cK$g;!!}V$Tvw`rs!2b->hrW@TLi>$-k&_SZs2URRa*GOd=kM9grKb^ao1Xt>ObP z0Vs{EPCwa+3CvtOQ9-Qwr9I7RLeVD zvx*3>WgJ$UYoQ&^ud7PyA+n*&h=N3t?`DXd>D$G3rqp)U-A|2Pn}dT} zy97N;t`4dJAP8XoDFPCWznedyLo~mMU$w;Y`(d`BvZNhVS_th)JW3jhbd7>p`*)K@ zM6fuCJlDj|Ok;?r;QO+DrG%2<49ib{aCCLOEkAu%f%Y~mHZ#}r#(MeC6;zx<62oc5 zAaZX(BmvGBkOBJU-HlyT4oJ9EHnS^ zp&eSIktE1yuyuRbQ~`9w-J%*|3%$u6^v2@bj4795 z=zU#c%+OvTR$8ZcHmK9U89)jFkmPr>0w5In*KhBP8?Ns=*rb#<>-zN&g)6ZDSnEG~ zf9&})O}g$KE@|khy1MS==JNdf{6QzCFe_tI0{ljSn#TBLO8@dsf43o%b~epy{ye>) z0LtAEB6?1kwitypkLvg% zn>r!}Hu5ifKijPPFD^v|>;1b?{$CdOQ$Gm1_*KIHF3|s#MUN8k=)2F4@TY&O2)o2j zDB*v7AOCtdK?R`G*&`|{dSi^xNZ)??yB9dBxNc|y*b5?^%X{rq&5HHoHvb|c=A{P(`OPO z<^O1vHyKU>$$K@JS^5Q-lSfI16@y!fAr8uQ_B z>aP5sqYM8S){9I)`w%K9>ccTTajwx8LIWL`7~s5bnhe{VWH}me0LD^|hWR8DQ)^3A z%O^8)M#ongw}ID{^PhP}oAknL1Q3ln9p8|e)u&!g@;R~XG9JIhtRkc|ea_Uxcs0l8 zrc%=GmwqK|msLYqgGWrT*{uCKB&AkV4Y|B0O7>(<;DGCau&RP_sA<^i)%8>4>%oEB zF|ss%C9xn26=%ux2qp@R`q}CF+==;VC2tB@8CsC!J?YIS*RCFi6o!CK9A^pNn-jyG z2j6OD@ot2pNjUjepP)~kZt9_mM0$OyxiF;$eb5;*Dcl+F6S1pBmmWHq840^zts;C8 z&u(yC8Cr8QgBA|{bcNGDhzKP~($wP>aemwKR1?>PJgWgcS0wC06)m;nMn$uV;)!sU zV4^ZbUQD&dQ&oYWUeFjhN;25XSF%49GCT!m4}VfhUKSlU`6D0c?L=v;PJRpD+5%f; z!I23osh)M6s`SA>brqD*p#{0Ov0@glbBtR?OMQ=o`EO90pxNBg>{|p^HbY>Tlf}5y z0O@f*J^fuSd$&8D=kg*nshWDUHp49O&8()ul|yoDBIQV5?Mc-wP-~>}E0WvFPp0@d za)^uZ*O`ggJ%YOv%GckN^1QY=8sGfy<}iV){yDz+`qwBU@S&1c#5?hdQ^|xe&xn*; zb8u|s(X!%yjCbB_RUG+%s5{f0zkWP`LV@Tl#S}TGi7TR3#M|s0QUK)k1bytu6J;zB zPdfmV8WB4{;+0(h(=#PnJ*Cb@6hYoZqZA1kq&ajWHy7SSrbtdi3yiEip=VZbG187+ zMO@#6Zy&rcxW~@DWbX#)-culYLJBPRwr%`b_suDIw~}X)Q!O1)y%wPE8rDS^nbH2x z&Nq^3Dr!4z^jbTfRQgT6=ZTB~{Z>N!uyeXzM9M-B;(KAo_i%}Hzx&xZBaOufH3&;ppI2eeLtpCCLK(u_tx_yC z(jo`UX~K}Zm4TCF%HhEcO5klIVWl^x&rN5?rKx1AVf$h0>8DfYNQmlV55I8}(Mi<+ zHcn5ipaQc0L_<{X1K2H(y2w2;NilYnc3$vOYagF|ysYF^+i;?9@&uw8p8Jp7vn*Ly zX-$*a?qHc9(mmaF=71{@j zYTcqCcw45h=DWZMtm%~6+{b#3e;s+|0yBi|rIK$;)BOH5lO@Dt2Y<{f{Jw5M6x$Y{ P=R_LUbgmYu+zt9aSU>EA literal 0 HcmV?d00001 diff --git a/doc/charts/chartsheet.png b/doc/charts/chartsheet.png new file mode 100644 index 0000000000000000000000000000000000000000..66b5655264fb5dfc2d360164f82a61d16df61c1d GIT binary patch literal 41321 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 F{|5#&tNQ=| literal 0 HcmV?d00001 diff --git a/doc/charts/chartsheet.py b/doc/charts/chartsheet.py new file mode 100644 index 0000000..f3a6e24 --- /dev/null +++ b/doc/charts/chartsheet.py @@ -0,0 +1,27 @@ +from openpyxl import Workbook + +from openpyxl.chart import PieChart, Reference, Series + +wb = Workbook() +ws = wb.active +cs = wb.create_chartsheet() + +rows = [ + ["Bob", 3], + ["Harry", 2], + ["James", 4], +] + +for row in rows: + ws.append(row) + + +chart = PieChart() +labels = Reference(ws, min_col=1, min_row=1, max_row=3) +data = Reference(ws, min_col=2, min_row=1, max_row=3) +chart.series = (Series(data),) +chart.title = "PieChart" + +cs.add_chart(chart) + +wb.save("demo.xlsx") diff --git a/doc/charts/chartsheet.rst b/doc/charts/chartsheet.rst new file mode 100644 index 0000000..347d3a7 --- /dev/null +++ b/doc/charts/chartsheet.rst @@ -0,0 +1,11 @@ +Chartsheets +=========== + +Chartsheets are special worksheets which only contain charts. All the data +for the chart must be on a different worksheet. + +.. literalinclude:: chartsheet.py + + +.. image:: chartsheet.png + :alt: "Sample chartsheet" diff --git a/doc/charts/doughnut.png b/doc/charts/doughnut.png new file mode 100644 index 0000000000000000000000000000000000000000..06025c389ff2f7501a67e009059c5d34b7a09249 GIT binary patch literal 94413 zcmZ_0Wk6irvMmY$f&~cfkRZV=K(Iyv!7WH|3GOrwjT2lNcXxMpcXxMpYg{kcXW#Ga zbMAZnr+cj*bJZMGv*xH8b9LZP8Br7@0wgFXC=_wAAM#L8Fbq&o&<6R5RQ4RwTYhaK8Z z4-X5=)|wBTQ|N)M_qi}XaDw4b{euqcd%v&2JdljvdbYw}@ur_ayP@s72>iX3f9taT za8!myuH1d#!zmOxKs`=&%Ho#v#94@?-}1Hu=X zZE0LQLs0-;D{_>SJ}B0aZ15~zi?_52&iZ@Y-ZzaYuAJ#T-kfA< zd_5_|YtL}4XD#AWI)&mPNO zd_t_nqsBx0Rrxof%5$Ga8-v3g0<7#1E7Zv+N~P(G$)-Oh?hsZgPN%u+7WruO<8chJ z&QV>CxZ0cqrwxI~3)BU7S$andhA&6%GfDkBau()m^l(GBmwwmzk$?LJUD9MHc=U0M z+f+VHuC>9k_1n>2nD$|8xB=dls|7`ss~$<;QCX{Q_9U;GNIkdxZ>cQWl{~}eLs`Bm&k zasX(Lb(W$wI$VR^UHc#cp|L5;LlEsw$dg3vBWUBfHKLJH(Ljt`TR#@KTO|7!W9fd( zaWc#GFt&X(tKfI=8otl}x#<_;+}9w@Aa?9kYS0@B`!h zELxjq>sjTHt7j8SbKXnM+t>+1z2*L)i^N3*1@|{JOQ-Pm%*X>>C4#fjJmlsZsBbUj zWc+6=ji|a8K6Y=%#m`ypk%F{~N12R_^RX8exB^1-d6Q*=%P{PvakIGeFC7(rm5%+H zj3s7v{f!nA(`3*7EjzY?!d}zyKE~|BkD&P@*;szSk5C`4^*WrBE*_>9Z@Vrxc@pP$ zxqNpVb~$W&ynEBhM_E{P3TCx^A#Gf1YxedhI$XVt$ph?x72I}Sk})Up7N9>6-;|_risN|R30X3wGWHRi=A1tZy>x6r@=s%sO+0}Dcvn4mlzI2KuW?S^zU{{oD{Y-#lCFRE) zt%x(G=tdY5v^g5*fT#6NHz$`H9!`vJA6_5Yf+Ga1AXNgBr*2Nw5=BMNFURbA!S1cc zxb9WFt#`h%f*zL%vQG7-i}Pb^9#o-uTdV#l^FgaGf(1O`H&!WLwP`;@rbLsm340na z8A*c*Q5(FIadrznv8GQpY=#dcpG9{ijKT|ASut;JZAHYz1*7q61Pn2emJP7;K#@9R+ zOGX+H#={mr!4083%hA!9+MopW&AbrVxZi05%mw-RcCol|N}5+&>~OCnorCVcRPcn! zF82O@BNf$ka&K1!dM6SZlH6#p~)AjdxIY#G0 ze7< z|515;z7w3K3e=u^%b3GPAxT5xsXi}vInUsmq#-hac;^hnp`x@ zHD17Er%dq5jLD`Q6+TZ`wDkMS{UBD^O`=A5>UAmP&d$#9!e4-5CAyolHF$^L5|RyC zlRmlr_hjG12ogaQmgaVP3yGJ6LpIGe_fFMkc!*20HS?*!L`V^ z!)ZzUG&^2FL4m~5Z!@+`@4=rUS!pOm&veIo->?a}7#$?3K|~U**%vk4l-ex#FVnTp zJNSmk$namk4t%c8+`BWW-<zSgJGd5=!SesCr?8#8928#aWS7i|^2Z9o>+ zdRpxL6GS3&btyR(oa{zjzj)sJO zIf<{k$#dn4+Q-(1zb=*-ZxV$z92ZsZ-C6hOyiq(%r(PCL_YOwtOO@*(APE&U!Ax{L z1|b@*1;zj~*Vm+L0~bp0Oli^cpD2N!gpWDJNH90gjxVMDSaF}zQorjsbXHCKk%bK& zTDFN3ZK(D=roqxmX^wgMJv|#O5np-M1eCgJDnF_!^^oV1ow`;}AHEpz?(mhIKFbPk z?vtY;^Uxi`RXVup4IqT9u=9p^{Dg&@Xx-GI4n@YclSV08k7FVqg9GQVf6g@4)y+&K z==J0t;sP3nCV@ZV%PV0sKV9^lF|B=)5FUBjUqmEEEF$H5#-STM41zYQ7S_#EF<*-~ zh*?W(-F+CI+kJQ*fb@=icb2^=DJH~~tn@ibzgEQ)4vA7MNi3<4p zxDJw70yRIzjkJof_tPAcQ1Q=z)3Os}LouTK%RsQQnDCq}%{?q1EWFn5onsVx{|GTN$M2==@}+infWvK>F;Vby_i ztLGo8pC^?xJcM3e=$`i-hfW_*Hq59T>|I~r%AesR5qDO1eqLQqyu9Tag~~f*nlOMn z^^hWqgD!ok?;SRQvX=1}wsmWjVCh_at}h*MsTS;Vg2Y0L0cYSNEI7KI^rumWcH1n_ z*6E4{Pm{Yg&op?#Vl$?G@G!ZLWFm)Ew>F17_&A0s;6C`5x+aW4TUpYdDSVFsBVKUh z5DK})_6Q$&Teq99AyKluP>aq0o-^Vp>CZnowrw4~d8DGl{Ab+_7ab)0o2yssgNjZK z$%rzzeVr=F?znj9KW))rV zB(hFeu$8-ib`E@GNbCsoGNV*3QV zvNR#MHs`9e#v=0(q^SG|7c8&{tR7R=IA-_Pt$W*aAaP7K6pCZ~x3YRf&e!|F0biX1 zCm08xiDHaM;vzWx1v6Q-CDm>FyFrn;7#b$K_5IBLZ-dWM%^A8;XdiOyNpc|`ZQTPM zd1WCs$m{ESGdX9R@IGmdm?U_s!iA0=Ov4UCHAxfL7e;AqPZF0>YaYIFbYM^s z`B*+31j6nwg~4(8F9;L`@4}88s)RpuPdD}EU(E#L zuHiH)|4wV4HJ{FJH8G4(Tho_x_PA#O*Fl*;oL63i^5IUwS~POShDpM>vnvfype#tv zpC{hv)9V@K+F7`heFrP~u$-{=@9#F~aYV_V#KH_i@mAwbym^ps=A_X8tuQqI#i0+We=ZqgF%NlPUX_*c%5-%=vTOXS&x3Q((3JMXD zvdSk{?YCcspw<(|StYEgga?_5>g)F_bT_C0%D0RrMX9RTw||)Kwd@_-_F@>X&!6KS z6$fs;!+?(|+VT~Wf^Rr1EAv4V$>wggd9PtbJ37Kz4OQjKe3H)?P8r@}+pLX43TzLG z7;ADE;9+{HSVzZwk-roLSN_~5GRX*lZ@9=%#P{1A7rVHy784Wu`5td-#9D6H*fU{I zMXT@TFo;@zeQTg@9p*u!P-MDQED?4 z)hb7KMmpS?a@(f2{-W`!-MRR|gva6avIyhM_{c>Wr)Pa9>~Z7xo0yw@i&bOf;qSHu zRA3B3n9s0Yhzp%U99!)g%bja7q*sler<^FPMa#2@kq_~Nn6m%Q485>SO_Vb{XrIsvk5-|<)J?Yy^l`bCa%SP zo47yXE)J1PR~qA=SSped$~+hESM9C$V*eyp%ie$eNGPx2vmNrGvl?llul^aF2=@-x z#0*ijmBq4Hwx>v^O*%CXUU~iCJ|~{-LBBh1Cx6r7kmOfR-L`nYZ-_M|g{Mcil4Cx1 z$**OonesNq!@v@ZO8QXc<*chFMaAsXiS(~~K{%--BA5y2TE2^|b~a1WJH8asrM#VL z^T6sJq1ZU&jkjtf#F|O?FuJ=|={Fsx(r-C&Cx+uPimV{mieG|;?7cbpb!-C=bic;Q z1)U8V+2i7KG`}UC49?IJ_mEAk_7jevb318z*ZUexWv;5I##Whw~XDC-RPY-Xp4(=eP@`?q1>FC zvy-;A6~&)|F9*ZaS(0Zb0FujS0*@HnFKV?K|=BG~4(=h4Njbto&`|;cUPM{Gh^o z`@s_g8?OrWSetY({O6J+4OYFzNmqNyKcS#By&f z9fc?kiW}F8PEums6Map%FN!&HPVH^|hD!?6^mR;Ar`$xx#yp%&!h9aE#kCXuYsJXU z$W1&19B-4FutvY8-KyHk|CZ#Nnm0&LvWx_{YQnl=ls-z{GUQy;C#mu~f|R@ST$NiJ zn*Sn7uhjZTR8|0B)XQF+2GRpdwRiB#QA}XhJZ4py*_ZTs`+oEA(6|PdXOWEt`F9B( z046gH@#UBAq0-rumuH~K$`y04zFIeX62J9a4EW$y7c6i4O%_x@Kp>Dwzau3lH1#A9PV*OZQmn-0T{QG-jIYJu`G-x z^07i+Dn_PW$cTo6I`dspdwp~6`{_2kKb8c9go+qj(xMvR7TIW|zHwje%WTVyJ+lJs zOIKeFkxL53Z|Ce$Tx+ft+Yy!WZTqJtZ5Tx_%xULVf4Y6wwE5z6+=xE6Smzy(8i#rvOY@lTlY_6i!6halDgs0s9}PpOIbs-fA|&lntoQH@+{8f|Zo zG=yx6Yv{jCXZx)m1p12!@S*U-+=g{%&}}r-T0(D4)hnyEKG5ZL)+aF-6+46 zzsArXfVrQRU6`3#$cBrVOElkQ0p#KkxXukan` z5d$X=p3>8Q)BOQyt2U+uePx}|@}%z*6LF(p%!mlvie${-TI<&fA-o4)I9IJ*Cr7@G zeMr&hGQ^ro!*>exWk)iCR!C1ZP_iEmeDP47W`I24)@4t46j$$)nWoA#x>p_aIBMcQ z{rR3K(I8N2q9+~=x3u*TLMf{+!4+YFno}yU#N<4);2zg$$n$id9`lUj;`Iokb-Ih9 z;!M~{JVADP4E}>fV1J%hrYh@=7na|DMJ0PTY}4aD4q;>Ul`%&{5;ye0k6Fi(Q<&_2HB^lR(!($A z&))%lUEl4)zm+Xt!)l-3=!>DNiZbxQSrQY#$+omn))I0}Xh5e5zIq&M3|_HGV_}Rm z9Nu%X@3&d9r~}}U5!f^!?L|2fChVt2#}|vk0ThvntHdF)U9%- z3|=&1xG$whrkOwJHww1yUSSNq=^BSCCie$c?`~}{nv!GBUw)>$FP7^j`$^ux*|J1Pk^Mq-1FBo{#-zSSTc>M0JV2TL@shVUQ zGnr7^z7PWOGRmq`{W%Oyg^GUbBx#xl`F~sWkCEec=;K_naUk$&$8v~@H1cR7_LQTT46^vyQ9XJ9>Mj>CS|c5zBsh72{#}z zWPFxPVcdr8_FFky&AX(J{qFSpfJ8R=GV;7DNp>R?H*WG+ZLQkvl)T<0u%H-t)0?Is z8^2o$3Ty3q(sXc71IFKweY;#Q{qoP${$dF&(q1-&!J`YXY`uT4!F2;j=C0<@J>*WH ze?}XQzo$TYujM%;f-3M*wPkIy+%j*B_WkI_!n7vL(UWCjGZgxfDoGZL>!WI{_P8zqybV{IFzW9Ga zEZ~WLzSPsdS_7^9k_f)>XnIGr7#yx$0g^sZL`2DI;bQEWZ%YYd9Wdawbs_{1zfzz^ z*YuRrjS5nbW)MiWdNahlMUraZlK z=SdD5Ks<4iSNhu zzA9%-dlBbOwh5W4&M#g)?o+j8r(b75hjvo`V+OpR)*Z8ZZkA+KlI);Bt|F9jKQ%?w zl69vM2WXw0_$^rCL(89}_m`{aT7NZd=Wb3RlaD_p-_JKVGx}WzbfFX}Xi(aZN24Ry zbJ5#<;Zj<4=UVMx(^zVw{F&@kgBQ@`AKA5;S?@0Nd!Z4EaGDo0G_<74OP*BAe)YY^ zoY}k3M+ef_W_R=fNC#DC(oYp&{8y17{_ad*Ye$#|@mHxWGr@Heg?|D%9o_gN!kxWZ zu?0`2UefI0bew?6al6soLvW1B{i*`zNj;(0BWV``H;X;}s33A-_Wsc8W)LuUeeP1& zX?vlk|NiQIGuMt@y&)o;1PVi523dF=3dB!%D>=@8EBGoe(X)lH%C%weA%(FF; za1hwR#~D(nP_e!obv`@AuXEIpg03j2(6z6BU?>dThZR6*gPeOy*L_>{>Wl8i^U3@W ze!L7PaYi!-JoA9m0_lh? z?g?a&a$(Z;KPAQw?G_AP5j;2QtWrxDz5F@K4C?}C@%4W1E2?MqLKCMnUBcHhmSXJQ zHzQgV&SH9@+1Vjvj=*>H_yhmSg5tQ=O5pSiE#+h`4zcn%f>sb|*$EJL+6<&Rf7;|< zpKm~LI-vzL7Zfsle5#At{hP=B0eBq+y3I_j8N1Mopv>g{>}k%b7A=k@KYlbiBO>NX znd!S?DoQOvk=mA(RCGyEMWZXq$NsJ$I@vDuuoBO$mny)J?+-Fmx(e-aC%lhT>xlL(80}Jb(asD7#{A%_Foqjnn zY&_oz%k2V6u+*ar)Qo+LA~`iSu;jJZ24kqXh|6W-Z?zu|iRy*4b;56^x_I{G4R`Q~ zWI7QVq9#ENh7OEZ4guQCgDWW4?dsvlKgpHQ_ZKD2$*;~9TvB%ETkOPLa0 zcPQj~o>x>QPv@4&n|B{?@Sz#bWpzIE6lT*ZX^=2Llw;K!mNl)_QVn5zx~x=xq6G%S zx~!v8{HXH$bX?_wlO5G==%H_EEKf>CG9^dHqL{9A@-S~ zhtBCmZ1OQ0QKpB`mm%Mk0bogw{e$G9;u(cS+&^1cD9yXrIW3oxK{z_Nq&U4D=rNs$ z_>8^v;o8mqySdlfHIKgL?WCXe{c~E?s|NGIRpBDP>H>mcPDp2tX>67a%kZYLl*4pu!o*B(xfMoSGe!tP)!;BBdvR>eO{k4DTq-d^>qbf-Tj^$(E6y-!rZc4^Y{gXw*j9^R;CqOO| zB4B677E7;cqc~HsbaHpM6=r>q6ZZ{|9?$c$kYBpdSgK0Bl}?P)2Ld*PH=@wZbJn~S zbr!-pDj+EM4u?tWI4UngpKbcanEK9`*ECGP?fF!l6to?&X!dI)^63+`>jB;fOHpnp z*b6LLI9m{bGlm|!+ki@b&d{s<6>RX^dM5roj_j$vv1vU*2tMn3Wi3#SeE1SA6BAmI zQi;6Da>Daz)7eK7EzpT_R}FP}V$0LzP(5UnNABm(@$}@_cC`hk?1LL)fW^!_TS-}K zG&{La8tYoTrC(Cjo9;Hc0C=PRUV@!dPGh;`-ew!SdIjny?Uy0vrHqx8jb-pY*%#imG@&CzfGHab+ z7%M_L_nP&)a4FsV3c|icDGay#Zg27#FuL}3a9l<)jsTMM*< zO~mIqKcXf9zuI~=Dam;~tuT=(jGSygs~XSa$QVLyYBpOeCo?vh!n3l&S?PRj8q?}> zYZ-Qx=JA4Gx9Uu{SO*NV9plAabAMz=^>_j8FoT#Udm8PJ+OveLtA+N2aWxf9B&MoO zrUZCG3RgTC+n#KCIBd4XTptedNHXhN{Yl;D$-zvyGATiQBrZ}p{LfcY@w_gzL6EC* zTy0mmo~p{q^D~dvH(qlqVs1`9%Kz%xe7ovGTUN+F!%%0j^i3*}Oi+v9l#h))g^R>-1MqF&d#_Hx7VrdyGo9^XUuifScgWgYXx zw{w#$JBJfkcP7p5=CnO%hC_KyISuC4USAxKm+Hp6Z}ZaJJADu_$FoGy*fmd@A){UJ zq^?l#XX`@SMZBEA*O%wt2_edLmMe7 zG~$=VP5oJAZ4P(XLOZo$VY~UYWXJOZTx~seH|RLV_^^bsk{`Ky$JvP);V$-` zFih_rR;iMsGL?27B_^Q|QxBV;!TsC#$5QLa`}|N8RytL3i)GV;PtA|+W z%jxgilO^SzF}_FhtAi~7&fVK-FJ*hFWKQ%k-rHoGkof^UKw;)#^is76=KBwLP`x`H z@T7{NgtBu-Yi(`*m^!8Y7CQcVbETS2mjhHCW~z9St)-~X=Ur$>jt=dq&WAMRdZ0;h zs)i*``^|69Z=>^epUhVX*)K16L4;lRzw%^hyIo_SHXbqBzgi`WUUq-J-_HtDOv1*+ zoyr)#T_=6LgP%WGmuny6f7J-QnIT!$DqedF?)uNUv-_P%bhuwi7NkOg$*U*%1q^%F1S?<7ZJf4TLQL~IORj~OAkgWMH7tD+>~;n zAnB8BPnVMR(nno@yKI2{>GHw!f?cx1qDg2Z#!HMH21Bj#P5~yPII4}HB7uG4ipg|^ zl8FeRkF1@J;o^tR?@1pxjTe)e=u-%{(vAt0*886;nE`Yx02EiyqwuVB107*i-?X%;*3FqQZYUhmV7(a_C|2qN*qX_Dj) zZa*I%PCUKOrNJU0fW=DgTQ%uL&olL2aI(%XhBbKm4spA=jR|2-kprS_wz+zF)5phs zRxlsOlt1qKOZC*KgDw(Ro`>@dq>Nvo)t)y6NELft{yC0kfPhS0_bg1W(hGE~S@56j z@yo8q0!I_)E$OLtdPGf@r=%0=I|y>}1SQF99Xsj#CE&r{4F&&=9NRu-s4K!Qrd`3V zwrvY0CpuBK<~vnraqQR~^acw2&oW4w%MJw*$LKf1`_0pgl4XaUYr=O!vvBi2XY77( zII@P(zkUqfFIj&pjRXq>GFY%F?DEm`ao%-wbObT+B#lYI#4>6IDV3;siDHLz0~0}U z6^W zQRzriVi93s8qGD2SGRLQ&|&r;N6jr7i?X(tM%jiL8preX@8?S{B*MDxpKFt*Vj0wT zS$@f0f@m?0Iu%w!IJK;MuuUHl&wpWGlIfG6pl_xuTDCnqxZW%nncBYR7Lx_%Ul<2V z;bv>45?F7dXU8(MkmgCtcs2s-!K%3*!;miQzLO;UUI-x#+wAA!;Yo>X%V{#4mM z&fPB2_PLGn4(B+>_y8^-{D|u6SJtKy7<0z!I~Cf5UjNFHJQJB>PQ@j-d9f(k*1_@! zE#-C&=emkBFQKjE(>re1#)3fk?dalaFY;c@mcCE7?>j*9EAFw?3toNqe!A-C@F2Lo zRG+%k7ew{3laO=_XJ&tYPIS(CLdl;OP)dO*4_jD4SR8aArk3W*;p)sS+Mip~o|!`S zm!NeN_yv%HC*o&`q%<>C$=iuJ-!^XUg77@Oi%vJ^qLR`bS$+NN#8!%_LW&jbeud6> zCzL-C^;><{@qw$oNm1iDlDOmfKBA3v1rp6Itg^g?biTg61?;TNFMoI*jz3)(!YxCW z1r;h-o~^b>{3z3Ep$&{IFj<|g?O*)N8hYIB)Kw8_C2{V@P{yDWS@Stg(;*>ov`3*S z{u^+$ICt{m0av^G;c^q?z&G5j@ZoJY$ch9Jqnv4Q7>yFKyM@e2^mKRU7X%*P-H~Yx zb#}|3@f7xkNzqBl$cVgc+KIJZ9Ij^GQB03^&Re4Crg=RE+OrQ~7nhYO)FciltMS}e zJ3ZK_A{L+TZmQt!T-{JhwJ+c$NC>^Hyu~E_H|h=JiAk5a5&$O%Avw6*h<>i6tUjS% z@SV?k#C{_E$l~GpMtMA9Y#NJ@-RuhUrId2&pCVX>-?@z66ETe8yH?XEp3oME#Lc^_ z$@oe*iXT=jG(LN`m%r@}jnjegomLYP8Zc9Ctd>B*WB$Mu?1>J2&~AS`FGpEryKeTV zclrrJXhD9FOA7AEKDxYg z-Ra)17>n~q?p%4Nxo&ecWAh+kjve{p%*uD`P%2Tytaq-C`nT{tB7hfDg7_E`nGD%$ zoA(f!rF~;#Cz2Y&#lR+D=xMk_x_8T*mUiO%jOu@oDmFmmOOLt6zL*TT!6Ie4I|zd$#aC(i~esxs(xWqoe$PvbA< zmz8yX18(}=B`Ptu6T?{0am=A#*nqxg8bPXODjwMu)FgZbdCEsymPO{ zyp^(7&k)ir!^Y$3%73~0QR?NCJz@_C^u@Wk> zcJ=Zr@+#<6!L@4B=qW*6OBB{dZv7r(KuGK1@XVNfgnqcm?L%X3aaYyVGr!fR8Sg&O zhD-bua+Qo}C&G^_qCdeVjK>Iz8UB0STCTkZ0 z27r(?$tz?47D13^`}Kyd(a=eCV2c5L?(vp$kx(`0U5J@`J+Bj;*{ZMfV${yL(jWlo zrW2Wo+F$qS?&4R_-hnpY4_Ov#=WtV^bzkZ?@t>h&c@U+JUUdA`)x@Q~{kPNU-Y2Jv zHAS7n^RXfLz`&7Pys#g?-ngL!VSpyaEEW|_URSQaz|0or_YTWokJnQ|$7Z9n&zy{n zWMe6ECt}rmio^;HO1Lk?QP!v!47cyHa@Qxs+dU$U!S7l)E3O^CipD=-Lu$mcTg?p- zqCL51dkid#-~o-8wxZX9%0nrm)ddji{aol%SK^C5o;7qLsS2T5Fq@w+*=5|#7)otN z2Yh;`kqx;ERLReuZ`Yc}D0Pf{*M2}AKDc*{s6SjbkP0pjY*&4Cdi;I*1M;n^xraF| ztRsbO*J$&^0rZ(mHf|G@&r##P1OOoj%mZBP!vd{`_tMYbdAO6m{(XN!G;pqV8u#Ll zyH--i($VD~i??AG=#HWA#B-G=P)$sm3>=o{zv~buIQax{V6NLr;GqF!;2;;VJuib5 zEg?){`sZX$eA`he@TEz_v|4qaQygMVC`fA7Ptq4iNUn9@ed;o9dVutF7uH+71vkHw zOq7`oaCyQ-HYb>Bz3YSx{ioKbQF%D&gO}oPx2j|Sx*aCX(#&g;%@I(Li++-@-xtR4 zVfz)t0C}=3iemq|oAr|lRa78C`z;Yq3P88(_FG+rlv~kgORvI5v!(a;9Rn&#eMxxl z`099eOM(@eflbPhm3cSnBvTyeYKltR{lmrcx#emGiRe+QsdeALn!EkWN!wyF4i*^} z7#S?=&}4B91*mFJhx2i&6f}N6^-Q6%RJ14*f z88y;%JnDMnS(;H?V1K8c#cM9^@zI?5FHd&jRp&@ng+$Czh-q$w$^jyab`+>}ng z)_XRW(@E0M1`kj>AT2FzAFh{qwc8lq-y!ErV4YQ9joMpHTWHBe&?C2WUFmA0Pt;7w zV&BU9BdHFsD@8D3bza-55t?hxwf_MhUQZ>3Aq?1b@(mtH%y2TOjx~jlD4JsPM)S1u zx%&~rDcUyrNIi;~afVf7TSxj^}czh!W{8UNIuH^#tDkbnmWYwdRV=j@03AanZA~2 zuKCoqGyc|7Gahqq0~)+jR)>UVv!J;0m`T9|)1*vf)yuDXcOd=_1u41_j(h-o~vPLJDn?j zwfWFN$bLRuocM96+Xh5v3cPG!c%!{%zl2v8z3)ND`mLlRJ7#YbvKB=pu5rD4hn<)h z*69Q%?D#(E(0JDGbm}0wmq!n4+`o=_8+4FR8eYcL(tSG>VZ;6(ZNQ&x2v$akb2f@Q z<>l|2ciWS?Izh`^I~MjJ=Y$BSBDK}txIEn%jtjd_siE5?Fc!s0bN2BmP^q*%3dyFM z<>kYNv3f>~jEtlCA~l;K4XSgd*c4xSvuNQ%=WfoOaaQdz%utANX1y#nX`EWmcdL|! zQT;hqT58}S6;-<>t`=uCi?!c1tuGC$WXzHgR6tnU#vj|PXRuUY*>b&lqH;{6*mJ!^ zKUrbyNIS1nn2JloK|45+fVR<>-|DeJ@r?*<9 zp{=_6^YxsFLGMK49089ExKIwJ*xcaV56+luVTVhEmD)qMT)ZextIj-yO`!fnkqbN4^B*G%z=Khp1%?%X% z&|YelbZc(4TQ$8tn(=xLJJ2;R@9-dm;Xk1suSZ`u;b|sj@VF~Rl}5pZUj0?XGWBoc z8o-2^r|xsrSO#D8`eH}s>m6Xeok@vkK{-kftKHD%j;g|OYpX|zq`RYwJ)pDp5tZ$} zyjShj*_~`VbQLpwrd|G-y%gTEVFuuEd%wD3_L93;{1jx6!Aqg?=2ZtV@N|ib*#Q5oVvrO#tdx&PP@D}}AQTVbf zxkvTu--@PIa}^az7AwE#vN&&eB`u1rEbv!Hw%$KI^S@%wJ5SHFh*-|#$3b)Mfop^2 z%Sq(;vcCrz=EO}59xeSMUzF0z5on}W6U=9({Ds~)d{qOuFH*t8~o*FBHeF_S`aAE3(r>59c2#=W6ls-ioQ8l4xXV zKNnS<_M1;>nXIROl{3oZIqoI0y>tKA{{%NDOzKZ4>K5W1s%)(zSf2KdyGa6e(V-WQ z^W)}IBDX5qS>^=p|FS5|Q#-FmPUW4t6|?SRSf})!9U02kh-B>i8mJ0^G2zsv{_h>;1-0bufQyae)^;EOHY z61FbK7Z#$1UD%NomdTPRy&f4pJw84wcKc;pXumW1AUK?Q?G(J*$CM0$mkAzek9>55 zazSgv-C`~K0v0Gi@Be>v#N0iW83K79v~J$pzl_}5jXvkUr9=gng2wEBWVXK{UxuLA zklUln)UjB`*YVxkY-)FTiMc@mZxUjGKhr^ zJMCVUYZ;%h$z!!WbR}vu5ik%*$^0u@;ltioF8C}@W1Yh;WlJA@BqH)T7J?6V*@pT< zkNTj3PK4z{>hXeZTW45co17SOvH0=r@Wy&P+0UFQd6HE^wAKrxx6g0#z0cXg05apI z4H*AQvl6>H!ZpzagRxIsbsr)uc+%}Ti#`Sux-)jw7Xy5{dkTcFW$R7xZ0En*?rCE2 z!3Go4Z6o1vqM)woOTFb~TG;(o(CS7zS%T ztzn`EwYO6Y#@Ps*`w0ov_N+HREq}Kp_wV9rUxTI;#v>gdAqaU{NS&SNB59w3>T)2+ zi}nX%sKLI;(Vxth9(X2gkUPG{-(A`LSFEI$A}AhIs|-TQjews8i#x8yp^q2v%VXwh zB-#n$0vKZ+IF!00x-$Q&gvL?`_kTM*f3z{;uTijIGmrGE7<%%mr&@a z6x~?huN~C9#5>e<$ewwVo*}b^|ES36T;%PceOd(KT=gJ^T6YM`aYKQa@d_$pg*Ad6 ze2%&@yJ@HE&d$ytG!5f`oDSDDA~m){(v+%>e~*l7!-DF3m2DBBlC|10BKKeK8^0-_ zp7sls2KLli&zXcAgQ`q3Oj%rSdYqmbJ73MfO#JNon!+JtsiNMzFAaZ%OBAMxFT!kP zBJE%(o=<=sv!S|RYvU<`dB_xc+Bm@q#?q%|7mgjL`GM~=badaXtuv;+SHyHEnPzeN zR@)ol`|j*~n;#mG%P3!#8Aaw|pcMA)9~!Zr>p_>{z&tvidpMhLY5z!TM&3t__bn0~ zg8&I0Jgj0|Cl7xB1^bzkE&1{|I&ZEt(RzB0z{@Y>Us5KORQ%5~2PTdBkKXWTrMJ$B z3UIDM#!@g#n*>wsn*)=7$~Yev57P~v;&sA6A;yb@ey_hw6L9DEg0u;fq-}< z5bEnq6p67d@2o10C^5i7vxhbUR~J*&C0|GsTlp19@&EzWL9M@`JgTchp(}pc_(O>c z138z-rX4vApN$xjE##+6rJ$}_k@;;jS{MXb>>CeztvptC9(i(Z0hbEZBnMkD+#f+e zJa}fm{w)z$g`nq`y%eyMlW*ZA8zLlhuyBatj48Au>q5mnU**x6<~4?q}FASJ-Q z5JBb686dU3$FK>BH*fIQ$?<)PJ*Ot^F1dB4zdXJaX6V?j9yNb z($C5H9L0xkQgFo;a#f9`pG_k85TP%@@3TCtxV>YLkG(A1(X#z%7+QMpm{`QS=9c@+iF`w8yC zzcV+6UCyd}sUB9n<})ml5CQG~T)rHuLO8d zdGh4Wwzc-&>rR*vMpoB>cIyjSE54N6{2ILp+iesrCMNg!;t$|kaU2-FmWokEld3P~bV1 zQtGA_>#J)`?!Hn~#?1(`;?@6GFG zeTtevn%XwX1a_!Ok3Hdj^_;5;W8!j_d)LvCRZZ~SwI#jz4hd{Uj{;MOZ0}$f0gJV! zWF20c+10?k9P9MkFwmb!iLb|0LnER{+~mhOiNJ&{5fo&`6h`{oDGXfpjT&cXRg3pf z3!35YK~A4%Z~oA~N~00zOGjRsn+o7#HcsXy;a4rCl1NfgeIS`SK@)jO?^c)@a)|8` zHXGFF$-54CmFYTyjTYJx@zXa|l_q)MW%Av?4bt)YSa~ef>V`S<-6o0LA~m%UDbv%X zzY$K=XE1bsEn#LeMW#@i@S63!e0`iQ4U83fW$3Q&(ebW`xDJTNglJKa4z5 zeb8IW%yjD2mcHTLVNG8@-H!`^&SeON8|oeY!#K#B5@Pw(2php#4TQ13n~YFO`gbDl z-e+bT#@}wIy5RH`CcVQ4tN3H4zqBl64!@(NoVP&{q;Qr*fI59HCK3qJ+zK_mXjjj7 zHlaf2b6Ob@^3FMFrXXqm49hM`>AtH;J2y$n=_df^#oz=t&7hPCH1VpL8f$Q9zC` z0@MrjH$e++iFL)!#D^!<3U=FiQ*I_*!IRbv1=O6QX(6hkVxk9txsWXeaH0p8R$7@+ zY1N)%R$?>#>$9V~A1}x5Jyvitq883?DxsfmvuDnG5@VHWp1<-v*nFDTX@yqzJ@@3N zDi-I!g}rPrT&)IWTwU6E#l5n@gEtXejgg{H-MNXJ6<~4)yzQgJH1GbYscS(o{IIRb|lf|+q)(tl$7ZLsl$f9 ze#uDs*Z?DB-_I>2!taz&eIIK6K82;9bHQM`IXPJp$Hkh%PDf>kKoN?c;zctS`D~8I z$|E{(bz^}TK&Cy_@(M(ZB`#*L7QCEBjY|>PocT|P2EvwI3p#Y_M}6G^I(_&Z*tq&9 z$lDw!2In2!ug$w&^ID`}AVkklL?BGqUqLyk?T`noo9utxbo*e4P20w6Q@DPWNB8+c z>(F+9?3n$1F#NseBT_y}IzBYprHgKlx!cieto{DpNG2JymC2vKt(3rRZ1u?vUAD)( zUxLs|T(+yIcBA?JW2UTec9(ECiS3_!XHp(R6&xX0H0m8MEndgd`=5Jbd77vXQlOb+ zg%VUzdlJQmyi)zyT*ln1ulK!{U)@HGR;N=?1dysJJ__UiCeLSqY!f-L=&2eP+Q_4M z2ORj@ZiII1C9*`;lWR4^6etxbYqcrnE4hDS%Uh0%IP4G=1%0gu{98dn=0JKjF+dS& z)LQt!*}iv#;fBgCD!wEsCNrwsOUS7iIQ9lj zv0cv7xIc32#0KgmP25RpR9pc1$bAh{2#qX35J2z|%bzA2M`>?0=C|Tl&y1pA;19h= z#!5?5Nu_0RH!Gc3p5LOZ_V!fa4H|q-h@m>!a)fj2-<^MM^kC8}SE|WoaK9MgcRX8{ zk2jD~w0*ENixL@n(w19(tkER?-Uu_{i!jrDm9g>mk-n>e?K(B^-m-1%BOAh zdHXn_h!fLv3GH;etXrW?O-Co>ki4mYFc5MVYT1PMnT*B?II@9_0$y8L>`=a`4I zxsa-0Lq$6U20`U=-AkX|ogI-t=hF4LU6J(FhRUV4EGdN6qu{!#>BEj$q(AAtd%}8k z-i!6s`Oa+|7uhLN5?&CsM*PY|S84tjJd{V=d{R`qs}SiaI}X3OE^w6qH}e0EwOs#$ zwfy{5Bxy;6RG1k%eE7$0zh0!Ioc|)?nqcQCWUdHGQJ6%8#e{Ov=C{Pw`Cm#12(l6O0uQuaf1yJ3cVp@5D$lE{F|GZ&ri>~?0TIA#sf0@F&_^I?+!NZ7f+;!V%EsHEf^ywLR zZGz(jV_oYOXqKkKEx}_9Q`~Kc!({$6@il~C1Z4TA7ey<-YZe{5?VfU&a9o7YI&_hA zym_TUCG7Q2IT|8Sz}^AEtGm8L=w07O!5@Y?$}ABg`Ee;Ydyy)|t$?un8#>>-^-@tO zS~^jX7eZYu3m*$xkxhY<7#SJ}b`i<_vW69qksZ)r&D(=J6(9ixO5>=~&4t_U7l*Tu z!O6B+I|nYPBYboqj-j|P^2rjG&=%Gn1`RUPo+u2=49+fLyBG|)lSmHW%3n>4M8-L8 zzq@1=oh%c*%P%dgl!YL*5@;w<)Pna6Nv5K^r|hjNejO~RMi`uX`dzC@vG=w+E-aVY*k$Rw z*v|zQ*&@y_Osf6ew4&^xRkTn$*%z4N!d2%ftp5Y?ev@{gj4au9_O(1Hk}a!+Rj0Tm z{vI~Shr8q8X$&IxVdS7;_3LKW zADc02vM7U&hV^5TihfSU5;pBU^t|avG69x0ht0B^>Y=$p5(Fl#E#ay}o38^9Gx`z} zG1HbxT)E!k=Y5n!=cUu{yrFSn)D4@QhSuoqnd2DXBacE2+@_C`1(`9myO4KTQ_Ktu zxr3`Qdh;3Dws7RR5vU62K%7jP0*b7?gAfvBE z{EB=WY3|=14g7N@C0lxg`BY@)4Fe9##K!Z!=Pk;>cF+p=6;86(FQPKovxNcB%ZSX_ z&P0^mmkL!q6o33!uv3To5zYVcFk6e2g#|aKS&RICdnjz)u#1Kd>!k?QS0fLgg-V#r zIMCO8n!>_5S`9#KB_`YNDXd7t$V~W|X^Tc?7rh}v!(wp)wO?e6bzcNYfy~wwLp;^U z4;##&qvjlnp06vaeBZd`QEzAuOoSfv8KYjbvl6HYl=7g=PwPdbGQ7L zg^ndrl>8akb=w@feo8K}%vLnlEq?`q^&){7Ph_L~KxNgMNV?j|?3Hw;)r20T+<3fB$mc=uwk!;<-^aBiT8KzWlar_nN(`klf075wb}+wDVRpLC+QF zwpM>sld>GgS9>Cb_f<`mnsmte3Sonbs8s_`zT_JL)jD3@BSIN06!35p+F#~!J$peAeQt62x=`pC3k}_{W`9%ti|WaMUU@>HtHn&h_ZZ(;= zOO~?s{}U1A=jd!zHw1<+x-TF<-?wzZt@|wS*!?z@289kfG{4;ahHMi7=k4Nm!=SGldzR^Kw zT!SyCmh)jH{&;>xR;41@^}uOP~2(k!NI&>Qzr zaDA^cCZ|nTKZAQytd4%9r^DHj*}6N>ZTB`3ARKufb2O!eWk}GWRSP42v69)QfI-uD zum~3JTEep}-X$$^PAq%dq^%S_FE?6n7m@xy>q8}ulPB;d4ELWdRDg!_$?1tIj#h~_ zpX*N_(c<#WHdK}Gup5VQRX8yrL{SL|4fa`;A^jW!~z@jF#Y3s{>asQ@$-QD})gL_T4p(UJz|4x$_;-;`Pl> zF6;dda#}o4Cz|=2AIkfMmig<9RLLvX&@>tt{LIlVa1Pgmevbd~JMxh4vR^yxjbC6S zrPHmFU@!Vu*IlRekpPnaSJNMeOsw_%7gKN+5Bfv}raBT`kP=D_mkiB86;k{pdKnWl z{r4*4D*gLBTsS1)iYbm7Nmpe<3bPoannyu^42Afr*2SpSWLe0klU!X&-BDm zSAE@6n{Lznv*2e@h@TK42fq*RxbKx@;b;k$7T+(YfBGFxdfaa=hp{T>%*}0SUS$7g z%r3@}m+Wc>pIFEE5RWl4A5ek|ImUUPC%6XhTll4hO#l!fvsXl=N9KSI$P?Nxk!g5R zts1?-JhpRSWV)Ra<|v|o91wtpPY~-S%YpJ%k96{wkt3-SV@`H3oX!I6BdN8?&_v|8 zH{O3tdDd4liH`qxYwhJ2w8}CYZ$++JU0>A*=tmsLZ<^bI6?3vGwg{&i?pBL<@zlUp zuTa`|v`}0RjFy_<5+kc;wS(uSle>VizqwB^ly@wOH3|);Zo^YJdq=nA*5!U8*JrOD zD|wf#*Ka*I1$~Nf8e(mdGLxGJ)1Go1O!MxNrHgV5zojkIgIIIO&^^AAmChN*#UXrN zI0r(HyI=WyA@Gkn%j(0R)b~Rz&5mx!WDW_r71epM{`%)X5UrF3R#~qD3gc73O7?oU zuUzj+<4k1a0#R4cU@~!4CS=&)*eOe*3j)-rcsbufvUmA{=P$rCt%(Xvdn%_Rpn~8` z?33ygB}=~r3riD1`&iYO#rNi(4JB(i@FmS^(qxnS07wAEB$Dod zEI8Qb6!1%AE@*zzQ5%@4v5>rgQ4HOAsshJwY?Kh=U0!CKwT(OtHZ9~&MsQ+t6NH)c zir`vmXgl~A-S1Q8!ALc>YVZfZ47nDE5(m6QG8s>>qv~f9MQ;sQ_e{p3j3#R^u3)%^~X09ihO+1c&CfwAbOI>H3$|Q1F zAfd3J5Wgo+@)S1nONpKrb!hOo9<>S?-HzS4wwHV~43xc2395n8O#hI3?yHPlWU^FP zti%8m|JQPRwu1Q@Fv?^vA#`8^aJA$JMvNdB6#mIW=1ON>$izJ9)L|Ib8v z_UN1n&$zxA6#gjX()?Ch>NW0A_A)^mexxSPQ{@7&AToT<3yG?3HzV2|OP*UqOxrHq zWJ{f+jFMj*6-CsB`F5I&4m7?0ZqV6Rn$xU?r1pO65-=lu#P=M-D~AX0hpulb6xL0Ne;=spZb(-vH-wZe$co76jw3sD(HkT@w`3_2>L41N_EN?@ zRBqas7CKC8joo<>Ji(RkT2g#8;(hk^-1I=bD-le5tl-6SV*KTcP(pidnvc1(<_d`S znls8Fb+!TnX2C4mfN;HqXAF!@0x5>F9c>azK|ZkSr^yri&%>*wLHGH1Yod6A>2;nd zl+WWwTskc#{Dj?~ne4bN8r<*iV@}^{TP-<=B=vzkc}k4>mE3fAuB}JgdNe91ZomFP|aVW{>C*qHf z64SUgE~-{_m8}^l28;_TJ-9b7!YV6CwLM2VIx4f~(UI&Fz=|Q}6|gzIN48yr-qv)< z9}U{I?`YM>i{Rb`XpHHZh^DMeKDCa8WUv_qTw5ND0#NYezYkM5)Kh=PBhhK#A<+aqjqEOBi<2qq@t`0vM|=2`1L+RKr7xAcG2AZ?$?u2S_|G(T};0M8SM^7ac|GKoQjhM z?$tKKS%pTrYH@9z%q1Ih3u-_Q*<`yJX8|Q8NzL7bN1BGU!JH{B0rbt>j~ay(rrgzT z$P#6)o-e>4@gZc=QVSZnY=EuWt%fby+<zcV!JP}E_Tv{ze6!KN8k;6 z_|w;7Y-otlfB5zsMW^~~k5b;5y8t*%94KX*+}&8n%p>>4~x~YTbZmTeRQ6pq#TY z+HRUeF|Syvm+)}m!LRspFa?BBg*3%V7@&PJlk==)FP@F|JQ7p(nYOo+iG6wqnwrxW z_}j{~t9EDpZDtB32>WHCuhN&ZnWB=4W}3MynkN=!@KoJ?Td!tI4f#+)vR%G-(EF!q z`fdH(t}h5{qw98TfB6jdqzM+&lD?dq7&2yHM8D)8EAS_*?RIaobg)Vc`|%Pk35%4H z{bOl}=x!R!mnDh+;rM$IKgq8h9yf2lc~QH#hFM<(wdXv+gW) zLQh(aS?`ndlg}*(5*j*F&l>aR?neLkz}xb}RL*2vd@y9Dtp_g=!*6k1G)DFp4t|Yd zFf?y&mO3jR9B}?=-;<~EwMp(7Sx7QP4Ek9fw+-(0WI(`YfzbDsdEf2T{CPuhVm+H( z9%L%6Z!IH)LMZ!QA{{v3Bsf_Q}@XU?FoT_HpfS$=903qo|s>Q#uT=|py?U-GbMc`=zq@WJ2WgE?TB%?S9a>IU!Ynt zOaM89f%3aRl#B0(eZi4|Hb#X`7)KUmtjR5+ha#?O(9|*pQd8*Z4P9Z0u8`@l))!$m z!6)qFZC;uPHb;#JJw_g@{~SE}K8Zm&-O*Z5qYH9|%e)C#886+vbO5{`Je}PIB^%im zKd_mf%J41o&#jH(vC7-aO*7=R!(l@(>HVePI$lp=jn!s6_+F3nH&@jy)_GoL?l%s| z!@c*7|9SiuXln7Ch2hI~Us3q{vlAEE{VW_Ry*g@t5+tUCluVJvk`goq{T~s@B!&l( zGQk|hclx}9-Ogl6C9t>%IY&7%s<{usz3H>9lPh<*Bs--6QWaf1GQ$j5*(n$QN;~x~B_8AvQeL8s$b}PEORPGS||2(`Vl@9%yu&iq5BTNL>@nvz|n&o>x=j zFdLg7gYX?$B$eV0L%rzN4s|>A%r*_tXQ3u8MCzF%D3&>n&vuY&uErM`ykZ~3JS zaSJmSG+GiL?2nHZr&dD(0z^!J{S_PXtqn#K<16iXJCHH;LCdQ4kn3wFW2JOn*GlST zhBa!4zyp1j&*MQL$}VIVco4FSd|qMeeUwvGJUw7YAmd%}5d_+Ao#pfdAujyuz{K^4 zVf9C;o<8Anp(`NFan41PP3pHfcDnk?kk3(SL6P^N;KEwFMpksO5udTiG}xLmS#06) zRO1zE#D-at#d*GU{MXALod%Hm6p9R<;>l(0o=zH37mMFQ{_eR$cFc|6#;6~>QjQIE z-JTlnw%$O{XER1g_dR2>aaC&*-qrgU{}^OS6YO?O3GBM3<+iWRFRr9E_^2^nS|%^3{}Wk-1R++a=l*K$X3%!#S}rgw`@X#ljgGR-C(Zp-Pha5W;aRlBL?>IFpAX5%z|Ziy z{MB?&1QF5ESS*eWe>TCHLKksmODKzJ?ay1hY3yW4xc7SAK7VpF>tJ%S`o}{j zfB4DaTC93u*=*tN?my)Ts6d}3`CXuFq3Y?|%)d~7TS*>+q%wPC1=Wp9z0?lWvymZc zF-K$Fo;Yi+$3kUL_ftr^&_@Tie;egSsrfLsJC+`bp}@6~)9U2vy7g24g{HykH+18lXv@mx z0k6H!rx{cR=#9n0TZZD1es-{y7qe&cMhiWPTb#eG7Z*}#+?+E0jklY5;>rY`w>HM} zOaMB-q7e$~LLzrD2f3XFY?LQX0`Fqpkbw)eCzXNNh~Z7CEulJW-=o7uFgEUnQD)QZ zZW^<3$PL@h0vS4e>)R`4!xHlO*vMXfhiI)nck8x4`Kt!9bfLEsbeC zbcu-W*U&^k1m1kayFn$!LLojiWFb3h&X}HF9&(y~`M!|F0f_*cupwCVT(1~KMtk|P zv^iezXQt$=8 zYq6~=G)8n+H8XufjEAauS#|hr^O4(sOO+tlZJWE>##IA8*~IQUq`OBd4Yn-PAT;O3 z4WuwqjxsKI-r6uM@asTKSw+fd3t^hLm77<~U?1i8^Ap|+gr8*p3*;rR1p*_tH$OMx zxlI*YjG0b_qUl0ql<$b*{p?X|m2$q+eQ7iK_KkS*cM*e;sK^YqO1PinT-jGileVLU zDnZgg>B2y&nQh;tVbxTMY@th!#OoiB;m%)2Up#S>{WLU9%zysD=tN>TV*(~;A*5w} z(+FwlMMMfq3jUN&_fsZq1gcX~d<5~}z{WVN3p7@kF_KJ=cVk$o(~O>!l$7t2p~~Z@ zgEq|8oZZAC&!Bi9pGxtgbopYz#RMtLAddL?ww-sb#=&H&cz+6Hlj{c9*en30a#Vb% zYJX%&_F6C3V@}93a9Z5@`*6ifdV2aT#K5ew|2nHbnXHglWTyDrJ!|)HkK>m&rYqrU z8(vanW3fLQ82iou<91*xO!Mz%kYy}(S{)rzs4v=yFjABvbT9noNHz2gFW6fj<9VD< zE|ne6^f$%9);&Hr>u+uN*Zced=xDOCj=chPb2gG$u=N^aO<@OO{OE-S;X#_%N}Eh3 zHfUhPgGYW0v>kMP9R_UD+}pe5gbpPnxzEtO5x@yr)QxfVTax%>6v?g$u-O*Of-k^q zyUZ?C3OgW1{2%LtM@O4duHT?v{F?p24}&5B#1@74NnEp=Vpl43On#v#tO%dron-;u z=gMIJdo>JfFo>z7`yPmKfC?<>TVp}50^#a*-ePS``c*3cCnG^gXt3vL;s)Cz= zqW8~tC#Nbnh(uh9jY7D{oTbfzZMEw-cJ-}?L+1C3U8dAcUKQW3p5R~}JJN+ez+ zEDFO?c5ydo5L-C27_YMQipls_aMNirZz`f?3Ux?u0<&ll*^zr$6*X-CIw^KW??wO_ zI(yrW)+~n$((#&RWLHrpRitTJq2bE6+1XcYL`^Vxp zxY*}=^V!b(&U^l+Bfh5*jqM0SU(o%yGwMIC{RMg})lUwSe8rwmkIeov%TKv>>CYpU zEFt9YQ=x;%m|+6}$9M2w*xo+qG(Jnnm8Aq6A6sqj?AZ74*WtMb0n3?mvyba2q?pAe z6Q5>`5-tzFN05IrJ~U=Y&W3wvUTkw`1=4AJcY`|IVwN&k$kLT!q!UqB$38qf@{2GY zSl5hMQ^8fPTO|G1^tR_4Qnmhp?(GYFmd8HadjHyb^6|t5G9_m0n$l6~zpx4?#19b& zrn+9;NY*rY&zV3j2aCMY-J=tFfIPqXajWEtv6r&*`6HPc3oC0}a`N8-O$zP7;8WEz zCauqVLOjYn6yUKFHy43@@J86w;ilaT24lTpq-iWSibn6OM5QL^&N=hxW!PJRn_I8Z3d5QO!`m8r^-_p%KsuQKIn@jt`~Y5 zQEjw%*K6=xf(bi^jiI5I6v*_#jf^Ve4a)19bwo)^E26xdu4%=_*buP5lxlF7Z8CBt zL>zQ;W+mD3Cwy}gdV6n|ESZAg3kp}Mrq1ySXu&7rMkM)>~_mI0u*QT%S zaMj`7K(Z4-F%5>}*HZTc)6nPlz4Z`1k&V}MDFg68r8});)v1uKwep6KKjin%)N%C5e z5bAKt*i3XKIKrW;nM=hQS4@SmLyTii5GW-=!zH6_b zfU>l-ws-Yb^pUkh$*=7n-zYgY4xL(1nyAw% z9*xTZ8uz8X!E^|g1Ox<4$2BA8%Z?Md&CQ=wiWS6NT{$}*=cseT;n}WewTA z6A}BTCiI|3*6E+wz{O{D5 z98Q?%G5Mi_dU5FcUYCJZE)hAuu3w%s1C5_&EdVxI4L~gVt@}orA6c@+SspYDE&HU9 z+I1s0l#t~#5u!tu3t*U^JUlvL_I`9aDy@71K*$NHZ;6$U{EOr7n%TbHh4n8@;Y85B z`Mm6_{LiXlAZ=he0i`oDyHq=40t=ymp&kOcICpMLEWVrRW5`oP(i6GoReN$L7g&f-3szL3nGhKIef% zlP&wK3mrF44ub@)T}L-**5S!A0M=;*fd`Y?p)GF5-Sv*A(}Ym!KOq2mdxi!n9J$L} z`Ttg4Lc@Tt2?EnguP%N59pY;|roE}UAg<^mWMP4-8WZW`sttvg!MQhM{_3=PJHQvI0r5H6(@juXakKbu`2Q>?*bDElfNK zclF|X-c<)9?L{c_i4vuY22yrU>xVRtriGY#OFJAb1S>xryGfEij?;2pyW^Qr)~dc zbc$9lg)Dc-p=4@4=g`XB{8KF7bw<*EST7+Yd;m0@N~}bky=4!$ zPv1jqbq?w5lRoNG7*-jO$Kq3KvQr~F zW^)|fp^PX~z*Kx$i+CbwI_A37OtTh#$g7+=G}+_4^`Ba-)E1F2fs z+mqPu)Bs18;SCy??FuL&Vn=>>c%Vqp!y(U?X6nfN)OT4EX^`I^Tm*DU&*(_7_mQka zl@=lD3E@l6hyYf7-Ak(liTM0uDbcPnOU5#a2zIm@!sp)5%QPiD7 z?Q^n!{-3`BBg;>2DBmeGUOY{MV6)=Rzur)%@+mTj${UkQA z)Yjxh)1dA6Emb_CxSNaf9S*q1FpMxPH6^LTs4&vTAj^-)jP8jKnCT5wG@9~TTV32p zv0ya<9Q7G?I5J%$zY4H0_kyQzP}%28*W}(8AOS0jfK0VpRJ#^+?M4^rg;q+V;UQMj zL?V$7*q$s)qO+&csHgV7kb;n)n^Z%r#vV_$j2p)z*kqU2DvP6xMA7Db z4UkwBC}@|%GpKi>%naWNP#(h90q8~BJ@Yz%JM4psVc7}izeYx~OJL^gsr>Dos z#ev4;j>v3*H(bK|e#`dEmit2BW-aE}yWb2WB!7Gsb z@TM5Jr(Bh^ORU`K>dXT#a6MLVZ>#BWnie4pi*uzuNZc1pig6UmL)foBtuI8y7$o8; z5UCXUowFY8ZS_+0*>X?P{$xrWl}3lId-HewCvw)JY5FKNU`u|#yrzQ-^`gn*u4A6T zMj#WT01>i{g$i`3!r${gcP4w9ARpi|RMLNj`XQKXaB8?#F+So$bl;!t@1|*JU_!2p?76+0RxSVzkj`k!@=a+bI4;H731=git1}{uocR09T0ui@AOrUwSUGr#s85QURRa#J{vwCs(1EGNfNDiZtrFL65?@Kbv1U}RIFM*8rVULMka$e)!yvM zEgoJBwaP#2`D`6&;93g5ta~5Q{!1iLz(N5S#1kmUM&m=LQn(~$3L_GvsT*;&8nR?? zjV;Qh0AjQXkj37RzmOrnETvFMQ0<2M-8wLXs?8>U@yL5-z& z-@C|utO)Plzjiiq^rYW49QeNHaoW7Md0aa|=s*O~UiOEejL+UE9j@fTuqj?Qq{acv0CW=b=Su=YB0rmqqZBD0d@+ste^( zM+`yRW%fR0a%k$aGQ77EF6h@85}`0v0M6HSL@SuRJ2Jv!9@&sM5#-1@--2_JbRo$fPp5s{IxhqLCIw zTiFDy|GbHx#H%c>g}=0Z7Y%{!(-NbJC zvc5)C@(B)uG^q=$#p!4Xo)_dtB4U%1#;Ni(0tDOwF_|+!t@6b1WGegbL%!g2UlZ5h zo7?@#*x&5*-oUvHN<+(`&`fH88=;_=xMHo!d1;fu+})cR#t6ymr{jjsg3~=$2FDr7 zi6Os9tFbG3BFMy!f1P4MsSJ*9pfi!w55#- zI;&AZX(>{3b2Ar2s5TlDhxXCE&FMh74WjhwcHWI}5pC;b+$1VjvJi?%|E&OwD3TjT zJ%A)Gfsp@S2Ez}wZ<@ujTiPxB3bcj$&8H7;^vN7~Umu5sRK+{%OO%a)t}q_oDAx`{ zW&m(>0~B!FUqfmnUgQ6fLXEJ@8&>h#3tWenAdOC;T${=)nHmqZnpe9~A+H!M za^O8Zm~@A@g5v9PYcCYA_`}q+2$7@u^Bfb*uqfXkTT4!}td84D0oAxS!$)nk3gKTn z{d|lq#lLh9nXn_o9S3I**}l$axQ2r;QJa~#XXiNAuj65mJ}ATWy5a4`2h40(N!`=0 zJNsO=VMy{tLHOxh&iec1W;r);%Lq0A^K_Q`?x%9?MigHNcptrq`+?0UYosR^N2H+S zBAHjA-}x$qPf-|?E0^$QPpe{@<^}!h2;9*@_-~yJp$oOd>M(yO3a8i@0<>5@QVs7~aa{eBqRKplBmC8>R!R-&5 zAPnXo@jv`zeg7^=O-r73X^DSmzT#SrC$2y&gwL;vuj&3*4v##6@_m1y-YJY7E*%&3%vi8{Ja1E4LfXTYe`6J z^t)cq2P5V($kqeJ+J%AFcwa1t$p@0`)kPB%@a$2ql*;Q60`^ft?B|mfj4#}=Z@P5Zm}USVKCG*%AQP?gSGViRHfLdC~p7 zXSPSRE^NZO*YeZc{pW9FjV&nJYE_cb_J+%*^5yg{HQ(suWHhyhlClgj=}1M45hvrn zvNX4Gp48nmA9#47f9G!0;3*8h=Q^pIk>LCOv#JC4uPC(7+tXV5eKOJOc_^9BS=_2U zqo)iWIGMxKw zxDJ*J+l|C2B$G`|nZTC>jQuh$2u0Yc1~t~ov8IKx@!0tZBK;m?IHbyd~%#?BY| zt^Vg-3b8X3>T84VDanXl6=NlZS*hHfEEt%CpxaxXHeD6uPF7TvJ%Y$z2<4gxF4L0# z%cF=;#-9v6NGLrxS#1==b9OcNB@oC~UkgdbY=7WWDjza-yvtO3?|zIr*e`<+xH8f9 zjEqT(ecR%%$xORxW26~jLO-UV#)o&2d8$1b|Hr>Hc!%l?KuZWe%xV;DW8lP?Z(xq< zzLBpHe$fSJe#tJ_-EI!zVvbxgfB0qJFkb4*=}`$sW!+O z>GF~Vl)|Y3%5i$S`I+6-6)vBe3sxfER&r$rJEMB2R$N5e2?zr zWAzVWPjLAMay6UVXGHm6f)q`i5cgZ@x->COGx0tdKD?ALEJm1P{<_M~+T|JxV&9#0 z`uOP}ZmV7d!&~HGhl}xwS!|kr5!}e+y{l?6RY;HivA3ItmD+gf6e*iWy9AX#<@FV*7m7dk*FMz>Yb6gt7Y|F;W);cc8KRn zjEM;22RDT{D`g}rq!r|uj(Tf&h@2t#LnQ+TqncCtc^P!gXFsFEEI6vgB)7Xo#+1-U zDt0T)eO-*_MpXw6o@?CjF5;)fx*t;9`gr+x$o&KTxu9XiV8e*H&X#O9=dc7Ncxtu# zjmDeUgAz`9ljzkdunwe`yHfH;3m8!*rzdw%^VrdP5E!2L6!`X_F=`#=uTc~EL0O{$H7s5f2w zY?m}M(6IcARXp-nZ1tRd+p(0ixIpWl^DHm+0G4I*Fc~28dny#W&DJnxR$aNnW2P+f zQ88HMMc&;o$}iOd3%MzWmNEu;7&ydQc?8yNXFZ=oUpoBB1d8~!8xVT&zJCCZd^`uX&hKLO+Q>9`0Y3*x@REn{Xwib zR;0{3fkE6NhZ`p+!_(*BH15$B2p4q;3m=xu73TxllQ|i8Gu86xyw=cn;}mkqN8(M| z+){pxPbHV98P*0>8Yuh?(F3Q6b*fQNaN$HZnXwztKlP7?!@jw>qNk{glt zb(XHYQY}IIBxc*OR6aj1c)%Q!f9tO)m*QaMraVGvYx4hfIkT(3e$qB}z6YlSnlPLA zlt>w+9_M8M0gWKQNQ4OtGE>-_l&0FFBjgwM`oUxRh7eRhE^xc0V-2ZG<58_znN4_2 z5oh9#!)`1M&wAw=V3r{u2G_x+c6jWqwsScv6oT_^_KGKxglN!l#OgkX8K(0fv*y2b zJyx`?Ypa4f;O`noe6UgyyT5|gJ)I)|SNaPH_4y;h*#mfwYS+4s?%rT+5Og7z+6Yw$ zEs#Gjd)4;6UlS^I6eyjYZJ-$*iKSM$m)bfOj_Ny}UytCNChdLvP4$)cL4L7qT@2Iqf@C5I zWnzh@E?X%)BZE#WfNFARiJ?n^&+=16En%ULxi0gK=05OSO^AE@kXcR8FH_U@9;d{Z z@)a6?dr^b+s?Lc(wQSTApP23qVbPxm55L`q>=nAvOm~ufrov z=^{4*4)$3PcLne;nf!SILV`YL6indp!m^u*RJ;^ba6?)xG6<<0wd@>>$%HM7xZ96A zpX}Ml%1I$5E0ZQGO1CU=R#w|h@kz~!K#4e(C`fXwAhPBxxdW*^Wua&)ED*V-{mKe^ z1;Y;(2aU%eTfz06V>Ob7jL)Pv(}g%i&0tx)=QF} zXPTwE7;&Fyj5-ibFLlF+%bk*Pn=jfc z3VrJT;5B9)S*eX|%j#_5EIu~|HCj9&S|syprA+3 z=sP~t;IVxN#eTBeRuYb%m{e|ez`ct=>|AL(T4X6|$}GK=SrOh@pG_f!dYuk??u>6%hQU-@AxmLvnK5NFRxNReCm9&KHXaakd9s z63^$+PJy@wwkEF@<)?=*c&ZtaxS&2ik{0TY6^VYXwQe}5_r72=*VdsV{lF67 z|J^clm_D(gnnZDOK|52O*lyef<5z;mNZ)ZGEV|PpKV$|(Td|RTnWd*AV}3|BZeK>= z7f7h?`e?TJD-e*8wevc6C`Q*$6hPNJqPxrG*FK%*~2#=_x#`-0`-cqfE*;iWH zXzRR)#P$iA+L-@w?$v()r|(@LiFJ#`&})Cny2R%Z>K{JulQWheaXPZsIyW|GKG;0d zeZJlId(u2fG}37ds`kUh0`>g0M?U9Ae5E)kVvay8<+K0gxmkVel>xc~MyN4Ks(--m z!$+uLIuW&j!4Judby;;7^Tn-~^tLfZ%7?oX+?zThK5_wk+EaI3*Ph=CGkEWD99O9V zGc)M{LgmB7sQ5Mr4|$LLEW^3QC2{Sy1-YMP3r9}o!UjgYD4}?j=~S1-I~E~!JGQbq znj(oP;{NMq$KLlRBaV&DaGK@3I4r|ZCULjFpf(ou5QKt1f$ig~Lsz(`r$JZ7=pF&@N)`G+<6`cg27(@6%KjP%%gq1r^uCV$nR5CMkLQX*< zI15reyQmel^6v& zT*E$loo&yBL}d~Ck3B3Im9Q)W6R=CElG0 z5994N9x30>8yl_|<*7X9rBOkkLAwFBzB@L8_ybF1AvjMlwwljnz>3K1v^% zTy@@LeJ zmls+r91n5Nvtnr8Y9KB~%*Jo9Jud^X8)<7u7nO>dDC&M&$1un>DIzU*Dz4HR&riGpB$83vQ*RF%%F z&@WqB&w6*OzvZAEsfg?8wR4Cs+cnj_sklhle!mG!3zP6Z2sI#UrT^MZsm#HBc*(w_ z^8Od$4X`E8Zfxe9$Mxd*&2g!{j@3jjw<}h~pb2#V*HP(e$;r)EAJ7_^yaWqWbBg3sRP@iyy;gmd*isq|g$5s8V14>`69%)w-CaDq1T?R2lJ zG>;!+4oO9Gro8;;A@^XpgBnd4eHqUZGV@*yiEyT(wt$Nb+_jO2eNxdm$sk;=T3uUX z2^wHZH`IO41GC)r!8`H_K3;eTSZ7JwBAxF`gdUj7I(`ZkRl^vqfx1EDx|`@V(Ny)( zwKNn*;t_l!9bq~{niO--v!l!(AqFckqDs-57^$VnqppGIwHJjNCl_aJATWfM%h)0- zn*|f}-)%4fb33yJg2Kg4C+5};6Cq;KjX>uU;W?Z|+{a_Lt`lNzlBd8`hq{*P!KLqc z0FE|NJ_Rk70h6X0Z+>mb^om~Xw_8|Q@$V^BE{u+jk_|cZ@^&tCv!p%liSRyV=uH+T zH2D2%`~a9Elv!6dN%^&~5c`%l3}4_GoBAMmb&e^sVD(mp^Q5BZwHZe>o1X87H;Oj0~X9YZS(l_bsU>614$G0;1g~Nm?#6xOIk)8pxd5?NpgW8R#H~K#!clee z4pr}~MDVmhk-!?6eOkyvn?%@E?Fv@)uOH_pe!pc=sw#Co*9*t982dzrXzmgo>K{MU zx&(*D1w|c+UlMOmRuOh`3+L7FWSgLG?x8b3lTEyh)i%yS6j)E~UIhnz?gldB>*%Ze zlP%d}A!$&=G`mLjrxo~HjZI?_{ehy6*FsaH9GD}{Y|)Rx`3ug-6{JDYBtj{;%BStg z6GtT*Zc6Wa<%}vj+X!;2a;Ep5_mzl2dZ##ITT*J%PT-W2fhGF5TIg1{`R*HFy1;a; zakn`1x)zx5v=6Y+lWMwL<)I=#j+c&?MI&1BI!7Tc@h24&9BJqclEM?+zt=CaCu+j}7m`KKK7Gl^|>&dqb)F?wa1@+Yum-HW@zCyrSFD_38NW-y159 zbm7O%1yvBgvN!X}>>**HI@E>BLz#K3ptR7Hlxn4OyaBu+)t{|Ja>NkBG{r(We=r?q z89_~vo2AwPj|~7!2z83!5-gZ-*N|z_mo!2}4WzSdwXZ|zCwR_E%y~}i8gqtMOq+QPfdldVwqAVBn%7F~iD(b6Y4Z~YYCDhOg(?9#(o1Hmh^ z7^1J}LI=^MU2mH!m7@m-M>Eg!5;$?kg6gE8Pwan3y)92WnLRq4woQtYI7%3hV>r*+;*J-0#v+;Od8DzTwQ zNFog9d3l}dYYLt?wuSOws?|*JZBNA3VYBdoq$Wm_?difw38XMj`kpIg1j3QK5 z)-s@cL|dwgb>Y%g&?RkzlcTMk|4=J5$Mk)V_x9Tz_%ouik+8E;hke!#+H5a$9`4z$35>$N5=p5poIfOGLsOu zNGAKXM>dqMJGG>EHP_s$wGq!s#Kz%;mBZvk469NPv7ZYBP7f|w`Q4{IqJ?_C ziuyG{H%2mtq5m0?>6rt@;M=i>kq|E?uE;-M4Zp^LoY}R_N3+GlFn2J#hUJ7{-DEPn zGG$C?{37n1O}S=x{SEu=Z%pNo^>?69LLjFAvmPAsX|px49c{YXYehq3f^YOa!)l zM*M^CDK5{Nk&e*V>Qx}=>C>Q`EctIr)7H^}AT*Ub{61>n`5`T{n1woJAVxxDXbdVZhQ>x= zrzNSR5m$9vpgU#|%o6#ie0Av|Q<`k07)+z1VB6YOQt2ht>AQ2+*k}!VG{=bOi&G1~ z*jf@6PEJrm*p~>Z{DdJS8oe)>M2zJ^7Ts)4rfp|tPo~7Eir&(Xku(eF7jYUfzfKQe zVg*|LRAfm7-cE-vuWFuZYrU$3oxaY!jxYxzxX`tnPA%P4q^sg+Ew!ofr2~&$c-mHd zA6b_3*X22py;kEwk@H5X_ut;5-5mXxIuP|1bG=Je$g$8yt7ySVo66njhDEYo?1i+d zWPLz25NU@p4LS*2w<_o=#GGjJ>A5r}t10y;!0b7pmPUhOLfjco=-+yvP;724u041a z><&8Py&;<(Bf%QUx5p%`DaA)gZO^y0kY3qPjMh^#TFQJ?di;&zxQ<|5!4Qyc*mzQ7 z1U57H_EmM6Ya4nmd8f9p42n4M^4J&1DCi$?O#-@A?mVI*a~dHw`$1Nw?c(a{^rwXW zbgv}803shJ9U>#;{uCm?sU=i^u2e` zm#R@2yi3}n#Q<6uII5$d7#dWIj|dhS5ALtSJs90 zxp!8|>*D?H>D{6_IFai7xGbe}Vtd}l%)_6V!_4=GHCd&%?y*!z)rWvPU!E4K-7qqi zFv&}iG78Fh>5Ao0dDnZRkzW!rv6so!CA%mp3?NQl+oo3(CQTbxwyBoF5Fb2)J9%x4 zq@t<%RoE50o)d0rL%y!X3pZ)Rs&y31vio34{jaf)f$!7F);LY%a`v!PjOC%kThju? zXcby_v9a;-tj0!Bcd9okS;{RTR*_55p$aN(anll^(@8YG1SY}kXzSfr$M;2A_3o0HQBi|nD6sTzjMwzbi(WtX38KL<0J#zGeCJ zssaw!jY#&efifntQDtQ%w7sKGXp13^VqBn)ELZbNnvqA_@zGkjX%8S?^8GWrnwFOE zCeV<9(uD>TSucax|6w#*IwT}=$aU|idx!uj_u3%rPY|7wL>H#i;&(_o)2FQ(*5-Oq ztk%itB0@}o9ys*^R7m32cy#uh*+%L#fy}}O9cb>ghM!5bjMUJLkJQ z5;7B$Qdq_M#gcf$<7~vrnvQ&%X84_qiQMLdiH-go!)|tR2Yk{$3#t97JLj3d{<7|> zD=nE?4HHJ~GnAP2R7YD|X0NEq{O;*KQU=^}c3vKXpdhJW-1`S@%IPY=+Mc88Q4pfMRykWhfoWkXOc`sdZ&zl&__cghiL>pUh5j~A zlO9l|egQ{HV(3lx#ELsh%F2e~sum5l5fCYaN_-Hja_(YF`Iu!!gHlKGiAVM$j zE9yrko)?M=sLf;OuUs*&mmx6?ItR zxf0)!ZmOH>hBKV-0=5QQr@Cx}p6A!Jq z-1wHt(kW?SUq9&VOYLHAch?#>?O=eQx0j&)&)Lw*$F%qhx#sfrt#Ku-!CGdn*BO5Z z2$GmM&m{d7D1LtkNt!?qlV&^`gc%5kVt9bjm$fBBvE#84Tv1+%HX~4%qci*{`2am_ zhOZzUl9Zq**Lc8DazS~QuUAS%RNIa_A8fg#d$o$1St`iJ?-~i1fht}$G}>xSuH%xd zLOD1p%PaYJ%@kfgU>j(>2T-hE)|5(lOE^^!kcGG0Q&1R7W`f+Dd|hA==0&Y2kF-P$ zb&3wH@OHxXE`y@&)*7SPTuuzljQPKBZnCX4Uf;Si2I{FFf&f-{`3$@y*+ID9k>goD z6NalK2N%qiR|_HZNX1?SJy-`MSjHM4r`e31(mAyd{VZxIRnZ9NJE@hz$M&#=t)#)B z$?l{tbR-!#XGBSg$ArSZanxXW9ym>Pv5PeKvhpK;A8(eJJZXMA}Xiie|KDpc1VdM z?TJiDssq!^EXc`q__1VbR6p216oc$tSdMk*xRp^aUNq59b0Jm!J`@QwOjFE&P>!F{ zPor*XMaAy_cyDkYZ-hn`V!}uND7y|X+=*=?Rhxr8)su*8sk}PL*VNpY-K;n9?dFL^ zb!kjfw)2AAZQ55_WNL71(MZ+s_na|?n-_*dMZ=BKOjW_hkX1&{rSrxckxj?DmnUzC zk4&#yG{qT;O}Z?h@D1}ZpG8s|lQfDCJ6TOFZOl{~cu70C(PPtTdS8Ck#n>J5WhHQH z4{ivI(cWq*zE4IUFCnR{sYzH0gT9SShrLQ zIB%dw;?saGCr4I`euE`Ckl}Q_a}>;H&@$0>E)3SmEhcsR*dt7hr!67?(AFA z;UKEX(i=?DZe|y?i-2>rgK_*>Qpr-;+8{$vs6qi2Df>knyvpBFsZ7biXgVQazf3i& zk`?8UsT$18Yhzzm_@%wSrkbbNE&|A;aF&1l`GR_<1N9yV3%F3&JPVq90Y7%916`~4 z;7fSPJwmZx5;?6aB~W%Gm@shYwb98hcQx$+`__5vJHD_uT_SC{tnWnG`jFf&`Lm72 zAK%VX0#th=C(wg!0V-%a;w3j658FoCB#p*pnXO|nCx(p? zHNS}0H{fM}{6tw((B1BG&S5z2z^!zD+ND@tdSU{7_?DEn{s4Sc85IoK0C72AZkGKj zegJW3MZ~wYcwE4qgq!MKzuW8%S3^cA4{TkNDjW!O;Y}A3wXis$l@P(Z2-m*a%i=zo zM(V2GYbC_94VWKjAd^k;XX1D0T;D&gyo0GQCFEGU?%rM#3>hL)m4>b&+sQMDuJ|Y< z6SYy*nez?Y&s>ogCC?iMgal;84lBx+w0)gG*rq%u_Lii+Wt6$1;Vp7o^8UU+uAah5 zp?(BY)0xx7wy-rj^V!SZ5u&7+N^4%tnKOM+F6zio=rZ z`kCUO-(xJhzC+~{Q92p0z`_m=PtM6<@ z<>6aGGT=}^7v|1P;QD+pA&izQR)~Nz6%T~_6uuiVZYWH^2dX3dY9(Piv5G8TQ4?JtvsjQ}y2o&XC zLNHre+S`50er}8tYL98$(nQ=jJX%R(`>g>jFU1v;ZJodjljH(cb!#xrZ_B8|RyOTe ziAIDv`SzqPE^&(rXJ79{hn&0xB$}qihyGuwVP9#F>0%5GBx zgO@gX@~SPLEoF!f(eG|l@mS;|Ly&pw{rZ#=CswC6hA$Cb{L{{0atn6^G?Tw2OP8g&*K>r6PZqnMCb~tT+QMY%%e_Stj$BZ=sed;d$tfuJS^SX?EP<=jx zN!{ea6DI@CtXFQ-<$Um|wsFNWIbXtxzQxBwFE}X3`J5qbMePJ%JIlXjfcC6s zKZ0q5@Jv@;h8afEXKoU>2$h~R$s5x8FwVUro2aw7Q{6~Mm64HTuH&2R>+Y5UrtdbZ zyP$}tIifXs)Pwe=@eL)#_h|USef5odIXVWzG3SeFWAh=e*D21l&4@>+reREMGKM=1J=s~-V0$xo^dAUFb84gsrXsu5t$n-*e;2k(d0xZif z*mNfxWRS4NofuMf4LXd%zk~H%_Y;y<^C}a)+|FzZc{hM5$E6=bN8j4VfAE~kBX)Mo zS3*??A|b&d>Be6*&*e#*kDzL>D)Z{JNq#pMs2Iy*j~P9nTx2$8?w%hw3^Wf&)BcM% z2mXH%r+j*Kb+D2ob3-}AbqiosmF>{S6%6i>*C96&V-(;J%1G z4_djN_j^WxRaFXve2ml?QoU~gSQ;X~T#6wkG z7tVNJxqcXI6bFr-ag`p6rQVWfRE*;Z`AnDe$t7S8*yxAthlmUw(m9D+7J`4MuDAwsO|f$r4k5<6QH z>-+Md_}Lc0A%5{7jwO2LawcH?gDJt+15LrFxFktGy+L2JxnoH}B8`%Wa$}&8x>uFC zZd!}_vFLJ^jh5KYwl9w>xLc0N`C$Rz3jYTRuUu7;<>+{5Js^Les?ypWRNcFvvUR)g z7ClwQs4HUX-b1RkPCm+c-~Oog&0jY02Z9$|x3HRu^h*I(#P7ZXaLnV_g_79_h6?{5 zW7xwBP9sVj-|so+QCTJ@d64h%MHX>s=qkil#9EXQS}8{?UP%N-B3eAk%B3I4=vF=+ z+9y(ZzwOkj^}5s0o#tweR#zB?mPzL0R|CDzF7U|r<5;{ZYYQXLD5}Bu5`v-smDJLcW!Q+{0t0E^ezp=GBft6^9 z&RK)*Tm#m@^NDZEBEPNMcCTlNI19Aj(QTmIp)EN^Lrl^HMxC}5wsB%1t~JP2`32YG zcZrM1y|mZX+Pd+CT1z^^RACTWuUqkB}9i^f>mD3MzS!?4VbzQE+U%26g0ie1OtK$-fFExmt&QN>j3V!RUm) zW8G0w{SNNg=ax5Tte>D3n@}1key1Ujjv56#^iDpyxGE;7vVEFwgP!C5<*LL|#;nRP za^%Ne`kMo@SM_es* z)ETg%qPfNuPkh)e{;i`LCp$;y4h}I!d|=Wx8MfBaWlUX)-tDpXY!#gu^5otWReHhR zll}vNB*=0`4@s`9F72RS+oP0kg7lDUN1G-)~CPAa2Z;hVFchr<}W5uY(hcZD}4X3826R51YpOSa- zxgHS2#C{&()GvCHz}(RVafI>7&zTpB0q}xH)Cb7*-o*N`k~HJ@CIs5f9D1Q9oh<~+ zi-pIf)uTyN=|!mY8GQ=9)uj06YZ<=6gbp<8C!h?2n6RDTaYO@ogZjYcsmR$FT}>?|ONyr%%~nXg zU)W|&aSFSS5hi6+*+hQ>sFn5oWY6j%f6stmimla~ho`jlet*bU09`{AIW{yNUKd21 zURz5R&8*b6vJ$u0{N{Zorz~~c2kmjzv2PjTs7h(0PtE7cOj}};OQ(hL`0YM7dPGM4*yq&a zbgZqX*JxyoOHyqkTVIo>=fS{|r-_7~*sYApH6{)|XKEaEr`XnX9nGGJAt&`Wb56Ec zFU+*W4arW4v<{l4`?{T1j$9qZq>9h;b1L3{`=$E_6#{r28MTPk3%Fo>I&xQFGjrzE zoIXTDU6Q?PCyGV7n>|q;149}wFKj$Qt#JZeX@{wHoGo`CllaINH{_lT(Cbg>V}#=o z1bn&pc1+hYY+K#Vfit^%!L@SJInuU%B+A{R(yRP{UhRSIfj-I+V$pW$`{%PBywv9`SD+3b3m+NZD7xy2Vin`(M*`9_%C#XIR-cuuP*G0XIh4s4{Uw;Ay|Bm{M0d+tf@*^ubb_Ym zY(Weq&{>;@8s=jQFNab`JGtr`4&duMW}7vr)*v?vxyijP8O@1^kl}#*={>0pwNCo9 zXG|g@(?P@_*Vu^)_`GyIvl9nElfCsRAF0Xq8hOOh|6`4;2}+sg%9ShYgPusS!PlHt z7S3VIsx$?iTd7ana(0|Ye>_1cqM+f-4q$E%=0Q`0*+7?`GPly_=KpUby&ib(jo*EkAy*PHXyRvuuq$xfLc?sL13`h?7EM#!~1+@eF7QYvU zch!{`(BRET(BD^x{GaB{;GO18(buoC!t4&W@@I5B$!^^{{fJT6m-C?C&n++>LzNz) z^)^4T2&ruAOTUJcQ1w4H!^uHwm%|RtrpV<>w+Lzo3kH7_pkOe`u1>;uXgT&&Q9;%M zm6whygaE{<%oT;w1&$6E4Yy(-3I1vP5Uh|mYPhI9(|nl46~ZidC2W){UY%j=g?iGA z%b+n+kw^DC{z7PXRQ{(5+mj_})TAI{dnCfGXl;#N*&K>gia)QTPBn|T=O`8X zQho6*xLe;e`dn8wHV$8)Q~1H&XJ99!9B>0noo_to%zidYQrb|9INlNy0)F~uGN`ax zGdaRyXbmq!{H79+=leR(3RZX;5QGsYibW`g%Gf3iePx1qAWRtV@DPDB&YS3DEv`{= zh8!R+=%T#aa!~O-lHZ&;T*MT~L0+NvyeOU;#CACGy_|WBB~&hLWP}C4^o#*_dOvc) zQajRr?Gsr8YEf`~5z{IHYbDR1}@{rA4`Yc4KK^Me89Nw#T{M`Fw^ZBt|d_j z5z5FrDkCa!4Yc&Zlm(rF4Y#mbr$NsilgX4dk$v8sj8AgLS?!Mx9JY;%KL{cQqlTEO zfij{jUPsyk_eb8)lYopdmkGfA{BORPoAsymO8cTDefxrRnPo$y#3kL)66bu!O9mhS zIwoWZtlWn+5!c;Rt0K3Epxuh=qDRhcG5E0IV~Jsgb`Uuj*t<0_tuGlvZ>A&rL>pRc z0jnk}TZF=bqbF-J$#qAHd*y2Qf&+s?$%GTt_zzop+35)!8PMn~q4t6C1wv#Q!~wa) z60_ft)ll8IytPRY4|d!-bt#kFABS@#hbA~hl1nr@%RkY{xhN?$9bfT1G>pT`AJyzZ&tQES=5Xz)Z(D{iiEh+pZW;THjQdB#yF_#(d zds))Lm8o@wDa{JE{qJJzWxM$b`#Z*hi*sw9IPJH(s(*ZzA`$X6%WrxtY04KBs6iM6 zS{%I5=ennnJ7WS`EY0Jhi+Dg|ruMIzv9W`qwOF3Av9ZhSG()_ z`KQlzmv})krMFy2GRx8SN00WF9BD+A{SIC(zZ+8fhsGfpLRc#8SEUR$3MQD0Jr}Z1 zYo%s6jXQTMy>@N@j^PW~5Pygd-IIQ)RKz&apk*DX(@p-mTSu_HSb}r%4B{j6*iZI zg3G{*I#}5=@NH}gb7K9^z60#y{~^2=;s7>B91SnmntO>vnEWSix&O$O@J&I|yMY$c zDA7)ra~IXpQofRZo#Ou(%D35Wi(98D|F!M`JKwU_vm%s_6s|_3g@er1R}r%?VofAY z5toc7QRgAzce&>~kPleDh?aXp1o_iG1)yV^i5e^E%dm9z**}V5cUJ^hDi15tBhZ(- z9^N~+1N^%grqDmESeySJRxG!Qe&AH}-Nk1lymKpZ;y-+5i~kXDLopcS+)KDsa61-T z7FmDzc|)p)Xo2z`Rie^}EZ8oEUighEbdns1gIHZhZ=B-w@la@_g^^lI1UKJ2l)m8nqGovt3*h;PzIDAyGe#pICPoCgaz41pJl#B|QD-Js$;a5tMV*U5W36Gb( z9{tk|yQlVbANBp9*dk$tQqKb>i`vU^$VRV>S%hI^bm_{^LvwWTtJyuHU=bYd zK1nVQ487)ci`1;sf9p;E+(1bzLb3(;Ry;3h1r~N>icI9Q)~Ix?dqd1+qC%TznXNPd z?TeDE6Xp>_GGCtm!w&x&o5&kJ#=t54U;XGKZCkY;RX><%WwAT2#~fPiF9T)i z{#KVCf9W@WZ zG~{?BDW00}$(f~Hr2ZChi~_oSEk)#*yMG7puk~uppSddM?m6D{6#n%I*&s4H?Pp3N zL-Ex;gK?@y%G-|h3#HSa_MJ003pT<7*Vm)|Jm$Ywfl$iO-$Hm448>`TpnAhHduQwa z-HR@Kq-pN=7d>*kamn@_eU5kC$sglR2OMgptEb1Bu|9)mRATqe#|<8Gw*1cXp}CR!uL8if#uT$|E6PsBEJgl zk@@J_s|2%D$<+a&$bl~v6&3tPPsaG~7=Hg=Ek0>OSbQuQvfj>wide;dygbf)H_4Gv z=jx6{UIuebErz4jmg?(I(HWcTmUky}XT4RCH&8!rfhuzL>h9Ygy`1iI;8?@aJ^3sj zlbxwgneufxRfig(k1My4X&8L|uI!AHrj>iXg7v!S*Z*ltKhYsGNGse7SJ<4rKfWwIn3F9X*9gG-z5Y}eLK4_VEoERX(x~Ty^IBHrPBY-&oqoZ>^Q=;6 zGtQXTcij=Cn$^&fMALv@f2StnsM4|2Vr}_+XC7S@;>8iGv67pn_U+$f!`-{#djXtg z@R>{2LnG<88=Ap`nZy0gE8In`5&C3P8g4hHwVRcj8N~H>T4w)#b{8#@Odd=FSCwN1 zvFnmRb^ZDG2ip?;upbgl=-A7WoeCmDPaMvMlnb{0&#eqpGQ=6;I}|Mmp?Ec{X>Dlq zWM-P8tg4XmcOHGXe;-Q3%5#B39=-qb3k$I!Gk_IZ0!vDik5N||#VegI7O-Cyxoz6b z&7iJ|ov~8y;m}rI54e@ihVQzASw#!j)?4qs>)&35IuPmd{l%8e_HLMAnk}yQ`SSF>ikSGv%?33MYuV1!Yn++!%)gKC?rO!8-Cl=$ zOBRaG+E}sS&{Zn$S_j?A_VdBX?&L1*yN|j1aaCl(+d~sRt|AEXC;yrG$A>egwA@@@ zg2FbjvlM-8EiKh@6Hvc9J{rM60ba}f0!0>oOL|`W$vW$AS1a5syW8z{w~w>Ec_Z_m z7f#9Llbg%j4@GI=U(#N3vtX9& z$@i@^6yXU2*Z^?>k9}e_W(ahK5PC#vxtxCdh;Ev==ux7`j}w#kGB?4LBBE1GPp|m> zWdfqW{2%U{4-WoqGX*f(QOM}GjIfA^2*0Y5*d2EMYjpqsiU9+S5Ij#Wq`LfKPD1?> z(<%>QKst3fysVXJmtOmZ3yTHOZ4ISe*Sg~>-fdWyK5{HId6Ch)Bi_<#UUkx5Su(%f z4^1|;C<4aWt(*ISleLGVu30OwX+~ic z_uSh#SgUBh;r^z^Ib2e96ZL}po~2nX+wX>?cD_D<{g6}nKVJ_&Q z{Td=RmL&-;R{xqG5*Jlt?Ejira+cDqFwMc~Rq%)vx1mA14CmT@|jGRMBNL z6dB<=Er7R?(Mk6;OYS?SC^Nj;c41-Ld}EYlF?rmvWwEsrh{KbY$+TP}tCEju_QZ!* zx@SU>K52XrPf38I?j5oGBZvu{l=JV4ZaJne^+QIe_>`ahSS$OzV*V*EDHP($ei$mR z3eojt!Y){Js5ktYlx7Y2?m!TMpfyJCW@JiTNN)Z)=XF_j(WeRYHb2IS>&aAnZY zBkiBbm`}~m>+4J3@0CcQJ2Xbel2ujX&<~*jiwTcv!5CWd_~kt_HP}8fwa|)P`+JO3 zwPN#)Zaq(083)S#$G7XcP84sljHJU4m)_O>b=ExgsM4<*U8=qD55ZGlRX;q$w!DZpFJh&C{Ogf4JduDfvduk)n;{Is+Xj|a z2->pH%195k_wcnb%592!bdv_#cddEKw1M?9yHE0F7X{zg1h#vByqW{9j z4aCQlfpy_4gn&3Z3h1}a#VCI^>*julbWD7t{MzAD6#nm0d!jARFz(+}q4VVg_G8_P zcJ%U~7<^a0nunMn^gJl!p9#LceXd&`Ev#$zv-ZAT->V52O7(6gsu?X{I!w{4)spV4 zfuRfmKHdIkQw?-08n2al9pU@^%}*{}!zs2BeH4`IAk;^%q>KF9lYN8ypJ6y)_(9@H zh!}L+DpMKtng{~%S!GewHrbrX_D|?rpZ+8oc#@}Zw6j2F{~U~bwUJ7KN#AQ6PyC_v zTG(0z|CACBkaAGgJiJHD_k7ez4zu?aa=-J|n3DW71qB83%F~Js`tOgATyJ|DdkMQ{ z$eZ-J3knPQU7v}KR5H@0{7&f|J^>zk;K)3ZPCnib$2EEuS>11BqVs!#ZH&@J_`J{n zUnY3?Hw=>cfB>PIB5J}%>L1&yN?4qu2zK7)mr)e;-V~% za!miPNm?WXR^lA;c~8CQd|FlSwO{5`P5i5NIuK(eU(lsykj+b(e4dd!hz9-8r?UJP zt6!}jG58B|GL@EjJ6%Q<<(To&@zDc=6*;PG?D}74k?ihuf%V_qSlw($-)rT+1qbQz!S5l1PwS`z1^j6PGOR>n9L9>~)4PQ&hL)E?!yO3JDhN({ws?0~W4(T#}zekyiGYvcW2`>swYMsQB2S2dxyiGh8DxCfx&R{l#O63ZjcvmQ-FcxS${^Td2 zz|)Y;5L=08HI#UUQ;m1jK*_Jg%a*WU(T(fKmHSfQ4aW|=S6je>5gPbhI&3!aW#fc9 zdQvF#BcVaI1NhC7xMjeWWk5GKp(Zc-Dc*7B*TU!nar>Cwq=AvC-WZo4Z-7ZeyM+Om zzzc@bDaIBKix*iIM$JiDIC8&FDzqf&lEPY*^m$-D?p4z+rs?uaO><89A z(7WZB!LZKv`kwcCewP2)9xt>^&cbyVvyPKkN4JP(}KNzO>IjD9&!k)_PR#5EuV zq@9{Ctd3CUZ_RcCP9s{6f&rRsWgJWf;%Q%rL3SV*N7bQl_3}5u3lb8#XdTYNTOOra|E-Q&|d6$b6Yi!mG5gxrZ<_4Ez zcfpVQqTcpGjD>Enf9MY*GdMCp;Z?l^Jlic3voN280cTMG(R=S@A0CFR^1V#x)P02p zYfYz-r?tA>(1SS}CqEQLDhoZ+&qwoRSlGY!QMRXk5qVb-W5nE`aOoxcHB;ZHTKvo9 z!cF=R6d^J1h+Qpl^Dst)fmPqln$ISn#4zewVMzo2faAph6m7f^LXJ{Vo1o^Wx^v*S z@tG6UF8SLZb!mD--V7Dy^u8z@x0qY;i|%8=G~@AOwZ_UqH##a#dD>|V$X80-{J4`q zQBP+a9h+Q{_+o;+x~nW9b{3zRU6upHZ!i0<6{qvv`l7#p9wyfU(QH&YAHe%YWh-vS zkEJU>L>w2dXn5@fYuLYB*jxFz^^C@V^`oU;HnN0{6Wk02T7Ins0N&zyq; zHN{Sr5q$d+t-<b;yGI>!zyOJENSH!)$Qm9W=LvTk^( zR@?2n(c=ED91_(1zrEQDZcYl%cg8p;sDzOeey#-k&vXAT@?Q9aDqSk-O^?N z?CC0yFPK)!SHxuwmbhf>(Tw$cn~1oJyVw&ti+U+>9Nj)v1m|a^0&Td5QhZz*?RrnvN31fzS!7W{r8htF%1$ex$qNTrp_kH*TV<*<5(t9Ae-n~q(I0}J zXBfKg%g)13S@xFW*|8VCbMlQ5%Nbky?g$H(u~V9xKF#K$t|-%L@OdUT(`k}v_*kn; z7Z{|2MT+?|P!Wn0b=;N1y4doWUjNzbtfUR*OsXz1x0Z)(6_h{WKgdd8`aj6Z6DlWG zQoBh%bKXJS)tztjM$P`vl;szfkt>vS-EC(mpv{W zg^b`3IRg&k;%wb0GeGc!Of~mil|4%&*`Vof(3x9|3qt~ZLtS~tyxYH;Ol$F9Ca{nlK--p$^n&_1JM8~>-` z{9ZqwYb6V+sn>CTV?-Ymj37gI#hF&>22%F}+QYS1iLVe+nB{q@ya}yjj-W8GS(>w{ zZ@V^bEpgU+V1lqwb=g(OyPvYZ*7>UM3xkhFH~F3_=@p_f`vV(9@MbEwgO`hV=KyLR z;pd%#J7sjukFJd;f!pLX?L-arxp9>Qsa7vWw zMI0kk<_0$#jily-%Y;Tlw+EIUy3yPk?Ru)&vBW7{a~`KKZaRnC$dt zghG5DYSH=UEs1lN+N+&-O}!pEX-c#77vw|wGcC_hC_R`cV%l{MCrk@`{?BbkdKG+wL|_r;OwSo8tV=sa8U5HIZm zpO~jv;8UQ=88WyCR5=3&tcw^L|A&J*r;4^Jf^KIbB+r~@{6=w2#|AsD^1QaEl)@qg zaYXVIb5#7G>aACa3T9V$68aiE_ZWR!p;v*;ZN6KrhAP?mus^PhyRts!ha3KeRn}G` zY3i_iRXtAINWdge0>7Dcy%ij9ODOr1>)PMy&Bg9Q$x!6?==^MH=Kg&$#?mZd(+!lc zBCKXk*h;?<`u=e7H;QIgU|S{>o;hS<#uE~oj}bfgq25nv^g(>k5%anc0qCHu&M!LJ z7tvpbx;>pT%JrjhTx|Vh_d{VV0d@VoH}8H1*Mi>lZ%6_tE3Y5;voYxX|M2yeVNtGa z*r*_l(kUs846SrZcM3yEmvnoqJqs>HB?qAIIhge>r&O znft!Z^E#s}_YdHQfti5(g*gzv@Dp|ewB^SLt9XJ|Q5Qa7aP)vWyOQ|VM>S&1w#3|* z7J^=$b%4TBW+RtaYhy)h*7P|Z!jgz)Jc+aPZL2dV;T!ftqc}3f-)JD^HJhZo9LQ|g zb-(bdmb*Od*_W4z7IPo17q=vSYJr-Jld`GYEYev1FjUE92yp@VVF4StcmSdG%}td& z`+fW{eM4|%C{W96KJ=2fVQyTa8mNGu1oL&kx^qs9w%j01Jm@NHBmN>Gga>yI$vSEB zRyJhCza0%x>FxG{M#E!Q^MMUFMF(cQU?-&VTUlD!>w1Rnr`5pgZoV11Tp34`zFt{} z;X>gcPPuV4nt|587;lgOo(fpkdWu8da)|yTJ1r(Hw=g7-)QJ`^CqIZCFo*~9z|sl6 z+$ZW=P5}Sv?moi6X2IjY;1+PQiXEQ{`&vN8P^4Arz{0sRX{A#DYDZ}{{fGb4Z1cOq zr^6~xGW#f7$piH~p2nTSW_UpMa=RD;dUrmFndXp0->-Ne6qMe6HvA32ejns&av}%= zTxek7hh=nojPv#l=hSO_8FPT>)$7_ykar-?_|TxEh#KfukpGP#283)Q0mUf<#o8I& z32#_*Cf(?q>mAkfQm=!DDp~P!#cX{TV?sm$gDKrAZKM$c{mRRm=+7U$Up|C+v|k|~ zGwb__uen<9==h;;^{8wwe9?Q`^!FZSgWZ$_U}2x5Pt&)a%LdAIzzLumnR*fCP0vGHPKXY7mse1cPbGO$6oB3rU4N#OssPye%TOI4=ykBQBfNl` z)alQ_Uy2QLW_G+^$$2hm)?JpGE=#oXo(Uc1BbFScg3^jIf%q|<2$@%PmdwtbVYNBw zY1x`7VF;3;U=wQC&QaG=43VYvmI^NiRcX-?_wg+IB2eJkyy(xN-(LEfGC*SNmHtzQ zlfj-%;9N#$sXc+M*YI0*=R$YaDUK2b%LN|G$C~{G%de5y zfiMKd;D3Te)D}t8q2dWU&odWX*_*sXv>cERpW3F3Y*rkAG!8djDOrF2qCC9;D){Je zZRYr#t5dDLrbh+l2Q%RbI%KWMIv1z|WpT)16(U&ZWALovPa_4UAk-WjRF)<}Z5vv~i$_(VZ|kgv)$ z^JkrQIVCHW35j^hm0d(_IN2q@gcE=Y?A3=JESCh|;vp~DrP|iccgtUN@4k{&U01Bf?=blT1Cr>G-)NU*p zTozsGA}uFfhTIDOH+s_`x>0^vg&MhGveI@%=EQj%)7fy3Z?TtUNsDX+6fM{CxdDIJz1cS~>G;m)GfzEnFO@)Jc?JrC1wuY~qg*k06#p|FrEOD<=4!>}v z@TW5r7mGueqPXnhA&Uwo_~-Mo27I{o@>i08IBm<;OoLS}gT z;xml#&1l5kO~H_9NBiD(bZbX72_Y#D5YYZ0-u1H|`phcKGNnuliX+Wy6*{x?O*ORt z#5pDPWj79sYb$2~QLrGKjv~gJJ;E$gCC<-h=*sHqrR7G|D_Y4NHtFA56Mi?3;YQiO zvWtL?jvHU}&AIeM$WcsFXZT^f^MKlL0qnQBZcL3lOg_%6^Xg-J8fKYU@e}-bZ5@JK z0<3pcE89#If*=b;!3)_@Pm`!ypnD1%7#CRrP{#Vxh1(OKYhZT}gmfNAe)t802}k0h z9bt5AOJ;Dx?X(wovIZQe^6F0$Gcz;1DF;8T{@z~%ut{5bzW)f8+1!oT$XH*Q7bJlY zVVxNU8{uXE;aIMz@yrQFH7|a+HT85zqDMrs6OUc1_oi|n)xGEb`HRk~mNf2JP{aiW z6l`K+e!Jg4hLT#}7M5zfXf&J!-H4%d3DFWa5U2FXDnY$VFl4RhsJfM1$9QgZ zWC<4Kd%7x(|4Y1?Y zJt~jEnE*fMEh)4h_$3f>`l1R<^3MVGI)+X=-QT8RXENkPihvJ>{pW^rXRj5`t!$96 z;>p9v>aG66f;BgVZ*jM0#SKE#0EKqG+@IsUfFJgO?NK~E*Veyhl0|c}{D7^=hP3De z0Q2ytaY|H%D$6K;3;opvRJ2f$k^YX@Ej^a={x#Iw$br9*2fl_ah}uwmXc%=pQM02b zDM?}@(Z0%8u%Zg-%t>H&hWv=a4}~!I+T9NYuW1H90i2y9?C0J5irsgGoiBL6U2E5e z^Q%azMc5{DXML+1^=`=k!{Z%f1zEp$my86`dL#e6@XePkpNF?k4d4~ElC~($G>TsQ zr;}%oS4tHt^b|sdJ%M9L|56t1`;?U61QbqFA@6|;hQR@LGW+;RU{^S#QvRua=_CPY zsFY$9q(O_#sB31Aw&nU|4dcbncZxlx@z&YaT*VscUyKPWL^k4#Re-8d0ad3R)9A#c zqa7G-K0ZR5gDFnQIYIQ%4jkp?)oa9ZR{8iPbU6}qh0mReHw~{YGc2;7l@{ySCku=Z zm+L3AgMNP~T_L~luW}(OtGd^cv3~pwQazwPkcWDQd`Rd^n3&&A#vFSVMu>oL< z6L$g0h98$Fja;!-!sAa>)wbqz%+#r%{E$Q^X?ma8*Fphk)eBU*c?&&@5(I+`8k+Ub zh7QQ))%h7PqL-HZk*_-xkv+3zGj!pdu<>nb-1zlsn)&&{8*?lC5=sd`c1ovIO8+ZA3z--A7~ z#?g?Cg0I+t(y5%>%1eZr^SN6Ga$0)E_B6c)QtmU{n&}qBGPPxnaF6Z7Vd5YheH|Z) zKcinqAzLG!TVsJ37@P|^3zdZQTH3F0YwKCd51KKbnaux?`7#IsAfpgkekw;cMYP5E zbSnTZ6L^RbFsC7~Nx&hJG*E9DeOL`_FkNs(7-@P@@4G7Qd}I@nu_?=VFDMg^@6)qh zA1Lg8%Kv8g34|#fBkUVs(L;I#N@f-l-!?;~zWNp- z{nN{iAth!9I2m`l>QC*#pZR#DOUf_%a(w>IKI^5}IDfPpwtYJQ;vcffVCMBLL_L@uiL{ z_32V9F6WV@9gcuQ7RF$_T ztS38mAV=DA6A)W2g&O|M0XhC>?4x~v5$zA6M786^0ilS-55Mf4(qmwW4eM14R6qd4 zsPT)|B%b9{N0*G7CzF>A^R~^I(Ge5L%GqK5u|0IEO7jz?)Y`P~EzL(C<9ICshq(B) z0Qw{_sV2re3%x+it~-#TaNHE(g|LNz;RY`+ELb5X*2U<2wNU}tw+CpTs+5ufDXY=$ z^Q&5=BZwH{2A8hf%ZNnaB)>x#6OS0WE5!zk-3GLf*pF;}L|Wxl@#xmE3 zlPRsuOy={9-@Sc=5+yBSrU$+%ekJUy^nX4O*gJguuw>he?P0qEkTcJW2LcD*de1|< zny7X{X}Nk$9#`GC;JiWKj$Q`Ga266I>D>uZYlwf7KK>qxpCbM})H7-0mi9`Oug};e z!L9Y=&ArW#`vC+Q`{0vl-CE0!p#dqO!ubHI!9HX8(#{|c7tp}6$D-Go%l)OVPuSk! z_ay3$Em#u;r8wL>tYxn(iq`gL+D%PZ`{nexbD@?A@^hUnU!eZf_$HN-Z81hQIY9_7JWu(p&jsf9|4;s16NJ{4MaIva0Q zCIQg8)hJ~^TT8ZT4MT5pAtP^FYdfZ@?@??-s83gm8rFb?w*J|)8{A^UPAlW8`LkR* z>gm}~cvtY6k(SI$O5Thyk_c&RO)lyNSVC_DcGlu9Im!vG$%~`g{WilBRHl^JPp#NY zh=_=RV0ee8}}Bua->?n&fVvZK#f(7xK2a+6~E{MpOm$ijbVE0@P1t2B=&pH`yh z%{rbHdQf=7Nf=^JL)=MoM#>1ybHX&?NSK!i!U93HHYcA;Uz!OrYYO`X_y)UwBacco z9)6(ag@L=`Fbytt<)qS?=c~{??^SB*H`Z1&=Yu0|sBxT|*y-s5TJC10_A8kFPO*gW z;Kc5`d;Oi%gnM(9fFn~u<4I)w&*n~^U1YGBMRC#}L~mdjIHdf@;ZfO6l%br$_m-ss zSMbyt;a#6&tIA>3J^kk{_7VL|)8}1Bqpdd*U0w$aIm%kfyvT>Qh)5AA@odNvFW6+1 z5>^>{+8=q4Gw`A~S`~CW`BN|7(p@R|%U^$&Cy-n$Ul9Y+bMpz_B zWs3W0fOUJ+i3Xmfb+%F^RWC6QIwD8Ba;waP6+HRn(_=}Rv-y|UJziu18BOT*b)m4M zLbrtGqX}s2cuROC<(II3Sp_aWDNj|?cf3n9Z7GIrDm=OeZ=vDb&@Y|N7e>V<#O`r> zrn+fB8WR8zuF3o2{D(4HE}lN8va0AnC@78c0xKF5^*Cwc{PP9@*kA|M4w1)&`$%-W z+{WE$jtg&-Ff0AyjnUk^^XmxC>2Biqnn6-7H2{oauy+k$zu)Gn8LO9_! zCiNUvRAMhMF9=Qfx@tO&D=nugs=}ULW`pOy;5@*jwqhiR#g~bXkiIurmX;9A_4ciN zKWt|xkzl!bRQIoDtda9`%g1;b1}F3M1@pM!M(3uZyTvIii#dN?kGT<-`=#~9epjXl z)GF=(2^^%KWT2d!Bsf5ZKMG6hW;4&)y<1L0!?e7SUM$F<^ZCA1^PO+Y6~VsyYJFu0 zvNC%iJg<{nWNqHaRD?Z)1wwde0z*7OXg)2@i=Rz6tGASm`Edk4X|3I3-wsftui11# zsnblVzC^xDN(61N&53_cW(-;4eW`8SfX*4k46K66lij%*Y}F}Pm=?A+6~h~S2p{jq zap>9M=(#3b)Rzj_a(o$<8GF$``K~WyOjK?e-VE1jww{cZwy@ZuZ*uP4z}`C*Z^qw7 z6*|%bSa*D-<3>Mf+pu+$p8%{rx!O%P#SC1St^oU8)Wu}cM+zrbbIxrOGN4#*eiEO54hd0u@<>N(70MPAHv}59GX;^#jPyNWoH^o?JPcNrh+uhhZ`#oaWl zHVaEE)8&j1^O zgFdbRT5AOc7}dlpPVhC-jdPs^1_T}u*R(7Dn5LnVq~retCJjjqlCyB}z~!&Ak+9Ta zFWz1yh~``jDs?0(?6N1E?%w6lGtv{+53^t4+%;&nVq${WJdxAv5~i5D0@&Y3!8kb1 zpl44;<8uFEz+?v+G*6G0;9r`KlzrzodwWoy3QUg(LZid&w!j_aZSFtxp_aHvR~TsE zG-=M(;6AXkVFnmL`?b3RW?iMid%226kQ+w(espZe05(hw6t?=}60q|2pb+$7m_^oe zXoOt$HQRO6)m~-)AeWR;$Z$y9+dD9{4)6N*g;5*6rx<4w_{Qi%SZ@{W4Tk3t%=XsT z6I&(`GYTJ{^{!hog1C)HZZKzqKG*g{)!N%ou+C;FtZDXB&&9ghcOFX@$mlNgH8-vR zxZ`d-C)7Sxh%oISyIDVW&i|p=&F~Q_0fQmTwpkjd7Qc^pa|06Fri<53!~Zw7Y{TX{ zLb~5CZ+mTU$pH0iJ?9;CNn^eJ6M1AXi-o1xd|~wz`nY+(a4y|bsSc0>qr)O!c2P5 zPU(1IYS@ZWRv%K7=>g$fYZSeoXNfWq2+20K`M`LrAj6esV5UV^p-Y>UYqqp{Ho8(A zCt0~|Gg=lukV|%Lcb_R6Q}GFAI))OP|19HJ*M*JSnNQc=ZlndVT)JWioh4afyE_W( zs<&9=@Y?g_&S`A|*~ULi9|u+#UqnR$m%Qi})i(TdO&EpF8|!nHYdw#beCu;9_=u3{|kEe2E%R!6yM*>^SVC-C9$!w8O(Q^cqQcl6^MGPEMMyeGq%s`Vxn2t z%$)Wx0Ue-ypKMYlkQG_E@L|I+`V-WW3ewB_e2Z$;|S7~8^v(P^MJZT>}1%Pu! z6(34eAfU+Rlsv@Jf*#otJ4m<#P8tRQA%6U$U716SRy6QdaBgWOcnHAxc2cBA<8F|l2LNdV? zl#|GWzx&$pNZ9HLwO!wHPKh@rZ^2|@$P7K=IMKO>-A9}O_Slgwf_Up*#9Mlwqd#%{ zHXj*vSAtxX9-X(I3_5p7meFoj`i8_p18AkQ$Ci$IMf6#>guYNaBd2vWB&u+}j@G!L z)}lXQ0S!P#yI-;^qF&nY@G{7PGhGRc^>!{Ix`3aq~a|q*OQipQ18O=?hP-De{I?8eQ)|0TMl~heV;PByRU`5z%^|jDUco z%2A_Nc`Zuw5*>~Mwf6Q|(wb59#3o7|IHcQRl@*yM0PjH}f|QSk25TPL-Lp_n1zemh zb&o+Wq}KF}(4=pIu83e3{osYW%iO7>LVN58HQ5I7#wBMg7I_iMlogssrTYr~9@`Uh4rXIEj6w7v> z#9O71-V82MIRIAC%;1k0wP}XGGZA&)wfYDWaL6UUAhm4#5(x`7+bdSe+N*6dT%7kH zo$a#h(TpAT2;GS&G~MqvTw*aU$N{eSyE>2<*M~gKS!1ZPXu+S5&NaC(?GjAnga)e6 zp;t$wAQEHJbskz3F_B&jnqbRKDi}G$YMc<}=hNorg{tG5m3;zR? zFT^vx32Zo*8ceh>PliObvfZYbeo@TXwzZN{@?n=ntGWyE4JY2)_?IQ5L`MOZ8?p-C zlC0a8qLVzuyAzyXuc;DDu(hDksUo#Ki^P4*D`Z5l#dBC9#ZFspB^r=GZ;=k zt7@#%=BBk*|9x2GHX$ip!f+vSQWC(s1e^J!V9S2aoAqV6?`hPp2Dc5V<4i)9SGtAh zf4Ie}SU>0W&hv_0o7oQE!{9JH7THQquEy{%9bme~YuthN^=!h3a-$CBYb{B7wg3PJ z#sE)`u?gI zf&bwQ3|MceH$0oPAsq;U<~LyB?Aa2E8Wh_1^*HDPxv}R?;Zt^T5pNo|O~Et}8d;G? z3ov)d3{2WMo(N+fWdJwNG%8x1#dV;}&6T|rK?e;=j(1yQ^_Vr;kiMi=#lMPvn86dA zxG_UdE}Gd8+{nu%x0}(lbJr&Y#=};oW9ICZN)Mp!S6Oa;@8ESX@DwZ&k!0`J6~G>N zn4dwCLTf#q+xA{dhEa!x0L*r`i%qRf$ITDK*=F-dM*N}%4N5l=ksWU=eD>{IW;8;X zG6zcjK%?JB!<$koJX_o9@x&K3W+r;EZ@R_5PcJ)f7|i4ZO&<>~*sk#-d?TdetBtB9IhRJ?S& z*CSqfeBhCO0x+^X;jBD=02vn*8Zes72c;3`_I!?SeYijpa2mjj-L^5#oEO!sTu)|F z(FRu8?&GhpAyR!wfUW|ieIaI zcc14yW~20-n;fDmu)L=q6-;bDr(D*pw_V7asL9SYR4wOBW66={^sP@U;usR}AIuot zyU1y3;&Y-;moUfCf9laBNq%RIj3(&JVeq8ZgBqLkYtzOBwoq63*0t8R>`9!IpA@ih zCfq`TPRQJo4S(c&Q-n!1T8rH15jV*b7ao?aL&U%7u#;}Z>-UTTUb|-SA&JP_^^wfE z8a5-*y02X*Y;W(r@1EzCk7Vv4LSEBph__<6yz+}z7}XSNFM_ipm1i-8KjqhE)Q%z< zf5k0%W4UM6nH07w_uEnMZsq>}U;Wu_Dm}1*Te+(B zSaVCa-auoXoItcS7;gDE5XHTXN0?z;rX!B*fd8JKn^-F5W%p}XKVBNxBgCV;`OD+F$g zifmvQaiP{+xyI`}m!kXjVoxE}@g_~L)r)tnOOvEz8edb_U-s&4}WBA>API;wsfMkr}3~5XSXaUK+H%Bn(o!X zQ+~he`=SQ|+9aT}iRS@7j0lGX=te_cg)$`r!SXL68s4(sC4&R8DXAT~1M7a+?%xxF zld>HZiM6%oru&@Jl4FxmeODx90f1Y&lb5P^mjbygiJsz}9Ey<|WU;V11EA zO4l4^5}I2rnS7U!{8Fw-1W1QGOD->N_FdXEW??yaQ_Jhs{sa!7LrAL|K!I6ak%j!KwJ@?3(wC zwSQRpAFA+U5W6$3+(Jtd+4QkaV|KEZiTTpxS6c*t0onjpJV(%a#$M;KG1Y(bSLr%FUHs(hSSx z3Y#IWJgVd`vgzW8m&nm9CNpm#F4EGnf*BAdm;64KgrcaOzoM0tUd~M9 zFLzZ1@^hZ=-mTn7BwSvt8ZrrgRj!4x(3lLOllk;4o*neC!cYcy80C39+Q{V3)0xh7 zTZD}Q6k;;u3sCXnpZz%bI@2}SfFgHLpMlp8t5AEHFoKlk>6xOEGUz2)vkJr7s`59U zAKdh^{_WRErI5PNU%jvmMK+m-{3O<{^}CUAi^)bY$Dd?T$s+uS`e4a;cGsPqv_C#A@fzi4-!0Eo=d%V1nRlTZ6|us zk68Lo3$FJUzs%CTz(~Qv>mlNE|G3_h%Mk#hV=ca{V%XQwN#A40tw8{V77Iv^DSJw% zh0RJOpz;H0Ag+?&(Dx56mDK{;)&VsKiAxKSUQMhrZSM$P(szez1Cfr7S6F|X#e9Cx zV4p9xy*MRrCC80Rx(=10fbbNSR@dcqQRL>t#IK2XWsT%rlkd&&da-=&%0^iy0B5(< zT29+?0krC@r`?uq>^w=p04<}BlA)f-G*hr16O#GDf&wI1=|sNP60C6MRp5rc>_gcB+eF;Qsj0W?&cdSE;9RLDc`oST%A~aD0Z6|`` z4!pqQ2ZhW#1f=geF-dDV?<^Y(h~rFWJfx~xG{oLdt~eX2EWlgO)INv()YRdrHgp+o zaVEM<66F41{I(5+6-ny~TwN8~A0i$Nx=wQz_*}?E2g)NeF=><%fh>9L+kB$1&+}Qm zQDHq81;>LO8%g@MKA6Er^lix1jd>AZafIjoG#F12ZJpiFL^9N3=j<@=-Ft+A$~anY&m!Y_c^p4X=MdhX zhI$L4WdJhfkLTQWA-$koTcxI8h-Kd)7ypDdhh)~bJd_*T=r9|`A43wL=e&GXL$g%T zT+CuzZwyX!<4esU{siMYzx?SqFB#;w!)U~oB?$FQzC^gZmT}V8*SGV|xHy9$9_asf zF^4}VTh(=C*B;q+OVT7e0_ASKb5M7`%AT72_BUJ?#=rmw4@jLydr!2&O)Z>C>{WCv z3z|aZfC10pQ%|&j8Eh9lVMlE*&Il(Q#67pZ*&2=&G?zcTLGGUq*45Uc0ZV*|P9Yk} zp~XcA{`!(X@RX3Xw38&QE?cRTnY4kHz=@`e*{IR!g=aa3!wxlXNSIaZ3!}cK2Hvmo zc}wQ2jS~|4(tc)-$-moja=+H(-sw}g{_%~ zQyVSLYG5kx1<6W4(<*<%l`%b02NY5kXF5{d;2)5GS32M@@{&SKf(bFAtL(Qz`5&if zj41*LF_7%ONAn{`QB;a9qZY`ZFREV~?wUg~6k+*_){IcZX<3?L58bclA!` zop~kFpe55Wc|%+`CBZ}8b^9)lJ{kg3*E*u-r)%ls3G5Dpg8Q7+a#AowSGUZlsy;Wx zh;aR4=uD?jA~~BW0?n{QnQ`u#QgzMr_mNc?mK*wt=*hkOb*P0X)bYzNHN{?sn~(R0 zi*0Ey@DVRt+?T&HqafYLT8uLCGz$^qy}hM%F8f#X^xFVOnc>+-TGwLu+>wWe(z?(0 zH0~&8V_cOzUY~m}yp)PRkt;mPMmi^sR*ENGFOVj<{jP)~udHmE*#?e)K zo|M}LlodE3lG7O2OM29sPB4MwgU>1 znP7I%;K$(BLvFr0WPh)z+f~EhzoSU0P(uUudRC&T&zGaH0@j-rWFypEg1jQlp=~*@ z&w*&=YL8|A;Ubei!GrVjH3N=e$*qcd#Tt`dXiS!+fO8fOr%CFr4X4EiA!AeGvUtXW zf8h39TZ;dRt!wf-A5*AZCTNd3nb(Ou9%$o&Kg-9571p)gn&hW8-wCo9#E}^M6zVE7 znWS%2Br18c4>D^^v-kN7OO6wTx6BEu$o;uZ@vzSoJa=RR1D||kFvasf)76Zm2;a@B z;;&jaWLd$T+JRt7ueHf;?o}%79Ci|<6GjmK(j!faMajxe3{E<=gycIVW;ZZPuCQVYiO2*2%MYd!DE z$r5)O*vKXo`_C)w$sElcT*f$r;K%xsDDR^*w6(4G*9Ss98P~+QrWt^Ot~4`KiM(le zn`MGmt5=abJ=6y?*CG1UA~e>N?v_g}f8 z$R?_^R9mye7@1t5(8b6?W&LJK+v-Y2>?qdevz`FHz0*{r zBVvgO#25F~8i13zoz17~`()^%Zx-`GoNZ2oaacf9Geso(lI%SoVX-N!BEa8`lBEe|y zzfAw^|BLCrVfJl%e$_%CirpKx|1PWx&3^G$X7A1o@wc$A@BcGTiv*((F`@I{2{PYJ zdkj$J$9wN;YGq?<8gR(6*!98H!}$}A z6-vtSp@!aVPW!5RSk@EW2XucZ#ZhAd=WxEb!E^!#&&0GubCSP~8>$=^{ zYJ7jt>kHCpIG`8Siag5H>~Qg(9v}5x*PNpMBlwfYg4=m}#pXGE?7JPe(8qYFo_bjo zZ#6a^ZULu}{`nI25{|ugb%=W{R!T+ORiy&zs|=<~Ay~-YK9QUY52Wtp$IiA7=XSQO+^!(LXMDhI9h8J1l5>$hPg8Rs=1W+zJ&QW+>EOia7~TFf#7TTW z-}~Z&u45l9_2gc-7%tDUOYrmm245U0%73+LVSz+TwAXWx=D7&*EV2p@@6G*-4@U8S z6(5w%DAH0@bIsfeH6~&(CF$#@0$Fp-$FfA&{Y)~*Lz%0kwQl1x)p{_`P20A93;_HN z-m^2qL6}sU#jobYMxp&lvSUs^>z}%&*rTJ!&=u8kXSntN;kEvj&PgbN=pk+2KFCw1 zSpEgX3Jt{Ntr<3vG0uNQg?CUx0C8Z^#OSJJ7SDzWX%3AYR{;^>3yK|kj#MLuifAQ( z;yL-!{==5$6-Fj0eSM15?+PMA5Kc&n+GGK2o(%O{qfJ@$J#lNO=)-tIJ?bs$KCjP* zbZ;&NKi$kc-&TAGK7U-}K8cnpR83EPaGZqkGa&`F0}7?!0SQJ z0j=vF^yT7=-C^&3WTQY(%HANdMCFgqL{3g>OfY|w@DbAgf`+TeI{~mi76Ty9C?hcw z^*KY0I#5C^EDd!w$Wt+QF!HZT>Do4t1&(l7CyvfaEK>6LAy#(;LQk7v!sXY=f3g60 z*6}Gk_u1z?wld6wA#eMuOcuPhvqM0~GcfAAym9lD!08X_;~TGr%ULUPm(6LFrMquwQoM)wcy{K~ga#tkw?Db@4&L1hjT3|0 z!k+74Dg3%v`-ZBX?tyc;@j{5k{tSe!NiOWZxUyg7soeQ?>o;J=W{!-EY*-FJ2faJZ zi&y06O-xE!`(1B25EK-&eQ1{HNs#nb?q5wEG}qt=X}-FV@VN^88zo*cCJT8>kB65p z)Q6>*7rHYHXkBWFxn<$7suqJ zdUgka?-J{*Jw1%y*i@G))Y0g?jR3-MP3(D(2^mK3PvX$EhE%J*sKA6;Wc zjln4zm3m-%Vj?28a#o?tcyKC@_8h;mNcNgN8$L%FQ4f@6C#Fyp1<2hs@mfYWWM?=) zo|5u_=y6c1S#f7R@5T%{^@Gb;xTg45EzMp=#DtrOOy`}D*W(uXNsMm7r?Jrn2eY*n zc6Pn@H)oN+r?I5gFGFeEB%;1I8%b$xw7ef5HA)?q?E-3jxQ0W%N$+ixNfv*AnT4x&aVgkVo9)eJf+`l9D4ZIt-2Ghwra#q4+)J;A``A2c*S|a<}ZVvX^d4-t`cX zkIthKSSv>O6;lx~{(G-v7?TpFdB%|wYIp8UBCkY$IMsGF#B}M~5pH|Ab#sTE$67p| z5>2Iqu=w!NOz{7_MSxJ+=1=d`Nr=zH!oL*1XyG88Ww}V|6Wt5na5j1iBq#!#kP+*3 zeh&9c7EW%zV{M*CA#gCqX6Hyn?9-<2ZA^J5j}U=(vxtq6HMJDMYsxVz@F51Y0KMq3 zHn>b(#mrS^K<4Q$#5TlJ4|J43Du{(>7c^C(bsB%wtw9TukRO=9z8-dKeeMu)?k>=` z75w3*o7D#nc2M$RUus~pQ*YFaD{{sISoNCl>X&?)0mHL#blS^TIVY5o@rMV^%jUZR z#6ZT_ZBZtt601KmY0MA;5`Ti0ri``%j*5pz^yTEHiWPmvbvF6Qv0mPmKrJ)w6dN&6 zrTFzPP>(UsP78(D!z%42a}^4Bg$%M%TMuSFq0%5kl>~!~#vl>nfz&DfGPfkwC80^8 zJ7#2ph)yNOj5eE*=o0VgZv5sWOPPw^IzfTg{na$_*xFi}Gczi$!w&c=e>hc`dLaax zEHqqhrQtnhN!LY(qJbT-$Nv)Dq##(eQ^@(P6r+ZeQ-Z<_M3(QUfUfVv3`cP!%YZk1 z7_!fl(w+C5p2gHK4ITTl%XBwc)Lb0Y)LBvu?)@h!Fb4@@A~sk}CCov8Tv1L#wFY#D zwV;A(@)~Zd$z_i&UpvlCTh0uXVSMm3q55~^o(Fsif9^HErlzL(;TO(x`LRB-^x&2Z zLOlb%G_`qKh?4Y!yv_A)%ozhm^zrDOX~xfRvq7{b>96K|QheB2Q`;f*^u6)DwiJe9`yW!yuVCExqZ39lq^ zN2Q%)L-|660j@nt8e_k@SFoe7{U5*L@K^vX1iqw~z;Eq?L z;eJH3EuOtE5(pe(#t>&NsD$&i_uq!_q%6t>tM1rbyjpRf$&rZp;XtK}1G;bsen`aw0NQo1s_>Q-w6^G{&{Harn#&6hj*;C@a zlb`gnvKi68rc6vfz=nBxY3$wRWwxNp->RAkEQX?Dvo{|-IEQP#tEmOCS9~(X#HD_Z zafV1S8^^Vpxz9c#I7-qLK+71FFU@aqeoJchZ0>gKcD+i#yM%EgmWq_jp2>em|G0Gy zqUCe@ot@S z77QKs22)4EVE(e1ebn!*k74W*avnBh)e$YcjrgQ%pTIzDf7%Q~c7d*G9c~S5P55SR zfqw2gsbS5ii!0}xydTe{SUjoXF!>Z3Kq`= z5^@0wmo%Dtg@1^&B05T2$+5Jz-WEGj1JKy3Le9B#b$wmFX3)F{HnNwU3lPWi|_*X^FMBtx)*s!p%eKi_&(hhS) z8!`Nx30RHem?oZsH*3jZ3=F|l*GO0wNp++)Dz#$iO?HqW(-J1efFfL)NC?I!#u&2F zZWd&zGc#*B>(soFl7?|s(y+Dhp_H*Pwd>o%e_)30M3RQh{RTeZdqTVMMr)wZa`<$Q*n2_jxSrmuWbVp@tSkj+T5y| zXT#9@JRtUKwv?Ab?uc4Rc{;kf;^=|L5=10dY(?f=i3t+YJ*@Z+_%DL1id?k`Y3;@T zv%e&ujy|m(+3UxXJ>o7#t3`@LXYa(q;=BBD9TwX3Livyu`Y0;Ia8*gM-36upMagtv zzhBv;i(82sD@*isNXWeWcMW~Ed{YpajYV#t5R<{efdUTP^hApCE z!)(n`;?x07i>XPVxd$SNfSpAkqV(1L1*Bqc@Rh9cvpqi3`x%sgm*Qz?~d9=w=xY69%2-W3| z)!_Dq2J(0)xcHT^C8XyIH=}E}0H>(Le7lJ?5w;9YJr%ns-4HA9CH?+38H%Bw=ynGy z>-&>CjjXyVcti0~>?6^3Yzb2d-ov3M>dS-E)w^Fy(C_me$Yn(+;Kg!`2C9}HDQa+O z@iq}-%nPCTVDb0}vTF%{W_D8*nYylF` zdh5pg*y6C%kFymSzXr?$NtgX{b2qjf}Os; z1bkXv_nZmEF(f;!rv}u+x?(z;n4+30H1_a>b)B>I$*}F-2q5-V1<}I}k#Q6XFHFs9 z$YFfmiD>7G!0i2e`yLAK{&;IUq33~t#x$_9Hpz0HMLpSvg~xxY+Xz%ujTrjC@8Gi~ zyvnj9=w9_OV(~H$)Bbwv=~k&!-<$P)3)%6ycr3Ra95VoM-@bCq?j4)U9Dq)Z&pbn% zEj%j%OSz&AlO;O-*T$WGQT2ylR-HXMCtGqnp$|pog2PS0?+wqc47z@qOqvqtS!qtO ze4luFgVRXb95T*1G?MEubtwvek1|e?8!TYbs2bk5Q4ncT_qI zZ#-$uv5J-crdgGw;9GxRs}09l!a}SF>e}$r>2z+x^>d21Wb!528~BMmtFPB=elYC! zX;&MIv&V*Ub-5Gtu_f-usMQ?5L0v=I6p5MILk%7snP`2`FBfBEW;!e0bjU=wO*Ve( zhXwDvgLW#cZ}g135Sbi}G@6+(zZ_>`y!Jo|h@9Ds`6wj(>HV*s;SJ~JpZYbJgdZvV zJVk{FI4^(LQiw#EyuhW7Yt&K)7|kTF-gTkTpx;DX<7+^qUknYE0kznBf!c!@j$(z{ zl|UE#SX*-E$waD}no=+O2o`x#e@%Y3EGQ@PqaQ`u=SyS6UChsrvk_s6t*@SkqF1Al zV!y>{VMy{eo(Fxkfu79&_;@~bMjeRR$-raxIax~?&?BPzTKsS6PX`aK-MDu1#%s>J z$M404eCoCJc)u8>vs7qid)t7+rjUtD&%e{ig6!IIPP%%OMEC`Kmz;;|np5M-m@||X zx8UX~nXhEmN-9AT9D&c9;eZ}L&e8C(D?FOL+?#`8B`K-%q?K<%rBoU_jLL{s8Zy>v z>g)WhgKg?0(g)f-Ts5~ZSuaqB%FD2NjPfhWqmq!*Vqsmj+R)hZ`NvD^?l-+S)=zhP zbX+Dr3h!`uZ5z%kZC*#1JkK}NQk{M)vrAbX?5~Vv=)-8FCqbGJ*9`0Yfol8rXl z53uZ?6h;?lpZfcD?kfGvu->wDjyzff@l=%m3CU%{M#u}Ki#Kt7lcE{_!emRwb0;PZ zqb=`4e*o22^i?0zY6-?-oawrg3c7ZkFa5_{>0T$uDJ@R8^93(C(3%sdtpdeIe-4C^ z#u=BrAY^*oQyOZTA1%5W##-+8;9dLeTRE2|y6QLOugawej2iIU(TK~rKvW4TdGB-) zjKWr)E3A{%D9Y^@P8ForHqROI?PH&49)A(;NO?|#7$oApbnQQLwjCE(DF4BaUu&Cm^kl*GW$UD8Mo4Bg!&A*pnCx1@A~bmuqceUImR z|Lp6UYyQBqXYaMvz3!mL{Yo_|>IGn(pBytaj6&^9`U%iyCj`m56FEBlt_;-zh`mGP zmJbVUFmkyPX!w0g4;@PLBB&W6TR@i0%v)a8kE;d$ZgiQe zF~dT{O$JIWEG)nk=Ych&0z=S5&*PR^W&^jiAh)O69f#S^U)HP)?ia#0`84z3EW`(W z?z_H)_uk&c?!3fPpZ5Mg-(I2}n*}{egovad&eoFlI5TcYeWnMu?z;(@*2@q+x6pRS z4l$0m)(ie3&wGFkps643+7FYhZ#QYRV)|KJo<&=&G~QdM_*9y1YxyLEeV+lRz!io~ z@pLZ**rIY3_H!qvj%O^mM{G(=t?Z4qCaCN}8qt*d#z#}8J1pUVsdr0@8v4@>iTo3x zZKP4G)fWy?kw9$S==+8f(__tAPE6Xm_E5hsn!vMzjJeY@#8wDvAxi)IBLz%k65=Al zcUff&xG#XCAQb3UZWt=^($U8utQ{!@=Kw9~r?DNzJ>u=-0|~{?6CeYZUs=zGlQpZ> zRZrN^fT$x2CM9y-q>U&Ma(8$4S^wnvs@JH(!z z8oR>`=LvA-Zph!JaZee3a7syWVp{`mz?|)k&I=(;63SBYaRV>Rw_-BiFi|dam2E7# zZ@-sF&NzR8Y#u(ozJ5)sPNBQu3%#F+-S`9?J^T#l)!;M7kad;2{gq{F%lT`lV=!6^ zVCC0cc9JScV9-t1Bgcm%!a{pm8YOo45~yj(z?vSeQ2Dw58tEg#6&19)EfCTdDP^iFkad-GGW22GX|Rf( ziIsx`cuKg1-J|mhiZ9G{%2%#z{VAE%>9ddzb4f*j#OwB2B-n8I)=NKy?JvSuli%K7 zr(RzEbUaMk@Fb}dsRbOI zi!piEg!F?h`H}(0@6Z@Pip^M!9E5gYh>E9;Q=~Q#hlJx6P%%ZtzTq`L+1haX7& za7zPyP=B9Q-Wacis$P7dR>`guy7hHlWYX)4cGcWnhuP9li>_H9?NI_U4bG=BZW;y3C+;3sf zD6TOHhtO90e zSCvo~#1I_>iqmu}nTQ01Wo4{1H_Y(eem?F*-D!TA@Z&rJtEt`_s#3N5bFjKOC_`O6 zidXwlFw@n3Ra7&~U@A<{V7XfUT%nLjUp^5fI-MeD>kcWKx=L~PZK@AcHJ ztTEliDd8M(TXw?=oUJLc&YY);rq~-I8^hh*K@$_*MBxrF)i>t34q~rV-=T1iETdm6 z&&>Sz)G~Ifu~OJ*5nYhVx_iFibg%ilq|xR0T9db}Xw-c)X1XGNGfC{&o&F1T#IR$Z zz?Rlr6isP&(5Ir>jns@8bFW(=XC$v{6~W1;P|;|<CkO1CwqPT<&OsblGM6FzdvZUT6%>$)lJ7jT2&e)ASv!A(#MKx^ zzFZtT1>U)lYF|dAThoot_D%MgyfypSr4$1JvS5!8mK)_VtEvad0}0CikR=N{2ny;a zn*Br?GAB1+Bq+gCXa8z->lH|4RQtvG@Y-F@J9JsN6~&~{kVrQ5-dzWgEMjhEy4UV2 zVJge_-tYx9^*<7roq!`Lbt}=~95iz4n?!YT+Qx}%Ph5>?y*zI1Y&~tvE(&hs- z6_XNMsMjJn*h7P@Dd&qLs&4_NpVc6HCYWX{k?WnNc~wYM4Blz^1>A%+xi`U(f5kZr zj~~lQYhIs&ysgWo?_M%OA!7JU*#5k$Rx>j0VTmb$UWgj}of4}T2aa6l%~NMpA+Yv0*d z(XSX&MNE-19M3{9Gvqq*Jh5;pdwBX~&}=z8#&6&rAj2XxWHV*gN$c_vsL+<6ie^UYT7n-+K47A~ zoON!6MiWMqX~r&D7HC@@@VtN>p2Y|F@l%jWvr%yZEc?=!$T%$FI|_;9WH=kR=SHQB zm%{G{Nc&JwlP;<$DYwU9qr2q6Rgah`sz2=j@G$(UEV=uv&txOs62T!1|Mus{?v5(* zhe~QN0F7P~)402!bTK4s5{Qex3HVT?@)9hG8A+FbAb|wSKa;#&+r31NR@XNTJhsm& zf5WFIghXTCkMtU@cDs7BPn`e+>yUk%gB9w(_C29-ymo zRbk>Bx(hB6#)2i^Tmd!Bzngt5NSjD7rqpzEGu2`MJ9ftc6Y1@9jyoAa2H{5jyc8sf zZ6VE8Z0s?`TJb+dNfmH5+#|v$v;1k~C}1e~dtWA(stJnHIbV|N4{e@?2gg>+VAd3> za4hp`$9?uRqchDE{4~`SELM;fDS{XPLic^S7Iv>A1w%!1hISdMud z2!V8@X6M?DbZ_GGKLopUBR>a zeh1__ym}1wQqq`nJ5Pq;hvDHEtr66!qP>78THE@v^=@sq_m0J(KAw<7sZmtZSN>m3Ns$!=5(9O*XV38SC^=GI>S7T8xlnw=e<-G! zur}Gor;4FcenbDZvx)aNl5ydj^)XEcc9EAy=Xj6T0C?8?{d1-n@Y-X!R_# zu+&m~mYChCk4P|;3*?Rto10r*K`dJIyTn%5Y$ADT&DV0s(1*?acIp2% zB3&Rf?-n`P4NZ7wu)?&QK<`;@&q)%&xg|Tymd6LGml;?M{1C|t`?3KGWl*z@*oq>V zsPy1UJE+=d?i+IK3a60`o2z2=9kwDVco(5oOZd`9mn7|JMATM{)%s!SWcB{l5WTp9 zx7F!NvRWU96t3}uUS&78WVY997kWP&zxK+k%~1{IH9tJvw|Z_|i0kW$#!$O6#W? zAjMG)8`9m*3QwnQV^hBR7!x7YZoe^}$v8td)|Ilu+PSG+lybpVdY1&H(#CxMXjSe@ zcC`OMDNnIe8LRmg5~-%p?KV1w!`*wH<`_e)H`F%jMxkC{eG=(J={S$%(@-C?_2uzo zG&67)iw>xo22+#f80-AC(j(kIJE^zArs;oC7N~{$b-=I?_GqrZ@7;?3ZoitUX({Nv zh7i5on3&%p>_`J+SJ3!1+WGl?9*gsVOf5ttlwaJ858Qz2>WOk$cgQmHuL9+L9&v&xGGAL>JqJ-X^c6cnErEOOL?-;xk)@e0c{>LVjp4Na1n%BmF%un zk%m9Cx;tWg;rFz^I$Z|mYKFiC*&bJds3|-)QOA13j<^FDlG@}%L;hSGV(Ll9xXsc& zaElrE#jXp}3}A^aP!P!&A_yHV z!LQs8MIkG6)k7_Ze&R)Jz4RDDOewUQYV-ZKGCf@1fTnrUzuv#ka*AXhdf%ryOiqu% z)&d(P`ce2$6&HkV-6^_H4O=G~=$K}d%|fq1)C=4*%J`gNzIT|DR~=@mXiF2@~r=*nhN>^#+~#u@uRrcu)BmoWQ2J zAAiC2w8;@Pq1XWkS8J|B_ZDURbh?@!*ZhWIo;gE|8c4N$8C-&i>%G}-pqFOEWV&Zk zhjFx|=ayoNw{;6qGwC6{il)GZ$lmE_eNUmgGF1{{t(nb^kBP7@ghFgA#8pbFs%D{p z5KgKhHx7buaWl|s?Y_>7N@!G2r{wTq6FftEnrKQzPi@$0^g3aPa<*G9Y4ZnBj4^+D z2B&?pp^jGiwfg-tzMqzk`!4BeC*|!3n z%XEj*FRmRpwG+<6CapHX7rS}#jvTi$^aqKE5bcJWAv=A^Wdf0)C2Kai4cxGBf9fF$ z-H(&ag@&OMUD(?|+J~!1CFg4-dsDm${m74McnHyy;xw1j4B6}0l;B-PxvIsfDcF3` z*r$ouLGwU6k<*PP!((fr3s%1eG_O+%Uv~4frPCVh&q|SAgD!CQC$ZdDt)?EDm=9+v zQ<{g4)vbbU7BbFX4B3Vd_Wz=n(R)ZB6DYqvkf!Lf8x2qMLmM+7UDRMM3blx}9dFi$ zyg*|kB%-AMfH?+uuoj%}`)E0a<2HEu!P&h@b(=wgf2xf(`J#ePJiJgPaoj5QB29}y zPbj|yJ^&0n{_ zjI^y^X}Sx8j_Y5Pm$eCu+vc(d!XL$jX!=rkZ&djo0$ zX=x8Q$K>KHzU3c|i4Wr5qOp?`7hEYfS=Yt!lMX;0y9}qVg)AO&g{;nbX7y@3Sz8g=l>Ww;#pUoHH}l%#5PXV5*VV8w@e^BRtya43_76IM7Od&=q}h{ z1@SNibC`6{!w99_@>4b;0Q>EmdF9}B8mZIC-+#I+%$T{eT)H%TN!1Za_IXcY4=Fgk z;efCGamzKl93yj^jgrc2_IMmQjR}RorGg8?64b<8-gnLqce8E1@@y?8`c81>+D7BS zcP_-s?@dRY+aqZ)@>q*-A)z0f)Acaa_J9uGhkz$XTj9zwqvdAzmpd$Dk5AWac2BME zIX!M_vL4@ja6N4QX|s4TmGy+Xa`&t5BQ88Xg{*P^c@QP&Q;Z3~=Uq8(ug1^zl@{b*Wsm(-G+cqga%47N-n+{KE1!UMmmM52q|+m8bF zKEORa&{^ncR>9nv+UIy>O%a+C&kaCp@W7)SaClTSmXzTF*7V z+R6CwRx$xn$-&|yTKtBKh|Hxd{Coe(Yl}{zSQJ%t9Mjnm?yNsAQIAzcws9_AtXrdz zIMTK-a*VCE%~rHM30PWLA!po%6LGyh8ZwS8p;_M=pv&rpV)Td~vyM?%bq`NE88L|5dh?`9_o zd@+QA*N)P{!0_)ogj4LdJ;wz)qB?&l5S#~EOn#w+MM)&1{seX18xyvoq*uy`$v6ddByju9ve+mPMD>#5g!Q&FP|$($=0AowlEjv&j9;KZ^QSZH&B6zd(;ZN<Z)Kotk3a=A{LZuALqb!g8Z>B;%4{lTnKW%@9 z!B`vny>ft9>0g%J-8Le4E8+RH2**R+p*WO7Us}(}{^8DVUj_xqK7W!QYR!F7J(l&b zNk?0PgM!3vj({Lbzn+5iD{8lG#S4j6Z zJ+_({a~1#*4ZqiYbu&;`V9YGnz6y?Z5E%^eJTrK535N_6%H~#Ha89n-&P8OT5zkpy zLjg!~L>%2*?=ja8)-DBeDdAaE&g_XAkuv+_vrVqNxg!Max9XN_%%pw4u`5BJK=tFh znc?-M6HO7rFgLv_zE|i>E3!Q`k&&#!5X)qg^Qimqs9}VN-!1yLL2MFk+Ph zf60>Vi`|jA*!ZPu*OT@908bOY6{m4w)~kQJ&R-Rbk_dle;r3Y9Gpfcb``%4SAqm~> z=KUj~RICL7Io2((IX>#>ShoBRM^jc3$B%k!rP3(CefTb%S*!AE*7IBHKMJ{gSV9-k zr?(h~?<8#BV6%+2n=W0iPHm6h{6;KIe=&y7+WyCYpF;BCvrBD7*9O)D`G=OSOS{Rz4}mI0Ko)4F=n{_mp!Lh+)rn5k8C9|$2v&qNGpqINL#Uqp<0 zWloV*>)QI?(CrLXdyAH#K$v*Ph9hm7BCBr1Xv$PfM%+cy9KR8-1vtQz^H-*@DN919 zf^!f!D2{t7DhRKR=Xfs2iq!x~S(1iI%XKc9^C#P6XJ|CX53k|YsF|*ZGP!t~-r|qp zK&4i(XY(OVFUJLIMT`%cv#adO(ny0*J{MGauJe$>i^lvy;fIs1?JN7H7&PNg2Hx*& zn@^?g;u|S}QeL#NXg8hcY{|t?kOcK0(86>mi7f=~>3nc-I%gb(!PsekKEk^JuixA~ zM4<7bg TkyPD1;%7IE4bxDc(wBB=o>uMF0zfPWz#%~s0QZXThvL(kc&Ex~ z6imL($cfxY9OvjO-3bY*Wp~%l?P$Iytp!oCT^~8#_rS>Gp-LN-K_g-^)@1 zvMY|?ZYD_6O!g*Am{*Jt4|W0Rt7J|gPFrM;nf;m4k-8#y9lZ0)%w3F%{hIRD`d;X} z)s3z~F-v!TCnNFoIpB-ET5{~uR-SUts9jz8>qzO_pVAMGvZaiM#=G^u2b|fPQq242 zX}Fpr;Ak~3DNaiv00OdPfEmW9$iGG-(Bw0HPT(UEP}8~$0yG)kq2t zsG{ZCPL|;gdF{;@kdG=7hXnHiYn?LpK8cInY2EpYNrk3Xx*pi-ozIgf>L}AH2}Iol z`1rS|b(KYW#1`?_)p+txf;r!FtAnm~nx8 zK|}oZ`h$v&O8aYQx=K;Luk7vfgi>UpIqbrXqZQ9o%>K?+?q#19PBLL>Z(>G7e|gMv z(d|1I`9Kj)j&<;@&br$7ly5dafthj>>DjaKR|m)muhOZ|4(kcyBy_G-dF{^2KleTXdj5O+r>6}BoKB%X%2 zKxWItX>6H?S#ZZo5d(*ZNElP2vfn~RQbId8GRI$%oWeHkQb0ag>$==!|7v>i*G5Y1 zwn0_An0ij~xXF#CPyikApOcYBv~%>EkVrs+rca#e!?N1LjUS!1|DRWa4V%yFE6Z}h z$!b@*ynoeWq_apz^beftttg1;wZVK0OUsKrtiKL<#_l=Lf)t23?u7jh{WDhs^=M~} zp2jjW4kSN)MwuGy9ur{l4qC5j|>$?ARx7WPz*kU*l&fJvl5pB z2fP-gVWs(=$m%~(2-t6rKa=6 za}_`4=qD`Ie}#nY2&nxg%Cz22VLR`7LwzWopPlU2({YTEJ(R#b51FA7kt_-luF*KR z`EEJ(17m)^I=C)zmT!OKaqduc_DBS+;zMu%${^G92$OPcZ}ts^FEIT2w@)9*{L9`Y zzVijpk^3y_Z4C4kZXKuCUiOZ4IFX;ElkR>r@mG$)He%E;VCQ_NrK59YIAXxFik6yO zS~yeXmyd$)ny^Dq|50w>f}Ki*>m*ga8wyrRmF;zhH~FR=bZnd}wOH5X4}Rmjt~B<7 zfH~#b+O|_da;>UYE_GfMZLLQ5a6DEV%=W$7^5e<%Pdha{K{)vPEsrpZ8CGMDd{At* zdVGHB47kwx*B*=uMJsp9IV`blup#W*1i&Xxd94%f<+ft($!QWRLdp_~*hAz1Si|04 zha<~LH2yEuvFWpPg2yA@0b3q^atuG$^0R#j)|QOuq!G6RnTBujW^+hs^g{SmL|V}i zGL;qNc-7mPut{8mx$LMo0wXeoo~^ae-Sc)rxmNJ9CI_i`v-TfFbh@{^Fhty$Iz zOU3Fp%?|=Y=P_T>Giu4d|LsoK;3p#K*u}>5Ina$j-JWlt=tn0rppSr2amPP_@H#{29)>60Gp_G%EV7-_&kn&NwBybzJ)z7wnYqWoKhGrojgTb zTuV?${{=f|Qgy3qSfWYI04;ZO)312f_Y7ccR?gr1At(fzC@`*93dF3h88q|7wJItI z7N6IQ6qY7np?1e0`A5t`%}e0)Oqh%^GFaHdhbU%1s{@!N6viD|4Fc6y4p&_)9@I3b zs;cI+Xmj3tQOEkTi~5FVor~J)|HFOh>Bl{t$6n})UshnV^TY9xiw&1_ren8JAoDv(Le4+;H} zu5Kq&+EmFB$gGUNgu}QxY$>tM#({LN`xy_2cPt^{MF_b)}S4lTA}b z!BRH<{LWbRf!YZQ2#0@AmNbR8X4+5NF6|wI9MwdHCQT1P3p#e0TIxc*2pW0Yg=57| zmc`0|;O@`AY3i?vu+~ypQ&(5TRQDZp8N@w5p70 zz_&_?IIb#h?ZvHkHZ$v_jlsUfAQ}yY^kP*hMo@Eq)ee74F)N$2x+`*69mHtt%C)|v zCb}Q*M?1g344N_qCGuEI?(pbk6=`SO-2mZQ9`hRMV8q(*RH^hUQ*t!E9jJP!?0~5S z&z_|@sLOySrxyCV&AmL`0S%uORVo}*8Aw%D*5d*_VD~JyejH(3hs(CeM6iMGWDm&YCI4Mxt1g(W3tfFdd=awE2*vC%&QktYx?S zp9jq>sV`Exq?|cpaOk8Ys|sMjg{9W2dC{k{D*^LE5@jsgE|XwsobCW!PC_Eg$-j>m_*qD3K$UZF=1xtg` z6GWXhu|qq~h4Fp7kzWPmhlW;2ic8vUPv;Gd$_ikKsx9ZoCJWN_tpzu=qJI7@gv(&0 zqq4gJSV5B~9%_sdPW;pM8R4GIpjS2suehuP9AbyO!>V?aTu?e<<)-M4QBzZ`I(;bp z*d`M6YgB2}fm)oZGXw%n2D^&JJy*5f;0|jVBoe;T*46zmh1U{Z{n5fM(b${i;!!H- zt9s)M1&lJI$|XPPE2kox@_ZWHlY5UYx>bm$22~%A&Ojg%0|CI=yQs!V@I3c4d!xN$ zZ13?0Ue$c;cA_gD^GQ5|u>h=`w^vTpV}m1x+X>0i(Z*kl9c?i@l=gZt@fT2Sucq=T zg6p_RB1wCR_4Mem#lxAmRBAzl?y~H@3ZNbtNrPDbpzzxt`0iC}h=nQGIdq zI9G4Ogg-e1%u>BAlM9py!R|*wqp7b3c zsrs4Kyzpz`>6;3@gykBt$Cd%=aY>1vAl>bROw8*HDA28lyTM9()Dc245TMpQM6 zZzzCz;>?d$8)o!JM*02JA#|(YnQv-lfU{D^3ALq2b7Mlq4-2b*?181+=#k7)pLeNb zRkHab)-wG4f>3hPK_NX4qfn?|;`getS#Z&J@YJw13D2zansuu|{A({{sy9G!c-%zI zz-pQ`&+R-crq8POa(;Hry#fJeJ}jr+E0?3NPfLu29)srom3;KU+vB0**o;};jy@bz zx}fxzxBLw#6?VPISi%7f1})^bw`CE7@=OC@M?LU3#g7i>N|Z672GBTs-#?vLpqi-K z^zN<~nTlQQT)ilMaOy7(EsFYv_Fb+dK8_7TCd}9JndhnDQGc$Q460hDQkUG{1q~v5 zO);vvg0r@67M2Vnj|oisCiR`Zn(WX;+#y_w`{(06Stifs8P|<9x^BVEEt|2xT{;$9aa`_ zXod|?j*XYzHmo*y1~`)N@)HU(jxvclKayL!-MXZE+`2cz|04jwUGh!qNBhSL{$Qp% zm+m|&e;Ux~`oSMb?=H=Bm(wBzxQg0%ntq6*nfy*OPs*@urBzg#%Juz^4ubVM+5Z7^ z{K}rXV3)4hKay@U7y^hY>5G{qGcTL6&XSiu{%h7`FNhOeOTx5JU3`cAa4r2DaCe07 z7N&)tR6;W){|>89|IJ-X&R33dcsH+!muC=Yl*gPF^1e|p`7jk~sQ6Y}L)(0D5q0(o z$s^5kmDOZ^Q;>jekO!23Z6urA8+CD<7SG?L$XG^`$2M?{_bhvs7?0arKcaEAV)ycx*>OfNWrJ@jBi%4iHVichIw~;<<8&@e{tN3{k&>J=b-zG zAnn&FV`+L?;io^rxYO~SqudOmM*!3dO6C@AP%#qXfDF1fE3MkVVs6*c-Pe4d0EEv1K$l0L-!YQc;S@ikxYw*V&Hy z48#!GmS_)8g}3m1a=PD0{c%UpNT}I4-Lv58eCMnBN2Z-TXxmN(lTWBLFJ{C`Px}ep zXZ-WcFFOB`X>h{7dcjz>qR!9G?Bxd(Z>}ESi1?`PYp}*@ZTpSm@^!I9hnVlUq)%Xx@e#3oURgQ^K9k=>?r6@c2P?xP4-al zZ<@Z5Xy)Ql{hqA(6@e>Z_WlIi#1xE1+_hU7!ddfy<$Z$BE0DtxkX{rXA;tfN9toL{Ou+O)?A?pi#b?+L zIi9bcmM#(2xl2AUXyvAN%S~^&v2S6um}0wFU|i;W^-0S9fKJY91yil>wtg_pI_nwo zjr%iG%u)=p6MI3XK6mtP4uM;Gd}`sPXA>?{wt2r(8g`HK02j6xRSSSQSMExZ25dqD zz$41F>vm>4;G?4Dbllu?%v$Im&mG@2zUZUq%NSGccK^el%UeF7y760@{MD;D?b^>aC&%yw^4)+Uvb3%l75f1z{(3Y*dg1_ zVfeBHn~=l}rqo;Q^?IKJ*L2tIhT`w!6?k2k&O$EtsPcx< z<}o6y-|Mk1FV~$yYX*b=2MeJ_1#|zFTQmA(|=A9fHtt7FhK%ax+ zJdl%+roK~SQ)vu!^8$%k$s)?SW(Kk4Elv> z8n3}~_i1(GYHHAuK}Q^6Ii4);tA`jI)@E9VyHC_K3UUG=^Om_)4X=VU;#71rzo{ll zv{v4AtMAHrAUi05PJG^y;jzSwK$ z6OGVe1B3|vqTv!KP;)?6O@8QzG3q9T%Oly7!*Pp(;u*WpV6b(euxzSU;K4>t>Rj{` z*B9h&`K7S|$7sZkF0{}3NTfjhW(7iYVA}85t;7(psUukMk!=IOP*hhG-s$(;n4(1< zewp*(Pu0LfLiFJ+~%IZc>8Z!k8Z2MD#hvyVnBh;&G#;86QEOBG`+FU<)^a z5IBpr8#8N55N_6QMW=Xg-Ld?;iNgU*7KYZB!p(kK-k&clKH*4jVc3=7w9m7L&@~r4 zA8%M6V#9nZX~QF@<2CmP+gWS=L;Nv55!%uNWls@}&^niu(#ls3hsPQ+3=7POn9eM? z)L*2MM90M$1AQ%9m0LVKJel7TGV9!p)jQ3oyBYT2BM~fzhuEb@jAoQGFn4O*Punqk zG~M_PI~#BAOZvZs2#g&qo0|r)qgimAzmY%Hd9-qC2oUNY-0jOI>XJPLLOi09EWyZ) za`)Zx;UhE*g6ZKuE1N5483w?a`9>>H0v@+bkx!jH{zBxNR0K2NBC}eC%${0Wv=}$p z@NcHT*VNZAD>Qtf*hre|4DRD5EyuYYU!^CUV`u;jBm@!_oH`Z0+6+FO%s2J&`NHCQ zyb$?QNZ-V>n%*Zll}?rT=-e<9?>so8b1PJ&-QgTU&%(OC z9Lr#1n@KWvT)5Ki1U)|EQ0B5QdYtJb)Yykym>-O0@SQ(Mrs7|5>|U(lq_z9x7Cc7u zos5$x7B_9(-yEgu9cCNKmJ{ILn!n1lt2G+5fvL)LOge$jCu-FHE!c+t{M3A5AlwCkPmMH}pj*1Hh=)7WhKUC3sBKnngVS6YAd=FJZb z8id{hrnNl~C)MvzXNNE^4j70&77R+K09x`@9A|C9=wncQ&xGloys$W)V4$4R;Fywp ztx(SMyyZHw-&MA_Ze(-Z8RQ9{af9Qz0fmZ}r^)#Yn=)?zqQGy~3MsIGhRRiOF;`YF z1>E^*- z76`i(idLbQ>_o~ZK7wbR2b+hNQ{87m4XK~MtXF$Rb$@>MjQw?ro20__KYHY;A=Za@q!7efPc9RsyASgW(A+Y97em8&J@ zgV1@9T6r|^$$nu6KSJSrpfW#z#v9&l{2HjObRps0=p6da6Z9_s;YudsT*iLdo6*nt z9}=6I#DY~48;M&M!_*G(#Jb7*$n8puKQ3m5UhxfyFUQ77{=UO|U;9mSS4k!$fj_&w z%v^4Ib7YBi|FiNV8{L9KD512M8t zLm>-s7>Z&J@*`*UN<$jTk;O`y^t!WnYDPUkcd}o=+fftE=gu$#@_(?dn6R{SeRLNmZES z^1w}qbYeTvUPt5~&nwSN+rKy5J{RZh1du^MTU(pn@MY&BQ(Gj~&+k3IMuqGXIe&_F zU;{dUsz`gyZvI$%x4;(7VS*G~{EwX5;fhpJVR#HSheU5#WCxxj|PBp8tzN{clA#va-iZ_m@jjwi{n&rwIp(8QSieRDHV1Jz6TVfrs z)4OTlJKT;_EyR`EGYzeV^N(H)eTVyO{F)5rVoQcCE$L0ceX^Ma;@h=ov)d4iZ}$%x z{?}ou9e@*!S`yMCn4daFM00);klQAaonjj^|1l>1GKF6%v+oM9O}tQgM9 zNoLoo35HO*bA zkKdYzu!_s7rmnEgK-=DB#Q;fyrFzYsFY?BX&@nL*8s$|K`NklLZO368g6Bvcdp*Bl z)2b#$j=EaU)e@bLwnBvyiOcbMn7Mx^?;%N9vTo@$yPZO_?k5Z6Im|}W0lryP7q8FY zCYXCI>x0}af)G3SRe?rPQW`FOKrL~@sIIBuuwCL?B7YQtay6cbj0~-Q8#T>u_oJsm zT=$5MFT(kjJ11j+dfMGjw|w@aJ}l#=n|C84M0K^vo@B~(j0J8ND;7U@5{9?!gv47gy4h9jw_y>2S}Ush0c_IG9J0Y z*E}Z_v~FR!F`uz)Zn=}NwlvOT_^&bU=gHk!|N%mD$+Kvdcna^NE7L_ zVP_N@Q$OnB#q5{Uicgp3wm=7&VC6(>O^naOTLZ@6pVs%=kf_g{?O8GE&H7_exMRW@ zxF}VKTGT*Ap!=Fd^)Xv`;u3nDEL&nW3V!PGOK$|8v*idI&kw5Vx@N>lgJuJAT=MQ# zE#HuOx&6duy8azNF`})X4mZxM*ASPU63$gY6}n#6RyP9`%iEe*j@XnpL{H3(+jb{g z7D3zZ@BRv6+O?<+y{V2&%`P4uyrz5h?WG?`>dpGR=SIU{ONXZI8LfZG=Eyq5CurlE zh7RR;t1%nM$7{?jm+pzv>toO^R}qYIT>Z!=wXF`3cYMhtoTk=K<9-YEnhTFgtmys* z8}(xmiJH|N*+$dAlho++9ghxrHsw%KbLWFwsVsA#=yf5>gEo}J=ho@ro~_jn z4K1bjH}ba_YyAR|zkG-En}1%AkGkC}oNea9Pqq&>(5pbe(PrJB!d$|g@79x~MlK8y z^7%3s29^=H>YCrjf{&(V+j3)ZR9696E0}>UI+GBppih{)+#U!)J39xUmkp zeD`mCRCPVbE1*OAI(R?IwTU!vU&45tkcGh9!)Y%y-e;?UtI)Ri?zmzX(G2 ztYrVJpw=m#^Q6zLcf9Z$s;KJZ`W9}(_C6EWcZdjb?ywWs zCXpK;j7+6hfBsYqV=D93!wmK)uAphRhuJh+`5K_B_eeq5m#BpF+Rb5mYvNh3q!Btg zj`i4^->2+v_Hhw931#w$*SaHgQG85zzv6n|J(-pHx>a_$c9j z^#z6VPD#4-Ycmm+FGLtDc5D2$tM~cgi*=30>R{UwGkznE0-0{4-P0_i&qmJQ zXXGn(B}%>~F3wtY@eTGv*}m;Z7kpqV-J7(BHn}zIh=tXJVGbKniyRWy3~O%G&57AF zrHriB#NuwP@QNPlP}x;T?du>|en`q77(8m$)z2J75Hunjn57%qTPRd$VFE3@f7kT; zI6GwP_j`)K-k@;Mdsvg&*mlQaPM zd-~f~mn3ry&;Q&M9eB>e_MWJsKp2_@VnHi<@ml2=DKC2m3ZbEH zp`qEi_8dZbsU8y-Cos4p0vngM_*|JoynTaKqF+Ir3!MB9cS{NI zk%tQ|)^m7MhnXC|eDB^+7L0&Yv<6`aadeZt>ffOHLD3osNpl77q(+U4`TB5|;>YD% zCH}V!k~4hkm(#fw-g#T!w67E%h2J$xXxJ@@gr26XR?i*)1=1ocnoD84Ci6C3s`6st zaE-+^kf-ow@jbf!L5(8s#ZsvFNKav#4oFhr2In#eA&5NW2=nba49x!u9#LlQODtck z{O-fFo#k2e4Lh-qbAVAx>Na<8k>V55>jXii^0s={+iE|Y6i#neCC0KI76XGo&m)82 zB#KrnwxPUGqWY&SHZj*|8WiNra^AnM9(n|rXo-D{&6KtJiTq;QnzW~{f8Cezpe6xi zayvv;h5(vprgPmpz7bL#Y<^i4FBXR@afgLmltogg$2s@&HLj+E4 zAF+1wt+P$->@_|ZAbX*#&AB>6gB=IQg>S$2~%G?~u$ zyJB(n%NF0Q@8;(|f*GJzPx`_p;Kj9OxV~#sk3sZ}4Ta|cwvW1x}X6 zyqfdwbY3p~R7>)v>t21~&+NUolvU&D?mX`DKYGvSp8o>6b5uR|p~D};b4`FMtoN6f zb6L|@gAbXkRh)MV`-ODQan@&H4Zhif-}SJ* z92y%S-!*t6w)9F(``XJUucts|N_7qfdLgUr=$x5Hm&i`y2nxfxXYiuWcxI1-3}EEA zqv2p)eC(Zai|k4%uBO{8mHUUx(mB?90weaZwUfU*1IJvGp-19EoONiO?0Igjw0lXy z!MHUea;=0X{8eOYgw_|)3^XI0~qmCVJk5! z_4fuy1W}}33)fEDKp5tj7X3DkPO$Lc0uGMcAoCe~ z4{GNh-)4EGkr3(frHT0+B=n)Z#H$EyrKy3N`a zx$o+>(tiL6y@;I*PiEkn3>As0wXM6pg-;PquEq&H^kQBt8;Q6R#W2=hkqlks`D4}Hg z@Zm1ULi^ zDKh?6`=qzf1C!7l!y?sDugc&Wg42#66NZJ~to_3`EQnr({IqU)aoBD_Wf?0}-voW} zy@C0j;G_v2QKjZoHUj^6s{qxPpLD*%Mb%tRQw;#6l~0F1pGH`S{B+-=(i4r{mo$8e zUo+pZCE?Y07Eshjp_`QXN1k;dAc*#HYmN|&HD0{jvWGAd_g47bnawj-`!>wHJa@fO z`d}2U&R#b=vS<0+*d1wFefp($YJP)(*H;?LXEJfxpAOE| z>~2y`gv^XL+M7wbXcUrp_co&A`<o5NwdHUU>-Wu0@t z#8ea%m9xif2}HFGJQ2`K%ML=*%{q&Ge7DyP8YZo_JGYUt`U)qypR;{wgfA87{>+oB znHL3!vE7e+TjnUx83q^z(9pMDS^ep*9-esSK|jqXdRr(a=+1#yj+YzrnB=;2;_#02 zL1jd3;=*){td|J4TiRRwW6s?w*kAn-qE6WW1?U6#2_puw2JueG*qIZx%~%3ZD{G8I z^MM4Lln_Nn3oiQNB8VKPwo~QIe*eP>H&RR-5V*gpRA?MX4ZTgjb)3wgWj?t%^V(T6v|qpQPrV^Vri{4 z{=t`VQTm)w_$_!AGTWTsiAWVxIy4xNy%3O*Mxqz%M z%7k;s(~30I%DPKQzYRzUl-TQHJY;TP)wuCIEfhgD^?gn153s14pe{%ep1h|L@pcPY zJ-3rH@2oeT;FUUcb1!4w6aBRue|te9f*;ybj{*q`b+?b$K*mK(;dN|X*;d&#giStj z07;K5If%g=LuUsd+Vo=mxI|_FJ@^dSq{Y*P-GVFebb^^a2YPh3VweG&JhCt^2r57| zV>ssqL{O5hzXg5VNR(%VjQ-?Gl=lZlj}*%>(!@ln1@FzML1RPQa6Qu)a@8Tnc}Q!2 zTa)t&i-Mot5S{~5qw_m<@uy)T_+P#Oyc&XvS;x3kwNzHytNQvn1Fy<`$EuonSaF5) zXB$mYr6gL3SG1X!QqgtHBvjR{0&ba7idviUq7_x}g+=NxjtSpsfL7n$-8Lj9ia-!L zHSjFrg?^^!0H8a3su;vFuv=6*kffNYP$x{+X^UpHUG1iH(ybFfm75xRuwcDeUWlT8 z=zjd1B2+Zxs3b<8H|8f7`~~2Gb&7dDtTfv$8}l8Ku0^Q~__B^CO*G}*j}>B|1&EH{ z)FD5qxJ7B(Y?~nw=VgM@c|PCO=!K0F+!1cTRktror;2pK*jogwgL#v?85uL@>0p!e zWlFOoQX1spQw}cJnP#VkvAe8>nMIr{ySqLxh&tq|=A9>Lb`yPwQk7!p`v?o8Gd0dk z--RUmm``y&^k&znnP8wbVE(KE+_zebIAkHj)b;GqdE>nBU(7D{C$mdpyZ@ETxNm-@ zj7EFV75kWVG>H8n!0*=l;dftUOY4^&V zzgb*T{O(2e!Mch5+ zx5M4M9wbL0(u-dZqdC$4GM-QZHASVsXqT>YKBf~naBxl!!!z!R!%M?fY-TF$3QOLp zeW{V{BDq%@#Bb$NW2)0UxaA+cN9`-JdRT>Kq$pF9){DVYf<+S^t2S|hqcE_%it?|6 z$jjfWDO(@G0Ijua7jFtAFr@1&UkdLlh-JDA+x5j_Y4!b4M!c>LikL)iP?#b>ux-TJ z*`uc7{fgJm#?}=afnST{CvWk7eGo%Ip;+{o-tTmi=u@&#S`odkP^L2}`T11I-z(4i zdia)$yOWz1!IbWLGWpp=f2-a&S3a24gIK~<%aK||3m89c|Iqb7BBbxl6w4=(^A&ZAR-pcV8HWH;}knmTZmpzWpPKB5_O7X z21_p(*;)wrI;P{R06i5gfS6zB{vU{$k1pT@h7rRVJUjmQ8%}6o(JOzH-4>Ajq9tN~_v|(9 zdL%HUU95>QpP{J!wj4%{3Bg}<6hAf7MI`cFjnZ+t+XhN04o8tx za-DK#dLoWdD$)6fY`^r ztdyf>fK(X;pZU$2DFd3z%CKlQi&FRnxXe zyA#s8=r7nz{p;O9@0^oa28xOfo#DP~L^+8@tlZ$RKJ90^sM9^4%LD)>aM zk!>lB>7|4?X=AU5Asd|lM9a0)7t2s{BXQ(vpMnPOqu_e6!JUP8>J9V#VzOxqXSG8xcjRA?j~kL9gS#F z>8l8XY%7947|h*1S!YYV8oE5&68pHhw5cqm#`d(tVy===F>Nm9{$N!OlCTue+prIm z=yk?sCr>|`qXl22Nl=MQD=1tv7`c#o^4_GZ;T`om2|3n-{HpLOw4}wmi|`5_Caeot z0)fF?w|wlvzO;}f8|LMxX`r|#rAAv)z-*>wbFo`S{Ey;b)j31;b%hXM?0`_MZsmq~ zZT^j4rL}AhI0tb8T32#e9_){MweSyBvaDD%IFUUk&ysp@QeZ0=_ZK5`x=|u05s}Vz zYtT0Aiy|aMQKuD73_l&w&@q}YE%v|%mrL?2$mqk^P^g5v%WT~k@|ntzt7r=MWMeBj zyqBB4s$7G`^n%S-ljxfGua1f;<sPH&n(kU`3;= zp_aCkHZmIZ?n`tr0Ds-08|$V^9^C+8I7` z8uhLxYYj*t8wRi_CZ#`RDL|!#B+_rTM!mf>sg@~OS+*`7O6jK`V6ox8>AWOLt2X3;_tPIAPhT{ z4n_kjg76YV3*lHaZ3V0g5B;c>5035FzdDEK4^V)L!i(u9CwKh1`=tYdY2T5SPvNw6FL}+S6-DO5mVCQ%SZakis<2U@ctlJcM!_6yhv6(J1FxTHR+MI0#^1W4p?{xlR6;ul(mUfAQEQ6U;z;g zMWGx&5oXXY3j4FWVjcS}?++d}c*{9BQc{5hi(nqlcj8A&O|5dR<6M8%B;ykf;7SRR z%6Mi*ti5I*%U5bKW^%>S5=G@rdTncale2d4^sLql}qt3W^sL7r)f z<49VwcmM9i(RJ9hR4}(#sl8lM`;Jr(gy-sfzH^95=m|CVpxSq}QuonYn5^FUj`_~;D#{0i0EE$b>M7dwE$2?tA3FB}81ZmQ;RKfE($ImG>7-f7H`^Xns!?iP zA7@gYT2uNyRrRLs*m?ZhJn;t_D5(NAB}~}zdt7bfV$7D>P@`FM#O3qF+x77~5la(2 zGJfbdj%})(Ntvc;-*Z%nM&TQ>ldebSjQ@Xsm9iWV7DA_=hgP1iNsfT-3)zFp0dz(0 zX#GYdnj*BnTB>|+EX?_V{*`BH-ddB9<}-QyYaFqQ$7ZL$U-v;1KsAMpK7(&_sf3>{ zHX3s*-Jy8RL3PHcBWgO-(YeCp?wOAf??JHgOO`gvKZw`jORUk*y;Um@C>Dv$Y*S`@xLl>pL5Rqod(K z&4jh@TAOLdR88k`R@CJwmB-;H*sm*a=#b=-E0OGoaEf9YFA%wv(O@a{Dxz&IYx(=8 zrTt*$@$X;frDe+A&VEx}EgRis6m|30hS{M6T6a&T$C=gxy}DynSSZm<#nOeDE@f8U z`fZo?-`m(Hi=Ox`v&P8@rlvEcZVzQKR2A4c8~I`vSSd@kcQw+Mdq)L8iX zeREH@D@A;o^7g6^zP{_~dH8E{Jk}tklKhzTG2SjS zQ^>X@M&9(y`SqL>K;_25C|g^qp;w+!q5k5sEZz;ZdI{&crtC@udQ)Fs*>i}UT{af? zn5XX6LF1MV$(`CQRf%B>sjEhisP`W@;g*SqiB*8Ajp#dNlieQ74UIsaeVr0$4!>wfa>G}oN7`6xf* z=YHHBFGF^_g$4GFwaGbqW&+OXSRVZwyQ^~glATNs8?s~{tjc4r&}~i?)sCVXSh&3O z74`xj(!el}FuKQd@Q)ku?tS+~M})oxNz>HZ&M$wGuzxq!V<*xegxb|$Pt!fv{Y;`+ zoA80OQ_jkZPDVu4isRKa;#`0g@}Pxg&-?xQR&8AW*}J`els&p3fw0vgES%H}2zgay)}@Dbge7GyBz9QARn&*4eoQGh#9%7qVL( zoJNAf;bJJ-C!chs`>S!qxfrjOqulVT=Brh7xNA%Q!Ob6k85?teq#45m!1EF@Xa=w# z#F%(VBn4UrwM!3EDMm0nc=i?j@ehthSXf|SoUIYOKMhL0m zBH$Tl7K$JR!}{6h5v%YA;Ae$(G(xNL6}kW4hyBp+slQu51oq1iQ7z(t&`cS(9ymdU zyBpmnN)HQuaeVe1uY-VM>VivXIFe@=0vP)w0sP8B#Fn3d8|s*GY8FETR^dYdXx1Sc z$C;adBnijUy%507%gF*K^wnXY#_$$o3e+M!EFH@L=C%9~=a}O9RZ1@gQTj(2_xJ6w z!2(_;d3T?u5yekgA#ha{AchAiP$awUAg2jg_=So0JiYBSBCaEEMMh|{<%w@$^L-(JRyN`%Dms2_zi&)sMj%(t6j1-% g&|#?d!NMU$bj|msX`12me}F#~ga$lM-aPPs07CW){{R30 literal 0 HcmV?d00001 diff --git a/doc/charts/doughnut.py b/doc/charts/doughnut.py new file mode 100644 index 0000000..d0d489d --- /dev/null +++ b/doc/charts/doughnut.py @@ -0,0 +1,55 @@ +from openpyxl import Workbook + +from openpyxl.chart import ( + DoughnutChart, + Reference, + Series, +) +from openpyxl.chart.series import DataPoint + +data = [ + ['Pie', 2014, 2015], + ['Plain', 40, 50], + ['Jam', 2, 10], + ['Lime', 20, 30], + ['Chocolate', 30, 40], +] + +wb = Workbook() +ws = wb.active + +for row in data: + ws.append(row) + +chart = DoughnutChart() +labels = Reference(ws, min_col=1, min_row=2, max_row=5) +data = Reference(ws, min_col=2, min_row=1, max_row=5) +chart.add_data(data, titles_from_data=True) +chart.set_categories(labels) +chart.title = "Doughnuts sold by category" +chart.style = 26 + +# Cut the first slice out of the doughnut +slices = [DataPoint(idx=i) for i in range(4)] +plain, jam, lime, chocolate = slices +chart.series[0].data_points = slices +plain.graphicalProperties.solidFill = "FAE1D0" +jam.graphicalProperties.solidFill = "BB2244" +lime.graphicalProperties.solidFill = "22DD22" +chocolate.graphicalProperties.solidFill = "61210B" +chocolate.explosion = 10 + +ws.add_chart(chart, "E1") + +from copy import deepcopy + +chart2 = deepcopy(chart) +chart2.title = None +data = Reference(ws, min_col=3, min_row=1, max_row=5) +series2 = Series(data, title_from_data=True) +series2.data_points = slices +chart2.series.append(series2) + +ws.add_chart(chart2, "E17") + +wb.save("doughnut.xlsx") diff --git a/doc/charts/doughnut.rst b/doc/charts/doughnut.rst new file mode 100644 index 0000000..21b97e6 --- /dev/null +++ b/doc/charts/doughnut.rst @@ -0,0 +1,12 @@ +Doughnut Charts +--------------- + +Doughnut charts are similar to pie charts except that they use a ring instead +of a circle. They can also plot several series of data as concentric rings. + + +.. literalinclude:: doughnut.py + + +.. image:: doughnut.png + :alt: "Sample doughnut charts" diff --git a/doc/charts/gauge.png b/doc/charts/gauge.png new file mode 100644 index 0000000000000000000000000000000000000000..e48c41ce2ab10cc5c5fd9f3c5d7de083d934ca9e GIT binary patch literal 27943 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 o-ldN=^RLd|YnXmN<#ml*hJAJQ5}!{74*K&>> from openpyxl import Workbook +>>> wb = Workbook() +>>> ws = wb.active +>>> for i in range(10): +... ws.append([i]) +>>> +>>> from openpyxl.chart import BarChart, Reference, Series +>>> values = Reference(ws, min_col=1, min_row=1, max_col=1, max_row=10) +>>> chart = BarChart() +>>> chart.add_data(values) +>>> ws.add_chart(chart, "E15") +>>> wb.save("SampleChart.xlsx") + + +By default the top-left corner of a chart is anchored to cell E15 and the +size is 15 x 7.5 cm (approximately 5 columns by 14 rows). This can be changed +by setting the `anchor`, `width` and `height` properties of the chart. The +actual size will depend on operating system and device. Other anchors are +possible see :mod:`openpyxl.drawing.spreadsheet_drawing` for further information. + + +Working with axes +----------------- + +.. toctree:: + + limits_and_scaling + secondary + + +Change the chart layout +----------------------- + +.. toctree:: + + chart_layout + + +Styling charts +-------------- + +.. toctree:: + + pattern + + +Advanced charts +--------------- + +Charts can be combined to create new charts: + +.. toctree:: + + gauge + + +Using chartsheets +----------------- + +Charts can be added to special worksheets called chartsheets: + +.. toctree:: + + chartsheet diff --git a/doc/charts/limits_and_scaling.rst b/doc/charts/limits_and_scaling.rst new file mode 100644 index 0000000..5d1b411 --- /dev/null +++ b/doc/charts/limits_and_scaling.rst @@ -0,0 +1,60 @@ +Axis Limits and Scale +===================== + +Minima and Maxima +----------------- + +Axis minimum and maximum values can be set manually to display specific regions +on a chart. + +.. literalinclude:: limits_and_scaling_minmax.py + + +.. image:: limits_and_scaling_minmax.png + :alt: "Sample charts with examples of axis clipping" + + +.. note:: + + In some cases such as the one shown, setting the axis limits is effectively + equivalent to displaying a sub-range of the data. For large datasets, + rendering of scatter plots (and possibly others) will be much faster when + using subsets of the data rather than axis limits in both Excel and + Open/Libre Office. + + +Logarithmic Scaling +------------------- + +Both the x- and y-axes can be scaled logarithmically. The base of the logarithm +can be set to any valid float. If the x-axis is scaled logarithmically, negative +values in the domain will be discarded. + +.. literalinclude:: limits_and_scaling_log.py + + +This produces five charts that look something like this: + +.. image:: limits_and_scaling_log.png + :alt: "Sample charts with examples of axis log scaling" + +The first four charts show the same data unscaled, scaled logarithmically in +each axis and in both axes, with the logarithm base set to 10. The final chart +shows the same data with both axes scaled, but the base of the logarithm set to +``e``. + +Axis Orientation +---------------- + +Axes can be displayed "normally" or in reverse. Axis orientation is controlled +by the scaling ``orientation`` property, which can have a value of either +``'minMax'`` for normal orientation or ``'maxMin'`` for reversed. + +.. literalinclude:: limits_and_scaling_orientation.py + + +This produces four charts with the axes in each possible combination of +orientations that look something like this: + +.. image:: limits_and_scaling_orientation.png + :alt: "Sample charts with different axis orientations" diff --git a/doc/charts/limits_and_scaling_log.png b/doc/charts/limits_and_scaling_log.png new file mode 100644 index 0000000000000000000000000000000000000000..7bb63ce009ad55aab88a52d92926dc940cfa04e9 GIT binary patch literal 57320 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 V>f#W2C*Fp1Tv6?C2Ib7<{{=Jc?z8{^ literal 0 HcmV?d00001 diff --git a/doc/charts/limits_and_scaling_log.py b/doc/charts/limits_and_scaling_log.py new file mode 100644 index 0000000..9699a49 --- /dev/null +++ b/doc/charts/limits_and_scaling_log.py @@ -0,0 +1,67 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + ScatterChart, + Reference, + Series, +) +import math + +wb = Workbook() +ws = wb.active + +ws.append(['X', 'Gaussian']) +for i, x in enumerate(range(-10, 11)): + ws.append([x, "=EXP(-(($A${row}/6)^2))".format(row = i + 2)]) + +chart1 = ScatterChart() +chart1.title = "No Scaling" +chart1.x_axis.title = 'x' +chart1.y_axis.title = 'y' +chart1.legend = None + +chart2 = ScatterChart() +chart2.title = "X Log Scale" +chart2.x_axis.title = 'x (log10)' +chart2.y_axis.title = 'y' +chart2.legend = None +chart2.x_axis.scaling.logBase = 10 + +chart3 = ScatterChart() +chart3.title = "Y Log Scale" +chart3.x_axis.title = 'x' +chart3.y_axis.title = 'y (log10)' +chart3.legend = None +chart3.y_axis.scaling.logBase = 10 + +chart4 = ScatterChart() +chart4.title = "Both Log Scale" +chart4.x_axis.title = 'x (log10)' +chart4.y_axis.title = 'y (log10)' +chart4.legend = None +chart4.x_axis.scaling.logBase = 10 +chart4.y_axis.scaling.logBase = 10 + +chart5 = ScatterChart() +chart5.title = "Log Scale Base e" +chart5.x_axis.title = 'x (ln)' +chart5.y_axis.title = 'y (ln)' +chart5.legend = None +chart5.x_axis.scaling.logBase = math.e +chart5.y_axis.scaling.logBase = math.e + +x = Reference(ws, min_col=1, min_row=2, max_row=22) +y = Reference(ws, min_col=2, min_row=2, max_row=22) +s = Series(y, xvalues=x) +chart1.append(s) +chart2.append(s) +chart3.append(s) +chart4.append(s) +chart5.append(s) + +ws.add_chart(chart1, "C1") +ws.add_chart(chart2, "I1") +ws.add_chart(chart3, "C15") +ws.add_chart(chart4, "I15") +ws.add_chart(chart5, "F30") + +wb.save("log.xlsx") diff --git a/doc/charts/limits_and_scaling_minmax.png b/doc/charts/limits_and_scaling_minmax.png new file mode 100644 index 0000000000000000000000000000000000000000..0db9c0b9bcfe1856ba2107b6874183d8eb7134b4 GIT binary patch literal 22311 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 literal 0 HcmV?d00001 diff --git a/doc/charts/limits_and_scaling_minmax.py b/doc/charts/limits_and_scaling_minmax.py new file mode 100644 index 0000000..4f41e6b --- /dev/null +++ b/doc/charts/limits_and_scaling_minmax.py @@ -0,0 +1,42 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + ScatterChart, + Reference, + Series, +) + +wb = Workbook() +ws = wb.active + +ws.append(['X', '1/X']) +for x in range(-10, 11): + if x: + ws.append([x, 1.0 / x]) + +chart1 = ScatterChart() +chart1.title = "Full Axes" +chart1.x_axis.title = 'x' +chart1.y_axis.title = '1/x' +chart1.legend = None + +chart2 = ScatterChart() +chart2.title = "Clipped Axes" +chart2.x_axis.title = 'x' +chart2.y_axis.title = '1/x' +chart2.legend = None + +chart2.x_axis.scaling.min = 0 +chart2.y_axis.scaling.min = 0 +chart2.x_axis.scaling.max = 11 +chart2.y_axis.scaling.max = 1.5 + +x = Reference(ws, min_col=1, min_row=2, max_row=22) +y = Reference(ws, min_col=2, min_row=2, max_row=22) +s = Series(y, xvalues=x) +chart1.append(s) +chart2.append(s) + +ws.add_chart(chart1, "C1") +ws.add_chart(chart2, "C15") + +wb.save("minmax.xlsx") diff --git a/doc/charts/limits_and_scaling_orientation.png b/doc/charts/limits_and_scaling_orientation.png new file mode 100644 index 0000000000000000000000000000000000000000..26f13ccbc5b20894515adba7af90e5ab1d1f5953 GIT binary patch literal 75148 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 literal 0 HcmV?d00001 diff --git a/doc/charts/limits_and_scaling_orientation.py b/doc/charts/limits_and_scaling_orientation.py new file mode 100644 index 0000000..c80c256 --- /dev/null +++ b/doc/charts/limits_and_scaling_orientation.py @@ -0,0 +1,60 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + ScatterChart, + Reference, + Series, +) + +wb = Workbook() +ws = wb.active + +ws["A1"] = "Archimedean Spiral" +ws.append(["T", "X", "Y"]) +for i, t in enumerate(range(100)): + ws.append([t / 16.0, "=$A${row}*COS($A${row})".format(row = i + 3), + "=$A${row}*SIN($A${row})".format(row = i + 3)]) + +chart1 = ScatterChart() +chart1.title = "Default Orientation" +chart1.x_axis.title = 'x' +chart1.y_axis.title = 'y' +chart1.legend = None + +chart2 = ScatterChart() +chart2.title = "Flip X" +chart2.x_axis.title = 'x' +chart2.y_axis.title = 'y' +chart2.legend = None +chart2.x_axis.scaling.orientation = "maxMin" +chart2.y_axis.scaling.orientation = "minMax" + +chart3 = ScatterChart() +chart3.title = "Flip Y" +chart3.x_axis.title = 'x' +chart3.y_axis.title = 'y' +chart3.legend = None +chart3.x_axis.scaling.orientation = "minMax" +chart3.y_axis.scaling.orientation = "maxMin" + +chart4 = ScatterChart() +chart4.title = "Flip Both" +chart4.x_axis.title = 'x' +chart4.y_axis.title = 'y' +chart4.legend = None +chart4.x_axis.scaling.orientation = "maxMin" +chart4.y_axis.scaling.orientation = "maxMin" + +x = Reference(ws, min_col=2, min_row=2, max_row=102) +y = Reference(ws, min_col=3, min_row=2, max_row=102) +s = Series(y, xvalues=x) +chart1.append(s) +chart2.append(s) +chart3.append(s) +chart4.append(s) + +ws.add_chart(chart1, "D1") +ws.add_chart(chart2, "J1") +ws.add_chart(chart3, "D15") +ws.add_chart(chart4, "J15") + +wb.save("orientation.xlsx") diff --git a/doc/charts/line.png b/doc/charts/line.png new file mode 100644 index 0000000000000000000000000000000000000000..17703b6727fcf83cc01a78a4271f55be2918b067 GIT binary patch literal 125459 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 W5+Gum!PFlB literal 0 HcmV?d00001 diff --git a/doc/charts/line3D.py b/doc/charts/line3D.py new file mode 100644 index 0000000..1fbdcfb --- /dev/null +++ b/doc/charts/line3D.py @@ -0,0 +1,38 @@ +from datetime import date + +from openpyxl import Workbook +from openpyxl.chart import ( + LineChart3D, + Reference, +) +from openpyxl.chart.axis import DateAxis + +wb = Workbook() +ws = wb.active + +rows = [ + ['Date', 'Batch 1', 'Batch 2', 'Batch 3'], + [date(2015,9, 1), 40, 30, 25], + [date(2015,9, 2), 40, 25, 30], + [date(2015,9, 3), 50, 30, 45], + [date(2015,9, 4), 30, 25, 40], + [date(2015,9, 5), 25, 35, 30], + [date(2015,9, 6), 20, 40, 35], +] + +for row in rows: + ws.append(row) + +c1 = LineChart3D() +c1.title = "3D Line Chart" +c1.legend = None +c1.style = 15 +c1.y_axis.title = 'Size' +c1.x_axis.title = 'Test Number' + +data = Reference(ws, min_col=2, min_row=1, max_col=4, max_row=7) +c1.add_data(data, titles_from_data=True) + +ws.add_chart(c1, "A10") + +wb.save("line3D.xlsx") diff --git a/doc/charts/pattern.png b/doc/charts/pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..de5ead78a10aec4cb06c19d5eab1fdd0397660a5 GIT binary patch literal 29891 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 LGJ@A=I7a>lb$jet literal 0 HcmV?d00001 diff --git a/doc/charts/pie.py b/doc/charts/pie.py new file mode 100644 index 0000000..bb372d7 --- /dev/null +++ b/doc/charts/pie.py @@ -0,0 +1,68 @@ +from openpyxl import Workbook + +from openpyxl.chart import ( + PieChart, + ProjectedPieChart, + Reference +) +from openpyxl.chart.series import DataPoint + +data = [ + ['Pie', 'Sold'], + ['Apple', 50], + ['Cherry', 30], + ['Pumpkin', 10], + ['Chocolate', 40], +] + +wb = Workbook() +ws = wb.active + +for row in data: + ws.append(row) + +pie = PieChart() +labels = Reference(ws, min_col=1, min_row=2, max_row=5) +data = Reference(ws, min_col=2, min_row=1, max_row=5) +pie.add_data(data, titles_from_data=True) +pie.set_categories(labels) +pie.title = "Pies sold by category" + +# Cut the first slice out of the pie +slice = DataPoint(idx=0, explosion=20) +pie.series[0].data_points = [slice] + +ws.add_chart(pie, "D1") + + +ws = wb.create_sheet(title="Projection") + +data = [ + ['Page', 'Views'], + ['Search', 95], + ['Products', 4], + ['Offers', 0.5], + ['Sales', 0.5], +] + +for row in data: + ws.append(row) + +projected_pie = ProjectedPieChart() +projected_pie.type = "pie" +projected_pie.splitType = "val" # split by value +labels = Reference(ws, min_col=1, min_row=2, max_row=5) +data = Reference(ws, min_col=2, min_row=1, max_row=5) +projected_pie.add_data(data, titles_from_data=True) +projected_pie.set_categories(labels) + +ws.add_chart(projected_pie, "A10") + +from copy import deepcopy +projected_bar = deepcopy(projected_pie) +projected_bar.type = "bar" +projected_bar.splitType = 'pos' # split by position + +ws.add_chart(projected_bar, "A27") + +wb.save("pie.xlsx") diff --git a/doc/charts/pie.rst b/doc/charts/pie.rst new file mode 100644 index 0000000..46ff973 --- /dev/null +++ b/doc/charts/pie.rst @@ -0,0 +1,45 @@ +Pie Charts +========== + + +Pie Charts +---------- + +Pie charts plot data as slices of a circle with each slice representing the +percentage of the whole. Slices are plotted in a clockwise direction with 0° +being at the top of the circle. Pie charts can only take a single series of +data. The title of the chart will default to being the title of the series. + + +.. literalinclude:: pie.py + + +.. image:: pie.png + :alt: "Sample pie chart" + + +Projected Pie Charts +-------------------- + +Projected pie charts extract some slices from a pie chart and project them +into a second pie or bar chart. This is useful when there are several smaller +items in the data series. The chart can be split according percent, val(ue) +or pos(ition). If nothing is set then the application decides which to use. +In addition custom splits can be defined. + + +.. image:: projected-pie.png + :alt: "Sample pie chart with projections" + + +3D Pie Charts +------------- + +Pie charts can also be created with a 3D effect. + + +.. literalinclude:: pie3D.py + + +.. image:: pie3D.png + :alt: "Sample 3D pie chart" diff --git a/doc/charts/pie3D.png b/doc/charts/pie3D.png new file mode 100644 index 0000000000000000000000000000000000000000..65de8e3869930262f70904f68d0b1711a685e679 GIT binary patch literal 40590 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 literal 0 HcmV?d00001 diff --git a/doc/charts/pie3D.py b/doc/charts/pie3D.py new file mode 100644 index 0000000..bcf2a42 --- /dev/null +++ b/doc/charts/pie3D.py @@ -0,0 +1,32 @@ +from openpyxl import Workbook + +from openpyxl.chart import ( + PieChart3D, + Reference +) + +data = [ + ['Pie', 'Sold'], + ['Apple', 50], + ['Cherry', 30], + ['Pumpkin', 10], + ['Chocolate', 40], +] + +wb = Workbook() +ws = wb.active + +for row in data: + ws.append(row) + +pie = PieChart3D() +labels = Reference(ws, min_col=1, min_row=2, max_row=5) +data = Reference(ws, min_col=2, min_row=1, max_row=5) +pie.add_data(data, titles_from_data=True) +pie.set_categories(labels) +pie.title = "Pies sold by category" + + +ws.add_chart(pie, "D1") + +wb.save("pie3D.xlsx") diff --git a/doc/charts/projected-pie.png b/doc/charts/projected-pie.png new file mode 100644 index 0000000000000000000000000000000000000000..b87b340a023b0ea3925cc5f1d79d659120cb4096 GIT binary patch literal 60325 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

=3Y&~8Qez1= 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

#+77^hKfidB%g#D>rX-+=_HGF+AP&2T@T9- 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 CilKo3 literal 0 HcmV?d00001 diff --git a/doc/charts/scatter.py b/doc/charts/scatter.py new file mode 100644 index 0000000..c4ad4cd --- /dev/null +++ b/doc/charts/scatter.py @@ -0,0 +1,38 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + ScatterChart, + Reference, + Series, +) + +wb = Workbook() +ws = wb.active + +rows = [ + ['Size', 'Batch 1', 'Batch 2'], + [2, 40, 30], + [3, 40, 25], + [4, 50, 30], + [5, 30, 25], + [6, 25, 35], + [7, 20, 40], +] + +for row in rows: + ws.append(row) + +chart = ScatterChart() +chart.title = "Scatter Chart" +chart.style = 13 +chart.x_axis.title = 'Size' +chart.y_axis.title = 'Percentage' + +xvalues = Reference(ws, min_col=1, min_row=2, max_row=7) +for i in range(2, 4): + values = Reference(ws, min_col=i, min_row=1, max_row=7) + series = Series(values, xvalues, title_from_data=True) + chart.series.append(series) + +ws.add_chart(chart, "A10") + +wb.save("scatter.xlsx") diff --git a/doc/charts/scatter.rst b/doc/charts/scatter.rst new file mode 100644 index 0000000..6fe0c35 --- /dev/null +++ b/doc/charts/scatter.rst @@ -0,0 +1,22 @@ +Scatter Charts +============== + + +Scatter, or xy, charts are similar to some line charts. The main difference +is that one series of values is plotted against another. This is useful where +values are unordered. + +.. literalinclude:: scatter.py + + +.. image:: scatter.png + :alt: "Sample scatter chart" + + +.. Note:: + + The specification says that there are the following types of scatter charts: + 'line', 'lineMarker', 'marker', 'smooth', 'smoothMarker'. However, at least + in Microsoft Excel, this is just a shortcut for other settings that otherwise + no effect. For consistency with line charts, the style for each series should + be set manually. diff --git a/doc/charts/secondary.png b/doc/charts/secondary.png new file mode 100644 index 0000000000000000000000000000000000000000..41599c17d5ea44a8db7af453625cfc9936f377c3 GIT binary patch literal 21534 zcmY(rV{~QF5;YpLW81b)*hxCJZ6_Vuwr$(CjgD>GwqNdd@At-gf6h5I#@-9H)?PK| ztlB3`PDT_C1{($l2nbGGOh^F;2)O0v_Zbw#&nM9htn<$ou!DlA08sT5?kNxuKajZ4 zA0=1d%PdGO#bt~UA3s7#emS8&zXlC;VvQyZQ|+p~$|Ys1^NX2g9}5dj&2^0?K`X1K ztEwuG#igzxzcJ(%e;8^gF*y*@*zYa|umI-x*gy=9=Xn!lrVfVhzT~#tMDvI5A80B>%0k&x7~$ zs1|aqguCAoLcUOlz{LDuw1NCVTAU6m6skraiVe`=Q909!)-KmuK?-F?$COwRy`S&T zVP$oCM@xcIQNI$EMWw>ZZlN)IhBI-idjb!7zswUg0P8-^D9jSC6*^j4xO@Bi@?Gvu zPV#RSs33#$l$iI*0#f(8jAOt|Y%s%~Nr>~F91@<0I5-iJk-wBm2|F7(8(`RAqI%+} z8Ub7As-DzTIi}-NQ^6r2kpBMuFW~?0P=y3@C6cH)Jf13t=?843eGQvb@seRCngvs~)hEQ-kCO=!{I?Z7B(RrE9D?YD6NazmV$P$nXquMJ2+thk z|J?o`2iBqNEXAj@1FBbEaBx)}JyEvSRw%mKn4l8>+eNQEl-W|7-H^!up!#PBmdMT1 z?CuWxh?GeG@qMW85aP~QWR3AY%+Cn#wpQ#th>(WK9S}|e{<}D#zAMl?6*G-!88=JA z-27wG7cFrIB>(NY3p&<*9tR8U{~Zb8A^8)x(B1!9-U1VAJB^Bj`9F*NpLSyHQQq;0 zU;j6C8<35{cw{6>ZIH2Fq{XBpzq9Bx>}*AppTiqPiX5+@nqKaFB6`lj>Ci6wdN!iy>-#92z{nZ=u6Bal8{8YYc-yQu_4m(tT z?whmc{ffNFS;Hl&w|q9_OVbMZ!m=cd;$Yih@L!dyAcEb{efy>?^GryH(Eq1ZR@yQe zsxea~kUN@;n<;aZqUmqjubpSJGwTe^Z(>rRYJQ6ExRqyhnhUJtsAZnN<}0N)u;o(a z{wUlw%FEEs;Mye)LcRS^GcCbEo3-`C)x^J*Pb9#<(QhUdEzh=;AMwJk$(xNJfyKN_ zqyJz!oR~gb{f5`1=fe}(Z(}=l*>ojFJsJ@V@frHH#_IjTCM)oxC#3jj{Qly#q33bDB4v591xCB1G+pfF z3raartK-FZ5mM!hhuhFziFycaarx`aP=}s_j4al!eQhT{SB zC$zIY9z~Mxi3-&LOS>18thMu+n=flnD zmKOh-%`Nu>5YbL1qvUgqyg>j>Z~F)R-2wxU%iey`lV^RCTM%D(Tm8>HpFyzf3pUQ! zqn4I#p8d!34aZiNug|QqS#@^{Ry?iV=p|n-P>QcTz$$w0X{g;Kq^m){Q%AN`D&4F9 zysse#GQ8u?_pka0*XY`Es)t#vH~%5)2*>JDe=426eLR!&J*P=!osGy^iE(;2Ttutu z3D1DMx&6~TZ_V4zb`(99BMUx?&Q-!qe?CVaZ)-4$t@0Nrm3Y|Cs=}61%g>7s0tW80 zbHD!Pl@=iiH7M(NIm>If);oJx@5`$9Kvur&raDKfN#1;5 z{yaL~I39%3-pUz4kb|z2D;gTnK$uSp)JQ`~cLau3Rh|-co};<%?e-+vRE?TjDx9pu z?9-)q|Lq5#^D&%iu(b|-CeF7Gf)er*%y$8ure2pN-ufct1S9Rc168!>mPoon_*B9Z zF*!NB$H+UzRxZ7&AXDEu02Wr+bZ;Bo(`lSJ(676{ zG6nU^vyGa&+Yg0%fou!G9%K`lj2CF2+(BvbQ&hxrh7-SZ$}xd+wT9E zb7`9M$(Zk8<(>a8Q5KN3?j#Z;+k<$v$>o5$_O=+@5EVGt_8B&_9Ddeq~^>fmi2rsAU(k)r?RhX4PNuxJHnZ zUsM0YWC!QuJgvucF4)z<#B}1_k*_&Rf*-Wxa%D7_lv@@jfLzP8h$=Q&v%hXxEwVgg z+hk@-2fR&ka-$`^9h0Yr!Q1GrirW!1EB%||mkn2>JEXMsJ-gyxH~cp*1VZCzoDL%P5?jfjJoku%><81x;JB(>fEfMtU#ga&i_6JT^90+gEa1t7*G%&QK@Yv-J|Ii&dZ3#23fYS9G21IO4r*6` z$w124!SX%bOXQ}1);d6Mv&pc3lwjMsAi*@rv;K6W8aG8IXRwKajp?bP$Wk*j@uh;J z{V%Y)>?A$V-B1g19Q8It=)H}5^yp}DF$e%st0_iM>YHZd{5AZgsi zdK-~G>TP8wNTi+qXT;$u_;~A+t&yWu+$62@{1@Se_l62j#qY%aA&Im6lTDuL_IqFBZ1*8!W6yMp zF+Nku(ooikDL!Z{G0QT~2;SK)Ro5F(my+OD7~~n%msjsoY%N_ti=x|KKXB2)KD*N| z=Th}H2?oL;9#8RcLL0pg!b166UDxSvO>gv^II*4)NH8rpP>Nq-M%*=+atRLbkk?|f z?)LP%zjygUF80$Z^&*`5mpQ%l8?$WMO{dDGkDS#uhc-tGhf5?ZqBLOJ=cI{tH#)c3`qO1%+60)!yM@e3|O%S{h4w(NMxz2$K-M^6V?FNjt2$jK)ME za)by4y@BBG4Q*IUQ70S7k4}H2$1&zYTzvHlWFYN>pZH}Q{EKQdO^Ty;Y7|fA)k=3^ z67yf!GMy;+%KGZzqk74c<03`JBLJpg-vb|X0?0;_Lm2+|i%kQ9MK^ke4F^*(mP zbbn{XwP+rAPY}sB!8$>4@`F+X0fuoigmwutYE%Q+!rK}#Z~jj!{GDKCSsAX^aN;8W zMb{9>goh@C9Mu0a_sD+GYv*$?>Hp9x=no!cLBvJ-pLzRpHTjl@VEq5ktKUy7z=Dbn z|DWmlAM|?NMKt<9RLu`kxbX{2bm)Izw)5xeu;;BT$;a_0zUz1P=1ifG* zF^qzdh`)aTuNUA7ur{$RwZHi4uCSFMK8?|4cP#|{56eiT0iiOzQKxux31}Qp(h{4X zQr#?>LYL|lNX+;Z0nzm5Bhc(|9d9pYYd3BJaGo#GL_+mIByF^ljVE*Hoe zA9kQ!ot?J#(`iuYZV*kbt~D~hXRr3uv?OsNj;0|B4N}~4xwBotz|y23RM8O={aC?hb zJgER~f{Z$w*&+O%J}Swn;;@p6IbE+$_jACzP>?apdQ65*(KJh zGo!~1vsgduaEun1>fM@hXFPA>DTPY~7kE6?fuR@J z=T%_%-%}9*M8!^r+4$4#f7q^Z01#D6y&XXr#($rRTw&kD4D?@XWIrds9Mr$s#uzSQ zAeXx1Ylc6)GffBln{(C$&v8#J zYl@LDUD?ZesI-y2QD0l>*o})+8yU-fcoHn98U}>qD&Z>%M!9BJPkt@!Ie&)pf%FB& z##VCDFo85faS(-uKqObC!8R)xD@+$yvp8S(NOo)_WfDF}_l6@1f(*=pD8|(dc8|@_ zYs&}8(kLRGhz{%}u%$MUxxOfPFYh&sL`q6ZNKOvX2Zu+6?DF@wzK9t+1q%u%Ehbq} zy>PBSa|0?@;b0jX%S}(tn|}yxkq{UMFl?Gb0?CISSZDhvqT3a&7EX(>6@^70{wyCd zJA&`OmVS!T(>_Kr;Klus*ub2S0|io#H_KQSJx0>#ha@A3qoeW;?>S?~t|mx*<%~U{ zDpeRum7MR6g@px>bJs5;uIg-%PSIJ12cC>Ok74G-j63}5D==b<5#m*GKWL}+f^|NZ z+hE|hREGO@o~TcqQfrrU-mfHjSYy%}81n=)j5O@U(jgkq zw%<%(HBCig8wNf`^VK?i0X{CoY###Ir3$7PtuK(orz=EaS6jiV3dH43=W#As&O$Of z6ZcE;z|i`orLG`FgD`oEuq~krlN>^uKKNnWTb>=&!{ zyGg(N^$4+0yDhX562aDC_uBRL{5ox}B33opnKpp`-lIf6BP-~ag=S{W)&_GRIn_81 za06va>TlhAnU>REN4SM$>4ffwnkPKYn)Dl&Y7J&w&06CEId2!soo0^d!3zY)6BvR>xTm-5=tLh86zOZ=@HL;M|1x%`DBT@dP8&cJhzXV!2Hm@_{ z(qSR|a^E;)cHqp$5x)*-zw|dt;uC50__RP!+@#>lb zmqklbWnJ(dHP}NE=V~SNJOS@@d82Ty{~{%|K0f6~*>hLy6aV0XX$)lrHc>;?!^nhI zo>9Q(iNO%}Lh;~{Fr&dAqL9|MbIZc^eYFY7b25P`_PZG-FxnY$JG!59Ph3Iqw53OG z)QBpI3p`?n`SQ@iw=fo|m!=MPA%@J}Vusfsf+)|MdV>#eVI0%zAo9u>QYDtb&PawHL2 ztQ<|s_;AN`W*)bp!4S>cXUcmIn)Zd(me*c!@NH;F4?EQ*S-hOl9e}bOA35*&^gpTL z6}}rS;CBYJ9u}Bxv+q3L^DN-9f}v?`K2&E;54gp+Bd9z*$&D!>br{rbImrRvqnLRe zr|P}snCXHqZ|E@g0JYHtGn@s=-)`4YA)xiEWQr`eIOpCBK^2jcvss#ekS|j_HD%;e zDCXUk#i%XE-(1%V3t;G8pV3qjst#U_oLyd^YTk<@6sBEw!%w1_zy8x|c5_^;_gu)U z&aYolsrJ_x*bRg!4oAk{B2d&s7OuH9#8) z-keG!XOi}3GbdYa7rv{KQHuovM8R_W5BJ4QURy=k$3+c8wc3m=3Pl2m4i}&|Hf!N! zqB45B+e?_rn_IaiPTh2EI4Sikl(#R^Sa zrP%O_rVXGwV;!ND55#wP{|K0OYFU288_keW^A9uWng27FG9N{6d%6!C{G`NZI#ekm zg;P|IpwLT>Wq*3q8Hv7dEF|R(Uie`P&gRx|+Z;g-n_9cani0WB60h>qYj<@NEQq(v z5rIpVTUAq~x2;^~-2c)-!aFnp`A0Z5=|xuNg{~l+;k1pBut>~0b{3H#(aI4FMsv9E zH`s%T7e$!4#)hQZ2l&!FC+~xW6OuLdKT%K24a(2b1he;8MJhcJ=HI+=EQ=(E_3ttn zwB5a2{)?#b3C27K^Ns{lP&h0HFZ=P4_wOr#yHgZ@KPa0NOzxT z^B0ivF7(Lvj%Mhk)T9-6&JvLa6J3SsVan+gZw@MaSnFniP7SPrG2|tppDC zM~6lS3``brV(Y_xuQ#ROx(IY+GF}GOgjH8+0sAqAOW~7~4B$pJg^@Z#H zbw%XzzHVpFTa!*E&MG()-bF=(kkDU`6{r+4AcfuPIZrIU`hf5VMweqN%FZt1~PUx_X zRHU5FKHF@|EPs41+HVVUd6U;2Htue>__z@{|AJW8XvH5-q-yd4GI`LVdOpyrC;jg@ zS8q!*l><=Ub$d)= zwW39)5-38`SzybzY#1vr629qCxRV49=XuV9OlF=z-v1DBq7b{1j6NU;$fWP;;=KJU z_8_(P;!fn_<4pfIT~LXO^_6%g;Tu%(dc&f3j379Rj==c7zbw~Mk2>agC5AerDOqoK z>!OPrMzsw;SoAAs=53OM1&Q@O@-3=XQMR#z1ATdJINuFe@H#yxciQ`G4K8AAgjSbq z0-8zHlDb($YBBJ0&ZO@T85#|Yt{nOz20g7NzHjtq?phmGJmj;VgQ+jNLG1zy*(h;k zP557x`$&ug>F4p11?y&|iHOL;*@z3#xKN|{E{vdKQc;wkhPoY^VpVI}nG+k9#lZe0 zZ~tgZ8cH-PI;xT2XD|7`k|5pB+7a|Pi+OfRW&u}!*cKN5OZpbfuV2wwk$e;4<880E zew#(-O#gi~ZT;(m8(}-C*j0Buz*bAxB8*~W=cmD@dgKq0m9d1j|!V|F|-@ov9jTf%#&6#Rw5oYQQBA% za_GqL>0F7(8|>0!-w|yrSa-N3S@Q)-)PWil$=w2eO~&BO0_r)-PBxFYv()5S9Yt{k zBQ0lH{I}ewsW_{z6u_V_9@w7l4clM$msYXgoz$K+50Vyk)Ewe)2{ETiN0As8UdPMs zf!|`ciuN)O(zBCCz9Afs=hOdY)8*ShqNGo5b0g|6CmT~MuFvwJkufw<)$3pn9m{nM zo0(ZyYeZEo8jitXpEQH?JBWH%SZkMa5u%Q$rF}A*Tf7WDhPT+wmn$45?V9yyHNi0Q z@@s3{41x1RsKl<^Dx>=7Y@q&zkjB`iRF*%{APoc%uHMp*(Rjvu^yu1o>Z( zE=x6kj=EfZtB@*K-ayq>leiiYAvhd`fSWZz%O|-RH{_ zS(lBtfXZ>8k!t&k(fFJeNynSc^8e?+4G$vo&8@tO^qCmM1`cJNZ{70dx*|up3QdX_ zMDD({%$am{YA_@2aQfp?3y;$IPn|V3rjeO^`rG);uDsBmOkJkiUn14o&u6W6QnL`u z5*sjl{tq`ttvQw<^90KR*H7*UxK5lEp7$N~v^g!j7VE);6-@n{FH9N2`&~g&e5H=n@~r}lLNr)4R&&=! za>~=lKNZWnCl&a$k14$cm&zAyyyg6&@Ih?Z)3ZYQX(yeOP@&WZQC+5reS+e_c$%=i z@kf8cCB%$GqXHdbq8V{QmzDmtRBN^h_=FS3dLP5@0V<@MXj-fgT5?mu$8^YR7I8Vi z{-)3~FB?4DTKtD?Wf15o=Lw`;RB&>kwYY&QO`MDmFQV}X1d@@mZ?oHRm;%iy9Mrn7 z5mNis;YzE4(88w3=ftuX6C^jvd$6puIO0EPnZ#vX@*tgzXG(c?{nGbT*AO8lo!}?s zW}EM?%TV>So^*ED<=KrW^$&qGhsQV30T!CWRyA@O?qtB^wFMuc6QRNq@vVuI+O_*s zySrfN?e``X+cgR`*w6A>niXmgxp}INd3$VX89wQ`pq`c<6@WTnVz$zYS*gjFH31Aw z$sQMI?!T^}rG!-rrWO8z$Ys*vj=tZhx?U&9SrX)&lpaarz_XfLS_U2i2Th!Z)Svyl*iFd&1n++s$>8TvE zc0U~c*Oc+V(7Xrw@X89QV~vK$=uE}?LW_8BzYq?G>DM*iac14Fsd610{ppU1pN!!> z*x=&Bu~b!II9i_(r5RdWUj)3-O6vS7z~RjeyKxd2Zw;nUKpf}Hr0{P7!%}iA!O;Bg zD4z6*NSb|Gg70YvS&@7I@R9#7@(mH2S?}yJh$Lm z2v65k#bDY;VDSRk?W^TXJCk?NWS1bku(rNFCA%-~R|b~b-Pj0oNwFhe0W~7bhE?zm zwi&crv}db~5%v*VAJEjTi$S^#CO7x@9^Qt>>(!kG@;efOYG1~55poEJOD4K|%}H<% zd8*j`8D-K};p0_JTpBJL!H=;cn{0~X$*aF&=B85QZw}Tv(QwZfnkJ$X#muo(dN4xO zcYPZ_DN-NX*3Q(11R@vAerIxK=*es&_(aB}Xwl2lD6MpYN%0*g+X2sSr_w!)RSVig zTCJv5S&!!&+=blSF}y`+tUs^cq>0wr-qF;8#td#|fYWY{Q(uH+EvxQ5x<|j#Fp!9T zNT&O!-w*b5ZfeBx4fR`FRS^x`zO0e;wcSk$`#>R$R9RZWjO$cSQ`8bj!JtD(*fuQ2 zfcrJ=kVd?r(Fa(W?`!bSc>}Bo-k=t(zrsdYWJnbA0v#HVLR&TQ`q(=3 z?{!0N%q&T|mS)h_$h8?cffH!h-8?au4uPa8EC)v0`lN`k;;VNGmI|2mjF5Su-9r(q zbbITsWEm;tq?oca^CmZ zcZ?s`GnvYdoK8^W*VL@eG-9UejZFmV4I>bH0q?@PLc_x#o-F!v>HzW|pHxNc8~xFG zB5}|B7RtS3?t+^?xSjPc2%FmED&VS+O3qG;@g2NJ`*kt|LSM3Ss_oJpe$rbBCRM$0 zp>0us=TX&SJb)KW1(+(}>gK`mOZT((y+hhq(WBNa*3==ks}PXs4gYLcVWOx%*#Jd` zy?y!~!FsAF20QISC&Of`xq=3(Ygum~imm0;UZ@6LsC zqWO8g^bo*1DcgH621lzUt%}ZWJ2tqwGVes+4qzVSS;vZ69kY2&Lc6iziRaY33V~(A zOQn)njrn1_%pmN<3{bMcsuSis{BRn# z0I7RrBA8AqeI^fy@0SqtDgVVFXrPO+kFjMil65;i;vP7QWS+}cB;NaAW8E5%GvbrI zJaQwr-WwU8E%`_La5`(8yAOY73ZCwE5)^H%15`b&`KGz)&m{AY-0D1JK)KouxXSAK zQ4tO;^sTlg8CKyDmK7ZSX$l+!iXKl9-<}Y)_RQrOC26>xK5)0hd7ib-z+Qp%=6l&m z;OGs-6^I$zxVmaZJyIcMH)TF%j?fd|Rise5J!n6c zQfO|aYc}Q#knB@gE<=(ayM;R3tHD8i3W>93)dAWlO;Tzg)ciJ0|+s^I}JO1i_5x89f=3jp3NX( zSe4!WXr8_F!hI#_#XP|{G2voh=dIq9i*(#USWA$uz$dzA%q|`d*2ZAOr?)=7OfgAR z*A6G=Un#{g6H;r=dn`8>&<1If(JEscCF|8SJ!>U7>0XaP?pD`(>{rEo-5Pic&MhV|mm1ZI&^lTm85>0;Fsoi# zCOEQyDYn%73d)Q5oH-)G`dIcyFv-HMV-oewW)ZTC*rYY4p(mb&dekuP5NY}?qdZL| z%hVzPG?%9(z}_qTJXRO98h0&!^>y^fVotH3_c{4qvz3XH>$!Fe8HQZPs=~L~UiCWl zE2Nseiv+31OZ=PJ*Ix*K6Oo;Z1B#QiTr?IXn)b2Wv}3ic-h7r>=M*0;kzsUEs1vXmuga*}@X%hphJ84*g{Q;jY+#9Y zHC@olH0A4peYcU6?TJ})a?_DW7Wh$S=tz)zCYAO%T3@_ywX_(q-;@W@V{{@xVk##U zo3g#+>i!J)>(8nOKrfuk6a1Y;>QC`Z(fro`j1bI?UBlMODMI}zmbBCSq0Q8Fxy{i| z=j!1n$eu}rz$ z6P_3|Z)1#xq+Si-O3SZ@SIsz9ZQe*;V}odI%O$=cZAh53GN?lqoN={}n* z!&95mw(3qf_@fR%_dBr$jRHA1UN zTsgF7QU9_=d-qfW%QkSgqFNK34jQuebY^Up=!wNd-(=TSzdI}wEUw?J@T2^7H=p)&BW~S~*~usyvduWfOU3I^bvcEL*b?{RPRcF!&aP zT^(Tto;tc-jnPJf)S{;sA7{e%QA2$uk4I(>;#?^nKUp2CfbA6!jSQW8yVq;G(h8#^mH0?3XOYr6=^QL9v1{83CH#lJ%JzmOco9SY!#m=S?782yX$>fL#;r7)zA#h6Sbp8yiJPx+*BA}(_dPuFbXX1TQ zEPA|moE!uTD2(f)7uS}xxdjfVj5_ouf2P-vvlWy&gg=t*`+7VMiDg-d%Qwdt1^(mp zf%i1CTM{6;%}&@|5YKA7UrWc=+t7F)WD}v9{--!K_YEAiC%<=jrr$^Jpt|r>EF$#6 zz2yQCR>73;rRIlFm=LV`>aYd#46PQ-eyJHZ2Emk^=>5d{Zl5GY@C#TdT6gxtS zw_~>M$g1Jfg6X@ni1g`MAsL_r^PecOGSJ(R;-QL|VXgrLes;4ljJ5LMANO^jB41d6 za12Gn#QCs$%8+uWNA?^G>p{;(-X}aJyb*b?n425b`j2PlVoGm$?Wb(vQ@s-EsHTnY zdg#2LW+>e`4LCVqF*J&X-NKtejgqqy7d|bqMQav)w&pJa@~fxm(9oHcx6J0nn{F?hai3}&u4hC(rt#JMzQT?NYJS^5ANJN{3>Cs zCe8BU35m1y+n?5$n(KFVK&EEzK&V^m%c=ds#4<2UTNB(g_IVmJD;s{cbrz=J{ zJQ$|Shl``fA!uKO?|v*FVx19B25a+#*v8mMjGe=+=WfJ^Ulse^uPe=X4>4m&F~Sjv z@ZV1D`%#kT@>KUoYg?S*16x@VY8I#H$9&mFaTERiWHnzl$@=G-SVxd;J6{Y4H&!L( zawfO7Y{EV%P;&O)l=FMGM#t1201Fsr9t)mR7PJ&unITx2r8Qxx=Ivs6w42FPacBMC z#Z)E4Dok)qu0wN>sYr5gJr^Gk4(ApviNIl+a7rd6xCx0l{)60=5N^*Co*CUf;Yf2{nb?tAU|PN`0<-%nZc9To2JNYw z$=Kgzk%Gg{BE1oiE$%<=LyNu*5=mC`U}To$aPD7tA_cM%aSTuuDbiXqsu&IBfdNyde}8m9h=NXX#nG~Yv;k08j25R;(|E;8j* zH!6>JZHFBSM?LtnPpt9auIhq;@x>tAbk7gp<0iXj!GsBM+a%T?n?peDmo3hRaN$a8 zX0eZhVL}?HV{LNg*P?}6Zkyg~d_8jTmlxEeW;=8VPR~D`02Bqg`(3FcoU32vb`9JA zpSmV+HAf5b!b5w`mtxMTBn4_Vc&>-#je6nQwwARR z1Gx-1dHZX!xEQZhPpx^76sS~90G{okF|>9MgbI`8M;KX}tlsJGb2sGQo3kOB=H_Sf z{=yfW?CvNb4iJPIR@3LWi+xa3pHJ>_i11JNwQ-F?A$7UiH>^{?DD{-4sQ zL_wGgtA;z&Xb`+4>8c@ZJd}a4%>ZSeD~`f1ebuso?^NCO=X>tb=MRWy8-y1+d~<673f>aWsvR9spo zX)G=SAT5!F2`O)fBP=WaI;?85&YU)FJ(VvzZ52;bd<5}cJR{V6U_0V@ZN<>STO+EK zcM@}e6RlSq4-B67>t#`gv6K6jqTiJ>KmIjPilLNYH=V|F02khvyimeoZ=%kbsjgC< zYQP=xl&u0aOM={DqQFH(1^v9T#ynZai=0Tx!2aIzFu)$$AdAD!&YJeZXM%UC7Eirs zJe@e9F;|E?NQ+aV>g#!mhFe!WYWq{3&>y2Nr=rAz)R}54OTH0*=lN*TdagD8Ff9vQ ze|lGI+eY@6*;kn8+0BLaqFi~tDAn8TS7H=<{`CED33Gly!DE8-$Odr16H(z4J?;A= z9xZPxj4qxD4vod(JNi+lMUc~k#@1Myzl@C^7eIV9lAr%a2>cOA`PmA-K`GjIMW#xv&9uKDR(A^CGgRx9_N}6GGtl~~A z0a#UsYIo6yEroGuFn;*9Kyqph%on>X&o{1VsXOt75ogj@V>t&ULbR7TkdR8P)hhjM zdaP3U7LztvVw_UTMr}>|fAXnef5cz%{c26~G#*y{ze8yozwa`9@}_YCp^h(x3k}Dx z&{mAGhc&ZF!h6;aWEW}uwx!Pi^pG$A;9>$ZI;E+8`agV; z=r3aRz+cu)pG*hy+lXmN;U@)}Ojc&=T5arxQqRSuXSBlGG2X*@`=e*+YPsD_6Z3!skaEf<#nn?;TZ^!@ zi|wrr_)hm{8aDM({Q>9==i6cDqwMpVVVk{j8qI`%!Jn-|78}Elp)Uts8?PRCtrZn> z64B~0s~gv}`dr61io7^2r1||g)-Q?fKk_N>Ml?X8eeFbulxMER#17`3-ruKW&W|~DX z=5YA*onaKXT)u&y-+xA>mJ?+0FaRaw-BP2S5Euly2`Nty+;J>OY{Kszj;mugJTRsVxdJ(#S@<;CDV zQIoSC=SzvZ0diTYLgrGSss-QBJ=J^7%E#vs^~Hfu_Nl_~^m&785{>q+DoKf@xHKTG zp3V-7P~>B2yA?n07||a}W+Pp1+e6MEq16=+qq}wz8eBil)la$S$QZ~&%>7??Yi~` zQxWJ!iwtja``?Oh9QDXGIk303T!UGdSQ=L=X%spo$nm-ib)0d4+^pPe{cyA?tvpI+hWP%LnkbjZBQUrFk!^cJw7z zxzNfR*ic(t;iHs?x;*?4QYT5JEKFF3Y+;6vtI81JF z=CnZH)x?ahD0h5*gGMz;@?H5oiHf43h9;4-pv7aJbfT>?ld$tb7}^Bif)@6(Xb*xk z1jt?B2%tE}i`pB&bd>B1ZkFb~%FW3VD14JS&O)C%)$&f<7eA#3&oV>tAGEO6SQ&ZX z)DNZ+ACau)gq!;>?w3GLxndzOsr-$-=&F8#5JP!!8gC_u17yFasUDw!80Krw0~Qv= z9VAwWf{tUp9EJpN+!aa|@SH%;FOt43ypk>rb>9Wj{4(eR7GBO)0%*vm#Fdx8j#YEK z5`cZHP8mLOrqr&<_<~WS;&)S)VQRv51_$ar=A{KUGbDn9*eJC+9Q!`&+PY6zj?w#_ z=hYbf2ULpwl~@F}dhxccbpFOw7IpxZV%v7?vEtG$c&g{i>-`@VNVQIT3jzCl2y{ zBNgc*V?pDbXz&PK9uGRAxyMlRomB6_fZn{=$>RRlw#HI`kD(pRsNx9QSZ{;7T?atx z*G3W(MX)L)sNbttDf=Fa;-J|w%~MW>wjn1_wrFeu^xHg$sg+7|HMbk;cLpi^={VTL z+9NR(s-b>!s3c&at)$rU4*9Fco(Q8*F1#E}UGnUs_=pk+b-eTIe|~%;x-=|)Y*FRK zQM%|pRkopT>Zx~AKla62qAlzTB(KZAcDe3T0iyk|nv59<+G)#6vBQ%~IEwr|7Iq^( zjMcaJ=sFjJ7~By?GA4W1(OPmxa4J?G12}MJ5qjusA`~6S4e&J0Z&5O)KL~K1 zx&{rpx@Ij?nrmO<{{gSN_W@>B;2E?(WSfYzNTM2H?nXb@LB``!~b>7u}w$A59Gudfbk9P>G1R^mPb~b#wg+RG<2EiqMuMvMdJ37Rfdm z?ccZo9smx}EoR)0>Qh&mpJ&^=7L&66USMb4g@hy}S=XZJj8y-UaZ$r%T%;2uMdU~P z8#tul=Ov2;U^RlAeuIOdT>y(tx|bPoxz`QCaBx@TWyf3{SDNsoZIb?@EyhJBGxYUp z@&AWC9wFOdY$d$r-KV~;xP3CUw`mWYEiT8EJ_tG`m}PH7A9$zcg?+#!Uxa?no$ULI zCK$gIl6?y+yv4PWmE$_LwLvy>!tgZP4H6xhr642Cr1+9JOcq;tBD>u9-bk@|*y01z za`mwt-JN?j@f(R<&ay5~F1?!niDii)O{C57{gM4S*o>?WT~QYrRXSrp9q;2yYs$c1-M;U7UmAOsR{UX1gLt$eDxwDwn|5y8wZb z-%zDGc5*eO$iZjB-LxY_Xr3-0&3=DH+QCuhQ{cdT8mvNdq=}!a-6DcKUvE!eW{2|4 zRqOLAT3%ZlGAsbj+Gv@C_N>u;(6eQ)piREt&m4tKs+CJa#pBJ0$#a=_)pwuiW$N2l zzXC?nKYh&;J)r{%miK~*=wS#YlS9J@C#h{Q^o<=fB#PkSVl@!0foP@2G zF60$LhBtcux--&&N`MXGbM{B%ObkK%XR_b0!Bm&3`0{p*w5U(EwR(%bVgvbSAwyJx z9oYrk1aFm?eefUnlQ7D$R_$sJ?`+7V7lIgIAT%RgQ^k5E3)WJsUjGh_wZjMA8f#R* zlau}y3xHeJX#Pm31_XpM@t^+!So%1c+J^ke-1V@1MnA+!FNsVR-4)ZcXb(zi^VyV1<9`_b{{)vlbiG&4Q7& zDb)bl6|_C|b^^f-EOad;^?&=Xw8}Oxn~dsidVGM@mzssA&eJ6U+Br;@p2aEB9c*hM zo01%rYbgkU4bw)Y`3of<+gd=2zMNu;YfL#k>6Y7i7K)GE4vJM?RrV+?<%|G2IEACe zuQFul2^z=}!2i?Bc}K$)eS2JqE~1VahS5e#bRov*ZI~chL?gyBBM2Fh zLbPCr-g`?B(I-j}5=4UFU6J2=Z>{(LoBz&QbMGm4pR@PAcYnT9s1o#z-jPqWl4}$o zJQ>{^^b5RfY4-Z~195g71@->Kz6^AbC9a{or+px#E$>h{kNRd$9`IafUKV~IkM1=K zjC)|-SL&XtMm3e4ZlzjTaTon~cF_veyIIL1lsw@Flp{Iwdy1#HWL>M5cjA7c?Kmo{ z`z=@2A6pNCqMBZ87YUU0{b8{%=J(tke+G(!AMnvPRbHtdP_%7QG-wo05ATG^f9^vb zIY3kD^7RlQ08X){4ksw!*YJw$i36PB;S5%b2+7I4R;%LOTT|uFrMq}W@`Q}Em$?6! zaM)d$3&%MhQWXv{5Hm3@_}=dv9Pcg6bTo7d8DOuWI=%Bs{173`XzIaxK@5RsL73)O z%tyKvRGo1+Dd7@{X-^*t+Hvl-unZO!ycLhg#%CnM1Z;3-r= zCMzQ*kN@E(JS5IiU(hfj;Vb8VPFFt5Q7XRM>vG36yk5xD{!3ovr8hR{DgyvnfeQ({ zxnxn=v_Y2NWA?giqx=5#w_ubO)jmw>{98LeFgLEmg$Gha4RA&oi0?zxGZLRZ4ee^N zg>;IF>E4ke0W}U8`p?;9FduZ^R6CsCOsmrGBgP5sq8UFqCUvh|9V*Y^7yq2Qgrjna* zPes$vv%!oDG1iufUwTb<{lRbgt*xyQt_`2pA@52`^=lYc;}Q}c>EYDS9Qe^rzmn10 zl_;aV4CFxNj}weO>>)nIJ(mbTw#c-#yB$uW3{COJuO>PpbeNO9kb#`l^LG2-p7mfXzdvm5pG zjL}$Hsd)D0O~)q>$^NeVV99>%O7{Imr-PZ>3zO^gq4WOgMD)ko`3hz)1UG5hRdIdv z#0jCkOnfLW54wCrhDaFb_omax>p^6u#!yzY!^ zUvdIU5U&E2Z|8n2W%`QE6ujRAY?X?6-qa*YOm4UvjhKCVitiXuW#bbW9riMt%RI;x z_(DLz9M|yvdf5jg-wifmXfDQ~=q%joxOVV7B@qT7KY#6s4Gu6##3Ln?=gn9wV6CWm zp47y1>!k(5`v!?zrZt_6Xn21dFeOzZGY^}%v_a^epLl4iRnDkt-adSuctm5BydjqKVA7o@vVl4gx zyvUXps9Oa>+?a%Ii-P#l60vIL+R370j6$xi9nC}c5~83VHeCD^pR?hw`>6QihI)=D#ZbOr8w9FfTrs{)iI@v336wAdypF#p*k$ z<I_uhd**y;#~Iv+O>DEefj@-0atkgX|tUDqf^y7VO) zK7T9LMum=UuF<<<*6lkeL5a$l8Rl}8u7NSr@rLvz9rq6>)IT>1T`NVm7uOBJJH}$E zX;&NrpT=YOb9k*er_2)Dz|BeJqMOX2R4z|0O<2obq_Rl3O4JK*$G3I#9LrfvVOvi9HkIr@>0e1}o zrm;+LZYZh<`Kgv0$J#rSQY}%eVg|my6|$xh$VbO>%+)ML8DXJyUpkT4MBU7B3`1pc zWJg+qxGqu|SA@N!HQdTdJ$F9Ik(Uub;_HRL&n)F zcAdFSG^KSe*u$ekvevY!+mcIJk(s;faPJ92_E15w3IZgL>~3jcLHON&2c~dOqc5rd>X68e_;-#7dD4 zMDzK|3S`Fh{TX{vain*0c~|;bQ7WLF(|UI}uM+^HfBU2jTcT&;x2Zn=4!+#PD0=Nz z8qe--_TFHd@C_*yV(2cZP9D+41&Mc#@q^($cSO`qE)QPF$$@4P4dPi6Vx{Q+dZ=(5 z13(?S20Zx!V2yxhif{nHVNno+^WWozo>(7nV1r{@Y`y=W$W8z|HWTCPgP${!nOcBd z;btXM{~?v}0JL0cp^+Z(&wHYYqTg3kD{1X8p7quTJ=?5je8r|a=vP9geWAwrZ8YF- zE(m`?TWr(z;xyqgAKUY_GFDP>ve@P@AzDu9%SyxZlYPjBvjGy%QjO$dx84Z9NTIqa zA4OIi8~ezjYE6Idp4nxuV-CKYSaLFIeIek0q!$3*EEK*MUse|eAysvnANNGrqhK;a z@^~?J-VGQ-S!vD`hYGh&&!FzPc+o82p2yKl%TfZw*gw0C{)S~P2P?yMbq<)3bylXV zMH`zVpWJB~3P4RJvbnjr+;4M=!d7f1ixXm)IIGf#wr}rpn7pWI1GU!=I`n}+tXI%iVMyCSClUUpmqTTFv8%L!Z#}d-yP;_M-nL%lc+nb z?I}&-W^F&Hz9!~z9L;%J{)&*Km;v!#8K{qg{{DoGwWhSK=8?#ZvK+GK{+3-j{S?$?jg7Zp zwQ0C!!EDY7!*!JP5c1_{@z}GczKK=e54X2|DE zpW1z!WNrR-J&E>ZRRMo=sF58{Cw^v5ughd2J~ zYijkF_NvoYlI&5Sr7do&T03qo-?`eQc=Z9Lf)G6YMFOv?~AAQ-TT}&Xy!yE{|$6;9!vDY za)G&{utT;D%vgU@Hj#%?a@J!c(+=XUdb-l5pGeqxv>qPkxy79{)Y=*tG)-)uQukr# zBHDKSQPbs|<&}aG0~tTVN<|1^(LKtZmNGF1*C@?7Yrb~E zF4@iB9lUzokGuXr2Nn2LK@5hmQdlSQEPfEPUbgOd#2QSwdvCn$0Hs2^xt4)taG4?a z-Z2WgVASj%(_f0bTBJ=nqtpbFp8UQ{O1w7qKG?=)niqh-cPK}&*yY(ODM*D0FDNcF z^ztXQjr{Mn9qI!-pA_CU4;ge5zQm*2eyp?e*!9qtO(_y;(Wmx9<%gSpLZfEWmzOE- zEuoWwX}Tqt=6dfELs8rDr)p*nx+8aLtblKzR?mz!nmX2$wfC4g^Js6g z%gYwB-{?ITD5h-BmNQnWYXB9Z@}ZiKfpY+p1Vb(fuu^lF0U6GnI1?hd@uw1OQ< zdLEe5M~v~e{LCneQ=41Ui~pxM2Y<;pq6A13CfxlwA9lJphSSH11X{XX7ZRc{Gn3hI z8#vNKpmj~GStSLEyM*j!I9RC3Ufq0IXpU3689x2a4FU!e!C#PKaFnMhKcw}m90 zX`G}W{X6sjZlr7QjlL%5kRGRDbr$>^7!d29oMj>c zGXkk8=dOQ`56Gjh+)mFvZ;C|#nTdX#K0oAqL0l-Es-EV~7~p>P{yGrkhf2SF)3U!g zb!S-aZVkSCFvBBbYNbPtqUOhSId_P25BLU<@ScSy#=I>VIZ^=xy)8@47e<~UgNpL# zE&^H59UF_*8nGJ7Dzs22IvpDxd2$}#z@hXb!nKGAxk==0BB1i2y z{D^+f{d4UkeBB)S)kS+C&zG)=z?By{&?=)hJV_Iz>dox znlybd9?CE-tWfq{rq2N5OL*%&-G>&vy48`8Pk0-0yTWyDde3*OliK)v%EWwj{3O$d zdPNJrY1roDO+9aumrVs~0pkX)k~8+CB@qBlL|`KbvygCbGMvP8$(JqEm5sR{%*{*% z*^?yZe~DvVIl5l8&&df)GwMIn)F8XFpv{Lya;uyU70nZ8AFg literal 0 HcmV?d00001 diff --git a/doc/charts/secondary.py b/doc/charts/secondary.py new file mode 100644 index 0000000..2f13c48 --- /dev/null +++ b/doc/charts/secondary.py @@ -0,0 +1,43 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + LineChart, + BarChart, + Reference, + Series, +) + +wb = Workbook() +ws = wb.active + +rows = [ + ['Aliens', 2, 3, 4, 5, 6, 7], + ['Humans', 10, 40, 50, 20, 10, 50], +] + +for row in rows: + ws.append(row) + +c1 = BarChart() +v1 = Reference(ws, min_col=1, min_row=1, max_col=7) +c1.add_data(v1, titles_from_data=True, from_rows=True) + +c1.x_axis.title = 'Days' +c1.y_axis.title = 'Aliens' +c1.y_axis.majorGridlines = None +c1.title = 'Survey results' + + +# Create a second chart +c2 = LineChart() +v2 = Reference(ws, min_col=1, min_row=2, max_col=7) +c2.add_data(v2, titles_from_data=True, from_rows=True) +c2.y_axis.axId = 200 +c2.y_axis.title = "Humans" + +# Display y-axis of the second chart on the right by setting it to cross the x-axis at its maximum +c1.y_axis.crosses = "max" +c1 += c2 + +ws.add_chart(c1, "D4") + +wb.save("secondary.xlsx") diff --git a/doc/charts/secondary.rst b/doc/charts/secondary.rst new file mode 100644 index 0000000..fee96a3 --- /dev/null +++ b/doc/charts/secondary.rst @@ -0,0 +1,13 @@ +Adding a second axis +===================== + +Adding a second axis actually involves creating a second chart that shares a +common x-axis with the first chart but has a separate y-axis. + +.. literalinclude:: secondary.py + + +This produces a combined line and bar chart looking something like this: + +.. image:: secondary.png + :alt: "Sample chart with a second y-axis" diff --git a/doc/charts/stock.png b/doc/charts/stock.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb7c56ac4e9d050b07dd15294238361df8e32c2 GIT binary patch literal 92652 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 S&%LG100f?{elF{r5}E)2 zk-0dnxlP?sxzlYrtp#eZT%n;wY@BkQrU7m4(1MlEFdGn5H#w)G$|@!dy^(5G+zFiM zyck>g?#jTUNUyXbbZ3?8I_W*(I&slC*~zn8Ltm-z%WV}`L0P%C#<+dDYU$lI7cl{W zWQ|<%uvPlCk}>`3*OE--df(mzKUF3)ptHv?~ zmqfZAEjPLBjFCL}oW5q(;V!i3M+dgBds{=)#EmLakhtV^XJ~LFoJKLIu^k8qX5^Sh zhQ%2E=l#1;YP^xCoAE{>(;ref=wP$80hRm70C9Dqy~7fxKJQc64S(0@ERP1>(BGxvwzEun3A^kel$ZwaOjBA`?#BG&bGcV8%BUy~0mG1!w~|w=41Ee- zZWpHvHw7HbEuQWb##hX5Xx^K#`kGUd802e=6gi)iui2@@aWBS+fnDDhE_&9u8_&eA zYxjPC>ulPL*ofvhol)jsilYCq!VE7;i9FYUQ|vp$59-3IO*%>a@2}aoxJ_dR!NEpH zaM4(0kz5Ylz;e!FZU8V^6VP0oQeHLKC9Vn^5A=_@?Fkgq^rw>5zwY;{*?xR?>_fT= z)!w~ixpVwnUJ7^W_R%_{Dr`8g@Fz<-LQW+Xde&T3w0YJ{HPgNfQ|~%1B9DJqO9z8g ziaW(jqT$Jtpv!gRY-lt>iIc=Hr7zG^1*>hEXqad2oZH#kMtDkxnql^165HrD3YdVF z!GU`zG-)wVuliXtPiB3mdXlLF?{L~x#k;#u>-yTf*QEL`%rveQOzBu!&HxGX`9+bX zW)76sDjf&=;Xz17DvB~S86CvF9UfT@C0?7;ueeT4H7idOMnBS&6pd?=D2n00ZWZFK z)UW124GGzdg3B=kD5>&S4yk6BjNYC82kAwnXNk46)*`^%!-TmT`Rj5Mj@A8954lJv zJ(GK*w?A2@zrR>4V5ED+jXYW+{34xogFaU6?D@BmGJ|%aiYS2;`}?V#tLsac{Sm}{ z`=k0*xpUG?I6p^vlcKY!A;U%PfPAweGw^NvaZ+KI6K&+HaeSkB?qS{)I#mj2Gu5Go z|E9PdIc+CZyo0I4@yx}#NMO`4g3xHn>Apfj3O&9&`~$L;&wb!$1XR3f6nd6=4V}7X zyKo3xQczWBIss@?SX4-}z3=coFKvahShfH^FMlhy^z=J85^%V&1vylJEB_pi6q)s9ITfULz9mI{fr{YtR+doRMdGJ+B} z`(e@eJ~zy<>FHMLsbA6S{G6TsFt6HyYdOoQ8@BXvRVBlu+Gk0cWM3wRM!W3wRqIl3 z@X)3mQ{D!HX@Nu$H&2C}Allj=sHvqkR(}wygZ#W@gH7~{7O(s3*USMd-Aw@ekIoTN zPSH>$?SXuXpINjxyk(%26_8^*h;h2=m-M*zhLnc_R9b=&LXbA0ABB1Sia>xObmfzJ zt>`D=0+oUITjK)Sr94J;P+1K5^o76-Q`MMZU=bG3^q|#<4WA)t?I#0;{kt|pa>PZP z-{-~j_x*&c2|bi~5$R;4fh1+H>CkCZqs82Z*NT>%3lF7d`{5@p(>W5{a_Ro3<(e@{ zmO{U=&ZdTf_ssYM;)P>FibU>tIm_jR>#JHJdC+A3M(^)NdIg|jUL~t&E5)nWX8*6` z$C;ueo)H3q(4IgViIvpUIjT>CA#yaw&_7^_ua&KD~uD zJTt|RRgc)Xtpz%7$(UQuJtoExu?MytzqWbndk(};l#V$C6?Av#T$*5_o2_Xj;L(U8 zxMr8tdU6rSe?R^(c|htKxi-6}^dfD;?|a;<8wU<8qSkFt(1=O)j3LuwtjK+sU!1*N zxdT9b0$28vDHQ?N#gzL)ps3BR6}Wa>&glk0DqAf0pX*&XKmp=j=ksN`%cOU;1G}Yf zY$%K{XBVOVO|Zpl;8tmS{miMn2$8*n;5~=m<#egwS^y)_Dp4b6_V=o(6ovr zz5XGqRU6Y0+I2X(=o^j{R&*(!wRkV@PAj&LhJq5&S|_cPnbOUsDs-p3lRR>ueD5)j{ae<@{F-OQnE)#&Il(MoTU&#-fDfq?C%~4iM9P;$@NZR+m%d zYEV-%64IP7llA!kB0;&!y!rObv!5UwoR=3f=v>2Wu(SD51BrcW_-3pfFeji`UOo;n z)SyrVKy`ZqkMu4#uNqZ=ib)dBewdfhTO7Mt*^XuK;c9af|B7$A;IzF^ukv@rav1k} z#Rudn2Ueb(_*|Kups^*_(nb=`LyDsj)Z0f!>lxx|TWocDRYP4L@$@%(N;dksJXoP{ zF*Z%ZBn{KYvs^0t=w9OoVzNdj$mr2}g{6VmWC8ih$*!at&iVVV#`F>jKIfk=6?|qq zce|Q%78aW7;P!z&QilMrgct_Gj;#xEk?1S_)Ryf4t|ejvG=UbsJASy?hH_Lrt|I1q zF0E*8dglsQ7FWdJe@?X_VtNb!MDX|%sysc%6=HmEt)Brj^5N93lJvnc}L36D=LbS7|^OUbW=VNFfFZ-LG!6NV9&X|R!Mf0OT^gv$B}t=Q2X zHk;9y^gz!9Y4rZ4aFU;vZ124wt6H=_*660?vJen%Ao7g~H-Q$D$-1B;B;JXIM7utC1 zVIK`7q8?3md}N!$5e_c={+u{|a~jNXP!I7k@*FbA(lDh@ItW$C$td?Z&r&`pTQ~E$ zuZY{3)nA>~IvOZMH0CN)z{;QEn?HXW)fx&{su8U~y64?<+!t{TiQc_SIr>;wvmglH_Erhesf zdEKV(o~@6;etI05ys*PH7`weWf-c?zs%WD(pw7E{n^(>vsmBe*x}zv%_K_{$*RXwwQ@vg__dsU7FP~`CxEOK1 zZ#+rB`Znribf5mCAkI*Uv^|A;AI4OUu+rtK`I+u-f(bE&ncz%J^wNEMn#zJJ>dH1$ z5WH-q2wTk5+yK3WV}?A}bmyypz(`l&#V0!7Lincjw(Ez|hXT)2okr@4x%)vE*Njow zNz>4j=LDsoxWLkherd&r7GME?a*UUTFKC*m^K$^T|FzD zF2}@CtN=ZWcq0^?CL$sfSrXX@8*5}tR?N*$6P{SG>Ygq#a41jr9i@_Imi$D0B340r zEJe>az0v0F{k_Fa2UvRSWNjiLSuX`lMg4*UfaV_Xm84XeP`Nwgm$c?ReCFut-w}a) zy*pBmsL{rd-_XRm5{uPc&kN9B?_%(kgU*z&dMXU!an0xY)V5cpL$uQfeH6QZVv7DG zW=eTVtb6x(S8WtQPKEF4sYtBPRil2zo>=zC>5~QNY??@Y4q~-hW=DKI8#Mr$bX8Gx zfdZSVogX+7CecrW5e921=}CSxR4GVjzlRY>(LVW}?L>(R?MbCIROXXO>6VkpSo7EG z?eJmS^Y{gG7ue!5jfX~(7)g`t^?EZ7{$(n10Fg`hNRo}zEWdj901 zwB-J~xE5NtrXky;MRnl$D*bjd$81w8Y!p_m@_TpTch1dqzTbOx>1Bx+yc`GP5XidM zdZDptBer%eW4QgWccz7LyO{cmVWP)W7VRHM8a6k2AQf1Av*JfEb(0l=^4b4Del@oV z#-j8Er|?XJWQipbQSS^y^q-mX81X@Rk0f~gF>L@pH}<3-=3bPfk3?vYwhO;Eyt$N_ zUQjZWKlJ+!rwGw>wK8Nk9tC%Ci=TCBn}I)%Y&!yB_{vG#3)XOhWjLJx-BKX3Lfo@v z%@8^SOj&$XoY7PRQOvfX08U_S*IDR#=ycYp7MnG|qHovY z0?c)LEKin|nDCqC&r=N9mee2=XC=H8>Q> zSM>tg0C@QPnhYBn6`}TcKy9O%)=ME}+Y-eQrSH8-`7FgMDZ0J-Ol?tA!DYq`;d>lp z?o>zdPo)n0lg*o)-}d(sC(ws8>ZY_M@y;o8N}Jx*VsSnv6=EIH>|`x8OZGkwTS!a{ zH1Hp3Po62^S8V6L-cBq}@whS9xFB~jYJGaY?ngn0xMOXTMO_|b1e3%r4(G-YgWu{- zw`?Ct1f{~CuLs#3)^~~^tSR;rd6^&6iiUia7GgDiP_uI(ZEVzQxMh_q(f#-H=VRtd znYFM*sAWnicc&Xk?dL0Xa)h!aOk|+?{bGgb-H^@=7#){*eu%rX3u^tDf^T7fo$i3CW`mtm7XD;_AdpWw}OCpC0jGA2iWGFcJCfcaalU>w0=uEGn1RzRP{M z(5h{syBN6%W21k{L%QJnsZP`-sM@D>7NohHI&2rHWSMrQ0boxS-)<6?^Jbn^w%5!W zl3<>V|9ns$=r7hpQ+|802@4Dc27bwOA<_9Rk$M;x7_Ar`Ai1|nbE@G4JXoV7_UW=; zs<9jOI-k_+j22tHu05?!RWx=!v>#-?+OALGRyV2q*HX1&hHwmH40;UA=3M<#?u(Hd za4e_WO*hI~(TA7=2owmN2OL|NuCCzsxA%F_H(gux>bWDANz2KXTY05E^)fTw_qsw# z%%By-Fjt~!y=kFn>ONg;a#3UPJLpln9{k?xK3#lokSV)4$Cm9oVyj_G(woVexdx$R z3BC~r;;$_iy30Wg5KOi6TqBy9Cd`{JYlheF@7ziAf8?EY=RUjjj7~rM08`7m8_!H*@k) zjD}UGaCbB9;c^G&4S$SG=r2Q3_$bKUh_=a^+f)ltOhIEPXFUT_e`9J*v3*pZ0a?F> z(X*n;96qF!Js%Sbc5aRs-A5j`BhWVSBPnaCup2bcLZotm2g=4BfesfM@_9qL%;``q zIiR8v1u%Cxp-4E%$Df&@y!K{G!ezW_8Xf2$I~s(S9$>TiF_%Z#^V&Y!KoP`jslJdx zj~(?L-UM^>FZ!6YES&lHfyal>8L&-zb@g$HZt1!-bN)oKOTL{(Wu?$zd<554*Yyap zZ0K7guW@aiBEX#7wg%;J-Q6$-h_hkVB|0Xs-Nhp-WIsISQ)qW!l;#pgY*k;Kn01rinQL8n^)MaQ31b1M1wz zU*rU8scJwg$BB2k=4gmK_RTwdeCLQ2{Sl8Zn>uevT)7l)hrGJzelL1IVMDIhiq6Ln zBYppT0-k7`~J@Q^O!);y2;4 zz&CgGq)H{qv3zYg?_K22?BgA5Ujr0JJeE5=gl;Y=oz^{220dW~87*?(1AQtA%uc1q z@D=+(t3th~{nw@^pM%}oYYmKnYh~{gM~c=LbXKRYHdlYTL48&3Z>(SJ%@gre2`SuJ zGI%_5>$CjEM`3J)3|Z%TwfJ*t!8^J1N>!tIddJwwYhR+e;k2R498AqqVI&-q*9CjZ z(9g6mHW7lSyY|U#ann9<^pO}tH(Pv+o%+9`M0~ z=%t1R%B*yi7YQOvkFhc8k?>QSyn&XU+XfT>??(4s9qAQA<<_1no{q!@=%?ovlL;0p&L^y>y3K&(JYJn!Gy$6w`}qv765mdy*`v zUq}BAR3A_PC8YqHyz8yCvzO>XkB*JvblaUKtUKU`=~#nV#>QcqF)a&m_soExSGPS@L`dcbq>ditu96YtsB&; zEYP0<>Rpznz0O-iQ$XuaW+|xz+3Xy5#kS!L(IZMyqA2Bbb{cVUO6^LWuq7uVvJ}_R zZ1O7*tuRjx%kf>3Jbz4*TST1Ckk3f_tZqT>Krt^a6^gn|XESl|fk2dQRDbP2i zbynz2{_#5QcGQ>o{jEQX4y;Bev}G@{bb0W#1SHCz0sr>1PI7 zzRTC#rb%#XUw=gXi~a4h(+~Gc;0XC zYWoK-GY@HANqG|ArD@^Y=AsnE+7owwkdF>&Nmt!n^dx~{THV1O?-rNn8i;#>&Qxba zbV)OXmn!%2>gr30lFmsy$b=nVGjkCFIr6PY=WiTN27ye#Iy=>*HS4txqb3wQ9pVS! zqq`1*CnrbGn`7kf+pc~5JN*Gya}xf#-ZP7fn!ZW>M|sO+%f$DZBuC!)KhVDk9$%ya zFQK60^<*(p0mYg0(X~TBIfY`UgqCX3p)yml$$F&^7~VMZ5T@sCCz96Lo-;<%VG-fK zQa_a}<||`3@W+aElRwstYAx(vU0sXKKN|RWH!#@IqA~j?+E(Didd~kL2$EWEw_Tcopg5_jjW4R#A5e&K4J>Yc;sSP5<|BHZ}_Zb*45ftT6TeLabvUpQlU3%g_ljxq3u2E8OTgyA; zPcWZ5W7JoA?kl0!@kx*e!yI|A08vhS)}Lvd(OyGIe`8k`qj_*JSPEI|r+U1BzxrUM zGr&a&=2b86lUpPx!{+J9tJzdd{#0pSQX$g{UZ0^-tACMNf4EOW`?ixoAN7+HOY#~u zfdBG}m$F@K_0<*hIDOJ$zR;hu&BivyP8V4Re5=NWj7ix+Joucsa8|}xebxn6nXCBH z`Cgp?Q}j+#%l~Fm+i}QwhNf9#gcee+!mgQnV4X?MLsxia@2-$u)Eswi72>q>jIVDG zK(w64!7+;2mkiQO=J-`#?OZPpEY`Yo`c^_MPSSe?fayvZo?^ad=Vq-tj{`cN_PgX0#%}Z`q){%LFZJIZ@Nr|C-mrlhw zP8>%GSf1Bq&W{hooW%V1hQzFhX_Xyd(<7ip+pJG`3xjR*n zWzk7B!s<_zw68kip(XEKpU+C>LP&VGF{s=_7~RRX^!3p#Z$Z;gt>=7y?iF(>Hb2Tg z*D|Kr#m#~LXqf@Uh%#9bz*<9tyP}ZWL3#7_dLw){8dmh;N(_(9-=Iw zMSow~u|Rpn2DyT1=7=zX)hM=y!;R7+53Xj);vVaa!i&^hN=-eD+^{&TIz&Grc#3I|Q(fLIyGp!`3$@uzN}=|7pqg^Qr~(MxRsjl>(KAzNIH;P_+8|mZNd*rmF>sP6_Ds zT4E_7z6$=a$n%cnYyzqYA#oF$kk;5J8W8~!4XyuEb zKY_%E$*MW6Gbe||A_|;s#e=3lOlQLG2w?)hgLyD z|51O3X=j^zTWp}KcCqWwh?cayFtEw!UQyV1FP-{BG|axPUV=~pWj1NM zUz=U=k9}9LM1;68f)v4|x_VVhg|nYHCnEm`me+o2f-CMds}aloPnXiK2=Hh4w}(0I z43h$ED=hQZ(~B=)0QmInoOq5zlY0t-;!%0nLZE)hO|2f>)D|r9?KBhd<(A=?)|zU$ zBOvoYVFaqQkyi7R4-hF(@<3KymKrED(O7%pOe!xUtxuk@XD)K$gQ?@YE_rK|-A z4wuECc}tb;r}%5+APvO3d((V#rGFS)Scd zohwl*jio3M=u+z6k`C0&zh56~6lAr|R?QZizLr4k9q-pqEai&1J8IDxK@GM^iO2>tz?<)M6TZrLc zQfHxN9h50Nd+soN&VuSm9@;VKNwZ%wa(h?3wg)paUV(w_(2K_ASR#&7>p^hhFXCL7 zw_kDAzLmRVx9w`{jrZqVa9VoW`iXW>BlW8{E-xjv{U{M((cgRhoZQ*ow!}lcfB2Fj>Nhg! zz5%x;1+IzJKSLO13}%lCk@Y(Np}03`X0zu7O3H}k=%MAW7x;|)!9fnwoYA^pt@d%S ztV_)V49uKF2AAwyXZblPph&*+0=|Upll5zyef*h3lRbTDwv>GuF+Zkk1fGmFW>ku6 zWvVbxAgHkYF7Vzv3g96NT+3kD$7T{Gm)oU%CywVk{G0_63u{6gGwChIm2u5(@T9VJ znFi03@&6i>cuCRX!#SJ8wY~87fmSQ|z(iLlOK4{AbsrX<&(*@=Q3Z;$^?r>V!W$0gLJs;tZVykMBCc$t^B(7Uk>iIAL&-9u*i1nl~jnwTd-KVjf{uJGEOm@>K*qqn`Pj|3v$ zT3UZJMapv(G0`b4cx-ZsT&2UJ0m=&AZ@KP%e)s8_%>ku8HJ_~ndAR)cfS(_siFd$@ z0dbn#!qb!VDd%MaE1g|n7p_~F&gzu+>k>d-+|9xR&O%{j@EtPeb~jzztaP~x-QQ?+ zD-tYCKL?KshjrCpprn5$F|6Axg5fnWDoEfYw}R_k!XV2H8kBzY{5M@#$|b;X_vMfS z>)|@v%dH1t{ySEbzHHC@ag(XnQK4Qyax>{zWI- z8fj5Dc#6pD_p@JDiI^Xh;`r`4Z9K9gK0S*sIBOEW?sE0ZJo7pcF^cD@8sgrJY`6CQ&?N4{4)RExYm5cX=E)y!yqX zoFE(H4~Xh_xZN%Wq7`Y$x2?No9qvQdN-ZI#mJxLXhVh$?UXtGl|M42;Q&?d(K^M#5 zT8OWuH1LJP@3?yhYS;Vce(?6A=mpcgy1YfK;DlF;`TTi41eoo-Zz*OQ9Jb5)709Po zv=f!dQTpi4-TP*u#|zI23JH({ReS3_QoB|7)H=q)7i4M&Z@&5x4jwnJS0H}B2dBOq zn=?!}zWVr@-hav4kXH zUprZp2!{zqtdIa+F%zDA7i}9^ZiPue;pEzNIxC zmQk>J;SlQR(K*(L&Kj%jDB7ti$|aSYQH~mZvxI7iK!!xzd|@JqZ4$Mw((9V-{fnD*O>wF}k3KpC3@qIcg|GVH%iMH+$}Dr;2)>?TyOzJ9>_XQPCk_F^<~WKNN2 zn=W4p7`7u)I8;k6IGR1Hp=2o6>Hj$DKa*x3V(~ctWOrW8i@PZzx|q}N?*e&WEq1nf zF|-ONP7vcms>vqJ2U(uMBR=`t{1X}flR;C2gBPj|0k}Csx~RhqStJ{#;%cn(^xO5U zlNdfnO^czI{stgFp_;MZkO5^So!s9DeIc16Kby8k34)Gq{ODOQ?EWT1AIeXVQSR)m zOKnTn6y3P zk@G~ex6j)dm#>VO(oX>0h_IS`$I^y#O=mjy#Cv&8pwcd>9`71IF;LB5bsHfe+2F|Q*Tg-&dXDoE)F8@pl zYMR|Kv#lX5(${Ayo7yyu^8pfPds2719DFJYeHPZo<$O3i9RJlltLJP zL&Vyj4gvj$I|20$a1dGUJg_OagEsWmW73V$tt`R4>U*#&id#B1!odN3&Q=qKs@A;% z#}<`=69~B z8E*SaJs)%OWu2GRsN+B49eZ%w-6SZzEu_&I!-8eFcD7{H=Xqs@X>XNPXPXa>>ApK2IcKI?ybSHu6#qJ;ESbJe||Xrwoc z_>G5heJgjtb2ZOb8eO*-i8Q*F3(hoZLV|jFt@y}tqTzbF4MrPjPX@~kdFjeNPz_e7 zuU#uYNzdZ`7r{wp(*61vPP*Ye^W#~jpwA^^9Fh6>2wlUuz6`0xGw*7>BZl; zlEQ>kgYV!-S!1=$tZH+tAB&(xdELRfB<<9tEqKHB~^uZ<{efpAMzO7PkWFcj* zo_fL-$-SvVPYa39U^D0J-PvxNcWZ-;=3BJbo|cac|Z0xMC4)yRI|%p`wkw79h78A-=JCHj=tJx3j|Kvap=?$zp1Ep2X{D_3o$r zc+a&R_vtn5w?#I1 zeN=08N?R8JX^>87t#akTCJVSO~VuX6D&X<0fEE&X%H zD{HG-4}LzL_r7meDn6*MLGzWzw9xv@)K7V%mD@bC$G+(j73F=#pqOg?)H#%f(GT=z zpi#@t-+@)<#=g(o)I!^0<~VN-ObvGR{Cgrlu;G8mRLhz;?(S+~V73JPt~J(!=5c&8 zzX05dg*Z#H%|tmZQ#70rwoSnFfgVoJ4ggVu8Rixr->_e=@L8hx1k^oKZ7Dq$o0FA3 zRJ`lH4BkU*J69O&)ar5aQRaXbF6YL&ATn7S!|B0ywQ#gj+Q`FRz1tq_uU~v7(RkP_ zyh5A1F*%;p#v7UK3N>uLC1CZ^GBUt1TI0oUQ85T>h+5)x$`yNdm%Mx?B>>0}QWe8H z_ZD-$7ndJF{U?0J;C}E=(UknU+GN@VHk%Z%TR|0&6+Ad3%yyA^jXIg57eDR}?&USm zwtws`uG!PCbLK=gR#RDkINxlM{hUP}5(e1f$9F#(8&kL~;|p1zLZK5l{` z*=2uEScozCn3yM0DYtk1E>cO|C*d2jcP7jebXKng-aHrhvfpI+yVde1=Px(qpHxWf zM~z-Db-)<&J!2npawP?i*Ma@8wvB{|$=)vlMyCK-oewAna#`-A9d-(mMiX~)*es|F zv>Tt8#v;jh_-Jidc;jTyb9)0uWY&{0hMq@CHu7Uv_HT%}oLsU*?z%8>yh?SH7s0M0 zTp(PO`nWB~7AwgaXZaWbL-@dd>!{#*$!=@u*s%t|FZx7A?Q(wT+=V_s*jsiih=82g zCR&RTO$cjmFZy%hhu+b;km6kfFBRSw!(6i#k~p8fs{&mln$(}mZtpEmpe@GCwT>k` zhFa>h8Kx~@ms@MS_FtlEt-)wjEvt3(tV#cobv9J6v^Z+!o&E26lBm11II0#R`p{=3-x4^lmr{%QjluhwL8QojVvMiPIxtAF-1abCSv{Y8)R zQO{|;iXet50)law+oU7zyyO zA6$yb$@{$&7gG23YeZ|{3-K>^&nbaMiqDcyY1VQYo+{G2bDbbfeeALNhCOm8B#1e4 z6Y#m8r6qqvg8sl_=281Fkq-Icj4sETPt=i+q>4yUV|>HstC09&N$>d zO*P$n=RUs3i_Zzk>lp>#HD!nzb!&b8iI9_VbNA`~n$n$&S)~ewTj=vwkE@ki@ z?Ed{km`UI3*n9g?`TXC%@b#d!+&-mnsxcsB^YGe=^-$~CkZ^2#>Db%YXL(;`IB_$Q zLnIqAC_aAjEisR9nkN9N97ts+B#`@wd>}|$S=3W0d)8_>ss=hTd$Ht&LNY$fNwdSR zHN1oh!ECt~)4ox88aJzfj00Mz)p|sbxB7heL8{k>unSyF3gR- za<+eCl|@07@`l3`cWx{Ka+BbZ$wSHe!0TVbjo8VMf?xFXvx+GN(p`=6crtz6Pwi`#!5dCZ9_{$(} z^d`&^K@6()bdOuy^Q_3x9X^jj15ZNb4N@NgCT|!4%Gw-`JJI1le5bQpVSoPwN*`>u zRw6`4EKloLqSijmRvlO_=h(pmVuBk9;COo2MeTi6ztvHFAw+^N=t}PRJ41y&``7w|@c-}UzY9PYwD5>e}&REM}X_kh>}T5NxHpl^w4 z{|(6lW-TL^S&iwsEbQ>4i_v;nIZ1JUZe^`(0ix`8Ye=dZ(>ibeMpc+K7Cm(HSXLqe zj9;a2_a3!meHodBefiSD;_M-2-^&$Fs@D*=K`|zO1Gpw5`OU5P`asUbWA6F($on@R zk23@vfyx`_+v2qo-Hs;{gQ|{1&GE=3m-n?0LgJ#y8L8L5S$K0u<2{M4T#dY6YLT?` z4GlNg$mRHBuZtMCOhi0pAv{8=)HC>;{Of;1@-5z<;`>Son>9GNY_1AZ&aIm6L(v=R zqa-Txv*1d?g1+MVEEh>v8;RWquCKl{rOHXEx!4@iIi-Njiw3kaEgtV)zS0fch!{wu z?->h!9;cuq>GG4C&J_hwlS~dxk5T?s(XoxBV+w}td&s<2LI5Sbo1|s>y|;{u;rcK- zfo)kVHmFI)3O~7eJhFxd zL2>49n7#dsHTZ9~R7*o|RVjOSS1#Cw@O;S3{GG3~9kZ*;=c`Pt>ja9RYL54&hJ2-^>7B)UFAGS^$mtiB+VC= zrRQwI1+Fa<5xF(2Z=TS6gZEGzAO$j`#AJ*_aejkz6dJT*7T1|*)px1F2>oKzV4BJg2}1oJcx7ZjkKk?<F1x+zk4(u$5c8*XOeQUbsQE%{Jvx{D+#llng5u55OiymcvC9= zV~>ZXL*j}=sh#?dIN0!l=RZHIxMX}$DZ#LKBJ`+6EeOgyjNU700yWr2&&gL09|d9C zjY)i6Tyg^RWI=n4gxtp&WZCMeT`emlDH|J-BpIEk2c0*3rL`DTNcYqf;G;1wgZaWIkjN(Xc%pXf z9LZx~Vv^JKh&s!LXu^8FcQ3PWx+0rV=@#!~VkwCBD)#MA`;*3OPW>um{r$<|PnVtR z=4T_ovh(;zRWAE?Kf?s%V!K1#yMBb;bGh`Ybj);eNz=09l{%J=|Clf$wA0af&U|@G zT;g84JISfN4uD5w7Ung(X0f-%(!H0a5pm&IU^R|JtaA5c67#UpdI+jw25T~F|4*(E zd^!#;F&67=g-K+zcrg)iti@`9i~6*qm18jeKLoal9N{ji^%V`nIuCpDf26eX+!6kkeB!yrh!AucewldXiBN z9{Mfl9$hlW>=kI>V7J8ikJ4j~#C!F@W%_a#C!*ErwEi><5URtFSOo(V*4#fB$Wxm73g1M%9P;Dm4BgTgxk-6UqOx#chyWIO#tW z*()5#_TQ4+f!kMBf}8+=^ke(#;J)}?BIY`zOWmZdwC*`J(T3`aS7)y*V1&8(>%aGs z6N9r)KWwW6j0YETx=r2x^gMovs!gO@#7i2=vs8$8>Ox-7kR60PHkVMwy7gyyZY+<> zYx7$Y{%3hYNa40+AsqddXXiQS$9yO-N21q)WWVm*@D}|?Fi1(#NAqy*p37RcSZvha)BJ(T2cX$89|2g*U0n#iY3RoVTaga^r(@U*7Ll%eQ(}b6Duk+*QT~*mDPnVu?aNH{FPQ!Q zxi#dUyuE||N~KKmoAny=q2oE@&e|#x^+t@6i5=&Q0N-?>S0FdWqf_cF0Zi=^u_lr@ zhgI#ZzG;jQt%N!fK;wSUQz~*)gL!FAWPz_+23GGcXz6)7KUH50iuA8$Lrdz?92Xv?t{#3aE7?Q@S5R;gkF3N>JCnK z3Uazue}KD0yze<>XK@*T;vJWh@8xWc&55X@8jzQwswwI9 z-Os*6+S>WMWxvu}dK&cvMZ$k**I;tK+21#|R&r3XbFxrH%iU;_u9Vl`)9 z3LWvqUGMhp|q zIekk|=Yz0Ej{k2sB;jT3mCgLQg5YP(Y$jQ-#~}jw6DvJ-%%cX4=?2S1xanASVfMqf zE{=bb(kU4dJO07x`$@AkvWC;0ENAnH?3OdVR3xiRUkd}%MV^`fnR3B6{-e01+vmo+ z@Tz9+`4S&K zK8~wP#j?LZ*M639q|o;+XW86)Wn52HPmWFQ;fT*}_{3TvhEht<;PQRsT8n)2=qAv9 zLy5Loi>gNZKe}PtU)?aF1fg_xdLi6%Pn;#cDT@^rEKzE%FEZHsEMyCtSGn+Y{n%XQ z=joS^6-%lH<0j0WNzyv`pGOb}Mdj@5EKIap1U>NzQc%x_TnZO8Em9T{04|3z@W6KiXBr!ZGVdd#T%aY;c@l!LwU z|6%PbgW}q@ZiBnKOR(VX!7T(!aCdiicXxM4aCdhLPH?y2?hdaLlJA~#?)hH5AFrxw zSJBY)UN+a5V~jO7g2>jw#Qs689rjPgdXpGn2N)3Z-kb^&|26~&;Whz0H60JK1YI@B z|FHmFE@+uBCX)q458TuY^kW+adEPOv$fd>&RdM%k!PZhtk-&V8B#>7zhUQR3{fY0& zOEU+)YCrWsnmu-~CMR6`bopm^oPUmscvNC*pvAGSjn6f`ID!<;R}q6T zP=Um~k;lzHo{#&IM_&ej3sA}yf3ov9uRth!tgCxV$DuG$^pWR9(N2(mkcmIL+Is_q zek7f%KI&szsHIZf;dr*-H(0CRi|ikNub9ktFjLyv{G(a3-Qz1`&DK!j#Zwy(+0c#b zEoK3Izr_DWE8^z=u|{3q>tM&l#B4kuRsW{b{_!0ER$=}96c|QqbW%Kms6HX(R6)q; z=VtbnlFw3Ivo9{dt%jvwDq%g}%U8Oxn)a!JmR!79sPsmJRwZ^?^jMLoVHly?)rsxlBmOy zNrC(R2^89bktf-^(UFb-^E64+O+@RD*>ka?u!R*?9BMiwj@*SH!iI`6T zcAPij#^`6yHOzZe=D#VI?w^zkn~>ZaXqAmdWp^1;Ivn(mh!S^+(dO^EN!*T9y1;wN zKoc}D1MA}b&iHLlH&f_)XqO{JGZ5-uC>6iWLQ(UeG!nV0EYs zn;rumF<2jn+P-Eqt$xO2v-5IKX5yRfq>8$C!>sEU+K^qGccsCoANJ{`#&*={2`p5Ck``NVaf)PL@yce?@pZI4qa#RXb@~Y1=Eai2Yto#%^op}HU4xhS)NL7du%Lu%h z;xf6m@p4;-&x^Lx6<_~=X-yy3X6nY6{e_ibU|tYBw!dZiiJsN2jTEU2W_5*%lg_!5 z+}sPEMufR<-?mnZOAxuo_4v2H`N>okC?i@YQQo7hh;A${BWV!UzsL-vqH1wOrtf$! zEP&u-*Pccdh%+?U@UgDzy#+XrLb5v^CO`WP<^2}wV3Fz5WKvxvy1M*f7%cm$cadX! zdn|c-11`=FQOPZ#k`87aKfQl=pc7A?>qk`R@78q=4FFpul5uFvm(%n8BPC7qQ z=agms$`w99RUtQivsNK?^Jm$9GS;?hi1?kwdr<+6&~b?BU*E;DQ;w#A`pxhqHe2>= z+vi6ol9wmO-^kdUhw%ARSnnOG;8I|QH=700*o+c@G_eW5l>%9dm@MU;FbJhZ527(0 zoeqaMnetO&!vU;V(T^SEG?bfezzJ8+Z@@lAWQDJ<6R8&e#D`lVPCS8hvaH)iGAb0+ zGD|a#jKHfBJH?7Uq|2!fs{y2wtlgY@Qif4iP1*|6=a zS$g$vuyhP7&b54;9$St?{9BkGiKw}e(2mUl6PHld*zL#i23P<)BqO86Z$-;PxsHe~$je797~0M-14n!QM1XrOR}{l;-xc8)jcz3PXc45e$cR?+x=EK;c9=WdzH2M; z#A0pZ)_V_R;$>)c>U>zUQp<#!v%YjMrymR+*q@=f#hA<@xdz%0?dp$a%t1hClXRY` zX28Aj0H0*i-(@rnHa5*R*hM&wEf+x%HkxZci`rAB(2A-4-&q@$mLsPS^hsiR381h{XH@kb*us{h)>= zrA%mDh5vv?1)vSahbxTZ@zm3Hed~28y!9)9DUWC+KwtXRmA8=%xe$i|{gDaAMksmd zxZ6dtvrO?=m6RuvELxy2$&}f-*pj+&0hEe+@TKeLc5B5v zr|&floU9c&SlU>I*89M`BZUJ8?W6CNViQAbp|A$RgSx^ET(A__k1<(c*_Xdx1=}ZeR(B9?~Q?Uck<5o7b6s7m8W6z!)T3>+%{Uq2mU-JWZ zWf!=h_+Nn-XQ?Suu=&9v4Ww0Z(4KD zpRxVEd;Pc9!wX6c1{9WT%0@{+={zk%*qtV<7B;-|Cwin6e6FB+Mdp9igJ0firEob~ z6sCI~k7xV7An8Z`NmO2K;=kN{YfWV1P4)*+T|jrcIL;@w0k_f^FNLh{67`*>WZFHR zemn70YE8CWyFJXTU!I=;a`i!G^8No4rmSS%WTqe`?Y((x5c=PD2!I%a>_EOZGR621 zUoyMb-@8`Yp77)H++)z5(@gh5`jfalmAh&)CTp2+Ko3c&G1h4V#A8Krt&JXlJdnij z_NSvh+`e%tnQy;b>Ac6CwNH@#0^^G2TTu*He`2`efdox*VR{s1Ldh3TU(C~M1gyU)Pa)XhjJF3g?KPZI&JFtWOx!(YOucabOB zDjGMRR!qF{rz`-QH1div_>@Tz2?#8wpI@A<(yzYu2PzX}+%{fG7f+{TYHHOQXjbLD z*mr%9UhJcwBXHQB-5WN!t6)3$4kJV~biXOMakfWrm@yrA2P;i%$i6^FTfeZ*vB zTChjwk2MqTW1T5D&l>1i505qk2stQ2uZlA9>(dEJTDmmzC_ju3ZG4B+SStQj~@`=5*% z=+O$Hm}rn`%j1#@6_Y}~^hkvI4N+}v(4*Nlo^5!B-M$3gp3ptJ2EC~EdcHl=RDT?D zya;GW%O3r}?Jsopyti!~9#v^gqfn|GNYfu1m6Aw)$>A*dDnmQU=b>jCA7Bm_A9?yrlFWru3`XJqL|MYO$9cHXVsq?&-5bp)B?}@;KnJf0L3@_CH zNXE7f4c_)S;w%;BVQ>C{@9oTV1>7l6DxHlrrK-%F%IvM54uy|rTew;cWG_1n+??fj zPRHA2#hlm$P(hRD&WC-)apBD!eBnaaFJ=$OB(R%-urld{#ue@=#8fxTGsPOjB4ja0 zzN-BMX5hoye;icE0RNHU<5l9uejy!C&jVFkSAe~y_WWXX1ua8!7%>H2{r6Ywxfp3y z8(A=;F5Ks(`)@>GEb5pk86?ey3-C*7xJKzBp~Z%$4E?p#615?#VT3LXZme;yLaBj~ zWeYgB;}!xoKb&wBhj(z7Dnaw$SVORnJd@ zYWNDM|3d}-RX|QQ!wB`zh-$@f=aTB#Ttp!%yYgb$BqVFGrR!=(N3aWR`jqy*mY7|s zd-tbTFeTO%nBS=VmfIr<$JabGVNqMM zg#h;Kgh1)nn(N8e?!@J0IF~$(;{sl}4n-oz&F}TvZiG6Ke$S)>_eX64|H(eaV-J4p z>P;?iLHcP=d9=13!!9zO4{lT7;^l{k@r6+ozAxu-W zD3*fmytI$zIM32*Gs_POI1{JnPRBIX%1=ON%mr)b^tvz(X?AKFs;^S@CbY-!aPGmh z)k7>(h25T8*vSfXq9<}}=fb&!Cn6>EFcrd}>`Yx;VgRpDooHN(drzk9Uk_r8E;_ba z%r!l`y79Wc;j};U4Lrv6db1Q@fzXqaXFhsF*uU83>z|L?Uq~(>Tm~RAqob`s{u*4g|*dbEf+MNL2BT_?9(?p975RkA*9#UrsG z38vn3$@lY|N42VI<)nFl$MRxsLPy`ChqVHCzp-_Jvsw3pY-Es<_n#~T325Xh)x=)P z16Bj`0O|`c!WIw(&ojEIT@8ZF&u8uz)_Osl z`W8G_6zC4dL7aJp(+i(MquFyfjPF}vkrJ)A@@1wXZeh|@71bMkO+pMCg;F=VdK30I6CZVNRWBLw3=L4CL(Tvem=EQuc@~(T?_^r^qeEtyf)Z^>d|;i4iDI7Q$Pln9G` z!*>}217bDClYNXS>&%_(9B?Y-lm+t3{XT){-zFHw8y<-1$H$1_2qy)^vLqvfB~;*- zib1pUq_Z6@2C2AbZ!^v;MgK2zspo-!=B``@-^8j-+IP9DaXD`UdZ%% zBDG0d!IF@toIc1QwZRw4K#C*dv6mQzbX(FTJw&XKnBi58Wzx_LrD~}T9b;=F zOs!rBS@0zDcJ6cZ$G+{-wz;P9EQycb@C&dphv(bA^IfBb_4YT(c9%<4C`n5iQUTR! z5<s(&GCx1RxsRw@zyHnaKYP7)&=8H+ znX4{Jv>sa=PADaH!O{;Qf12Nv1UY}tIz3iv3oez zohiI~=-E-AuK&P8hYyVsM>ZWBxm<&FnhI=Y72LUpN}b!g^k1#-(1uc)h-9*|J5!u1 z9Gw+_RiCZxY$$a9)Htl5hC^u+XG(U>jqU|RzC%dt4ED$>DdOv*zLsHwDhO>Jjq4kq z0e2=hhm0Higs~vZb=5ll*VgXi2*c|HqeQYf_e-%6hwii|T z@Laov`+<<>)&;fbpeh;-_7)>eO(9IHUn=CKf!su5A>Yo%Y_}NGVqtkB$(VLy$k8Bo z`+GSe@u-Hw$e>TLX6F#BEW_xQbU%W<>)KUh-P*=Ey4dX%NX>yGn~D0gcEcXgGDGV6 zyff;!EfiTONO8QPsi8t=u{&nQk_)u)NGtkRSsjmfL z9QxvFGBw_$jC2-~r{Zkdjo|nHKoShscJbQRHjQSUKymdgTEhvwF^G|l9$xZ857)rt zynkm$E`|ckgPD{NKutiPFVZwqVU5Y)1_a-)59zP+DHZ7G!*IzJVU*|R43BM<(6=Z4 zK$8wy!kK*`Eyh}5gcA{RKv8$0GUZEemuT%5M6yMY^zDm*jLU z?^9cOLESC;lwlc?>NMQwCJ-EjlJ2_qf!3VD#RHoJ+62+%5`s%Kj@&O43LB(hMhWE- zp$(e&W+BFOmi!AT`Z6cOgQ9tzy`_BZon$)`vnD>?a}fl&pVRQE`uEInfmbS5X5XxPBM9n*N({<=NDyz(l9C7dic8C|qIHuXP?Nge zrFW867L$g%Xx~A-_pNWQRQMU)6(CXjhc9TkFRoFYM4toLn(GC1dD$d*`FqF27@kNH z9It!bGadT#iV1S$J-U(`pC)(>fOL3|VKTYz9&WtUzPvH#zU~L<%%fxFKJI!E`kaF{ z1f=XooB5{S7z-Y3MS2L4Ryg)okYp$P8FmlzO|MDK9%+FmXfk`4&|;&vz0&E1Xz!-W zHN_scJc;8Pa!|ME=uFRs*toJnm0+iTfZethg!z+jIr?qyU^zkSp3N2j%Ub%2$v8@+ zv7ivp&c8_~gSM>%b#D)`vruB&cefs%;>TonM_);A2Ys-|K1_#gLAiG)3Jt}PUys`R z0C5^;DI2!m3ZH0~x#?YWN9PdX5fD&*h{iSrNu73&EF$F&uR`6$q|4j$+)`wOM-p#{ z63)p4-_Aus$mJLUIHSx5(*szM9X>q2cZ&*tzG&zx|J9kzxKIKO_ZCvx{K=Y0^dtl4 z77)L^LkC=j%Gj|GUXV+3Iip+#MNNXr>lN4 zg%&>t;Ru6IgSkN9t@Drr8%ngp6+uZl$p1?Qk+nrtK|L;QARsEa_19 z4IqyeksKTEvmP!Lv#}Y#+9`0T^mO{CGT6s$g*%5>cCwXnG60wWGVv(cnYT6_%{JzB z1LxxbLQ(AoCQ~-vYoPV9FhAf=V--@Q1m<@-do(aBUqmW`ask3q#O+hl&&{zTIeA2G zcXAL;5G&sGBq)1k$D3Y|!&pq(TOM-No%q;}W>sk7m?Ch;B@LnQjjfVp&uNkt4{@a$ ztP9S1v#oEhOh-IQifD~?IGje^dbmZ^K_v3QmC!1$EiRP4deyBq^(*8KOelt?y->NP zIXg_?fG^>*l$#@+A2~ry&T^Pm85%6vEpNKSQKF67Vv(YSM$5@@4kHWNAsrC0d2VFDU_D;7%4Kf-R)UfeUUJIh9H}ABG$j3CO znGodQU|HKBZEQX}9C&=)_b$+5GnOI_+Ps|coakk2zZ%gF|Jgr)mlZi;MUl)Z6rAlL z>#g>{xpTy_@O8vLp%`wOm&w^iuf9WzotRjV@22j}Ke3t@C~i34Vx74rhI@?asu~J` zaeF#3&xbojtb{Y=eL!JJ>)Li01O!ol9rloJE|^qAL?os#z1@?smPv*vLgL%hGw2~5 zpe~8Tk#xs1GKbVZ-Wak;D?&j*Ia<{bC;2;8{RLei$Yp+D@0SeiSaY%hCGU$D$hQ-j zC{VjmNBdS+3f>@}UYL$)b)<#!4OJ>NKEH)6r$QKFuuo>E9=%Y%%Y@FzxOXZ|B%hiE zKApQ{Fp}+Lrh7^N{eJBkI*%j$L(~*8zc3K0ntZph_pt9#ST!n(HyV;-Bk4q;yq%^E z#4xZ(crbhQ^s4E70jojLe9}}eZU@As$mW$~e>SqKm-I3tj^&j(>=r1SIV|g_lHb$3 zOBS{yamzz%0G9iKvIos5M#M_^JW2+WP3pUbRq{=R9 z71%V!4;xQ45*42*ej$1Uh6J5s+lQQyk#9EMl~q?~{m_ac~@QP#n14n+dV`?ASM_ zksiA(vHXH%fOqIkC(7bi%+BzLS!UktfuK4dl+Who4`#*KLHx*&)9JNUx%Rp z2iM4fSqwUW7;DAlL~PQ~gY(Q#oOo$e+v22NLz@+72?Zb<{;Ujf^>vlrR$tg|Bplq1 z1@W_L3gr$5t{3Bh%!YG=4*YRElU&FJpKpe`*VwZ_j!Z%LM1bB_QSbB4fzHB^YSw|; zB(=Y{4&-ASIxmY+WRblp@fYi?6>bjR=KUT6x4JW0=Cg8NGC=O|CY*Y0g8-8E3jiR_ zo{+}v4JTEz+q{Fu>BjHy#!@{^O-oOpB^Q01_lBdIA!^omKV>n298Rf-q0O;C-Up2>$E%x!j-oQ7Kb6=_Qv%Ndbh&6GKY zYCt{q#!k<~{UW5fa)r{i>I%R2J}@g?H95UZz5xmzG1l!I)F#zA>765x`3cJVShC9* z_3JXM-Sa&yvkZL`6qEOKx>ul&3*r3-l4ZI(hOrZy$Aog2_27wcR{xC;z zjxw_(2SA*t#Pyno$_dMN)Sb4E-knA~!dW_42KISaBzXvA;Pd4pitz?-rMp|N<}X+7 z?FSm^!=N+P`o~Ojoi+{wnb}f#ute1k4`kNazl9ZhrVj z)uuoOxNRF>g2ZRd+wZSn&{`g){gBBM?p3TOZVPs}3hui5oxJi^6mFFwi{^AO4Mx*~ zrCA^0)Tg97VzQlf!~%G1{{6UL7y8m$-BY)4 zc|u(uFwb#eIgwwwo~FI=Cf|FLac&;+sdaWe=Gc~7W?DoRl945%ImQKW2ub{~@6MUG zmErC7AFkjdus`a4OOhvIwtV(>2anOT3F5XVJHEaD*PGo{5vvK%7LY8?N<2}C zk{{I@V{hDXI#U>KYRxm(CH=Ig-yA2YF;C%qVczw&6iNugHD3?Ihoj$>tQ_SW5?5JY z3X2Bax9emA>X44cC_O#p%MV-)C{D_*d-fR+M+yuV@4xQn((VmA8Xs|*+CN#WD~l~O zPouU0q}6XKWutCnxxBw%z9KwPav8B~hl?xu`?Ly2hzO04zSVpkE6Ky$+(LPn0C}GX z_+Hv4)9)A;75Fza&$4vK!zxpm4%l?zs0Ri9^Eckgxf7o2@IS`B67S7L4!AmFvJq23 zxtTM!9TEgY!W#68diKTUxZSweV6d+R%`G1RpdOXS7lQ8ZH&VHx4=VTcFDgV)5t)Ln z8}aUUA{QUXD`@a*%&t0amIr+B z@o{4wO_^BILj6Y>imbQ&Qz*taIa}a;&Ru6sT5Q%E-wGaurT0|u8-%?{hCxk0Zs2s=F>Rf$Y$$|G|Nm%hJGQ(x(vmm7p03(>Z z9xX>;xGedsSk?^21J`hT=IuU$QA}N;ojV(#yJWR>jXTlN(MFSfmjg7Zp_~qVmsAZ= zc6gL9ed77Gdp@4X%oc40I7U^S3Oq=RwT#T?&P^`z{H6N|4)1=_iWml0uNY5bNB}fh z4k2t>JOz7I4b@&A$v5+So zTVA%547^K}x5nR=AbA7_}(CS1lNgGd0 zZF{F%2$^?hjGKS`(s|}#;+YpG_Bv#@pd|%n^S3t-{z_FY?>%@bK-$YR!<}I~J3XBv z5W3sm5BFsB1JkeVSOkelbtQjO@tZ#Ishx>9=V`>{7f3E^y|Pq`SczO&7o0dRO}{pe zcq=}MFPJ2ghcbgJB+!LbVsQx*z|({*k{UV`cy@m1duqCb`%|6wbqHV6w6aNli(FYK z4M2W=nh(!T#@Ez`lG7>iAK(cB1GJHpoR6UaH6oEtZi*v6J@lkP?lEJWZE;mc%hgAB zRUa*zPfST01=>-h6p|O!82dQDT@9zOA@t4H8>~Maz3dgHD}&KxOB~c?2&fEu)OchQ z6uH+V3W0g&Bm?qyeR?4P5CSPceGPkSu%OT*GRUAwEa+>SJR|G0RXj{zc{T9x zyoZ=va`N0&Hj_S2g`Y|aU{h{o#y_soej5x&fsGRs7F{fF6@+mXmS8QyT;MWSC=BS(oIGTs>+0k|x+R-(A1i-B^fEmJvv` zy|z*W{_YQ32*y+p@FnFYi&ukhmyOHn$J&jP?WKX;%Q_L|Bsh<-#(PwK&h^7p_Vb~g z-6?I#0CPB&&Ah+zhW#DyNYo!e*2zbcXO-Hdh9r9UwYQ&aAhIQPxZ25%vN+H1;J@h} z*xK}D;>}g);ME;*giXHNu3ija9Cf0JDZ~mh8U|2yDQ4>a>x1t6zM-n#9=?*Jx#iQK zAeDH$B43l@?fUqlB{-ypjMz^-R7A9XM1yBAt>}+I z+bb`w*-wkyxG~Zhd=^Zd!(W8>79WHtCP~Ovai)N~eY~#Ty5vd@ATlVz!J5hDTk-TM z0ruy&C%loYPyL%-Vx?Q;hMz9fN9vNezCw+f-@~>Fq!7xyhi@0}jT+_j{%BZ<1U6U` zvsY%YOTj%@aMc1c*76=~CAximoCnVE8$zNBxbsA&RrL5za=gxtA!Ras( z!beim%xTI0B{(P8%~=^Uizr}oiZ2JY43PE%$%VY;wpLa@q17)a@K6r@Q|4^9D_ZjvzX+=Sz_#)G6Qr4OOGNg3< zjeA(V0(EEwuWW6+&g}Ck;jWOE__F-T8*R#F0BnrgD7j_gTNN=N`^Lw4UA_##Jq-t^ zepS1C1vnx{h+nMB{IzWP9gM=>JORQwW?g1Iqv`&{8{ZW(k~+n_WSeN;?hzP|t}m{d zb_on0HN?5)Ao({14xHbQ@2%IbQyLV|Xd+_4OzM(YxjlDxWKCn5*LAmGZyO3$zZ#>qt90y*MvF1QtA6Oo`6C3L4;j`RH& zM;cUa11?;M2giaBn#0{LOZB(Xt%ppepF9_gTbQdtAq_YFFoQHa&Jtn zJ$lW|1gLJ4l;{@Qz2ONNE)X^3-UkyPUxnt@ie5v-Zml?d(boTJD{27Wy~&F|>&X0s znGk~2g@0tq!Ph@j0E17%V2phFJomMMuy}wwcs@O!*m4ggO4UGpkrk&j|H*4e{Oa2r z&k5cSsROMuAF&TV2$+EPZVlnaFXuA`8I-|3@a*zD$rp%7bkYsZTqe&k)RMzPR-bmV zoUfLl>E(^_c>!i@SDWIB^wCi4_t&&LEx;?dG`nrAsKu21RF|C=OU_(f@-l-P%H2Nr z4&l30_cj?9wjairW#4rZUUo_r#>_OLnGShket`6%64sG*`>aE%K}TYvwMYIbRV zaUZAV3Qnj-9!y2}yGu+cr>SmV=@}-2amaCXtmR3Q$nXW$2Vup?=HYs!sk{ngl7YCpfqgq=47%r6Lw+PCP^$UKE)L~BoNLP;v0>C42 z!AqSWFAF|HIiDq3ag^!>qyyk*_wCtcI&6OYu}LfvMr?rD>YJ%+Dt1tgw{ECzv{GIc zpAQ}dRKE0+5PqO9$3+Yh2U89gc>DJ4L|%Arv}&K+&Xrm_awyfT?V1KDB#S$AoT+~h ztNN+5Pu~cJJ;)O@KQTa}7V`U}T2~-eOOW*Sy?xscC+`bFW1x0bH86!exv}}V7gMSc zo{Fk1$9~AGuGmW=MplqnY2EBv~MU>&qj_VD+~_aI7=y;0-f?GAkvUYYSuGb*$*v+ zfg7xmknbj^kiqnt1k@Q(yy64JkQY08dmC%=$*H8jcwLcPsU*K{n_Gs3K8dMdW_(FV zjg%qaYKLmc4>6x=&{_pulj9^`pFf`QHG9TQ{+T`RmC!2UaujPO?hmB5ySra>M{QkL zXkL{h|2I(0gDOC$(=anQcb`O-1O=$rNq)Pp>-J9u`zty0=F0rZTbeW7!NbAP#IE=S z2HLikr+-W6yIviYv|_i`GkGbG`+p-Sd$El`YV;TZ^O~}V4E^H>JZa;w8d++~+tc)C~vP)*Ka^qYGyMIGz9dSNI6UvZV z&V847+y`1C#&L<$rcns zguK3dQtNX`Y@%C}0jS^6Dk{LRnbhv?+MUP!+F}Tztlbf;hgt23GA8jvn`Ob%B`Ovf z4o`jim9OqettQg^j%>4C9T)+*$&UP}?lX{jI%B#(m{vCM#4(B;mXmtS1+!qs??f4T zbhuV)GBX4hKYh5}zXe;$eC9H-Z^D^CQmiO)XEF7`G87tY4e&?hV&{zCQ9v z68KF%MtSAPxUwCGFyP?}EydhHDk}OVTBlaY9lltwWi{iBQM;n0>(uK^shU>0T`;&k zDt(0L(e9`1HRHdr?-(qOozU1N!fbAU7!-qbUb&>+vop(V#clfPxmjpXE8Lfl zbTh#?<+M-kX5+>jN#4|{`1!pVO$?cfj-?EmTP!b%xr)&2p|u1)!xVGKvknvqVapCz zM~JLbZvKk6PSL27pp1Bov9_0RWVeyq^(~{_s`W8m73xX&s5U*GRQ8GfTj^h6CwXUb)Vr=!5BN}j z)+hQstI4w{rkstxiAjC*pV0}RQ;dx;B_6FW zg7^*wd36QbT2(s8psIVc7A8L3`XiP+YH=9knCvUa!V0-SG8x1718)UE@;4|88`A`i zT8le$InOw*y&yLZLIV&MP2W)p=Fx5QnL8ULGqV-NP(X|N*A*FQGV+rc%)@kVE9xQK z2s{x8>=F3iX9k|Bt35A50)CsRzyGy*E(lm3@TjR`EKcZ18}ie?QwDkU0X32UnslF_YBWjwUHGVor11xqL8<*f{Jb4y0kq+4=(?uA zm*&&GznI!Hxj=Arn)9{06D{rYBUaf{11^ru{pRX&^&0S*)rkGRKV||2aRHz#bxJTo zqM4TzihrY$Pn#w{A@rNnA7zVUXVv4f6ZPx@S4Z|}F?^pf=kz(!N8%$Z39yN7mJ?i| zk^_!-Upi9t%$*dTpZBEU`tse!3eEh^m2Vwf*{iBbm6(hdCL{plC1gFs{dly>I8_q# z>Kt%GqW@F?!=`4h=stxuXm?*|WE7_dS4J62&JVfP#^LXs+~)*AQ9UdQ%IiB6f`QY1 zW$u>}0J$k2uJY8sJ}FOCX(gZu+J`S}A?-QjOrvYzBF^9OOL`Vdnwk7kr{rRx5#_oa z?p`72!OClg{T)oKRignj8Qnk!>#fs0RP3?)gmPb8OPo_>s%KC{go8q-t) z-Ex@!-UQ%0PiyO2@Ajv~q0HJ>E5Onx&`aoAGPDmLg2g_WA<7~p#)Zh82_@zh`}4qE z$lPsM9EP?Bn2I{+HY>@dUt1x7o&}fR(pCKrfOK#B10X+Bkax{p{6>&J&gGmhhx$DA zQ2x{csA0Kd%HJ@G*&%p=$J5;Fhx-;n?$!;c!~Lp4Uwr)^YLbn5X9|luMe8xOBWADI zn%@Ti8TD;2>^%z&K=g{VDxM-mKT4#4w(vR>M(-Ss%0|~;LTLjENdCc+Aw7C#nK$q@ z`Sum(-$$+~(}~IZ_h?>H?iSm(29$|jXB-Dnmf2QZlPg?;$Y7yiywU`YH_OMjh=3{& z+V<)br~Yi?HzPNdSH=FfAAby(zknuO7KHRTH5R3m8U06!rmPfFX^NnN*R^1BhPUU8 zar6k*uZU_*ddm^R*ETtIs4U3c` zjTwQK*tWd%+Hv8h+sl=Jf?Vw4NX#10{EWDL0=_&K65*#>R`}F*41fJr(lLz7-X8Wa zyk)gB*ZRXS{F@uTH3|0^m_D70X$#?C!xkt&dV_-LaIJ#V|9G^B#n}6soP-4u&b}t# zfEa4IJopOtekeFn#7+gijKnGTrC&44Q=F1<8ck;8J;aN8i_SowsWs?6H{pyB`M>GW z-^T>%vER+y@`S;h84sKl`G8Nmhr|xn2!OiL-)6!4(rhfdO8C(l#;s+7hNsf)gWoIv z&>E)%(jL7#CNg_Vy|68!+M!#{nm(8GF-g@%NHz@f~rgD5X#Dc?A2b$968 z(AXUOw)S&$S~p99a8?z5=|2=|Fn#VvL^TRcfam5bN!B&euZxN4D2P0WZ(~n4Ha5-%yOg*C_9zm~Jy5X;#I00q0J^c&-HGBeAm z9i0FiR}*=XI1exmI>7*vSkGkVBvaxDRxaqREMqM%(8I?c?0*Q|f4tFCg`59>w>9pL zWh-zd!$^L{&B@u&GGDA*3L{Yq;*6wnhL`eN{_i#s0^vYXq{iqUY6c27k5bIr{s&&w z?w)Swpf_%d!ZL)Dg%DZb2w&^Mu zL)}+m;MI}x=gN)-6f60y#As5=-zWWF#t&!_Dv4sM4sCD~+hmcC0pnMo`SanQOthCE z$~e_jiW~_kDY0;p14v=eM{!}3y0+D#C0LR?G9rz)$nwczQVl+FVUI$ABHg3^LSpkx zfX1;*6*kXm8S8}%eD#4Kt}rkL^${Q^EGc6<*SX#doem5`_INPCYBOg7wSAaj>YEV! zug3EMkBaaCyo;ivos4w0s>f-!Mnd^peA8y22{f)VX+u|_LcL#7`Wari_&;6!+Idn! zfKOKIj1m{P9_pA0@goK2YgB`>ht*x!cO?vs)?Hu0n zc%3{g?W&~EudGzFEVnR7SKE6gv zqm#tL##SNo>Hf9sQW>Kqvwu$@85Q8^BKhu4Ux+(TIb$G|cZsQ~p1_qjM=bX##8dz0W1h%LUk`4v-j z4K>^q(z&#-fgnu73y~rvUS3eQ+)rX1vSWwVrGpHaX3kr@M&rL-8;pTN1de{<@V6*f zI_N5-Qu>VhlU?~XBZ4M!sAWM#Rg+imrcLEr_d?$N!({#I$3Z4QfF1q#QG~H7TVB~g zII!dZoAmqBxu#OZQOS7S;^xG5yjP9H&@k-3&Av%Q`gx6#C{}a7D961wJZhD2!5--h z))SKj$^7~Ykb(GryVqYmkx!B}EQ7Il0rNm_ID)U|fFB$$UEezmgAtveXa+_0+n@Hg z1>OC7K^rrRMWc+B{aVmlA@MTvSI8ds4hV$Xl~hfSfwff9L`<^ORb{jI>Ib)++_(-= z|3wXWfrbzRwz5A9JNG{eTT6%r11Z;Wp>?rhgs`_33C#oqM&p%#_-jN>ivc8h50PoU z`Wj^hZ)`Slt)EpSC2mcydbU@$k5#yHbS8&4F^OM?`EOV9pI`iQ57qxVN%>=~oq}>y zVVpkXzizSP8&uNa`Oy!OId^0VFz=-$wKOhA_`<@%uM{K1{}WiRiK7ye3s(>NHGqfW zacPisb*(krkSCq7r5o?}=jLpr`C83Wt@Z61Phh$Dfa`s{r1HA~4@*58qHAA!c|Liu zdb+p&y5M@;2myE?8vgy|o^0MogVP!DXvX`{jQ6ey#_v(3fFq;`85zG%G&>F5V_;we zdU<)NDiBZ+djkWjs4YB_YPi~SD`XBtnsZ=QZx8pywS`~2{a$GvM0fnsmKE1ITO~}# zyf=k|5P{=g!O8+Rp-{GWdj_P*-2|3&Wl5ID%&3l-43Ng%8MI0%W^93S93}WF3jKDk zcI9rgaS@H7RFAv4BlD72jG)TO2z-n3?!*tqxPu!}KPPdlpVkPRRpbWsn=vL2FcK$fVMOzJ5&9x!j#l0z6d%o7F4`Q2bVB zxygQaww%D>Xu;oNxhehw(^i-h;61e7bZGlz!aNyr;&A{7Am2BicaUF6{V^oMiQF&^ zD%DDgx^Z(2@K{0P_|i5_Dz%ur*Ek{Pfk6w{DaPaM`X1l>mY!8A$zL<`8Opr{XpH-P>CTHpUl)9Q_-@r? zthGTmgc15RP=wTO0C{TvMsvoDLD96)vZ#cB`z5^8A$LSrKaQX#PL#V7xdJ9Bem8`t zS_e$z)KKKa#t-mT7NcE3n)QuOD7++OMCw~myt>J$7Y*{`?Zen8jaZrWtmz!>9F5d~ zD6EA86w5NzI8M?_L`ez1@x?e-_AB{%yKKMp?l}LWGdDC8HqS85`uL_C=ojPqwCDu! z+CoNiFPUtBTl`{#&FN;@F*-I@H%P?6ffb!b#V;xf(aF^{SFv0}sm=`R{QUgI5rYE( zkJAqV-{}pYtkcgzXMjD;|Zv9dN72){wf5*hGL1Z7Bx z#XgRRzDk|Yauz{f@U$XlN&j&YY<1sOLo(R<@n&FNx8dd>MsA$nDb&zuxih-1*m4^i zJ~B}v>4VkOt!4vyKWnK>-2IrNTk)6vGG0jC(gmYOfQ=~T`Twx>7C?19TiSQ<;KAL4 zyA#~q-62?TcRvt3xVscb&yr{&j)O0a*x!9?xT%8YSyqxWVW|td*%(lnsx{3uy z`;`RbtMw=_jowkrd^$Auj~5uh(U1D-Cwc+X2AwU^fHL_7+=#9RE2PU$P_Nxl5Q=w#NjapF@L+Wj$^L}!i;j#iBA&@T_YcK4bT z*?cmvK@wtgxo0@dCbRK0RF{)5%B-mpRho4f#*P9sxF&h3y>R@WEVc=^peIl3AiTT) zE2hn9Jbqd7mK>sWM{Cx*5c%1#L7lKp2Y3@@%*c>TvNcP zT*I@E+n)Smsm56P4b-0}(f;vb-R1sdeik-Jn=;8hCGjW7h+qYLe!MDB>Z2weGORh+ zBtH%ce7@aASz+Pr6+q6oo`{EjR;gi^uf~wz6dZFrqP#SO;^C!Q6NFQdC78yuEYy-8 zFF4PQrg|}`WRp-zKXy_bhCez_w?eKjHqe`h2GQQ<9ir-$)5Sg{&om1EBoS$xyh?qr z12Q}x9uF{SBQ3mF{StbK*I_(zPRnmylp7hUW%xRK{)eZas+ZJer3QU@AC?UI*87d;fFR^jK!IdHwm>odlpGw)OUW%l%VNd*`Ij z&NBR|{X4&L-=V|xF(tnvffU(w2?;;{&Ti${{{x)y`h6n&+$LTi!1?B}*QVXe{}+`x z{o#6Wlqo;CD|Ea&sB95oxINB)_FIhE+6*YqVe^3BZmk(b7)-;gAPD?Vbv09HvRn$M z9pN5bovxkD$=dpQqF!&2TzasAz&pAMP_}2I!;8cgvRJ!;MjzDWklZVpkdGMM`RU~) zufQc`K_kPULS3YtDp0Zhy6f}3s<~YD-+LK==eHn&ddS1vq;3eJ5Z2ENoA_Ic0HMsg zA9}KK?Bj5+xwG-eG|YHp#&^_8ZOn2{jzz90c(G~{gD84I11*GjQMzg~@yN%EZi@sf zIP9i>Qb?-4SM5aCs;`<1tUVUaKXLwm9&{!6YXum*eh8d3mFMlcNB@U&J@Pn9K6xae zHkDrDEp5-Bvi2)iA1U11pfbY8l)Xap(O~cnrH^EsG{F<_a~EdI2_#3=XRFh&%(^kC zn;5M+go56|$d|9zZYSJ*Cs9US46v)&R(YZ_Xs-&2olRIMy{yEh~~5r@?bq2Put@u$k;-K;|Ie1jnw zT8mS&vy_w+aCz*FyrQ?lKzCBsQ=m8KyA2;C@-`@QLpY<{f5@M zPol6Khc}|8xqH&4PlD~!hHZ)(0c|IViv+I;75(-eZN5HtLm%`B?U97;sx1tk+cA#s zUZcYM-1QP7`~)k)uoI*yy#CYoY2tynsA@qVtZ*wySjGBP#Ve!$t2%#u?nGOI}x!VMrUBkcDY z_^5u83YI_FF4WX1M_=@6xi_Zz60~}ac7Ch730U>`ZYg@>vAU{PJpG3l;3zHVA^nem z?5IssU;4L0u`qk!u+Gf*fpWC^Sru~zx(m?LdGZ$r2eiG(=UB`FrIGhrW*b}4DCGrH zM0>`^_b}?I&i40%Ks(p=-f+BGp5=n-h5_C#jmE-f2>GHU*1oj1gpwoOZeU;_eLgnD zzg+~Lc)=NF@1=?)$MQr~poW2d2$(W${k&<`KqbAA58L0V!l<8Z%(OvxEtRF{F;8Y(PpY{)%dx1BPv0Cmg1svoVWZ^?Pr1jn>7c2&(oPbe-=t3FCJ zhH7r1AWKyh!w$bb)8#53M_DcfUtRj*DVdY;jp;d6a~dc{S?iN#bn~Yv z)@NVf?PDGHOiKWQs5nbZbN8=Z?}$|PvMeeU27p5D4k(|L?wJZVIx?ahRe!SCCBW@= z@u$9?ZLXLkSrgsWF<3i4w#o@U;}2~Ktp$M*SeO>ZO=J_hMvW0OGWr4>(|^(yY+(Sb zr_7RPC-jNJCG8(+ELe|8qNzzX^6jAl{bC``H8sUo==Ym75@ftD=+#%J5M1arFkRuo ztwPA?DM_zvBIZO}=?2K#TT#T4JoW9#Ub+2<2pu=j4Pr9!-0T((c?BDM?=zy<<<0X( z^QOS5cD_pUshqfB<93p|;dy1?5;X!VUpfuN$&0m7T}^e^S1Wg@_>C8iP0S8K!Ht|s z^PgqI-oDIU^=&y&cd>%c#ENf>N_RbC>4DZwaEkNR;Y9;$A$+VH+nc{l{NY@lpMsaA z+fa&CilSKR+CA?rO^fV)j9MSNo-F|$H~kEh@(P9N6R!eckhRTJT%SG;v;CMr;}NzX zkF0J$3Z!l+1gCdHF8a^z=qM#fHJkaH#Yd2X-x^llhaDObUR_@6IIcm$fc|09lg}VH zrG;KS>`rji7cJ-2lLT{o?q^r|cI*TNaG&3t(;MBni=np~)MEJM_1?yyz}L1AAg!uc z#IW;Hw}b>LH8*)~l|;|;g8dPSbvp?lG`r@6E3p35u9AJs3fC(G=6K1qWXCyEUS}&7 zW1uqmy+)d3;oQ zyWKP`(Ekx);2WvBa(3Lme9W^wYK&(~57rW;Nf_qxboEmB_|Hi!x7L9_+DjzZ3 zeb}jF|60ZG?K(uux8>poAD>=|&OJv&mr{3ilGem;ybO>4f;d(Xj~9uP!P{!}`fx5i z<#@0X6Qc2N*R`1p)_l?sD3nUrhFh~KX{z0|pxMdWXy%`ot48RWct51Qtq{!@1j>z`f9JVZnsE^AO7ha=x!u$5GX$Q)zcR!`Ru`ss7mu zouB#`ib8g^9YVA-EkV>jQz$dt?%^ax;&+hxi^+aVFm$(4dd{~*anxmA;QbB_RHz%$ zLfh7g;HF%Xh=^$4)Snl5b#s%bOua&RMI}$1iS<8c1sLQE2Llz8fj$d5V8{4apF1n6 zW&Dd==kR?KL8%Bfr;ZDO##`j^xV7emYEgruZ&U_GGR47rApL>_r+EZ`R1SFR`7qF- zCRd7fuxBQdNZ%V=F62|aS7B>@f8olzxP~mCkQw-qXjFaDpm4`}9N1^=q+~-ce4w|! zA8cu~;N>X6o6`QJH?3uND$XILO~;_*d-%J3 z5s0x_e#SagkBzSJQ&95bPFrw0>s~>578-~E-N%d6y?PhPV3Ha2f}bj8V*O`PV$3#_ zQxgaE$Fl`Cx!P!joR4RqcG}HCZ_^`n_oB9Lmde7=D2=YRdcsTGFlzFB&CRL$QfIEN zs8U;!(0PAN;PZk^N^A~mY}d<;NRV^7tQun7#ucHx?C=Jfj9}G?ec4v7Xe<&E5~KY1 z0IXjFNK^lzk8V@|s~XNM1@_ zun6L1=Pi3(pX%#?j2o|8X2-_*QB35o8~pYXMl-|C5e{T%WA z-*Au%49qlmrOiR(PwE{h;mtJ3Y8z=gU4li!0lvB)S)#?Zvzk(Wa7WL5>T~k{LT#YT z#dr|TYg@1gcu_0OF@kc~NKyoj-yf@<>ET9++`kw*cdd{HLVH9F2Xy>jOfMal3eH7e zn(m;r7(S}>{; zPWl6-8IXKCz&AfU#Ksf{f4(oEt)+~$48VHH{4Wq~D3r4_KSiS;pn7{H64Z{3&z%9Q zA(y0B40wuIJ6sJQ3;Zum_+J(oP^$8f!Q9+@yL*@BbJ*(>IXnFt=`1&Bh|uSF#28TCRl)$eDuu z>ZH)U%nBRaKD9SgSF4-zgEGN_)0mC#zlQPO7hBH%_$_{W_;k09?5E!H-fVfZj$=DC z0=yWb!4P4Gd9evrNqzs51OMk5q!59Sb@-1ED!kTA3_KeZemqfT_cjFrEE#h-?uE|0aoDUFf+$+E^D*i` z6hQ=t`5@r1K*m!lFjTEOwYo}(i*xH`kCyg=z8iSL)sNV5^VP%{#TMpgFtIG>xFOKum5NDdMmZHCl7 zYKKCCN0S+oyMpWbL8Ib8iy5gcoV9Fdw%-QN%NxTQlFjBPxS0j@&_lrEK%i8}5{;*j zql;kmhC!`<;M)X@(_zVtjE`eb285Cc;ZOAs4BUMBz`W@%lcO?`PgUyi%;s!ldmtGw z*!?D&cn>DTaEyuFg(CWHY%}>0y6Fkldg1Z(w~ir;Fy+ssjv9k*7PA{Zp!UVjATuS! zebD7W-gatnJKdgJIOtm7_ma|HRV;GfeHP(kIqa#}FkRR&=e&>JWiz(Fid(L-4N~{{9(SzgMD!f!^8;W+&Iy~ zvxgykDTW`gMq^yjInIWVR%X2AMglWcVexTu%#~^lklEo>Zu*IcjjCIxg5o6xq1VZf zk2?8at5%vHet52AYs>kvwuY}OuhQEQa7b?#uHW-*q)_`UK<3y|zpFIC+Gij8ttv4( z^##Ot`YD*h@hDkFb{^z~N|2b~W$jmnv2E@&u2qH#kZ+kqX|TwMcS7(j->00J|?;HqH>M zOrL08A}FJwpI}~CgXyPS`78IvM!A$eO&JKPMq6q2(yaoVUzT%^JgtezUeLHBjaFSu zR{$lvy#U5*9T|}onp;a^z0wmbN)$!j^g-gK%jE{a4cWTS<9Y}P*@bWBVWy_04%q5q zF$$I@_Fhgbfq8;dsS$QC(6>@19`WYF@osdG1UuXL9|(sLDpz_93~X9J2H#m1ut`Og z?M`d-Qz|`Uqt6U_t`Y7x*JrGN7-(_vW%pxj30k~4%YK+wY+DZ8-~88|l`bePy;^oC zu$PRa|0AYxI07><+P(WXC3NRAW^1dJgC4J7YfGpLB&gKt!kM~vdXrAc#Q1!8DsvzK zz$3a4T#t;5ke%&%UmL)2naJ-WJ|Dpx@%lZ{{Jt9T?Xm-`hlq32PqQ2|rf zKj-?HOwJXZvq<}^NvIEPE*mOUu)p_DG687HUsd}O+G6!^3xmm=P&yCfAMshmvAhti zIDCNLT{Zah*PiQ!&D(O`4xTJ#Y%@P!U!DAw$Po1o=cQn&a8s=^DTc-tl;LQ;nLrA zL9dl-Yse%SbYBe6(0Vrs^qt~ zg*x{kTI_eSq@Iczn1rM1Ding1r42O|{U2l?;Sm(bXf)YsZGJxrL)W@RQGdo&{^?kQ zYEWZA^ap!Roo>1)9@4f*`8=C2dO?a(iI#Pvq!U3xt&|ql>YIcrumVwjcG(JJ`Mhl(0v zC*1kx`T1_|=i{i+SORA%1xK%XgT>LweQ&L)QBYrl=9%6Nb0_yTZPNVLzkOcdBRU47 zf|)}U5XGgM2fejRp?b~umKftTOTsB%6_ zea%Uo*HdC%=GK~|^`r$8u?%{eVGN8Jsb8C4UCvMi^7R*uC-#m|)_UrQLayQVw+2*0 zHpiwmHC*n+>k!3=b5o^5(3gFko z*zu3GY|vH$%C(s(mXp0{6SoSh>2`*#|CQUO6WEZoWR7Vdg3BgrPAOLPuor8dqfp&4v=lkE4LT=0PlOkby8 zqZX>2ZOR126i*e9O63cMuaz(Sm(fcTw#dAkJh2874H-GF<6keM=I`QTkTzDPTyq2(oyj?*sury)`U)nmI()F`b zCDXdBgcb4i<%M{%33b8^oF;oHcyn{2MgX7+^r6A@A+jJhD^EY@YUZJFg^#{QsxEWX z(_;D;nFH{I`3r?5N$3tQo44?|nca!kjb~jHJZy|KrqIlsZ+dQOHn58=rf3cjj^db) zR_A(-a^U_w>@>f>{4WYpaCZ&{A5}TFEoFYg& zTSPTELKn6Q#xf`>v*UK4dQ#HPFAM~#0_8B;63i}Qh<+)SN2mblwHi>{em9K>gv-z@ z&b+Iwuhmb~$~tWOpL`T6=EhvBJBX$HE3C{^xI0G_Z$S(%s+mRA%-6hxWWl3SjCv+Y zFlWP9B+)j7W+9P&Y=9s)oBTYG#VXT4l4jA)yDodgMT&&wlm4HW><_X-!&D7G0I5za z8d2m?J-(`!5eL8D4(?39gIFa7O>UkHt`UP?$37xGBSw;VNhc2ZN}Nn1SJ6su|65!A zm56J_iP8DBMv_IG$P=wP-FqHciLMM~ zRiRSlewqc%EE4mdC0JCdCP=zOb6Werb5=7uSkagzZ2LYK$spjYN1e+?OQDmd> zU86}ywQ6NyfS&$j3xn2VYFj%B$Z0B1aSEl$+t;c&v{#~{t~BzgWMGGd)vgn12&dX# zn+9!O6Eoc%0o7s*f0tFlI4VdLsOD1tGOUT{btaz3)^KRI&b6&Obd(}#X&szK$-P=% z{VFgME>~^dO|;Zmyp-oM!r9v*uW~Ue_sOt{ps001tZBW$b_)pYc1!DO|_O z&Qn%HOx=Pb8C%POcI79-NXFVV75X~p0pVq+mVtfeD&u06f>Uk0s}cqtils8GI7xe?0@QN% z)-<2_oDNmq?cNnlNj#pbGN}_pI0K`R305?-;;MHb7jF$5Ts1Eu#F`tPzQ_Xi?Gknl zi^#N>2_=f_Uga7bpQ}L{t;S+yJ-XYuqM^1&>tmp9qH{yr_kJP4suySRb8;iOTS9I9 z)sHfjE}rivvW&2%u%M`^QXgF7g7!Cb-D35clQL}|OCPmYJD>jj`9-+KX8tnFa@xGS zc^Wl%^^)dlNu>vNfc3Xp#&{F6xX?k2ZdrX&&z{_wRbp!ROh6F19-K6P6oxT%qcA0fG(dKf_O)wINY?pviN5zQ8jK zfU&*TqQ)Tge9P;Y58gZ5nz1O^FMf2$G1Rv5MJg|mF>S zMJm2kq&}q8KS6lP_P+9TA8e(~*egX@Rkkot4<&-Vzjckj$SLIeK8}ORf^vw4f_yXP z-*$A&e1)z86d7yBV~O}CoqLK8GRO6ydE^r8VCJMV!a6uHt^hOoFXIifps19Ox>W*m zOKTDJM%T`*?&jmg^lr9PuT!a-oDL+)6!lS8T_e0&9wf3d3V4H%Z$q-<8ia!(kn$j; z{In#Ae)hM=*RHiq#2pP3AoJA)77|F!eQlRqU606(qEJd76T^NLPYP4g*f>bGX z-oS=@+0o29$lgHbb1kzE*&r2%x`oPg;<`0jfM%3G63o>^_KzhjI;g*>RNY_m3%tuv~xY#&DW~FUz*^l77-YbGh zeCLjv{9Dn59iBnB6|eb9ukU%_gKs+0{~a1=6Dh~#IfC)j717)y<@YWA(`ycV*4w#2 z$R%L-_wTG6Om8{Q1pS7o9Z22Rv(dhEtE&DQp#`$ceC{|yZTedKw2<_1GfmBJwUV*8 zvbQb>78QTnYwuB2od841(t^^&-$8RY{>~+0gb5K?h~tK!TaZOAecqt*4OMk;f)8($ zR_%+6=k-^6@<*q%Jjk?aJWHbO3kgSzUNNr6#O(fj+Q$@ z*yj30x<_^tPGkW?e*d+GvR#(P2FQ6nECbA7NkOnVJ-Pd03Cc4Xdt>9YBiscJ!%ty1zjH~Jr7bQ(;|%!L>>ahsH?r9HJ&kiJ>T zCpAU}72Gfv+J^z=WQv(78U7}9HPj7MPnz-yOy$H$I2a=J^^5gP1}#;rEG&ql?B_B} zT3SDR11%H{S(*KQaykLV z%J|HM^6hq2#(MXiH_`h~(XCK@6I&x08D22J5`5NkFAd4aDj0El#VXC;zp-d&yC7Ab z<;gUtNDejS0HuTP{(6o1fMbt598r%xBfk)tDj z!*`8HBXcccB;G5*M_E7aCj@2Rd)7SOoe3c!W6!udE$x9 z%NJ*FXMW`eqVCQ;FfKQHtcN9)*ivXUpO8Tp87jjx_~0+>N(F*0R#`aoXex>9?sq|$ zLM&BfGQNyg%G*O9Xs@6KK$U8!$hZ>U7c%Kv-xP9IW)ee0frXdWV-C9(RhOr)>3!Y- z^ClBE%A6!Wnh5hLiXJVZo1m4vMr5sqr<)q07Cn3`lvXgnU^F;a z;_Xwq(c0{f)YKi?b2OIzvi|T7lit09h0mniz4EK<0AkUPL|~J!Ym{#Y@EWlD31NmL z%h^(oq1ux|S&^NTtFzVDEu-Nn=`Duh0|!QjsJcA@Tb17XU!Uw}qL~}+o!YhH{a;|U zhD`Jbe(GD-%ND_;9C(F67GGdzgeF}Nn-j%^Lb}3yrXIy^EMxxRkiw5sIuvcLAwmGI$Se(*pql%=E&yX|AQ%AWci zvCvr;`uUVzvUCAfEUy%b9k*1YICN6d#E`I5lDQ7Mmb`5pfEs2J44vwiWqRz+ zCk|a!ZkS)6T?RQfejok{pKsXw#(ZhFlLI20$wo+F4)eFMU{A8v5GMP zY@33~9Wo^pY3zdRFca;pV7f@&9@JU++%P>mnl+J&&s-A{>l2fws@#A(`$cdYB%YNO z?KkN8&Lrm0eO`Gf7w9yrAG%*&?qCcvEo-P&EaDV;MGrqp#+KK?@&OH5hG_-{;PcOv zT|Yy1-Q(#BAqzksBUD)@V!0oAgLlTneEBA^xo%f?u#g2H%prhGwEQ z(B-hIpXbO5xAeox(!^A-RQ{e<_oLM;Sdw+rHY~MTVk9(Q%*Z_iF zgpxh)K916|!P3cBhcojrGmqK2X;)`a1-r*#Ya~2w?s7>fSb7>4OnAY8p8Do;cbElI zkc}ee0zNMeewvUlm*WGRw@MuRXQ3#AUw;QfkyOxmQRKB5Kh$qL^TFG1Xme1%E@Z-$ zEW;uLw*p_h^GCbHwPFa=gjwj-9(`6fp(g*90EsTRuH=9X!;7F$&7OOs$Qq|vN{)uB zq%)eQCB-5N9%(}`Brh|KsQL;%vqSM!m6EBB;N?*KEe1l@Me1KCC41Ipj<%bPZ&A*jLrZUxx2JI}wTai#5QSi+Aziv;_Z8RK+h1mlx~RF0L;Z z6kfmSz`*ArSZ9QHW|iZ94_pHdTN>TC4dvy3hrRveqB<~4h0v|p^D%EQ(zCkHh+g=y zaiBo7p^<+EHFMH!)BqX7icZ!&2VUv!cHj3G_caaK%aWcI z0KUwYKbIykJ=`c%sT?11g0T{k>wI_W#H~{X3Q=!#$($wHT5!Tj~d#qgiskAyVUgly);L-GrSkiv1@59g~QxQsB5(mAcI zxFW%lqp|v7*am0-HqI7y--V+TA-3}<4&sWW7|?BKmR-Vn;QxTdzHhVB2SwPDv}xCw zMUr^GLa2-=oii3A^_8z0EXO{(87W6Ia>7iJ+GDPUmS6_o|6+-9@;IKv&W+}Y!IZx% zd8rfp9PbhVo86f)LeVTTjl=OdLPw~kV9Ff1@s^20ost1B%Jyy5FMr`9f_Ys(xi}C& zde_GfhTCF-|4<#hkx=Qgg)KnDXs8?`uXY8un$cD;bv;K^A4HqnDq!cUz-rRhiWv!6zW~jiePB1H|o4(3YrZ?>Gn0o0e zJj0h$=QTmj>Wfh;NW)6P_O(E+`QH_43_ROq|labTX~9&aRCExv$eyM#)q zhrD{BQ^>?hR$A~X*R@+)qc@KXJbw@oN?_Z(L_{v4?X#XBY>1HKYbt3{*zmc;V_w6I z1rBV1m<3f%+OsNcPMWde{XG(9*9&VDX7Ay-mS$Bn8#0GD<(sLvj~YUWRG{{%c5GJC z28=3N3L%?&^}tu3w6B^G=yKg@q@JgU!2XW#?iI%jsG+j6aZ?*nPdW!%t>k}g`Jra8 z);?GI`-?Kul8dLE9T(ttYfNChE?8Q;c}ska&&4CNp^u4>-^Izv|&lT`_V<^fUZ1QF%ah{FT&@eu)v`23H6rZ(hu3b4yYU+qE zXd@w>U&jI`EP*FdCDE8AgGxmAnupAQld~~=P70F~0wh#`i-ZJ^vc!+B^z9TKD<%%b&9c3f=6^pOIA1=x%3Kw;xKh*_31MZq`BYR`Y|I#aLy<{3 z-ZLK%H>ku22Blbpl^uvzG%0e=8f_H$1HuA-t$IKD=~|?@DHEJSDJ=uj`(umj$?Fh@ zDpzpsZbdO}8J-Py z${t+H>R56Kw|08IzdJEdN6K6-XMOW@YSMlLj zO}Bct9FI@Q?}r@KAra7pvLmntm{0Zr7E1aJa>$d+gvE1;4x=V5+$Y#;RjUUPER1df*!v8U{*VYe`$x zX^QK{af*8Tlm*g6Cxp_z@p>nsK+PLwg!aZjfOBJfpx>avz<57=x%QO+k>R1==a-2h zF@v^*!(BD-|KaPrGS{%plA4^>yY$3Kmmb-2x%vfkt;#lE`BnAB97xyqm8p84m0>wP zA7SqtM+uo_WK0`FPfak$3WKA1+?{{(fMHofR4s$?L<`2$-`@>#iX!~7u08O@*~ft1 zzR3znBgpAm5pIm{&Yr{T@R!q)P>!ej2i5nY?-Jj)kf5G(6(sZ;Z!E+BwRd;Sh0>|& z&V4Ny5FZNi@;@!+;(lH_Vt23ejQ;uK3|-7RHPTAZIv4OWTRT=4Ez4!;%+>q-esKgm z1ko7T`HXC)%J-hOzW(mxz73Q(Ae2q50^AxD_+WlGTMmqiTjE?j#WM^yEqVM%MCt#2 zstoQx4hFRs{8M6|-OG@`T6|+~xOn}QgT~6OZquJhwj9(}bQV*E%_je7OG6zx=aI~B z%bla&yq*)?=nI^~mT<1w)PHILlyM|nP}0*Yy2f;yx`!Pw;0v%BInCO_%LSeJ5hoA# zu6(iH_+;RJe?=O@?)s9%Y&!BI|AX}=%}b~5R6pCgC$~vG|J=Z=Ak%QN^Jh*x-OwgT zi%dv8;`HkaMfqEPuGwXRtKaA5kT|0qy?8j*e~mmkrvyqppG9rJpvM^OEHam0Apnp5d8UdlpR=tx4l_;+SR4yAz<5ow$gKMKr*NPUx- zVUOnFG4O2i>ST%YbDPSN=!{`PI@i^hy1K=t6&K~h@Wl7xPys5^*RGHIDr_ECI?&QP zumqwb%oZtVL}Afu7noNX6DZCipPJhF?yQih9kqQf>>pCwxVX4H?KAp?jf_Ym5b@(9 z^IrMi?DWJpev}kt{XMPfLgH!5Ht@MuLq|t%z6L_RzCYUX4nP8UkclbQs zf+qS?Y76q+UkSWDX;YFCL&s7>SOktxtOdxR<@>hkj-Xm^?ZRh-o}vZmJ0z*_4?l!e zj*K)D4T3dOE?yb1z2dF9h9PDYeH}R?JnCWANSvVI7l1G*#q+yqFm;&M`a}-9 zfz=0xqh*`B# zQU7rm5^~iCWt~M(#&lbccV()u>D9QnyFIJybBpF7k+botv5J^btT7X2x1l>Kc!y5M zCxE6lm_ASNGVcJ-+AAkTmhSdm;$#R8F++WX-Tf)sIywEbfu#TM7noZsU=@Fl;9>Tc zM_+A;kB3=Y85_MbtbslX)z6yiIRk4-)(fNu2R5@-2=f{hL6`f9=bC02OEOgQ{@1Yc zc6stxr;H#d5*fJ$CdK94dJ4)%O?WDa>06>j{n>DBB#h=St}M3#G7}Z>-nnO{N^v1n zhX7W1Z&7tjF}q8ddo-1$Kr_-{Uu*oKOGBj%tqCskLN(&*rxmNJ?L-azobRJSCN8DWRl%S^~ic~ZA|pLggZ$x%zgSaXryTiojhbSqJ~M35}2TuyDy5`kv8 z&!0*OOWPZwM}E%KzY1SM?C$acAkI*7(?UY*$3;2_eJ<`tDXA>BsqAq&>{(VIb-k4? zRz`c0nXQMJ&dz=i9##dUso?uPzoKemqO|2+2jJ9Ve3%Ud(m z@V1CO@9K0?d9)B0#XOexUKjcD_p)>!255ctbT<8?Ep3()VM z^2v*N){eN-)M9#0ulY7V&Slw!DB@ZXU^?>t6uT?-XQdj>fZ-FHvXU=IE3A>^2P1@@ z0o~*9WW&){HAum852!}I(`(xxSYtb5yD;n&+ncopgN$R7m2_>_SePS((iZiV_UdON&#LCAY8_5X~LS_@M$edXkBSiMiAHdZCgA zNMuvkqyJV#ZI1&uv7s~6LV2ykes&yvM`brYZ?>9hFv02c zaU$PXRfikQUNCx5>zy9a=e3tMOlp4z1aCYe@nyi4TJ@Q`@p%|jtlc2`#*N$t<4iEbn| zoFe@2ly3p4ns$mljuf-MJ*TA077q>mv)JZ`WmV{fgMX}8+GxedG)ueuw6P_&AG{0YZl4G5TT)IgR#r-WffQPEa4LiBGum>X%l%~){tf(@c8NHx` z8RmgRKD`D-mA9U`6q=pUfuHnh^_k@` z1k7e?O;WVWv^qL4WJ<;LW#f+uLONj?m&`}8_r6x$5 z&f*G7_ErqF7^qOZ0s_XaZI@lvo39&>Pjl`UEr<5oL0Gjzufw2B=hU6s)!`4!7{B{+ ztvcQJot9dwMF#TL=j_k7U+7p^w9`+Zef>KY_(%p7Aw;U1fyaKLG#c1ZSyd^5|&TIzAev1^~)`ta+0FyA|AH*gx)h?8VD128YKu zw=|g`L{h}>v*>AKRWsoFrFPT0a-1D%x{}E(UzOIr+@$*MuJ%^4ch;AoFUk>~b62iE zQ9)01qH(_0A^4^CA~dIuvNhainmb4SM9G=b+6V=_W``XA$-+XO^o_j8&YFn1gZR}bl)SVlN&6p-RW6m>>Xiqo;O5t4cdHJy`4a#(4=^umC=kn8*|7K(o3h@Mi7Rax zxzCg^am^ixc6#CfS-^v*MMIV}Q5s)ZM>?+JPWD;iCSHcI`c%^z>H2rIc<eRW6BJ|z6mN8`O%?z2o-24NKoH`@+uy%WXqtg zn!4V{=h;%lc72}rleNCFC$Z`dhi-9W)M2o)3O%3hTzcvXhzWh(SNT<>fV==ZN2uwU z1(lUHP#PH#JQjnfnp$F<<)45!pO5E5h7U*}W7nIb80+}^(pPsC!CFwxC+b>xMnFgG za#I@7JAE8zw$J~W?V6wLI*4*Kmfw7?%e_R5u_5w3OdKwK$C#^8BFW>-wCCiV&Rs59 z;|6l5tWX=~&q&x1y3-)Ly(601_u_tgtlS?p>OlnyEX!;h{i)j3F3(OZ1ODk*$&^SK zWAXk9uplSKu_}H)J;$!M$I=&A`7OA7Zg)1U-SAYb+o87x{re(>yhJRKa!k1gv`lu?%p`m~S2qEwCAV z+Tj$G3nZ?I3h(9G`dJIbg4f}_d~oE@4Qp0hb0;+OCxUr#$vSNLU+02 znPY$OLM6WGHFm+7(mi2`p)q1}{!Ww+LMa@zoRI%XU8k9F{n+VRc)4?VcJ$VAI}w_+ zO0(w@3Q{{4+XSks+ZXL`vR2yR57nZ8yvY8yiws6qqTSB&eaV>{5h0t$y_zP}Q_ z{oFL?$U0|FNhEf(9UKV3q(z*};Dny6 z#ubD3q#eC|?@KeN#lNHA?O*9^2c7C>z6(hSHT+MYZCb@m!$#Z;(Sf?Dp(Mg1G7 zTVwtO*n;T8UpdB4#L*OkcSZR%Fy^;$TOejauWfk&cK!hJk3)mpqRsJS;YdLY=qSjvI}^f=l}7!I9lc-KXEUDqf)>ZHoDo8aT#G%Phra=zs!19bk@%!nFX9hPqH0S zj&_sh6jnS!0^he80xUKJ(0wyt#K@Biq9Li6h&VqNLyXUy!u~(9-ZCnVHrm!jLkR99 zXmEFeOM<%-+^unU4GBSld!xZ!8}|_0Y24l2om=_#KIe`*?%*$fy1Ke*y>mWuF2r&O zw9q};WQWgrTs-kp^K3H5Ol8OiX(XFbh0QxH^fH-G=Wi=OhIw`VK2R|xkGsD`H_2JO zs-r5MHD`C4@n%#_GM&No!l}h)!53~bX<1cT|AgoSsj0XBWO6Lq+)PXTQarDHd>&=H z@0vPtm8I8$JSxl25Dr&H|C^u3CqYNGp)%_)m6pp*B%13tc zVtfmFu02a_K&5zW3QYyTiJlEPbmC9G=$H7w?sdn@Hk3$R+i03{*XIfc(fz7F$s4q6 zsl{~@6ZR`ab0^+DF68=b_H;>0d=0nX(d*_3_!K&jwK^J2NH#`@)5XPgY-IZ+Zc&14 zUVQN)gNCJb_50d=RCN`;D#p?NXA{tf@X9>MX6I+5eyT}eleEdN(^S}psA}<-BfKe* z&?=rz&MxqXEx42@%2XE3ES#99L zS1+9{ag(=kh6B}o($On7Falp5neA^&?~Z)tu9KpyDv%z|^ewnIfMbTA(D{iYf9a=% z1UAkXd2ugy5VfZELXwU9PqQC4ghMEBk)x5y(9|i9%v7n7ejSlQ?u$GPh@^Zh`TR0b z%L@K5qk&abfB`CN;`3}x{T45ZSI!O9!>QWQIkbYG5j2|rgpcaT06!O(hwv*Y@LQU0 zyC;s4zqfMgP^>S${d14<7N(~0l*S8vQri<^X+C*d3w$|h=@xS{GEbC8^j(>2(rXiy zG7ki0r&%*;gPJ3IVRX0yhuQw)wbmfxJC1@+hc_5>0?*0%i|> zkSn0f5&_tQRgg75Eu0O1lsXJ29CU#GvsdkSjUV=`&f3N>qU0>obV2efApd z@bQ`RB(wJu$>7?eo!v~28+pSX)79JC9; zA8HzlP#|#p$rW8z{F#2Be|dkHq!P(tK}RvAYEPMs%t zJGa)CN=}2)LS&abL6drBUyJHIU5nt+v(ls5kk8av{qow8JYyIx(!3ci(n|;L9{q5g z^Egbz;j1Wj8e^;esdr__WeK z4q-|U|3CI%Tna8PqktG1OH&b>sp1P-%5T9oCps3@(PON1aRh9v;mNQKJS{a$PPm!V zs=YPnTpO?N8iIL8>;vo1CSz%4x4gX7fE^9nZ!O>hmDQ=YEg?A^&pr|C;3ks#lSf>K z))uUprPEC?dv9uFZ}VQ0yUr3)kTrT)k2~d|-8N;41wWa-KKk5Ij-+C1ze$E@iS!0v zHOtMp!B_c?{po5Jstc!zpInnt;Ydbgk7q^8nFSPV8TgX%o1;54$`z)D;@J;F+K5HO zshg!Xg!Hgo1DYsYd{wtH78`aS!XqkPu|^e7Lh@h8 z?(R#<&(QFG>A7EB;MYMn7`hQljuo`6Ry1+~LBbu&GsSSzPrtOR^#jg*9o?QqxeW~) z;%=-TA;zfEyY~o_zSHXnP+!G>&)nBM@3aoFCelu6$K!%Q!oZ7uJSh_mpwAVGR*HbL{Vf5^+Hb*<@*wq zM83%(xlx*~8YB$P3Hu=wxu9)Ri zW|sNyr%7&xEY2%|pE?~ysFfpbfbPYr(z^zATRi_mHcKxGrf`=^i-wSHU!@dIFG5cp2nezESxFY~_vwdziU zS8RP*ayc=nqa<}1S2;b2o?ruVyA%duWS zg;Cbj+&KWk-=1r*!x_MPO3w-Llfz0`Is+?fU#r(8o8N;glVSVP^fH-$#b^x$wNU~> ziaS5{M6d+Tsn$(04UB^D#FY~$XP5s{D!^d{L`R(EFqIgg)1KRh(VboVBb<--wvJ|m zdm!|wT*jhWe(Ngj1ZUb-&#~q~#*;X_d#2PmzuJFQ=5wk|!O~jcTiZH`Q$8qVNH- zp3m9ooeW0hpH=_2yV4?e*BKwnn8U%Y|BFc#j{k>AjTEuoeI(VZgV9VXbT;Cryb-b`ziU_z}oG7i#JSo&{DvT1)u|V5<#4C_O10%axV*F0^u;dCP{i;|gL# z?WjU`M&6CLW=`6~8oKFBOvKbp2L*!SqNh}S+U52t10%O<2xL%gEoI;bJ!3uAH;pS>l5h(;(oYxjjaCm!`nd#fk4&MF<1T;G7lfOZ#8H` z!Kms;6c2?#v-}QyEVZwb6Mp>MYOs9PuZw&3Z>25S!00`_fbxg|FRDaWqcNQkxnNQ# zWEvMS-2nB6fF*Z+{o>79o2p#7SGVhW>wO_-q-*80f=LeYvb@W2p2d-#6AsPmtW9U_ zZ6h!7;{UZI5bf;jq(_HDX9Usq!tc55_%6vchz&AFnTwhsz^fE#DjfZ+cVxNFKt_Bat z$B{2@p0D=g{|0fgW|Vust|q1a8eC63`pt{A0xA1FOk@X48a=vE)+@l93f`6hSw6bF zh9da>z!JfTlyomW^8x8yfrRjHa3D?!jmxQM7-XtDN*A7F=JauPcGmRIcjR}T_O`C= z!ykVi%~iuoNJwb4xHFFmoC@CDGAm7d8UxG>Gd^~g9eG5iQba8+t6oZhkCMhpKq6jF!rra%9HuTkv?|CHZ zXkfJZVT=S**nVCvTsH0X1m9f*!P`zUM6pCMJ1S&1e_Mtsb-1A6vz5E?s@ZJU;CgUG z;&+t7(RFye_c+Q3BM5+ekkiX_JD$7$|9cLbyX#sn{4{aw+lU0lU9E@J`PZMAFv$U) z@Kc$W9lvCpy~7is>pDAR>Zm(B{M2MWT?Gc(#O!H?Z?6{slX%>Ik648x*8EUBXi}W4 zzVJ>=6DL^S50T=sKHumwx5W9(JHZ_g3wj=3N`b%nsZsVqYA(MZIe5p;BoM3y_xOYQ z5>tp?r;BSf7b_clSPIRJkN}JxO_&y-1ZqdIy#~j3qa&wW4Hm3qVcxxuAn{&j{wx2&@cxA zfhwxukT(7~MtWi?r4l;cx8F4p4uvNKe#U-V=&r+l zUJ;o=w?Zr)&&nlaC(R6D=lO^B7+9vAI@ zrBCVYw=mXCSr^|q`5uf_t-nvA-=@E5aX4<$y0JcbeM={lkL$Ltuhm-yP7`M6{z(`; z50&Ii1xHj_{k4LW6iAC)|7`pMD($p!*@5YJCKNG>Mr6g$`WYsqY&<@{3r{q+$Q$qA z&v0Wb!c}S5A$%0y3;o_@RoL?koH6!R_fy*yg6u}Ltr&+e-pWJuC}Qw;3yi)jJ+)L8 zfW0S)JYNW+k|~hJ+$P0wVn691OxRP-Jz~Vo^ ztc|2PE2T19epht#LYi@k7CQ(f`|+G^NO3L)ek^0U-b`7+KK#l64qo(}m~`u3+14xH zHk3#F$2RL-Zi*`<@2N>Nj_WyKM@Cq z{TEq_Bwu2)QD$qk&Buz+sO4BMUcbg2aPXm3!`n5QHM3sfa$dn2;*m)!8Roi{vX&MF zs}hH90wQntc)$B|`{9QddV3OG{RPmBEz4iK;n{~M^+JF5^)v4jP1`S=pl^Ig(HRJWZ*YQ1cLjCit0&;f;8V0v;?!+Y`ZuqaElbwltyTi%?bSI zU!58Zay23cOFGBfN0Wp3uayaWsuUf0jV58$uzJMg=K1CE)O7vNkD`(iI8X!1yLV^zyICz- zI4SK<7eja>R}c_1!QI)IP}rCEZBcCRBVhih`qm;_mEI%T>{D1b>qo>UW>c*h1q|(z zhI%Z#KG-xmjtGP}#HV>;aEzGw7aVnQz6%T9Q(9eJIm8*6)E$ZXQ+HEH-NZF3v>EaGL2sUghNeNV9G%#y);6|iV%eksmjZ=f$9nJWL7O)&*k)$001#$Kn7c|hV z9xqmnbD$+>9raGb2euJu!b1%uCla9N`|yY39G1u&=>Ff+*s+NU_4-~E_4>5okqrL{ z|4Ub6VtXlS2rU`;c%#JO@9)qu7Xx_w1=@+oL?Eb*>)xnC<7)&HU*F%EK=@C-X$M^>^ zDZ@e2Yuk&Q5668pW?i9Jvy*I*Ws{sFQ$!j;GLJjvXFo&4`S9?`{1^h=M-8#a8@3{+mEFL{9 z3AHH3+9oTfB=Sd=c=^`-TYt#`3R^*9KTX`FytI|97#i<^440nmA#Yp&;2kkUpU7B*hjtTo5{NA>mZ8jU z44GP&a=55mLH_d{|D(@O0Q$V=ZMjPfs9AI0bL{5Bu=7yGd{iU`-)oYv%L<`jw|A-| z^2@@-r<3^_Y?+>7&rnH&OM(OzmJmdYyR|}joQYS2>y%)Li;B;D-Ln8xYkuD9GvL2L zcSBtIrG+m|0w+DN>vKEa-ZtSQcN+wnLp7D%T<%L%CeKKMUc-4Q-<(nR+0BrR`ayxgPdQvNcK}3QHr*70AvG5s_f?s?_i~yeZk04z|ZLO-ogjgET?j zHcQZp!@&REyyq${kPi#i!|+L*w1$N`I}JgLoP*M@P=>M1YjvRrRgmU0#NI>YFJTx~ zckX*V7aYfME0M@;Z5{@r)SP?^ui^-n$T{k;fv}&;51DD+4pVp?06qLTF)Zp{FyzNg zaQXG*OJD9tRV_?E$d6P*&FSQF*#5=~4NJa-bh*A1ZtFk6eh~lv%6|+0NB(2`B{tQ1 ztsq#JIXcme%Bk?;!(y>|MV6AfNW>KKaLqHRCcr!xC0bW(3Uf4#_z_ThaqyzSwp67p!3mF>blk3hUnNthL+2OJ;xOUJzU(edO<@3D($u)Ey+ zf2akQpAfMCM7`XCbxy(`u{{WMi0nD6aNH}kX~;G?;Bv=W~BRB=eQBqBGAS32Fen9@YKvGQkg0?j4!XBkY|r*O07 zRYS4a3OU%8lchLpuFM`68XCrPctnEgR5bqo6t9pGe>GqjzwVKj!M8_mM>F-FEmS(5 zE|0CZdGKsPY%DqOd|#<3|J_qcNp2sTdj15K&wa?`2U1ye$jGAuiyZOe>8an|B{711 zKcMBqeC7_?ry}uW|XBhS`xU@JGEK$_kD(L^{f{O2~_%sr__<3^jk3LAH>XwOjwpdgLC?W$xoRJ6O>EesAc2+&c5ZO z)k-T)FE|Jrv+Ek#w<5J)UYa1iK4F9nNa$SCc(ih;6Y7jQ)-;RpE#B}>@9C@u_@k(^ z$@BjH!Tx1@|AjLJuIq27D@LZpqN2H1S72|P%AT+zrjM@VI#O_RjCLK7RJs#nLyK!c z_i$J?P-OuGu5Pm?Twwf}Q!GmDx8UD09e8h>4yKAg;E}F1w)9OBHS<wiEin7&Mm9_-!uPd^I==L9j$sn<8l%|!_a@PDRR zi+a%O-T|&a-`b0vpJ|FCwJCiNVWQ}9=5h$wJKh~^vqeFlL-4h%8zn%mP!M6%qN``2 z=34_p*p~3tQkR?&(-2Pk6}^S?P#{d|4+r1Z!?o0XE?_}R4v5j^2V1zb9H8uwt0U7{ z_g55nPvzpc<5|=e=9%(#e-h*I3ml^{3>n}iI{zaR($#{)eY7s7A5N@(FOpEsWuz&*pqkEwbrPhy;&^`i1H;={o2H9L#P)B2#T;Ley18dd;k& z9RC7Zp$y2Vbg5Qz-kvZV-85&x?6cBCz7zg$OS4?p0e7ZLf>npY&tVIL&RFN5Ed*4o z5K(v&&&b#SN+Q5^)wE2>)Bd4Zdv|tiN zJU%q+yn>TRS)hSH&dhc zUJUeWb3B%8YA%}2&aU$lcH7nGUN(2K+(l6yY$MdAh$WP01!@R}Ksriyj1&vs2>4L% zgEHm%v@gibPs)4ARHzP`Ty{4Af>GSdtBK=d;^odrH&9YPzdEqp%lc*XpO}kD0GPAx zU>NMw8-qtfj$;kICFM8v-?(O6E5)(ZF%BnGi5eBqX7KJ7(c*C<2E?J*t&U~g^Sf)S z=kcU+ga7mpZ`tIj4(c_8sTs+s>FJ!$=MnbeZu*oT@KEHCqU>I1U#gGUtZgvXmPLfa zu4QOYU(dDsjqV$$_i174+;z5W>*{X;t*h%CgtXZ7ySXNjQY~~MD9}piA9z#(V4+VR zfGkPe5_Bcr+tB&!a-9LnRE*gL;jNrluvP-(t&dAO2LMr5+Ywx+)nK0yM|6)7a2H&Y z0JP!DxO}WwoFHn|h={h02tcM2o{A*;x;cOB%9)}?Si`LKe1wl`=u1@bJ|na_pgq## zz^5)Zm$e1OLqWQ?dZ8c0Lg}Y%tyF)|{(a}!m0r<%YjNBe8&u51OV~y+6{}MduQENb zl9%9>nO4AAOI)QNwu1J-RWC_^5nrF%Wt@DE)$JA}NK#jLvje@J>Z`iRqe_X4syHz| zAD|zoGP_E7_1SYtsU!HB@jYL{@D|29Ha>2P66|RX_a^1~RmGuL>Cdc%C3%u@cSw-1 zIJ7{aJAqye_wo`rhLT4kI#VsQ`?cg`6g=ceMBp0ftEzta324E22pghsiFJ=>R|2zp z%{T%m6`6%USOO=QRCP~befye1y5e&_raHx z>_Xr;%G{497LWC{p@#+E#W&TGK+;KY-T4;g6kQRgH*1^ayv}+7rnCz_3@G7N=NBB5 zcItc!vW#4sC$Y2w1m^iK?c^B^_7YEfZyPq3yb67earR^;S^NhBBPvDJ z5DLfyML4>^D|a`TvLRdR30=MBZf8^t`hAB9yBX3W&p7o}jEW?iAg_@6SBvgh&oiIQ za(qvimOno%4^Lc9$J!L>AuO5KDJLA{?Shyrtf085bsRRd<94B?NNqHa7;`P&7$?~N1WKNq!R=@}y=9R}XLM%%>5p@Rrv zWl4=HS|a@RDf049tU#aNK~-&n4*rN8c|&d_SJ6S;AA-w|6|+x})e9r}OV~>saFB-l z#;)p%f^PQMR5<6I;f5c&fi`r8je@0ZkuVZ(3iaAWaw3@K(?)b*;Y zd78=Yu?aBlkXH=On1EyVjr`tG2YySNY7F7cR*XqA_)M>z_vUn~yQB-oIZpmO{lugdFyUohdL-;?O(=3zV=dwr5MiDp8eeM z-L#t-(Xir;!n5kjsWT@RvikaR8&3aHV?LBXZhkfC1c;$2+%`#%orHo1Cc;OR6&-zc z+cau${!hXcPoJyT`3YFKUex|pJVCNw+gk!Tm# zZQNaK%HmqrUX)(6M+^B$cQ$m>qMvWYR(^w-C(VsNmb!v(@fx2;0uUqBpk`! z5^O&7G8WwcXFdIR0#%pMB{!9hN z#rWu7ln^cQ(zx-}AF9AZ-fR5sIP{8c!-F& z8~LoUA+Nxe8|5t-OOeMTPCcLFI+o6M>uCnxU4d1VieQnahtai_jSC=_ge zKkiMC>BVsJ@|DNl-d-|?8V4jK61)SN&0TcI1E)EHNXIssfQ|WNKyA5qDUgnL@znoTpn*k){x6WVE`%}V zNDbJEl@b-+(-B{I1+*Dilh#!PEcOZG%0Uhdem|H6ZgTjYE7I+4!nZyThOHn8Q_jSGdg4`m<9Si|$wV@U z79NTlphyKbi)fD4Z%*x(^8+0HwHK|3W)&}Bz)Cq|!CuT?g0lBm=r_zh*GKB4VAzK! z#S5Zv80`b2p@mJb7FyhNUqzI^<`L#_DW<2F264YBBcLf}3W3OE7N0eG_{ek}BLsX(Q1A@vS8J zFV80#A05vH6ChK#wgXmcYhH*amT$FKn_O5}Y&qTThCFg&CUdP$uqDDiNx)k)cM@Q~B0A%9&)5H=hL5LYEck|Ei)S;+i5&aqt=R8_gaX1w~b`G7HHA;Fpb1t@& zm&Gp?mv>WH#@)b)U2Qmjb`VAk15|*?8QFIPqD&VWmeArd;hOUNP^@P(Si1{j;!5Sc@OQGOyKfZg;A0TZS=y_1)So9oi!-E)3-IKG`*2_G1-P>E)s{w!&=?!mkWH;a(a5^DP>!@d~zJ`e|ydS%@2X2KU*Jt!Q_e-=0t9eKqm4&LMIhGj5g` z+%$sbH?+5SZvk18UN4)UK0g0=uu_tOk*}_vSE1$^aoRV%GOiOsRfvF%6YqF^OQsQ) zdyAy*_Jxls-lH7X1h*{mX_Rk5$WTo!_&+D7c{@qh5%6*@q4wK0mF&+q-M;DZK=4@L5Fn#5XAKDe2@5n_Eo^?in} zx}?83l?rG%lq6Qe@!uOYy#d8pN#7MjtL7km43SF$XxS@Effu;6V4H~f)x1{Z6xA#E zuVf{Vn@r7xI@M6OFOVAZ)SN*g_W!R^GrL4pT7|?J9tMrLv zG9Ij5F-m<7LL=0)r*p8N@4)KQ%y$CoM5CXBpj#dPJ2}T zJ2dy(OG~9mRo$pVS_JNt9UjS?e3GQt#9@TiAfA20H*X83QOF&I8k^_M@$L87W~dB= zM7E1)lu2yFJO7Y%fE*qVEcVOj25UP0jx-JA`S4ywV^BwP1zA&$bjUDfX!?$$0iY*# zkv&M|S!0*>N8EFeT7U9)X#wT|eK=6@yyL4`lZP_NL<9-&odAo^U=fKZOaAj_=;`Kx zWj+fpjk(lqmoEQ*aOGR+(U2cCz!V{B4}Jrtl0K7P2Wwf8pnt2>?w&0y)=-#bJey|tpetxc`F&T_bl}P4RXWz8 zgW6Fxt`zMQe#kW)2DqU;@+%|uxbHzvO6CaH1NVrP`Qz}1m~e}FLKj^WKeE(}Vl=&16`qKYk8T zrHj_p6$@KTg7#qx?%e=zQLb=XMoobn+;13fKRTNgCt8;(L82r-b<6tY;SL@QposB2 z3Uv!pY4A2qty;+oAe9eYv%sA=(WW@jj7&`SU#Q}E3>6^`+4l17##XT3<8ebU*Lv64 zrKRG$lx$}*g0n0QWBZgOsQCT~uv9O1R$o{TAk)5)>e!8$BeJq>5uyu4=@>FRbd(B$Yz1<=>y+-Q+1)P9!e|F>Ic6I|`ZX}Dt?oNc zqz0-F=oO~a8A<<|mRe6(Ebz#;;67oCU-Eg$#C9+zap?o849`M^xk~ZWYg$DB8{IPF ze6(sIDhlE@tkse|hR{WJ9vna-fEnv8;p@TIjxSP3L*|)r8*ad^mr8??73te?L;|4U z2~uBAA=Jj>__O1Qm+ar8i8@FyiQk6tvLW3fhPBQ+$!gS8(Yr?S$_8udJerMm(S1Qz`c0_1q}oDJ;B72qsFk?dCRehj0W0} zN5cR#f(oxbC}{pf0q92x3W!`Rw++f+WbAf}v>#9Cgiw7OkmlhmfLZ=Rw{u z^Im|xE|MZ3EhZ(eyqubkNJdYMWSBU$=P@>B8pre3)ZoVRK1NZ6Wj23wkTnL_TFEAE z0i+Zc``_t5lS)*_dQdJyw+2Tt&Hs`cxD*VjhHhLor9j*kH#pU)_DpGIkp&(c_T{3s z;>~#8GpiXZoe8q|WqF)tbE`WK{%CVD&yPy8pnf{iZ$QzN5Veuym(%WBp4Yq19v|Ct zb@}0gzm1EpL?U-1Vs~fvv{i{2Zt^YIW5c9@3!7US%wbfR@*kK3Mq1lsQbf7^_!%$P ztN5W>UWcdreK6sqmo0aQ+UsDO4=ixDLKGo5MT)Z3o$1(tdf%3QigaJWK@qam7HzAv z1hnZxBwZ_VBHnML9HA$-goxNcOEI>K&~eq{!+^KGIcrKmPmE5gO<_+Ds1A8oH%&mR zneSFz{?ER!MkY<^81no-ZJeBXO$;tP2^?gdJ89(w&V)=)5F#!?y?fj%nlhIGxOX=H zv6Q+KACYx|M_1!VmViAigz-N~2`MmFlw`v)u2g5Phah?t`fmbsN$qdd=G+wnZ0vfTGq*M<`o;zRpK`YLzJ#B=U0f>S_Qkb$18A(P@xRTzeioHC*J1KuEEghq<71fArwsx!9ya%54% z^>nzzfB{7_YCzqTH=tgDI^;WZ34xI!G#|yNRwVGo9tm8l4Y@Dt5`+o6k0nLKUIW1bG5?l$rc_?;;hK&m2TH*P{-! z0jeo&R$C{1-@(mrV5Z7iT_^*TH&ETTRWl`!mmh`8$33+yWC4lQ`N4jv+Dw~y_|T>M zm7aVtC6%HtF_3FJtRTVEY*4``ibwS+Uy1)YpZ-pZDzuX;#aR-FV5nfT0l?M@-O6(m zE2vl~e}V%@02tHe={y$9gu}Cq+)7cy+C1EqRu>RYGt)A}vhy06nR*IgcI*dn+x450 zwS|e+g*}I+V9zOFTBzETc*!!Ezt3r&TXjRM`hov1aGJTo({6SX)NZu6O5N{&PW@k} z^K1x3V;Z1K0{e)lzA=UlPvI`nsp?p_o7%&X9v9c$dBZcyxG0caG|?E5V0cv^ORHt> z{c*Zyf3H5gYO@E)rgPF(zY`b@^#?8qmTII>rsVTU+O(JWHTFNn?8*dB&6h4RiJ#T2 zu@gc^myGP`7gh1Hut6MKfdp69jaKpu3tcJ`P*;Fd9vXS@8cCY`6|LiA$J(b8xyL43PMR`{_Ue7!6laq#0adN z$Ph{xQ?%^_Qv)L&bDQ6m0oYTmMp^1N#)6~A5`C|nX`f%orX1%$S*1YD%cRhsgoS<2 zrqZkVpDG=XJ3s}bpC(;XBNe%IF(okWeZj>!3yRG4oz8HZZ{<6Y#nOWA2;OVs6xDy4 z?v69JjlnTA;@1<^X&px>iN{WWT$U9}R*$ZY!~c#DQiln)@z~=EaDzDYbro=Z3`#jo!Q0g&Y{WS4e*Vb;|CvzzdBLzV z%V)v$1|)=9`f$4|2FSor9HSv4oh}&Cm+K^L3MeWz^)@$M)(cFT#CvO4n_L%lA9!di z%w&!r@o)n#F<%L?cp)lZP~KH$>!dA~CjQ-cTq<0xHq7+KMp4B}%a>IW^lXV)TA*iA zsjoHbtPSYU9d>r11&)@g0_D_PfK~;#ohAQfSutd9*ZsubHSfFxF|>B%E3ff4EyE+S z&8@paKMyzdf*-^qW^~Z<=)l8F<3<3>kUL)RyJ8~M5T?1k^1m*Yxspp8Qvji z9t1M!x$ZI(Cgc$JzqU$~CnD0hn0dis81x=EF?8ig(w=mJcS66GbA!slfNz%wu|z0n zUm6}HL3kuGsDcLnxeh;O$3+cPyI?e`?nmo;uj-=A%yFSX(8+bG^K~{vrEYz8{PNzk z{HT8Myr=TiUALb8BNk`CYpAX+9g-Qvu;IgxKT<%cT<9BQIvwkzOEiq#-Acr^H z5ccA#sem72j2YQf?1hBM%NHn5N9kSikMSXIO~j8$#6dP3YNO`F@0ev>s&pyYj+x)$ z&1w}!GmAMJm{vjm{^f<6)`{&xR*%1LFvvqJpdU{$z-qbHp$)QbqYNlS73jM7K`JRZ zVa!tw@g8yaA%LovQb2C~PN4$7Y|?ymeP_8kU1j6p;XPbC>vNGa(CxpTq-g}4e7<$D z(U8c(NS1MN;S3KCXR}}8-M-w*jb`?4yG)l{ z&1gbQ;jo6O@%gqr*r&4kHEP*fwlL2kN%Unkqe}fJt6MB9KmiE_*`SwgzSpyXwSk+y zLC5P(K03pDj-87ogC-G@jM0lM5F#39AYqwXs?vtk8u zNe{Y7MzE>Dh0B5IN`;C=VvX;LZt8$X`K7?zUh@(GPkh~z;)!P0uo5Q~~W&tAjk zuE=W>#{+1Vg=JdR? zYeE4OG^vooZo_5*9r#Zq1Fkxm;-5PoED1XPDyo%~H{cp6z|gORb{aSZDQa#kUn3m>-Q5*A1Oi7+$7Znp-fLEtX%(3CWewfSgKKno{i>PD6|34`L5o zKse24XO%uFPk8sqKD$FoIFK8Z&lj_^ERdS&lrbGH7{M?5eU)=4N*=Do<5W|Xs_lA4 zm-iR~#kJ!`4w+PmaR*yX))k)b^z-$fEu{=9iDR=d&Z8P+KLlCP7`kMqT8?6Ti~-FR&Qqib60 zf8_94cJ?3F&43)9;vv+|C%lZ|&u=d)rO8tG=b_wTBlQ|-!v6;Q>HdJeNJJ-hlj}J+ z5kF-|fL2){%l!?|@;dVyD6y6_aE?EFfh(JECA@Kx_#AYQFoTZDJ0D^-C4PChUwlQN zeh8a0ZL??V(Ja;&WX%z9ZULt#4=BX^H($&)6m&oo=y~^S49MyYbNsQfQ#?|29MUhl zYH-)r*St;}2sBEWzYJd=X5V?X@8v`+`R2H|xO`sIRv<9+t!_p(V_+$$>A!nD%0TwS znO^97m4UV)F4QDQ&?&n!k*BS_+0&@ZVPYtHYp~9s=h0ERQrehZ^wkNmAwrp;6H$G< zl!76Lf?OI=B3o2jCmSW<(>O!{T##|v8n4JN1N~%2YnOB@IOb|Gntsg_PxIVS~Zu2e88YP0XR(w$b*+V-=_sKmgk0Xx z`p+Nr$2J@jxbnZkIoWqlWY`3)K9VmI1`qQsJ8o+nl8@L#lKS=L1Wb<=yXUgPwtGCs z;zuxkE$>XM*T37`af_-6T4}Tbg2PR}k!U>jqg2nk$~pu!u!LRdP!7H+3{{z_p?eR5 ze>RDk#8Cf*)Fnx3de8Rj$d$K~=_jXXHmnfMSTpxPn#-+?6=Vza%0MJmL1ojRQ$xYNp^m>B!Py@X=y z!J^BU&@FF8ETZ?$aOwr7V}ur15jo#PSQD_q*aKdC{}p*UbDL(HM0SS%z!=1_e1J$J5%;wZtXZc z8xHMqPK>RO#U4n;cETTT!Z?yd6f-~H8jvVAK8N~dP_%?y4#U%K9a7~ba%_dd+>6~l z=RwePZ%-52+#YMVbv{>U<;VBvoH4iAW=zrqEN8g^*2(kEmlTa5da3Z!_0i-f4cCFd zE>`eI3H8XGRtV=zy{yE-W<#!7fLrvn=DAP>W6uLU|I@Z<&HuyJS4PFvZP^A1?!nz% zgIjP35`qMGNN{&|*93QWcXxL!ilD*W-QUUm`i}0`eSh%djG}6svuoGdYt1#+oImA; zzyo8pX7$oW4zC@SK}#XZyyZhDdz_-Mp8VUpTzjS3S8k+j+XH}QtstcQ1Hmss( zSwiDj62clPpy)BN@_K)THQEtvTMuBVzBQwQK!mBcwrSOwfY|F=(Pe;VllBd0 zv6Yx7i+GD~TNXx-MuhP(Dv@B-gd~l7 z3aebw8{yDbXGX%ke)1Xk61JfnoN|5~-(#9Za8G5JP&EsZoV(kAhsS*!j%&a)?p6C- zalSc9`0T1ErS9_Vd%{DIp+#i~O+4@m3iVIP8U~j@$Gc?C4@u+S1Q0<=>7l1Qu5jM& zhD_l@l%-;#H>coYRp|O$|5XlVRUYC9!Q*m+=Ya6HRXHjK;1SJSns3 zo?G9PYvQSmWv}6CwiEjBD{8q$;l0jLkuRuG*DMpPC(}MejCJY+6$J2!aI3fLK6#NR z66r=qhxP<4E2A&bKwdv0&#g=B&VOp)8n^ycrx)FR3IA+!^AHMJWt0+VW+&M@STZz* z8ZB$c#ctM*{Y4W-RaXcuUP^U=)}SVEgtEp4+qiVf*n3qqcoK}9_`LEndW%eE|AoO^7| zl3vW%vbnCfQdSPpPhRyPnS$&01uj*Em;?V!4<}QEIgJ2uh9hA6dDivzNk&EnjY!_8 z<=AgmrAoa{A2MJl3x%GIYCI!P3FFJ(S9q#-0?{KIzs8qMpv6t}z?Iw_&U%Io( zLFS30v9{rL{2!MYf?<8=r&wEcES9N%H+&-4*n z@tcs4AhW`^Uo3?9`lAw(APggG0^`w=r`Bb;9`u95i8i_p>*9vif`NJqy&H7wX-U7p zM)GO~8480sl{aDMiM@!uLc^yqay7-1D#KLhda_dJ$7Bo|h?CLG-N0o_uHSRDM-96? zE)1nQ43$a~^@OUCqx2P~HsI|;2MK9q^wApo3~{IAHQ(yk)GLn!j`ph0!c2uAwWAMn zhxLa>4aurQ+>HWGc%I|Xo?-984tqfb}eU^D-#rYdk{%QdW zM^gAseQv+!ZGTDgLm;uGzPc_Xr>KA?32e zg+1T=LUAm19s_93AFWfx^dp=q>1ZMOi%5ww0>S6Pgv7ceal5URaHgeGC#$y&9yxG!l*9#VyA$BHaf^E zclog%sIZFVYjxZd zurwz6!94wCdjO7RL($n$qMDlcmseLc&L^72CMIJRR8?9{C;)Xkj@!PR(`tz$zo@9s z>EwIMyVorTXwwV}lqED$6GtMx#SA`kWyACMTqFoRZYM~0g_gCma?t5%JbCp2K78j^ z(d{S!qNPhcKDNnTN;DvN2SJyOg&+Qn&stcf&h>l^VEJludhNL!80y^=+|MnuIV}?s zHmnArc&tfoXS+3b8j{rxY5RRxHx_BF#*ig%+e$2pymyU#D)E=`aXlk|ZolcUN#=7Z zRda8)-;t?`U!D#6b;3xl@?GRgDWF&38~JEyU&^*Q>YS1U6@Abmd>|UNK(O-HTdq8` z3Uc);tuc?AEQAi%JsOx=E@z6 z(La4=^zd|kR3Rc;i$` zee{*M^=2HZ)4V51mr31aSJr|^z&^@Z$`Esgi5DGxM2Zl+@4H(wCIS^=xYoLN3$yT| z_EMhpWj!Dhs{_rgHyG0H_~dJ~3HOpN+viHjmzSy|7aUb@4nmWesn<2Z0mR54+R62p zq`IMv8aADQ81+AgIFo%=N9?{LqtI|RO^IVR2oN@2X9Nf;M^T&wem(JOF9ugXy87f2 zLitStTH=TolF=@VBv=|#H%HT~Oe<@}wh*^VP4&#eAd!))Xv2gHczX?kEGrwE7nDs$ zrP@?a(C{@w>fVFp@H3W))*;B)Zojvi>X?efm(2yFZYo3^TJ2!fRwE%=cq{6d#RVFb z+4pM=coJRU!SOSB7q_9eXkO1XAl10zuDtXWa3;`AyN@)jd-G>{obpaV3NM7&eq@f@?zaTP4w!PKGFPs73=zq|MlCh$v-&cvPexn@j3S6I;v z+_>@3m0C_P(IMVkZS|-_xu7;N=?g|Kn`zyT?=lQ0OpA&cjD*Ns@*M-_u>Msn^soP` zSU??53gh&(6|aEa)*IdJwIm$leXpE?{1`a7pB4|-*7d^xmyucg_JVLbDV~f{oAG?L zi+}0i6?09K|JVixsSu(6G%2unAxBglS$63`3l)D&*Gs&_)>f5%p{sF^iJuCk-~W6k zh_eEW%+g`Udl_*#AW&V0pVacR<+>sq9V?4L9h0**u+W5|X=z(DRZArV0d;M|2TtKv zL9e2^I^QWdAb77AJ!Dhy+vx!i3_pZ6VVxCYHI3Hl(9-{Nm*42GzV;8p`Lw#W0p8K!?+@jX@SF0GLRU& zLV5=E5Tm1BB|=j(*|6$ffw)iAf>~sE{cXKMP3U2PZy^X}n}F$2QuHxaT{-o*jRHpQ zvSaV*5BxluZW<2XRcRNth) z{&Y7bXxxtMy^pT2op~;@bpn4eWy-wmt$GcB1rzq8`fUG6pUF66NUP#b@zs$liwGNT z&_Xh^IWQGwg+Hq1$-|eZ(<-mn`F>tltcqw>6#dv8CgHjsjn=0+- zcL=80q{>pC4HuP-IQ&c8^|(Fm0W911_8$RN)tL8)*u%tK6^5ry_Vynq`K!zc^C+-L zSpT)B6ofCV%Uz+0e))^2D+_^CEj}_s&njt~lT{!cOk%4ZremkFE3~IE6bCulgOt=S zzn<-y`%2xYS6`U5Wt9vVc^K9V=k$kGbUZIri>(Xc_u2lR!2% zHHb6y8-O@)C1i?N#yj^-d1*+OW!c(^MH zldLE#2GwD)F1HS2PP|V)AmxN*AsNe0#_WEU=lrSKT*`=Dxa7S6)GWw|)@|Mehf!^L z@{9lJb)l3#Yc|`dPBCCg{LoO{8Xvnth?3)$Rh#{*He)R`d~V`l;htD)fR1|H&XReg1($)<$K^c^?r$nj z5wWU1kmU<9;*##KmZevvF@jjLhXO^iGBebCr4kYit%8+9jD>E^1-(GjI+?of8YqU_oF*Z;u;6sV4iVYW60>1*2Qx;i6SmHKB^!u@`*!C&dv%Y zT?N9n*H*c04}JN;XHv!0^xjsw+$EBr`b)DiOxZ0s8nCE}8p~dx_LVV-Lp|Y))TrR+ zLoVd7!`m7*>ngi^XXw#^{udfmODFnRRqkXVuXts`EMRv{+d~5wgkdEo@_Q45lQwJ% z327IZ{XLzk1ExFL1x{X}Z% zQ4t~OSeQtuv!69uuq-vHBMyk`b~j!Kl#SV}vKnZ^Czm4Mi^>s5P4!>C=gxhBCo9xi z{H&zRcl_jbkT@?m-`4plwmSN$L#}+iC^Jk`ud=)OODF}(6Fc_o;UJs*F1LcCBu@^eIaT@%H%!Tc1)t8y(f7$9cO0MC@u9kiNEIs#8WoRJX zRMT6orqZK9iG(gau>M$1p*|#Ys`(YlEC2gSlo zfu$$09>E%wr#iqAL!cz)vx-&)Urm03QEbFxt>;Rz!73+$-3NoRV(75Lds@vTUoly< z`#%Awtoq&W+5zy{6~Io;XQW6r02viK^HD(8#_ps6Jo;rh9&x+b3XpQYKP;>Is;e6f zEG&l6U?jRlJGXUzfD-dr%DWHMh&UK5wO1cC$247GP$l=iGYMh}Bs0xkjuBoMhA_@v zGup!uLWbzIzpopt2rwIySmd{1opf^&$bW+6cd>n&Qjcwh^YHE@zAzLwuv2Nb-RVU4 z>9Xfs6EyFcz#?EReX6+EySQuS>sF1J71I+Wu?M>xoo^*F-QR$t7A-Jj!J$;Cm|7)P zbMEEq&F|B^U$D|@OPR1HDIdx&pzE>b4xNDo0)7J+pq(8~F9hgtV38P0V$67%EI@pq zLsyJh__k3r7F|7h-8K-ofrnaAhp|6}TU10Gjq!F(+*auAnSC&Zge6Jz$S$tQ0bK2J zrt7#r4N<06HMr4t%RQ^1p+QPxZM9S{(&l`^>bUTf1v_)xFG;XApo2c!|-4D*FO6Vk~dD4v))iw@)6C`4OD2;sNvF6}7Nv44Ck z5s}FZoS#47bT^#f_aC2iZa&ITfrFmR*@pgT@2{+q6gWuA-$=lhR-2x}aWL*-sJY76 zFtCLE!T<7|2Y>!)*-=f?p86inPM%JgbMGv+pK!f&+oj6i`hirop^0%;MyJxlhOy`y z+6*!5=3}$!z#cQSH0jX5z(@^cw1fR|Ui$nQAH7=bCn$kD}F@ z`8y9Y2R^y_Q`Xb7YOj3wn`{@qC7;NeOJ@{>eT^}dJHjVaj|E(3XL8n2_l#Uw>}7t4 zJq)wqW~uD-6%O$>=btf4@c~JhluPA%@3R0)BczXej(rJg6#?={D%FAl(6=`^R@_H5oflR^m=qp=M4#(&}>|0Sh&S zXqFZl43lHqlWor>v{AOpxL+LKbMw@a$fy`EyV)0yYXkb5dfV#8WR;^~&^YHzVlM;tW@XW?9L}aJ8S-fd-F#>atB2 z(cOwe424&h_X{g9{vhylTu1wD!jFZHPQ=r*or36J%gLIp1?iqo8Q|^ulvYfg8D8x? zZhRN&swPQ)TDwFf{zOmazhv_5=P*J>y~6MjZa_WCW3csUP6 z^1k;V0`F(%4J(kfl_F`1rS3|@ie;OG_U0&j!bCjktqS6}3HUUp%q%xp8U=g6QQSwh zmmY-kY1ME1*<4XN4`K+J`lti%`{CVgqdmHb2*7X5IpQ9hJgFcmcAY_@%RO|tF36~9 z{6_zpE|3wvkIzvC- zOntwo11yucQ}8`o1AnEV!NT|3af36)^9-=2L(u;C9 z)8DPM?JOjiy|f0ts|N91nz({3sNrJO8#k2H7Io?*s+N+DW)?WVUuzlF1a&N~zq_kS z3(x&hdw<|zjDB)P+;;Z^Tfb>hT5irHonF<+ZDj~dV zl>U|)w6`~mjR`WyJ!KW_$TwHF#hybRBPcJi>9|o9xLq_-Z?UHuPP+q!$@QQHfI-Rg zVTzbgss(bp^FJc(fKiO;YAfey$DOWR8W(brLe^l0%UYOfh2~@r6}&ru%M6c=#f4m3 zD5st!iej6)g%+FU3Wb{om6KO`s7TQE^&$r8hvPt|m4kb5`5I3UzmpO0p$w9CS)z-z zAP#)B9za-CMmi`ok#y62PC*PiK;e^)ihRJngD@k^-tLz%8u+&%91A^S zKEy)PQ4J1?(^o17?*{n%v0V$Xe}nw(PK3r?4WE(yB=1)~&3gJ_Y}>iXgMa!QNHqq3Zaf^A>4kLkI+HTc(keI-{keLbFIh@2DxO+$Z5Cac`moFAwk`sP5e$4*S$Zcdxl76n zj$SW?N4p3HB`9VoKFP@FpwoeR8o_N21iz*Jdi#u9bGC~9kih%(H{pU40KgX4A5C7X zEq=W(iu6SXjb?l+2L~yf{v?ubsncZ;8%hvpzhi-HK;Bw_S6Ce#!+JYZe5ojyqVKpi z8gqOpH&-waE=QYlf`!TrN_Iwe&xwO{6U~1v18XNo8SxhB)7Ob)P2+jRVGQs?drYSw z{7lFQ@HF^d3>PD%G(~JmG9nbLO@e7;%3yiGflv2v5K33|-MccqWJ63z=g9oPftdL8 zH_^G1;;K6ETpd+tvJJ^Wb8U$jpn+ay$S z4a)2dH%qj;dUJeW%tiN#!kx4sa|TCJl`R`S9F_Ax(r*8txhz&bt1rXq2y5Tf*d z6#tvbNq1NL_xfrr$$K!EBf6eOthbf?Y_;ytS=>uIP2W$=!Br?gk99MhOdOHM4 z66b_B)m)+!*(nLcketuSjQDu&`IfLYkb2_kQZ@)U>l8!NG!vDCnJB0X{Qckca6+M{F}?#?%2f>mxYJo(N$Sn1 zF(0e>an#OP6`Rm-iReSaO==}#TLM_3p()s|t*t4uzoEXExlt4S12)X} zC^LnnaWh~6^&yd|gmc|pcImow%35{Ta57D`31PD?K9o$oU+rTaEB&(Gp=q9eI zq+)Vp7>mP=76h*&XhvUT04e7S&yng|8;SiAE7Yb13F8_dw(Z^mePQ66b5pY+|@3E@z=4748($K~}mN zX6mFjIze?=Vv?Jh@vG$b1skxNq9}y>x{Fl4Q`x-?*XpTjt6daNxO@GHOV}6OamPIT zIDOFT9R+o^*MczTLSY563B?g>9{H_x*}#3yYMta!8oDpT#(DFI=us2uywO)Qq0kLf zoQx4>rF^^ls*DOOq9qf){RR{>dbl}PuPafr{dLn4&#!L|b6;A5ekxjKRKBV5Mwo%G zYn!ou)|=R!$VkCSD+evot_2s5%3wBu^Xhf<)hh+DKx{*naDqy~G8=6CU;PtvHPL6n zv06V#0zGA9gXy4PP+a}#l(k^W!$;tuLT(+UF>;he`f*=*ITs@i^jQKVfE zdv)09`bypJRIP3_g0Dlpn|>$yakEk$qQmiP@YvW_|7X;QeNvxyp6yRC`(+ZNm1hgQ zscAqz9Bm#W-1{D~Ot#ENU-MDhuZZO&{1Q^*glG23FV$DqOX-X);>aDHY*Rd_U8CRj zTo`SEvUDnRW4&T|-tk;Yl#j1YJ2*EIvm`6_Ex$T^V?9bIuQi8!DM6-WB(!+3*L#uWnJu2F6|j*pv6Z;bz3Dnrd>FgK_;ZRBj-1sYQEjdv0{Su{gARHqX5 zreV;_BZm9eT>WWjE>Y$ROD7W?H(`hD8yCZ-t3;V70Ch)eA`wFz#DCqEQ(*57;K|9t zTky1-sk=H1iAQ}8iadZe!!#}44bl5kmN>? z7SXVB8(;CBB<$!Igwq?ogPiE&1P8lj5@GC~cwMM9lEmcPFK9yDP79mB`x(8-+x?{n zS~clQ zx0l1*%U!?N;Y#60jIDHH(tU-GpEyg4?G*#ZMk*Q!179dSW|o0Irj0dF--LMZ<5(c# z@JHZD-nWw#Gne{SIrR&Y$6oo$E}A`0I1$YY(==};vq&78%0 zXEZ^V4t$4TrOQ@v+C^SY7v0bKzEOJ&_xV!6+K!EHupc_8m>D5rXoKtXr58PtO#s?u z=PIO2lz2k|xY!!vvQu#f$e=y*$RMV}5!iAA8A}M;rGVc+8@s}Ys-W)Hl1F)qu@!u5 zsOqjnk)wPp(`uQly7U69TLcNU@C)t8# zpsHa69sgEp{FGO+aCW#k#x$n1c$}{@G8PwuDWUL-tY*uuoG<&4B zsry+oG_b`Xqq@8E8?)s*201mVuuOhfcA(VrpZdHgLSJ`RUMxD*`|e3Rtut+7bI1A$ zbk6_pL>B#^kaTQBAiWexOw_P_`N+B!+BW_y)J$#Iq}Nb{Kp*G#-^^-zd9P@UQ5q@g z;zZJYnokYLUNxju2&D5%6EDkHVpPGP+}6&IO;*dne|s1Vs^zh9#6sSLe>Lhy;%ER@ zXiS0gZm1iq9`L_nN-FwSMlZ_h-|iK7s?5a@(D4Q<;aHEr`C>2{H1dVT8~%-b7i*JJUeMB<-@SA(WCdqt=gedE-o z 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%0AXqo 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%0A}3jJB{{Bx)z{E)BVta|DV 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 NJYD@<);T3K0RVb-&>8>$ literal 0 HcmV?d00001 diff --git a/doc/charts/surface.py b/doc/charts/surface.py new file mode 100644 index 0000000..6aa144b --- /dev/null +++ b/doc/charts/surface.py @@ -0,0 +1,62 @@ +from openpyxl import Workbook +from openpyxl.chart import ( + SurfaceChart, + SurfaceChart3D, + Reference, + Series, +) +from openpyxl.chart.axis import SeriesAxis + +wb = Workbook() +ws = wb.active + +data = [ + [None, 10, 20, 30, 40, 50,], + [0.1, 15, 65, 105, 65, 15,], + [0.2, 35, 105, 170, 105, 35,], + [0.3, 55, 135, 215, 135, 55,], + [0.4, 75, 155, 240, 155, 75,], + [0.5, 80, 190, 245, 190, 80,], + [0.6, 75, 155, 240, 155, 75,], + [0.7, 55, 135, 215, 135, 55,], + [0.8, 35, 105, 170, 105, 35,], + [0.9, 15, 65, 105, 65, 15], +] + +for row in data: + ws.append(row) + + +c1 = SurfaceChart() +ref = Reference(ws, min_col=2, max_col=6, min_row=1, max_row=10) +labels = Reference(ws, min_col=1, min_row=2, max_row=10) +c1.add_data(ref, titles_from_data=True) +c1.set_categories(labels) +c1.title = "Contour" + +ws.add_chart(c1, "A12") + +from copy import deepcopy + +# wireframe +c2 = deepcopy(c1) +c2.wireframe = True +c2.title = "2D Wireframe" + +ws.add_chart(c2, "G12") + +# 3D Surface +c3 = SurfaceChart3D() +c3.add_data(ref, titles_from_data=True) +c3.set_categories(labels) +c3.title = "Surface" + +ws.add_chart(c3, "A29") + +c4 = deepcopy(c3) +c4.wireframe = True +c4.title = "3D Wireframe" + +ws.add_chart(c4, "G29") + +wb.save("surface.xlsx") diff --git a/doc/charts/surface.rst b/doc/charts/surface.rst new file mode 100644 index 0000000..b828869 --- /dev/null +++ b/doc/charts/surface.rst @@ -0,0 +1,18 @@ +Surface charts +============== + + +Data that is arranged in columns or rows on a worksheet can be plotted in a +surface chart. A surface chart is useful when you want to find optimum +combinations between two sets of data. As in a topographic map, colors and +patterns indicate areas that are in the same range of values. + +By default all surface charts are 3D. 2D wireframe and contour charts are +created by setting the rotation and perspective. + + +.. literalinclude:: surface.py + + +.. image:: surface.png + :alt: "Sample surface charts" diff --git a/doc/comments.rst b/doc/comments.rst new file mode 100644 index 0000000..7d918f7 --- /dev/null +++ b/doc/comments.rst @@ -0,0 +1,95 @@ +Comments +======== + +.. warning:: + + Openpyxl currently supports the reading and writing of comment text only. + Formatting information is lost. Comment dimensions are lost upon reading, + but can be written. Comments are not currently supported if + `read_only=True` is used. + + +Adding a comment to a cell +-------------------------- + +Comments have a text attribute and an author attribute, which must both be set + +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.comments import Comment +>>> wb = Workbook() +>>> ws = wb.active +>>> comment = ws["A1"].comment +>>> comment = Comment('This is the comment text', 'Comment Author') +>>> comment.text +'This is the comment text' +>>> comment.author +'Comment Author' + +If you assign the same comment to multiple cells then openpyxl will automatically create copies + +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.comments import Comment +>>> wb=Workbook() +>>> ws=wb.active +>>> comment = Comment("Text", "Author") +>>> ws["A1"].comment = comment +>>> ws["B2"].comment = comment +>>> ws["A1"].comment is comment +True +>>> ws["B2"].comment is comment +False + + +Loading and saving comments +---------------------------- + +Comments present in a workbook when loaded are stored in the comment +attribute of their respective cells automatically. Formatting information +such as font size, bold and italics are lost, as are the original dimensions +and position of the comment's container box. + +Comments remaining in a workbook when it is saved are automatically saved to +the workbook file. + +Comment dimensions can be specified for write-only. Comment dimension are +in pixels. + +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.comments import Comment +>>> from openpyxl.utils import units +>>> +>>> wb=Workbook() +>>> ws=wb.active +>>> +>>> comment = Comment("Text", "Author") +>>> comment.width = 300 +>>> comment.height = 50 +>>> +>>> ws["A1"].comment = comment +>>> +>>> wb.save('commented_book.xlsx') + + +If needed, ``openpyxl.utils.units`` contains helper functions for converting +from other measurements such as mm or points to pixels: + +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.comments import Comment +>>> from openpyxl.utils import units +>>> +>>> wb=Workbook() +>>> ws=wb.active +>>> +>>> comment = Comment("Text", "Author") +>>> comment.width = units.points_to_pixels(300) +>>> comment.height = units.points_to_pixels(50) +>>> +>>> ws["A1"].comment = comment diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..775ceb6 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,311 @@ +# -*- coding: utf-8 -*- +# +# openpyxl documentation build configuration file, created by +# sphinx-quickstart on Fri Sep 10 09:50:03 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +up = os.path.dirname +HERE = os.path.split(__file__)[0] +sys.path.insert(0, os.path.abspath(os.path.join(up(HERE), '.'))) + +import openpyxl + +def AliasProxyGet(self, instance, cls): + return getattr(cls, self.alias) + +from openpyxl.styles.numbers import NumberFormatDescriptor + +def NumberFormatGet(self, instance, cls): + return self + +from openpyxl.styles.styleable import StyleDescriptor + +def StyleDescriptorGet(self, instance, cls): + return self.key + +if os.environ.get("APIDOC") == "True": + from openpyxl.descriptors import Alias + Alias.__get__ = AliasProxyGet + NumberFormatDescriptor.__get__ = NumberFormatGet + + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'openpyxl' +from datetime import date + +copyright = u'2010 - {0}, {1}'.format(date.today().year, openpyxl.__author__) + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +release = openpyxl.__version__ +# The short X.Y version. +version = ".".join(release.split(".")[:-1]) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +if on_rtd: + html_theme = 'default' +else: + html_theme = 'nature' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'logo.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'openpyxldoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'openpyxl.tex', u'openpyxl Documentation', + openpyxl.__author__, 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'openpyxl', u'openpyxl Documentation', + [openpyxl.__author__], 1) +] + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} + +doctest_global_setup = """ +import os, shutil +if not os.path.exists("tmp"): + os.mkdir("tmp") +shutil.copy("logo.png", "tmp") +os.chdir("tmp") +""" +doctest_global_cleanup = """ +import shutil +import os +os.chdir("..") +shutil.rmtree("tmp") +""" + +# Invoke Sphinx apidoc to generate api rst files + +def run_apidoc(_): + try: + from sphinx.ext.apidoc import main + except ImportError: + from sphinx.apidoc import main + + cur_dir = os.path.abspath(os.path.dirname(__file__)) + output_path = os.path.join(cur_dir, 'api') + modules = os.path.dirname(openpyxl.__file__) + exclusions = [ + '../openpyxl/cell/tests', + '../openpyxl/chart/tests', + '../openpyxl/chartsheet/tests', + '../openpyxl/comments/tests', + '../openpyxl/compat', + '../openpyxl/descriptors/tests', + '../openpyxl/descriptors/slots.py', + '../openpyxl/develop/', + '../openpyxl/drawing/tests', + '../openpyxl/formula/', + '../openpyxl/formatting/tests/', + '../openpyxl/worksheet/tests', + '../openpyxl/writer/tests/', + '../openpyxl/xml/tests', + '../openpyxl/conftest.py', + '../openpyxl/packaging/tests', + '../openpyxl/pivot/tests', + '../openpyxl/reader/tests', + '../openpyxl/styles/tests', + '../openpyxl/tests', + '../openpyxl/utils/tests', + '../openpyxl/utils/formulas.py', + '../openpyxl/workbook/tests', + '../openpyxl/workbook/external_link/tests', + '../openpyxl/writer/tests', + '../openpyxl/xml/tests', + ] + main(['-f', '-T', '-e', '-M', '-o', output_path, modules] + exclusions) + +def setup(app): + app.connect('builder-inited', run_apidoc) diff --git a/doc/defined_names.rst b/doc/defined_names.rst new file mode 100644 index 0000000..45abcdb --- /dev/null +++ b/doc/defined_names.rst @@ -0,0 +1,27 @@ +Defined Names +============= + + +The specification has the following to say about defined names: + + "Defined names are descriptive text that is used to represents a cell, range + of cells, formula, or constant value." + +This means they are very loosely defined. They might contain a constant, a +formula, a single cell reference, a range of cells or multiple ranges of +cells across different worksheets. Or all of the above. They are defined +globally for a workbook and accessed from there `defined_names` attribue. + +Sample use for ranges +--------------------- + +Accessing a range called "my_range":: + + my_range = wb.defined_names['my_range'] + # if this contains a range of cells then the destinations attribute is not None + dests = my_range.destinations # returns a generator of (worksheet title, cell range) tuples + + cells = [] + for title, coord in dests: + ws = wb[title] + cells.append(ws[coord]) diff --git a/doc/development.rst b/doc/development.rst new file mode 100644 index 0000000..643eb73 --- /dev/null +++ b/doc/development.rst @@ -0,0 +1,199 @@ +Development +=========== + +With the ongoing development of openpyxl, there is occasional information +useful to assist developers. + + +What is suppoprted +------------------ + +The primary aim of openpyxl is to support reading and writing Microsoft Excel +2010 files. Where possible support for files generated by other libraries or +programs is available but this is not guaranteed. + + +Supporting different Python versions +------------------------------------ + +We have a small library of utility functions to support development for +Python 2 and 3. This is openpyxl.compat for Python and openpyxl.xml for XML +functions. + + +Coding style +------------ + +Use PEP-8 except when implementing attributes for roundtripping but always +use Python data conventions (boolean, None, etc.) Note exceptions in +docstrings. + + +Getting the source +------------------ + +The source code is hosted on bitbucket.org. You can get it using a Mercurial +client and the following URL. + +.. parsed-literal:: + + $ hg clone \https://bitbucket.org/openpyxl/openpyxl + $ virtualenv openpyxl + $ hg up |version| + $ cd openpyxl + $ source bin/activate + $ pip install -U -r requirements.txt + $ python setup.py develop + + +Specification +------------- + +The file specification for OOXML was released jointly as `ECMA 376 +`_ and +`ISO 29500 `_. + +Testing +------- + +Contributions without tests will **not** be accepted. + +We use pytest as the test runner with pytest-cov for coverage information and +pytest-flakes for static code analysis. + + +Coverage +++++++++ + +The goal is 100 % coverage for unit tests - data types and utility functions. +Coverage information can be obtained using + + :code:`py.test --cov openpyxl` + + +Organisation +++++++++++++ + +Tests should be preferably at package / module level e.g openpyxl/cell. This +makes testing and getting statistics for code under development easier: + + :code:`py.test --cov openpyxl/cell openpyxl/cell` + + +Checking XML +++++++++++++ + +Use the :code:`openpyxl.tests.helper.compare_xml` function to compare +generated and expected fragments of XML. + + +Schema validation ++++++++++++++++++ + +When working on code to generate XML it is possible to validate that the +generated XML conforms to the published specification. Note, this won't +necessarily guarantee that everything is fine but is preferable to reverse +engineering! + + +Microsoft Tools ++++++++++++++++ + +Along with the SDK, Microsoft also has a `"Productivity Tool" +`_ for working +with Office OpenXML. + +This allows you to quickly inspect or compare whole Excel files. +Unfortunately, validation errors contain many false positives. The tool also +contain links to the specification and implementers' notes. + +Please see :doc:`windows-development` for additional information on setting up and testing on Windows. + + +Contributing +------------ + +Contributions in the form of pull requests are always welcome. Don't forget +to add yourself to the list of authors! + + +Branch naming convention +------------------------ + +We use a "major.minor.patch" numbering system, ie. |release|. Development +branches are named after "major.minor" releases. In general, API change will +only happen major releases but there will be exceptions. Always communicate +API changes to the mailing list before making them. If you are changing an +API try and an implement a fallback (with deprecation warning) for the old +behaviour. + +The "default branch" is used for releases and always has changes from a +development branch merged in. It should never be the target for a pull +request. + + +Pull Requests +------------- + +Pull requests should be submitted to the current, unreleased development +branch. Eg. if the current release is |release|, pull requests should be made +to the |version| branch. Exceptions are bug fixes to released versions which +should be made to the relevant release branch and merged upstream into +development. + +Please use tox to test code for different submissions **before** making a +pull request. This is especially important for picking up problems across +Python versions. + + +Documentation ++++++++++++++ + +Remember to update the documentation when adding or changing features. Check +that documentation is syntactically correct. + +:code:`tox -e doc` + + +Benchmarking +------------ + +Benchmarking and profiling are ongoing tasks. Contributions to these are very +welcome as we know there is a lot to do. + + +Memory Use +++++++++++ + +There is a tox profile for long-running memory benchmarks using the +`memory_utils` package. + +:code:`tox -e memory` + + +Pympler ++++++++ + +As openpyxl does not include any internal memory benchmarking tools, the +python *pympler* package was used during the testing of styles to profile the +memory usage in :code:`openpyxl.reader.excel.read_style_table()`:: + + # in openpyxl/reader/style.py + from pympler import muppy, summary + + def read_style_table(xml_source): + ... + if cell_xfs is not None: # ~ line 47 + initialState = summary.summarize(muppy.get_objects()) # Capture the initial state + for index, cell_xfs_node in enumerate(cell_xfs_nodes): + ... + table[index] = new_style + finalState = summary.summarize(muppy.get_objects()) # Capture the final state + diff = summary.get_diff(initialState, finalState) # Compare + summary.print_(diff) + + +:code:`pympler.summary.print_()` prints to the console a report of object +memory usage, allowing the comparison of different methods and examination of +memory usage. A useful future development would be to construct a +benchmarking package to measure the performance of different components. diff --git a/doc/example.py b/doc/example.py new file mode 100644 index 0000000..f98525c --- /dev/null +++ b/doc/example.py @@ -0,0 +1,18 @@ +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") diff --git a/doc/filters.png b/doc/filters.png new file mode 100644 index 0000000000000000000000000000000000000000..153dfed22d48c331cfa6e1631a2d573299eadfc9 GIT binary patch literal 25958 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

SlmmB0G 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 j%j7(!o8&#-p^R|g+u!X758G literal 0 HcmV?d00001 diff --git a/doc/filters.py b/doc/filters.py new file mode 100644 index 0000000..4691f10 --- /dev/null +++ b/doc/filters.py @@ -0,0 +1,31 @@ +from openpyxl import Workbook + +wb = Workbook() +ws = wb.active + +data = [ + ["Fruit", "Quantity"], + ["Kiwi", 3], + ["Grape", 15], + ["Apple", 3], + ["Peach", 3], + ["Pomegranate", 3], + ["Pear", 3], + ["Tangerine", 3], + ["Blueberry", 3], + ["Mango", 3], + ["Watermelon", 3], + ["Blackberry", 3], + ["Orange", 3], + ["Raspberry", 3], + ["Banana", 3] +] + +for r in data: + ws.append(r) + +ws.auto_filter.ref = "A1:B15" +ws.auto_filter.add_filter_column(0, ["Kiwi", "Apple", "Mango"]) +ws.auto_filter.add_sort_condition("B2:B15") + +wb.save("filtered.xlsx") diff --git a/doc/filters.rst b/doc/filters.rst new file mode 100644 index 0000000..0ec0949 --- /dev/null +++ b/doc/filters.rst @@ -0,0 +1,19 @@ +Using filters and sorts +======================= + + +It's possible to add a filter to a worksheet. + +.. note:: + + Filters and sorts can only be configured by openpyxl but will need to be applied in applications like Excel. This is because they actually rearranges or format cells or rows in the range. + +To add a filter you define a range and then add columns and sort conditions: + +.. literalinclude:: filters.py + + +This will add the relevant instructions to the file but will **neither actually filter nor sort**. + +.. image:: filters.png + :alt: "Filter and sort prepared but not executed for a range of cells" diff --git a/doc/format_merged_cells.py b/doc/format_merged_cells.py new file mode 100644 index 0000000..fa4c135 --- /dev/null +++ b/doc/format_merged_cells.py @@ -0,0 +1,59 @@ +from openpyxl.styles import Border, Side, PatternFill, Font, GradientFill, Alignment +from openpyxl import Workbook + + +def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment=None): + """ + Apply styles to a range of cells as if they were a single cell. + + :param ws: Excel worksheet instance + :param range: An excel range to style (e.g. A1:F20) + :param border: An openpyxl Border + :param fill: An openpyxl PatternFill or GradientFill + :param font: An openpyxl Font object + """ + + top = Border(top=border.top) + left = Border(left=border.left) + right = Border(right=border.right) + bottom = Border(bottom=border.bottom) + + first_cell = ws[cell_range.split(":")[0]] + if alignment: + ws.merge_cells(cell_range) + first_cell.alignment = alignment + + rows = ws[cell_range] + if font: + first_cell.font = font + + for cell in rows[0]: + cell.border = cell.border + top + for cell in rows[-1]: + cell.border = cell.border + bottom + + for row in rows: + l = row[0] + r = row[-1] + l.border = l.border + left + r.border = r.border + right + if fill: + for c in row: + c.fill = fill + +wb = Workbook() +ws = wb.active +my_cell = ws['B2'] +my_cell.value = "My Cell" +thin = Side(border_style="thin", color="000000") +double = Side(border_style="double", color="ff0000") + +border = Border(top=double, left=thin, right=thin, bottom=double) +fill = PatternFill("solid", fgColor="DDDDDD") +fill = GradientFill(stop=("000000", "FFFFFF")) +font = Font(b=True, color="FF0000") +al = Alignment(horizontal="center", vertical="center") + + +style_range(ws, 'B2:F4', border=border, fill=fill, font=font, alignment=al) +wb.save("styled.xlsx") diff --git a/doc/formatting.rst b/doc/formatting.rst new file mode 100644 index 0000000..e44b2fd --- /dev/null +++ b/doc/formatting.rst @@ -0,0 +1,189 @@ +Conditional Formatting +====================== + +Excel supports three different types of conditional formatting: builtins, standard and custom. Builtins combine specific rules with predefined styles. Standard conditional formats combine specific rules with custom formatting. In additional it is possible to define custom formulae for applying custom formats using differential styles. + +.. note:: + + The syntax for the different rules varies so much that it is not + possible for openpyxl to know whether a rule makes sense or not. + + +The basic syntax for creating a formatting rule is: + +.. doctest + +>>> from openpyxl.formatting import Rule +>>> from openpyxl.styles import Font, PatternFill, Border +>>> from openpyxl.styles.differential import DifferentialStyle +>>> dxf = DifferentialStyle(font=Font(bold=True), fill=PatternFill(start_color='EE1111', end_color='EE1111')) +>>> rule = Rule(type='cellIs', dxf=dxf, formula=["10"]) + +Because the signatures for some rules can be quite verbose there are also some convenience factories for creating them. + +Builtin formats +--------------- + +The builtins conditional formats are: + + * ColorScale + * IconSet + * DataBar + +Builtin formats contain a sequence of formatting settings which combine a type with an integer for comparison. Possible types are: `'num', 'percent', 'max', 'min', 'formula', 'percentile'`. + + +ColorScale +++++++++++ + +You can have color scales with 2 or 3 colors. 2 color scales produce a gradient from one color to another; 3 color scales use an additional color for 2 gradients. + +The full syntax for creating a ColorScale rule is: + +.. doctest + +>>> from openpyxl.formatting.rule import ColorScale, FormatObject +>>> from openpyxl.styles import Color +>>> first = FormatObject(type='min') +>>> last = FormatObject(type='max') +>>> # colors match the format objects: +>>> colors = [Color('AA0000'), Color('00AA00')] +>>> cs2 = ColorScale(cfvo=[first, last], color=colors) +>>> # a three color scale would extend the sequences +>>> mid = FormatObject(type='num', val=40) +>>> colors.insert(1, Color('00AA00')) +>>> cs3 = ColorScale(cfvo=[first, mid, last], color=colors) +>>> # create a rule with the color scale +>>> from openpyxl.formatting.rule import Rule +>>> rule = Rule(type='colorScale', colorScale=cs3) + +There is a convenience function for creating ColorScale rules + +.. doctest + +>>> from openpyxl.formatting.rule import ColorScaleRule +>>> rule = ColorScaleRule(start_type='percentile', start_value=10, start_color='FFAA0000', +... mid_type='percentile', mid_value=50, mid_color='FF0000AA', +... end_type='percentile', end_value=90, end_color='FF00AA00') + + +IconSet ++++++++ + +Choose from the following set of icons: `'3Arrows', '3ArrowsGray', '3Flags', '3TrafficLights1', '3TrafficLights2', '3Signs', '3Symbols', '3Symbols2', '4Arrows', '4ArrowsGray', '4RedToBlack', '4Rating', '4TrafficLights', '5Arrows', '5ArrowsGray', '5Rating', '5Quarters'` + +The full syntax for creating an IconSet rule is: + +.. doctest + +>>> from openpyxl.formatting.rule import IconSet, FormatObject +>>> first = FormatObject(type='percent', val=0) +>>> second = FormatObject(type='percent', val=33) +>>> third = FormatObject(type='percent', val=67) +>>> iconset = IconSet(iconSet='3TrafficLights1', cfvo=[first, second, third], showValue=None, percent=None, reverse=None) +>>> # assign the icon set to a rule +>>> from openpyxl.formatting.rule import Rule +>>> rule = Rule(type='iconSet', iconSet=iconset) + +There is a convenience function for creating IconSet rules: + +.. doctest + +>>> from openpyxl.formatting.rule import IconSetRule +>>> rule = IconSetRule('5Arrows', 'percent', [10, 20, 30, 40, 50], showValue=None, percent=None, reverse=None) + + +DataBar ++++++++ + +Currently, openpyxl supports the DataBars as defined in the original specification. Borders and directions were added in a later extension. + +The full syntax for creating a DataBar rule is: + +.. doctest + +>>> from openpyxl.formatting.rule import DataBar, FormatObject +>>> first = FormatObject(type='min') +>>> second = FormatObject(type='max') +>>> data_bar = DataBar(cfvo=[first, second], color="638EC6", showValue=None, minLength=None, maxLength=None) +>>> # assign the data bar to a rule +>>> from openpyxl.formatting.rule import Rule +>>> rule = Rule(type='dataBar', dataBar=data_bar) + +There is a convenience function for creating DataBar rules: + +.. doctest + +>>> from openpyxl.formatting.rule import DataBarRule +>>> rule = DataBarRule(start_type='percentile', start_value=10, end_type='percentile', end_value='90', +... color="FF638EC6", showValue="None", minLength=None, maxLength=None) + + +Standard conditional formats +---------------------------- + +The standard conditional formats are: + + * Average + * Percent + * Unique or duplicate + * Value + * Rank + +.. doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.styles import Color, PatternFill, Font, Border +>>> from openpyxl.styles.differential import DifferentialStyle +>>> from openpyxl.formatting.rule import ColorScaleRule, CellIsRule, FormulaRule +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> # Create fill +>>> redFill = PatternFill(start_color='EE1111', +... end_color='EE1111', +... fill_type='solid') +>>> +>>> # Add a two-color scale +>>> # Takes colors in excel 'RRGGBB' style. +>>> ws.conditional_formatting.add('A1:A10', +... ColorScaleRule(start_type='min', start_color='AA0000', +... end_type='max', end_color='00AA00') +... ) +>>> +>>> # Add a three-color scale +>>> ws.conditional_formatting.add('B1:B10', +... ColorScaleRule(start_type='percentile', start_value=10, start_color='AA0000', +... mid_type='percentile', mid_value=50, mid_color='0000AA', +... end_type='percentile', end_value=90, end_color='00AA00') +... ) +>>> +>>> # Add a conditional formatting based on a cell comparison +>>> # addCellIs(range_string, operator, formula, stopIfTrue, wb, font, border, fill) +>>> # Format if cell is less than 'formula' +>>> ws.conditional_formatting.add('C2:C10', +... CellIsRule(operator='lessThan', formula=['C$1'], stopIfTrue=True, fill=redFill)) +>>> +>>> # Format if cell is between 'formula' +>>> ws.conditional_formatting.add('D2:D10', +... CellIsRule(operator='between', formula=['1','5'], stopIfTrue=True, fill=redFill)) +>>> +>>> # Format using a formula +>>> ws.conditional_formatting.add('E1:E10', +... FormulaRule(formula=['ISBLANK(E1)'], stopIfTrue=True, fill=redFill)) +>>> +>>> # Aside from the 2-color and 3-color scales, format rules take fonts, borders and fills for styling: +>>> myFont = Font() +>>> myBorder = Border() +>>> ws.conditional_formatting.add('E1:E10', +... FormulaRule(formula=['E1=0'], font=myFont, border=myBorder, fill=redFill)) +>>> +>>> # Highlight cells that contain particular text by using a special formula +>>> red_text = Font(color="9C0006") +>>> red_fill = PatternFill(bgColor="FFC7CE") +>>> dxf = DifferentialStyle(font=red_text, fill=red_fill) +>>> rule = Rule(type="containsText", operator="containsText", text="highlight", dxf=dxf) +>>> rule.formula = ['NOT(ISERROR(SEARCH("highlight",A1)))'] +>>> ws.conditional_formatting.add('A1:F40', rule) +>>> wb.save("test.xlsx") diff --git a/doc/formula.rst b/doc/formula.rst new file mode 100644 index 0000000..e52a12d --- /dev/null +++ b/doc/formula.rst @@ -0,0 +1,91 @@ +Parsing Formulas +================ + +`openpyxl` supports limited parsing of formulas embedded in cells. The +`openpyxl.formula` package contains a `Tokenizer` class to break +formulas into their consitutuent tokens. Usage is as follows: + +.. doctest + +>>> from openpyxl.formula import Tokenizer +>>> tok = Tokenizer("""=IF($A$1,"then True",MAX(DEFAULT_VAL,'Sheet 2'!B1))""") +>>> print("\n".join("%12s%11s%9s" % (t.value, t.type, t.subtype) for t in tok.items)) + IF( FUNC OPEN + $A$1 OPERAND RANGE + , SEP ARG + "then True" OPERAND TEXT + , SEP ARG + MAX( FUNC OPEN + DEFAULT_VAL OPERAND RANGE + , SEP ARG +'Sheet 2'!B1 OPERAND RANGE + ) FUNC CLOSE + ) FUNC CLOSE + +As shown above, tokens have three attributes of interest: + +* ``.value``: The substring of the formula that produced this token + +* ``.type``: The type of token this represents. Can be one of + + - ``Token.LITERAL``: If the cell does not contain a formula, its + value is represented by a single ``LITERAL`` token. + + - ``Token.OPERAND``: A generic term for any value in the Excel + formula. (See ``.subtype`` below for more details). + + - ``Token.FUNC``: Function calls are broken up into tokens for the + opener (e.g., ``SUM(``), followed by the arguments, followed by + the closer (i.e., ``)``). The function name and opening + parenthesis together form one ``FUNC`` token, and the matching + parenthesis forms another ``FUNC`` token. + + - ``Token.ARRAY``: Array literals (enclosed between curly braces) + get two ``ARRAY`` tokens each, one for the opening ``{`` and one + for the closing ``}``. + + - ``Token.PAREN``: When used for grouping subexpressions (and not to + denote function calls), parentheses are tokenized as ``PAREN`` + tokens (one per character). + + - ``Token.SEP``: These tokens are created from either commas (``,``) + or semicolons (``;``). Commas create ``SEP`` tokens when they are + used to separate function arguments (e.g., ``SUM(a,b)``) or when + they are used to separate array elements (e.g., ``{a,b}``). (They + have another use as an infix operator for joining + ranges). Semicolons are always used to separate rows in an array + literal, so always create ``SEP`` tokens. + + - ``Token.OP_PRE``: Designates a prefix unary operator. Its value is + always ``+`` or ``-`` + + - ``Token.OP_IN``: Designates an infix binary operator. Possible + values are ``>=``, ``<=``, ``<>``, ``=``, ``>``, ``<``, ``*``, + ``/``, ``+``, ``-``, ``^``, or ``&``. + + - ``Token.OP_POST``: Designates a postfix unary operator. Its value + is always ``%``. + + - ``Token.WSPACE``: Created for any whitespace encountered. Its + value is always a single space, regardless of how much whitespace + is found. + +* ``.subtype``: Some of the token types above use the subtype to + provide additional information about the token. Possible subtypes + are: + + + ``Token.TEXT``, ``Token.NUMBER``, ``Token.LOGICAL``, + ``Token.ERROR``, ``Token.RANGE``: these subtypes describe the + various forms of ``OPERAND`` found in formulae. ``LOGICAL`` is + either ``TRUE`` or ``FALSE``, ``RANGE`` is either a named range or + a direct reference to another range. ``TEXT``, ``NUMBER``, and + ``ERROR`` all refer to literal values in the formula + + + ``Token.OPEN`` and ``Token.CLOSE``: these two subtypes are used by + ``PAREN``, ``FUNC``, and ``ARRAY``, to describe whether the token + is opening a new subexpression or closing it. + + + ``Token.ARG`` and ``Token.ROW``: are used by the ``SEP`` tokens, + to distinguish between the comma and semicolon. Commas produce + tokens of subtype ``ARG`` whereas semicolons produce tokens of + subtype ``ROW`` diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..3ae0f81 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,317 @@ +:mod:`openpyxl` - A Python library to read/write Excel 2010 xlsx/xlsm files +=========================================================================== + +.. module:: openpyxl +.. moduleauthor:: Eric Gazoni, Charlie Clark + +:Author: Eric Gazoni, Charlie Clark +:Source code: http://bitbucket.org/openpyxl/openpyxl/src +:Issues: http://bitbucket.org/openpyxl/openpyxl/issues +:Generated: |today| +:License: MIT/Expat +:Version: |release| + + +Introduction +------------ + +Openpyxl is a Python library for reading and writing 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 +`_. + + +Support ++++++++ + +This is an open source project, maintained by volunteers in their spare time. +This may well mean that particular features or functions that you would like +are missing. But things don't have to stay that way. You can contribute the +project :doc:`development` yourself or contract a developer for particular +features. + + +Professional support for openpyxl is available from +`Clark Consulting & Research `_ and +`Adimian `_. Donations to the project to support further +development and maintenance are welcome. + + +Bug reports and feature requests should be submitted using the `issue tracker +`_. Please provide a full +traceback of any error you see and if possible a sample file. If for reasons +of confidentiality you are unable to make a file publicly available then +contact of one the developers. + + +Sample code: +++++++++++++ + +.. literalinclude:: example.py + + +User List +--------- + +Official user list can be found on http://groups.google.com/group/openpyxl-users + + +How to Contribute Code +---------------------- + +Any help will be greatly appreciated, just follow those steps: + + 1. + Please start a new fork (https://bitbucket.org/openpyxl/openpyxl/fork) + for each independent feature, don't try to fix all problems at the same + time, it's easier for those who will review and merge your changes ;-) + + 2. + Hack hack hack + + 3. + Don't forget to add unit tests for your changes! (YES, even if it's a + one-liner, changes without tests will **not** be accepted.) There are plenty + of examples in the source if you lack know-how or inspiration. + + 4. + If you added a whole new feature, or just improved something, you can + be proud of it, so add yourself to the AUTHORS file :-) + + 5. + Let people know about the shiny thing you just implemented, update the + docs! + + 6. + When it's done, just issue a pull request (click on the large "pull + request" button on *your* repository) and wait for your code to be + reviewed, and, if you followed all theses steps, merged into the main + repository. + + +For further information see :doc:`development` + + +Other ways to help +------------------ + +There are several ways to contribute, even if you can't code (or can't code well): + + * triaging bugs on the bug tracker: closing bugs that have already been + closed, are not relevant, cannot be reproduced, ... + + * updating documentation in virtually every area: many large features have + been added (mainly about charts and images at the moment) but without any + documentation, it's pretty hard to do anything with it + + * proposing compatibility fixes for different versions of Python: we support + 2.7 to 3.5, so if it does not work on your environment, let us know :-) + + +Installation +------------ + +Install openpyxl using pip. It is advisable to do this in a Python virtualenv +without system packages:: + + $ pip install openpyxl + +.. note:: + + There is support for the popular `lxml`_ library which will be used if it + is installed. This is particular useful when creating large files. + +.. _lxml: http://lxml.de + +.. warning:: + + To be able to include images (jpeg, png, bmp,...) into an openpyxl file, + you will also need the "pillow" library that can be installed with:: + + $ pip install pillow + + or browse https://pypi.python.org/pypi/Pillow/, pick the latest version + and head to the bottom of the page for Windows binaries. + + +Working with a checkout +----------------------- + +Sometimes you might want to work with the checkout of a particular version. +This may be the case if bugs have been fixed but a release has not yet been +made. + +.. parsed-literal:: + $ pip install -e hg+https://bitbucket.org/openpyxl/openpyxl@\ |version|\ #egg=openpyxl + + +Usage examples +-------------- + +Tutorial +++++++++ + +.. toctree:: + + tutorial + +Cookbook +++++++++ + +.. toctree:: + + usage + + +Pandas and NumPy +++++++++++++++++ + +.. toctree:: + + pandas + + +Charts +++++++ + +.. toctree:: + + charts/introduction + + +Comments +++++++++ + +.. toctree:: + + comments + + +Read/write large files +++++++++++++++++++++++ + +.. toctree:: + + optimized + + +Working with styles ++++++++++++++++++++ + +.. toctree:: + + styles + worksheet_properties + + +Conditional Formatting +++++++++++++++++++++++ + +.. toctree:: + + formatting + + +Print Settings +++++++++++++++++++++++ + +.. toctree:: + + print_settings + + +Filtering and Sorting ++++++++++++++++++++++ + +.. toctree:: + + filters + + +Worksheet Tables +++++++++++++++++ + +.. toctree:: + + worksheet_tables.rst + + +Data Validation ++++++++++++++++ + +.. toctree:: + + validation + + +Defined Names & Ranges +++++++++++++++++++++++ + +.. toctree:: + + defined_names + + +Parsing Formulas +++++++++++++++++ + +.. toctree:: + + formula + + +Protection +++++++++++ + +.. toctree:: + + protection + + +Information for Developers +-------------------------- + +.. toctree:: + + development + windows-development + + +API Documentation +------------------ + +Key Classes ++++++++++++ + +* :class:`openpyxl.workbook.workbook.Workbook` +* :class:`openpyxl.worksheet.worksheet.Worksheet` +* :class:`openpyxl.cell.cell.Cell` + +Full API +++++++++ + +.. toctree:: + :maxdepth: 2 + + api/openpyxl + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + +Release Notes +============= + +.. toctree:: + :maxdepth: 1 + + changes diff --git a/doc/logo.png b/doc/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e2998ba33a3619a7c4aea7d3cf25d8ff6a16fa40 GIT binary patch literal 5505 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 DVB%lC literal 0 HcmV?d00001 diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 0000000..47406bc --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,155 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\openpyxl.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\openpyxl.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/doc/optimized.rst b/doc/optimized.rst new file mode 100644 index 0000000..5f5087c --- /dev/null +++ b/doc/optimized.rst @@ -0,0 +1,103 @@ +Read-only mode +============== + +Sometimes, you will need to open or write extremely large XLSX files, +and the common routines in openpyxl won't be able to handle that load. +Fortunately, there are two modes that enable you to read and write unlimited +amounts of data with (near) constant memory consumption. + +Introducing :class:`openpyxl.worksheet.read_only.ReadOnlyWorksheet`:: + + from openpyxl import load_workbook + wb = load_workbook(filename='large_file.xlsx', read_only=True) + ws = wb['big_data'] + + for row in ws.rows: + for cell in row: + print(cell.value) + +.. warning:: + + * :class:`openpyxl.worksheet.read_only.ReadOnlyWorksheet` is read-only + +Cells returned are not regular :class:`openpyxl.cell.cell.Cell` but +:class:`openpyxl.cell.read_only.ReadOnlyCell`. + + +Worksheet dimensions +-------------------- + +Read-only mode relies on applications and libraries that created the file +providing correct information about the worksheets, specifically the used +part of it, known as the dimensions. Some applications set this incorrectly. +You can check the apparent dimensions of a worksheet using +`ws.calculate_dimension()`. If this returns a range that you know is +incorrect, say `A1:A1` then simply resetting the max_row and max_column +attributes should allow you to work with the file:: + + ws.max_row = ws.max_column = None + + +Write-only mode +=============== + +Here again, the regular :class:`openpyxl.worksheet.worksheet.Worksheet` has been replaced +by a faster alternative, the :class:`openpyxl.writer.write_only.WriteOnlyWorksheet`. +When you want to dump large amounts of data make sure you have `lxml` installed. + +.. :: doctest + +>>> from openpyxl import Workbook +>>> wb = Workbook(write_only=True) +>>> ws = wb.create_sheet() +>>> +>>> # now we'll fill it with 100 rows x 200 columns +>>> +>>> for irow in range(100): +... ws.append(['%d' % i for i in range(200)]) +>>> # save the file +>>> wb.save('new_big_file.xlsx') # doctest: +SKIP + +If you want to have cells with styles or comments then use a :func:`openpyxl.worksheet.write_only.WriteOnlyCell` + +.. :: doctest + +>>> from openpyxl import Workbook +>>> wb = Workbook(write_only = True) +>>> ws = wb.create_sheet() +>>> from openpyxl.worksheet.write_only import WriteOnlyCell +>>> from openpyxl.comments import Comment +>>> from openpyxl.styles import Font +>>> cell = WriteOnlyCell(ws, value="hello world") +>>> cell.font = Font(name='Courier', size=36) +>>> cell.comment = Comment(text="A comment", author="Author's Name") +>>> ws.append([cell, 3.14, None]) +>>> wb.save('write_only_file.xlsx') + + +This will create a write-only workbook with a single sheet, and append +a row of 3 cells: one text cell with a custom font and a comment, a +floating-point number, and an empty cell (which will be discarded +anyway). + +.. warning:: + + * Unlike a normal workbook, a newly-created write-only workbook + does not contain any worksheets; a worksheet must be specifically + created with the :func:`create_sheet()` method. + + * In a write-only workbook, rows can only be added with + :func:`append()`. It is not possible to write (or read) cells at + arbitrary locations with :func:`cell()` or :func:`iter_rows()`. + + * It is able to export unlimited amount of data (even more than Excel can + handle actually), while keeping memory usage under 10Mb. + + * A write-only workbook can only be saved once. After + that, every attempt to save the workbook or append() to an existing + worksheet will raise an :class:`openpyxl.utils.exceptions.WorkbookAlreadySaved` + exception. + + * Everything that appears in the file before the actual cell data must be created + before cells are added because it must written to the file before then. + For example, `freeze_panes` should be set before cells are added. diff --git a/doc/pandas.rst b/doc/pandas.rst new file mode 100644 index 0000000..9897b3e --- /dev/null +++ b/doc/pandas.rst @@ -0,0 +1,93 @@ +Working with Pandas and NumPy +============================= + +openpyxl is able to work with the popular libraries `Pandas +`_ and `NumPy `_ + + +NumPy Support +------------- + +openpyxl has builtin support for the NumPy types float, integer and boolean. +DateTimes are supported using the Pandas' Timestamp type. + + +Working with Pandas Dataframes +------------------------------ + +The :func:`openpyxl.utils.dataframe.dataframe_to_rows` function provides a +simple way to work with Pandas Dataframes:: + + from openpyxl.utils.dataframe import dataframe_to_rows + wb = Workbook() + ws = wb.active + + for r in dataframe_to_rows(df, index=True, header=True): + ws.append(r) + + +While Pandas itself supports conversion to Excel, this gives client code +additional flexibility including the ability to stream dataframes straight to +files. + +To convert a dataframe into a worksheet highlighting the header and index:: + + wb = Workbook() + ws = wb.active + + for r in dataframe_to_rows(df, index=True, header=True): + ws.append(r) + + for cell in ws['A'] + ws[1]: + cell.style = 'Pandas' + + wb.save("pandas_openpyxl.xlsx") + +Alternatively, if you just want to convert the data you can use write-only mode:: + + from openpyxl.cell.cell import WriteOnlyCell + wb = Workbook(write_only=True) + ws = wb.create_sheet() + + cell = WriteOnlyCell(ws) + cell.style = 'Pandas' + + def format_first_row(row, cell): + + for c in row: + cell.value = c + yield cell + + rows = dataframe_to_rows(df) + first_row = format_first_row(next(rows), cell) + ws.append(first_row) + + for row in rows: + row = list(row) + cell.value = row[0] + row[0] = cell + ws.append(row) + + wb.save("openpyxl_stream.xlsx") + + +This code will work just as well with a standard workbook. + + +Converting a worksheet to a Dataframe +------------------------------------- + +To convert a worksheet to a Dataframe you can use the `values` property. This +is very easy if the worksheet has no headers or indices:: + + df = DataFrame(ws.values) + +If the worksheet does have headers or indices, such as one created by Pandas, +then a little more work is required:: + + data = ws.values + cols = next(data)[1:] + data = list(data) + idx = [r[0] for r in data] + data = (islice(r, 1, None) for r in data) + df = DataFrame(data, index=idx, columns=cols) diff --git a/doc/print_settings.rst b/doc/print_settings.rst new file mode 100644 index 0000000..668d06b --- /dev/null +++ b/doc/print_settings.rst @@ -0,0 +1,76 @@ +Print Settings +============== + +openpyxl provides reasonably full support for print settings. + + +Edit Print Options +------------------- +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> ws.print_options.horizontalCentered = True +>>> ws.print_options.verticalCentered = True + + +Headers and Footers +------------------- + +Headers and footers use their own formatting language. This is fully +supported when writing them but, due to the complexity and the possibility of +nesting, only partially when reading them. There is support for the font, +size and color for a left, centre/center, or right element. Granular control +(highlighting individuals words) will require applying control codes +manually. + + +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> ws.oddHeader.left.text = "Page &[Page] of &N" +>>> ws.oddHeader.left.size = 14 +>>> ws.oddHeader.left.font = "Tahoma,Bold" +>>> ws.oddHeader.left.color = "CC3366" + + +Also supported are `evenHeader` and `evenFooter` as well as `firstHeader` and `firstFooter`. + + +Add Print Titles +---------------- + +You can print titles on every page to ensure that the data is properly +labelled. + +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> ws.print_title_cols = 'A:B' # the first two cols +>>> ws.print_title_rows = '1:1' # the first row + + +Add a Print Area +---------------- + +You can select a part of a worksheet as the only part that you want to print + +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> ws.print_area = 'A1:F10' diff --git a/doc/protection.rst b/doc/protection.rst new file mode 100644 index 0000000..b67722e --- /dev/null +++ b/doc/protection.rst @@ -0,0 +1,62 @@ +Protection +========== + +.. warning:: + + Password protecting a workbook or worksheet only provides a quite basic level of security. + The data is not encrypted, so can be modified by any number of freely available tools. In + fact the specification states: "Worksheet or workbook element protection should not be + confused with file security. It is meant to make your workbook safe from unintentional + modification, and cannot protect it from malicious modification." + +Openpyxl provides support for protecting a workbook and worksheet from modification. The Open XML +"Legacy Password Hash Algorithm" is used to generate hashed password values unless another +algorithm is explicitly configured. + +Workbook Protection +------------------- + +To prevent other users from viewing hidden worksheets, adding, moving, deleting, or hiding worksheets, and +renaming worksheets, you can protect the structure of your workbook with a password. The password can be +set using the :func:`openpyxl.workbook.protection.WorkbookProtection.workbookPassword` property :: + + >>> wb.security.workbookPassword = '...' + >>> wb.security.lockStructure = True + + +Similarly removing change tracking and change history from a shared workbook can be prevented by setting +another password. This password can be set using the +:func:`openpyxl.workbook.protection.WorkbookProtection.revisionsPassword` property :: + + >>> wb.security.revisionsPassword = '...' + +Other properties on the :class:`openpyxl.workbook.protection.WorkbookProtection` object control exactly what +restrictions are in place, but these will only be enforced if the appropriate password is set. + +Specific setter functions are provided if you need to set the raw password value without using the +default hashing algorithm - e.g. :: + + hashed_password = ... + wb.security.set_workbook_password(hashed_password, already_hashed=True) + + +Worksheet Protection +-------------------- + +Various aspects of a worksheet can also be locked by setting attributes on the +:class:`openpyxl.worksheet.protection.SheetProtection` object. Unlike workbook protection, sheet +protection may be enabled with or without using a password. Sheet protection is enabled using the +:attr:`openpxyl.worksheet.protection.SheetProtection.sheet` attribute or calling `enable()` or `disable()`:: + + >>> ws = wb.active + >>> wb.protection.sheet = True + >>> wb.protection.enable() + >>> wb.protection.disabe() + + +If no password is specified, users can disable configured sheet protection without specifying a password. +Otherwise they must supply a password to change configured protections. The password is set using +the :func:`openpxyl.worksheet.protection.SheetProtection.password` property :: + + >>> ws = wb.active + >>> ws.protection.password = '...' diff --git a/doc/styles.rst b/doc/styles.rst new file mode 100644 index 0000000..7205311 --- /dev/null +++ b/doc/styles.rst @@ -0,0 +1,301 @@ +Working with styles +=================== + +Introduction +------------ + +Styles are used to change the look of your data while displayed on screen. +They are also used to determine the formatting for numbers. + +Styles can be applied to the following aspects: + + * font to set font size, color, underlining, etc. + * fill to set a pattern or color gradient + * border to set borders on a cell + * cell alignment + * protection + +The following are the default values + +.. :: doctest + +>>> from openpyxl.styles import PatternFill, Border, Side, Alignment, Protection, Font +>>> font = Font(name='Calibri', +... size=11, +... bold=False, +... italic=False, +... vertAlign=None, +... underline='none', +... strike=False, +... color='FF000000') +>>> fill = PatternFill(fill_type=None, +... start_color='FFFFFFFF', +... end_color='FF000000') +>>> border = Border(left=Side(border_style=None, +... color='FF000000'), +... right=Side(border_style=None, +... color='FF000000'), +... top=Side(border_style=None, +... color='FF000000'), +... bottom=Side(border_style=None, +... color='FF000000'), +... diagonal=Side(border_style=None, +... color='FF000000'), +... diagonal_direction=0, +... outline=Side(border_style=None, +... color='FF000000'), +... vertical=Side(border_style=None, +... color='FF000000'), +... horizontal=Side(border_style=None, +... color='FF000000') +... ) +>>> alignment=Alignment(horizontal='general', +... vertical='bottom', +... text_rotation=0, +... wrap_text=False, +... shrink_to_fit=False, +... indent=0) +>>> number_format = 'General' +>>> protection = Protection(locked=True, +... hidden=False) +>>> + +Cell Styles and Named Styles +---------------------------- + +There are two types of styles: cell styles and named styles, also known as style templates. + +Cell Styles ++++++++++++ + +Cell styles are shared between objects and once they have been assigned they +cannot be changed. This stops unwanted side-effects such as changing the +style for lots of cells when instead of only one. + +.. :: doctest + +>>> from openpyxl.styles import colors +>>> from openpyxl.styles import Font, Color +>>> from openpyxl import Workbook +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> a1 = ws['A1'] +>>> d4 = ws['D4'] +>>> ft = Font(color=colors.RED) +>>> a1.font = ft +>>> d4.font = ft +>>> +>>> a1.font.italic = True # is not allowed # doctest: +SKIP +>>> +>>> # If you want to change the color of a Font, you need to reassign it:: +>>> +>>> a1.font = Font(color=colors.RED, italic=True) # the change only affects A1 + + +Copying styles +-------------- + +Styles can also be copied + +.. :: doctest + +>>> from openpyxl.styles import Font +>>> from copy import copy +>>> +>>> ft1 = Font(name='Arial', size=14) +>>> ft2 = copy(ft1) +>>> ft2.name = "Tahoma" +>>> ft1.name +'Arial' +>>> ft2.name +'Tahoma' +>>> ft2.size # copied from the +14.0 + + +Basic Font Colors +----------------- +Colors are usually RGB or aRGB hexvalues. The `colors` module contains some handy constants + +.. :: doctest + +>>> from openpyxl.styles import Font +>>> from openpyxl.styles.colors import RED +>>> font = Font(color=RED) +>>> font = Font(color="FFBB00") + +There is also support for legacy indexed colors as well as themes and tints + +>>> from openpyxl.styles.colors import Color +>>> c = Color(indexed=32) +>>> c = Color(theme=6, tint=0.5) + + +Applying Styles +--------------- +Styles are applied directly to cells + +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> from openpyxl.styles import Font, Fill +>>> wb = Workbook() +>>> ws = wb.active +>>> c = ws['A1'] +>>> c.font = Font(size=12) + +Styles can also applied to columns and rows but note that this applies only +to cells created (in Excel) after the file is closed. If you want to apply +styles to entire rows and columns then you must apply the style to each cell +yourself. This is a restriction of the file format:: + +>>> col = ws.column_dimensions['A'] +>>> col.font = Font(bold=True) +>>> row = ws.row_dimensions[1] +>>> row.font = Font(underline="single") + +.. _styling-merged-cells: + +Styling Merged Cells +-------------------- + +Sometimes you want to format a range of cells as if they were a single +object. Excel pretends that this is possible by merging cells (deleting all +but the top-left cell) and then recreating them in order to apply +pseudo-styles. + +.. literalinclude:: format_merged_cells.py + + +Edit Page Setup +------------------- +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE +>>> ws.page_setup.paperSize = ws.PAPERSIZE_TABLOID +>>> ws.page_setup.fitToHeight = 0 +>>> ws.page_setup.fitToWidth = 1 + + +Named Styles +++++++++++++ + +In contrast to Cell Styles, Named Styles are mutable. They make sense when +you want to apply formatting to lots of different cells at once. NB. once you +have assigned a named style to a cell, additional changes to the style will +**not** affect the cell. + +Once a named style has been registered with a workbook, it can be referred to simply by name. + + +Creating a Named Style +---------------------- + +.. :: doctest + +>>> from openpyxl.styles import NamedStyle, Font, Border, Side +>>> highlight = NamedStyle(name="highlight") +>>> highlight.font = Font(bold=True, size=20) +>>> bd = Side(style='thick', color="000000") +>>> highlight.border = Border(left=bd, top=bd, right=bd, bottom=bd) + +Once a named style has been created, it can be registered with the workbook: + +>>> wb.add_named_style(highlight) + +But named styles will also be registered automatically the first time they are assigned to a cell: + +>>> ws['A1'].style = highlight + +Once registered assign the style using just the name: + +>>> ws['D5'].style = 'highlight' + + +Using builtin styles +-------------------- + +The specification includes some builtin styles which can also be used. +Unfortunately, the names for these styles are stored in their localised +forms. openpyxl will only recognise the English names and only exactly as +written here. These are as follows: + + +* 'Normal' # same as no style + +Number formats +++++++++++++++ + +* 'Comma' +* 'Comma [0]' +* 'Currency' +* 'Currency [0]' +* 'Percent' + +Informative ++++++++++++ + +* 'Calculation' +* 'Total' +* 'Note' +* 'Warning Text' +* 'Explanatory Text' + +Text styles ++++++++++++ + +* 'Title' +* 'Headline 1' +* 'Headline 2' +* 'Headline 3' +* 'Headline 4' +* 'Hyperlink' +* 'Followed Hyperlink' +* 'Linked Cell' + +Comparisons ++++++++++++ + +* 'Input' +* 'Output' +* 'Check Cell' +* 'Good' +* 'Bad' +* 'Neutral' + +Highlights +++++++++++ + +* 'Accent1' +* '20 % - Accent1' +* '40 % - Accent1' +* '60 % - Accent1' +* 'Accent2' +* '20 % - Accent2' +* '40 % - Accent2' +* '60 % - Accent2' +* 'Accent3' +* '20 % - Accent3' +* '40 % - Accent3' +* '60 % - Accent3' +* 'Accent4' +* '20 % - Accent4' +* '40 % - Accent4' +* '60 % - Accent4' +* 'Accent5' +* '20 % - Accent5' +* '40 % - Accent5' +* '60 % - Accent5' +* 'Accent6' +* '20 % - Accent6' +* '40 % - Accent6' +* '60 % - Accent6' +* 'Pandas' + +For more information about the builtin styles please refer to the :mod:`openpyxl.styles.builtins` diff --git a/doc/table.png b/doc/table.png new file mode 100644 index 0000000000000000000000000000000000000000..54cb8684a287486f12a0f55fe60cca8e6911a21f GIT binary patch literal 20324 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 Z?O$=lt;%9_@v?cB^nu3OH2_EG{{R+>_;COL literal 0 HcmV?d00001 diff --git a/doc/table.py b/doc/table.py new file mode 100644 index 0000000..afc7091 --- /dev/null +++ b/doc/table.py @@ -0,0 +1,26 @@ +from openpyxl import Workbook +from openpyxl.worksheet.table import Table, TableStyleInfo + +wb = Workbook() +ws = wb.active + +data = [ + ['Apples', 10000, 5000, 8000, 6000], + ['Pears', 2000, 3000, 4000, 5000], + ['Bananas', 6000, 6000, 6500, 6000], + ['Oranges', 500, 300, 200, 700], +] + +# add column headings. NB. these must be strings +ws.append(["Fruit", "2011", "2012", "2013", "2014"]) +for row in data: + ws.append(row) + +tab = Table(displayName="Table1", ref="A1:E5") + +# Add a default style with striped rows and banded columns +style = TableStyleInfo(name="TableStyleMedium9", showFirstColumn=False, + showLastColumn=False, showRowStripes=True, showColumnStripes=True) +tab.tableStyleInfo = style +ws.add_table(tab) +wb.save("table.xlsx") diff --git a/doc/tutorial.rst b/doc/tutorial.rst new file mode 100644 index 0000000..7ad6566 --- /dev/null +++ b/doc/tutorial.rst @@ -0,0 +1,308 @@ +Manipulating a workbook in memory +================================= + +Create a workbook +----------------- + +There is no need to create a file on the filesystem to get started with openpyxl. +Just import the Workbook class and start using it :: + + >>> from openpyxl import Workbook + >>> wb = Workbook() + +A workbook is always created with at least one worksheet. You can get it by +using the :func:`openpyxl.workbook.Workbook.active` property :: + + >>> ws = wb.active + +.. note:: + + This function uses the `_active_sheet_index` property, set to 0 by default. + Unless you modify its value, you will always get the + first worksheet by using this method. + +You can also create new worksheets by using the +:func:`openpyxl.workbook.Workbook.create_sheet` method :: + + >>> ws1 = wb.create_sheet("Mysheet") # insert at the end (default) + # or + >>> ws2 = wb.create_sheet("Mysheet", 0) # insert at first position + +Sheets are given a name automatically when they are created. +They are numbered in sequence (Sheet, Sheet1, Sheet2, ...). +You can change this name at any time with the `title` property:: + + ws.title = "New Title" + +The background color of the tab holding this title is white by default. +You can change this providing an RRGGBB color code to the sheet_properties.tabColor property:: + + ws.sheet_properties.tabColor = "1072BA" + +Once you gave a worksheet a name, you can get it as a key of the workbook:: + + >>> ws3 = wb["New Title"] + +You can review the names of all worksheets of the workbook with the +:func:`openpyxl.workbook.Workbook.sheetnames` property :: + + >>> print(wb.sheetnames) + ['Sheet2', 'New Title', 'Sheet1'] + +You can loop through worksheets :: + + >>> for sheet in wb: + ... print(sheet.title) + +You can create copies of worksheets *within a single workbook*: + +:func:`openpyxl.workbook.Workbook.copy_worksheet` method:: + + >>> source = wb.active + >>> target = wb.copy_worksheet(source) + +.. note:: + + Only cells (including values, styles, hyperlinks and comments) and + certain worksheet attribues (including dimensions, format and + properties) are copied. All other workbook / worksheet attributes + are not copied - e.g. Images, Charts. + +.. note:: + + You cannot copy worksheets between workbooks. You also cannot copy + a worksheet if the workbook is open in `read-only` or `write-only` + mode. + + +Playing with data +------------------ + +Accessing one cell +++++++++++++++++++ + +Now we know how to access a worksheet, we can start modifying cells content. + +Cells can be accessed directly as keys of the worksheet :: + + >>> c = ws['A4'] + +This will return the cell at A4 or create one if it does not exist yet. +Values can be directly assigned :: + + >>> ws['A4'] = 4 + +There is also the :func:`openpyxl.worksheet.Worksheet.cell` method. + +This provides access to cells using row and column notation:: + + >>> d = ws.cell(row=4, column=2, value=10) + +.. note:: + + When a worksheet is created in memory, it contains no `cells`. They are + created when first accessed. + +.. warning:: + + Because of this feature, scrolling through cells instead of accessing them + directly will create them all in memory, even if you don't assign them a value. + + Something like :: + + >>> for i in range(1,101): + ... for j in range(1,101): + ... ws.cell(row=i, column=j) + + will create 100x100 cells in memory, for nothing. + + + +Accessing many cells +++++++++++++++++++++ + +Ranges of cells can be accessed using slicing :: + + >>> cell_range = ws['A1':'C2'] + + +Ranges of rows or columns can be obtained similarly:: + + >>> colC = ws['C'] + >>> col_range = ws['C:D'] + >>> row10 = ws[10] + >>> row_range = ws[5:10] + +You can also use the :func:`openpyxl.worksheet.Worksheet.iter_rows` method:: + + >>> for row in ws.iter_rows(min_row=1, max_col=3, max_row=2): + ... for cell in row: + ... print(cell) + + + + + + + +Likewise the :func:`openpyxl.worksheet.Worksheet.iter_cols` method will return columns:: + + >>> for col in ws.iter_cols(min_row=1, max_col=3, max_row=2): + ... for cell in col: + ... print(cell) + + + + + + + + +If you need to iterate through all the rows or columns of a file, you can instead use the +:func:`openpyxl.worksheet.Worksheet.rows` property:: + + >>> ws = wb.active + >>> ws['C9'] = 'hello world' + >>> tuple(ws.rows) + ((, , ), + (, , ), + (, , ), + (, , ), + (, , ), + (, , ), + (, , ), + (, , ), + (, , )) + +or the :func:`openpyxl.worksheet.Worksheet.columns` property:: + + >>> tuple(ws.columns) + ((, + , + , + , + , + , + ... + , + , + ), + (, + , + , + , + , + , + , + , + )) + + +Data storage +++++++++++++ + +Once we have a :class:`openpyxl.cell.Cell`, we can assign it a value:: + + >>> c.value = 'hello, world' + >>> print(c.value) + 'hello, world' + + >>> d.value = 3.14 + >>> print(d.value) + 3.14 + +You can also enable type and format inference:: + + >>> wb = Workbook(guess_types=True) + >>> c.value = '12%' + >>> print(c.value) + 0.12 + + >>> import datetime + >>> d.value = datetime.datetime.now() + >>> print d.value + datetime.datetime(2010, 9, 10, 22, 25, 18) + + >>> c.value = '31.50' + >>> print(c.value) + 31.5 + + +Saving to a file +================ + +The simplest and safest way to save a workbook is by using the +:func:`openpyxl.workbook.Workbook.save()` method of the +:class:`openpyxl.workbook.Workbook` object:: + + >>> wb = Workbook() + >>> wb.save('balances.xlsx') + +.. warning:: + + This operation will overwrite existing files without warning. + +.. note:: + + Extension is not forced to be xlsx or xlsm, although you might have + some trouble opening it directly with another application if you don't + use an official extension. + + As OOXML files are basically ZIP files, you can also end the filename + with .zip and open it with your favourite ZIP archive manager. + +You can specify the attribute `template=True`, to save a workbook +as a template:: + + >>> wb = load_workbook('document.xlsx') + >>> wb.template = True + >>> wb.save('document_template.xltx') + +or set this attribute to `False` (default), to save as a document:: + + >>> wb = load_workbook('document_template.xltx') + >>> wb.template = False + >>> wb.save('document.xlsx', as_template=False) + +.. warning:: + + You should monitor the data attributes and document extensions + for saving documents in the document templates and vice versa, + otherwise the result table engine can not open the document. + +.. note:: + + The following will fail:: + + >>> wb = load_workbook('document.xlsx') + >>> # Need to save with the extension *.xlsx + >>> wb.save('new_document.xlsm') + >>> # MS Excel can't open the document + >>> + >>> # or + >>> + >>> # Need specify attribute keep_vba=True + >>> wb = load_workbook('document.xlsm') + >>> wb.save('new_document.xlsm') + >>> # MS Excel will not open the document + >>> + >>> # or + >>> + >>> wb = load_workbook('document.xltm', keep_vba=True) + >>> # If we need a template document, then we must specify extension as *.xltm. + >>> wb.save('new_document.xlsm') + >>> # MS Excel will not open the document + + +Loading from a file +=================== + +The same way as writing, you can import :func:`openpyxl.load_workbook` to +open an existing workbook:: + + >>> from openpyxl import load_workbook + >>> wb2 = load_workbook('test.xlsx') + >>> print wb2.get_sheet_names() + ['Sheet2', 'New Title', 'Sheet1'] + +This ends the tutorial for now, you can proceed to the :doc:`usage` section diff --git a/doc/usage.rst b/doc/usage.rst new file mode 100644 index 0000000..33ce534 --- /dev/null +++ b/doc/usage.rst @@ -0,0 +1,164 @@ +Simple usage +============ + +Write a workbook +---------------- +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.compat import range +>>> from openpyxl.utils import get_column_letter +>>> +>>> wb = Workbook() +>>> +>>> dest_filename = 'empty_book.xlsx' +>>> +>>> ws1 = wb.active +>>> ws1.title = "range names" +>>> +>>> for row in range(1, 40): +... ws1.append(range(600)) +>>> +>>> ws2 = wb.create_sheet(title="Pi") +>>> +>>> ws2['F5'] = 3.14 +>>> +>>> ws3 = wb.create_sheet(title="Data") +>>> for row in range(10, 20): +... for col in range(27, 54): +... _ = ws3.cell(column=col, row=row, value="{0}".format(get_column_letter(col))) +>>> print(ws3['AA10'].value) +AA +>>> wb.save(filename = dest_filename) + + +Read an existing workbook +------------------------- +.. :: doctest + +>>> from openpyxl import load_workbook +>>> wb = load_workbook(filename = 'empty_book.xlsx') +>>> sheet_ranges = wb['range names'] +>>> print(sheet_ranges['D18'].value) +3 + + +.. note :: + + There are several flags that can be used in load_workbook. + + - `guess_types` will enable or disable (default) type inference when + reading cells. + + - `data_only` controls whether cells with formulae have either the + formula (default) or the value stored the last time Excel read the sheet. + + - `keep_vba` controls whether any Visual Basic elements are preserved or + not (default). If they are preserved they are still not editable. + + +.. warning :: + + openpyxl does currently not read all possible items in an Excel file so + images and charts will be lost from existing files if they are opened and + saved with the same name. + + +Using number formats +-------------------- +.. :: doctest + +>>> import datetime +>>> from openpyxl import Workbook +>>> wb = Workbook() +>>> ws = wb.active +>>> # set date using a Python datetime +>>> ws['A1'] = datetime.datetime(2010, 7, 21) +>>> +>>> ws['A1'].number_format +'yyyy-mm-dd h:mm:ss' +>>> # You can enable type inference on a case-by-case basis +>>> wb.guess_types = True +>>> # set percentage using a string followed by the percent sign +>>> ws['B1'] = '3.14%' +>>> wb.guess_types = False +>>> ws['B1'].value +0.031400000000000004 +>>> +>>> ws['B1'].number_format +'0%' + + +Using formulae +-------------- +.. :: doctest + +>>> from openpyxl import Workbook +>>> wb = Workbook() +>>> ws = wb.active +>>> # add a simple formula +>>> ws["A1"] = "=SUM(1, 1)" +>>> wb.save("formula.xlsx") + +.. warning:: + NB you must use the English name for a function and function arguments *must* be separated by commas and not other punctuation such as semi-colons. + +openpyxl never evaluates formula but it is possible to check the name of a formula: + +.. :: doctest + +>>> from openpyxl.utils import FORMULAE +>>> "HEX2DEC" in FORMULAE +True + +If you're trying to use a formula that isn't known this could be because you're using a formula that was not included in the initial specification. Such formulae must be prefixed with `_xlfn.` to work. + +Merge / Unmerge cells +--------------------- + +When you merge cells all cells but the top-left one are **removed** from the +worksheet. See :ref:`styling-merged-cells` for information on formatting merged cells. + +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> ws.merge_cells('A2:D2') +>>> ws.unmerge_cells('A2:D2') +>>> +>>> # or equivalently +>>> ws.merge_cells(start_row=2, start_column=1, end_row=4, end_column=4) +>>> ws.unmerge_cells(start_row=2, start_column=1, end_row=4, end_column=4) + + +Inserting an image +------------------- +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.drawing.image import Image +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> ws['A1'] = 'You should see three logos below' + +>>> # create an image +>>> img = Image('logo.png') + +>>> # add to worksheet and anchor next to cells +>>> ws.add_image(img, 'A1') +>>> wb.save('logo.xlsx') + + +Fold columns (outline) +---------------------- +.. :: doctest + +>>> import openpyxl +>>> wb = openpyxl.Workbook() +>>> ws = wb.create_sheet() +>>> ws.column_dimensions.group('A','D', hidden=True) +>>> wb.save('group.xlsx') diff --git a/doc/validation.rst b/doc/validation.rst new file mode 100644 index 0000000..cf6d5cf --- /dev/null +++ b/doc/validation.rst @@ -0,0 +1,113 @@ +Validating cells +================ + +Data validators can be applied to ranges of cells but are not enforced or evaluated. Ranges do not have to be contiguous: eg. "A1 B2:B5" is contains A1 and the cells B2 to B5 but not A2 or B2. + + +Examples +-------- + +.. :: doctest + +>>> from openpyxl import Workbook +>>> from openpyxl.worksheet.datavalidation import DataValidation +>>> +>>> # Create the workbook and worksheet we'll be working with +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> # Create a data-validation object with list validation +>>> dv = DataValidation(type="list", formula1='"Dog,Cat,Bat"', allow_blank=True) +>>> +>>> # Optionally set a custom error message +>>> dv.error ='Your entry is not in the list' +>>> dv.errorTitle = 'Invalid Entry' +>>> +>>> # Optionally set a custom prompt message +>>> dv.prompt = 'Please select from the list' +>>> dv.promptTitle = 'List Selection' +>>> +>>> # Add the data-validation object to the worksheet +>>> ws.add_data_validation(dv) + +>>> # Create some cells, and add them to the data-validation object +>>> c1 = ws["A1"] +>>> c1.value = "Dog" +>>> dv.add(c1) +>>> c2 = ws["A2"] +>>> c2.value = "An invalid value" +>>> dv.add(c2) +>>> +>>> # Or, apply the validation to a range of cells +>>> dv.add('B1:B1048576') # This is the same as for the whole of column B +>>> +>>> # Check with a cell is in the validator +>>> "B4" in dv +True + + +.. note :: + + Validations without any cell ranges will be ignored when saving a workbook. + +Other validation examples +------------------------- + +Any whole number: +:: + + dv = DataValidation(type="whole") + +Any whole number above 100: +:: + + dv = DataValidation(type="whole", + operator="greaterThan", + formula1=100) + +Any decimal number: +:: + + dv = DataValidation(type="decimal") + +Any decimal number between 0 and 1: +:: + + dv = DataValidation(type="decimal", + operator="between", + formula1=0, + formula2=1) + +Any date: +:: + + dv = DataValidation(type="date") + +or time: +:: + + dv = DataValidation(type="time") + +Any string at most 15 characters: +:: + + dv = DataValidation(type="textLength", + operator="lessThanOrEqual"), + formula1=15) + +Cell range validation: +:: + + from openpyxl.utils import quote_sheetname + dv = DataValidation(type="list", + formula1="{0}!$B$1:$B$10".format(quote_sheetname(sheetname)) + ) + +Custom rule: +:: + + dv = DataValidation(type="custom", + formula1"=SOMEFORMULA") + +.. note:: + See http://www.contextures.com/xlDataVal07.html for custom rules diff --git a/doc/windows-development.rst b/doc/windows-development.rst new file mode 100644 index 0000000..2cda1cc --- /dev/null +++ b/doc/windows-development.rst @@ -0,0 +1,82 @@ +Testing on Windows +================== + + +Although openpyxl itself is pure Python and should run on any Python, we do use some libraries that require compiling for tests and documentation. The setup for testing on Windows is somewhat different. + + +Getting started +--------------- + +Once you have installed the versions of Python (2.6, 2.7, 3.3, 3.4) you should setup a development environment for testing so that you do not adversely affect the system install. + + +Setting up a development environment +------------------------------------ + +First of all you should checkout a copy of the repository. Atlassian provides a nice GUI client `SourceTree `_ that allows you to do this with a single-click from the browser. + +By default the repository will be installed under your user folder. eg. c:\Users\YOURUSER\openpyxl + +Switch to the branch you want to work on by double-clicking it. The default branch should never be used for development work. + +Creating a virtual environment +++++++++++++++++++++++++++++++ + +You will need to manually install virtualenv. This is best done by first installing pip. open a command line and download the script "get_pip.py" to your preferred Python folder:: + + bitsadmin /transfer pip http://bootstrap.pypa.io/get-pip.py c:\python27\get-pip.py # change the path as necessary + +Install pip (it needs to be at least pip 6.0):: + + python get_pip.py + +Now you can install virtualenv:: + + Scripts\pip install virtualenv + Scripts\virtualenv c:\Users\YOURUSER\openpyxl + + +lxml +---- + +openpyxl needs `lxml` in order to run the tests. Unfortunately, automatic installation of lxml on Windows is tricky as pip defaults to try and compile it. This can be avoided by using pre-compiled versions of the library. + +#. In the command line switch to your repository folder:: + + cd c:\Users\YOURUSER\openpyxl + +#. Activate the virtualenv:: + + Scripts\activate + +#. Install a development version of openpyxl:: + + pip install -e . + +#. Download all the relevant `lxml Windows wheels `_ + + Releases for legacy versions of Python: + + * `lxml 4.0.0 for Python 2.7 `_ + * `lxml 4.0.0 for Python 3.6 `_ + +#. Move all these files to a folder called "downloads" in your openpyxl checkout + +#. Install the project requirements:: + + pip download -r requirements.txt -d downloads + pip install --no-index --find-links downloads -r requirements.txt + +To run tests for the virtualenv:: + + py.test -xrf openpyxl # the flag will stop testing at the first error + + +tox +--- + +We use `tox` to run the tests on different Python versions and configurations. Using it is as simple as:: + + set PIP_FIND_LINKS=downloads + tox openpyxl diff --git a/doc/worksheet_properties.rst b/doc/worksheet_properties.rst new file mode 100644 index 0000000..c652212 --- /dev/null +++ b/doc/worksheet_properties.rst @@ -0,0 +1,57 @@ +Additional Worksheet Properties +=============================== + +These are advanced properties for particular behaviours, the most used ones +are the "fitTopage" page setup property and the tabColor that define the +background color of the worksheet tab. + +Available properties for worksheets +----------------------------------- + +* "enableFormatConditionsCalculation" +* "filterMode" +* "published" +* "syncHorizontal" +* "syncRef" +* "syncVertical" +* "transitionEvaluation" +* "transitionEntry" +* "tabColor" + +Available fields for page setup properties +------------------------------------------ + +"autoPageBreaks" +"fitToPage" + +Available fields for outlines +----------------------------- + +* "applyStyles" +* "summaryBelow" +* "summaryRight" +* "showOutlineSymbols" + +see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.sheetproperties%28v=office.14%29.aspx_ for details. + +.. note:: + By default, outline properties are intitialized so you can directly modify each of their 4 attributes, while page setup properties don't. + If you want modify the latter, you should first initialize a :class:`openpyxl.worksheet.properties.PageSetupProperties` object with the required parameters. + Once done, they can be directly modified by the routine later if needed. + + +.. :: doctest + +>>> from openpyxl.workbook import Workbook +>>> from openpyxl.worksheet.properties import WorksheetProperties, PageSetupProperties +>>> +>>> wb = Workbook() +>>> ws = wb.active +>>> +>>> wsprops = ws.sheet_properties +>>> wsprops.tabColor = "1072BA" +>>> wsprops.filterMode = False +>>> wsprops.pageSetUpPr = PageSetupProperties(fitToPage=True, autoPageBreaks=False) +>>> wsprops.outlinePr.summaryBelow = False +>>> wsprops.outlinePr.applyStyles = True +>>> wsprops.pageSetUpPr.autoPageBreaks = True diff --git a/doc/worksheet_tables.rst b/doc/worksheet_tables.rst new file mode 100644 index 0000000..c060f07 --- /dev/null +++ b/doc/worksheet_tables.rst @@ -0,0 +1,26 @@ +Worksheet Tables +================ + + +Worksheet tables are references to groups of cells. This makes +certain operations such as styling the cells in a table easier. + + +Creating a table +---------------- + +.. literalinclude:: table.py + + +By default tables are created with a header from the first row and filters for all the columns. + +Styles are managed using the the `TableStyleInfo` object. This allows you to +stripe rows or columns and apply the different colour schemes. + + +Important notes +--------------- + +Table names must be unique within a workbook and table headers and filter +ranges must always contain strings. If this is not the case then Excel may +consider the file invalid and remove the table. diff --git a/openpyxl/.constants.json b/openpyxl/.constants.json new file mode 100644 index 0000000..54cfbe5 --- /dev/null +++ b/openpyxl/.constants.json @@ -0,0 +1,8 @@ +{ + "__author__": "See AUTHORS", + "__author_email__": "charlie.clark@clark-consulting.eu", + "__license__": "MIT/Expat", + "__maintainer_email__": "openpyxl-users@googlegroups.com", + "__url__": "https://openpyxl.readthedocs.io", + "__version__": "2.5.3" +} diff --git a/openpyxl/__init__.py b/openpyxl/__init__.py new file mode 100644 index 0000000..2af840f --- /dev/null +++ b/openpyxl/__init__.py @@ -0,0 +1,30 @@ +# Copyright (c) 2010-2018 openpyxl + +# @license: http://www.opensource.org/licenses/mit-license.php +# @author: see AUTHORS file + + +import json +import os + +try: + here = os.path.abspath(os.path.dirname(__file__)) + src_file = os.path.join(here, ".constants.json") + with open(src_file) as src: + constants = json.load(src) + __author__ = constants['__author__'] + __author_email__ = constants["__author_email__"] + __license__ = constants["__license__"] + __maintainer_email__ = constants["__maintainer_email__"] + __url__ = constants["__url__"] + __version__ = constants["__version__"] +except IOError: + # packaged + pass + +"""Imports for the openpyxl package.""" +from openpyxl.compat.numbers import NUMPY, PANDAS +from openpyxl.xml import LXML + +from openpyxl.workbook import Workbook +from openpyxl.reader.excel import load_workbook diff --git a/openpyxl/cell/__init__.py b/openpyxl/cell/__init__.py new file mode 100644 index 0000000..25bb2b9 --- /dev/null +++ b/openpyxl/cell/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from .cell import Cell, WriteOnlyCell +from .read_only import ReadOnlyCell diff --git a/openpyxl/cell/cell.py b/openpyxl/cell/cell.py new file mode 100644 index 0000000..0367a21 --- /dev/null +++ b/openpyxl/cell/cell.py @@ -0,0 +1,382 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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 ( + unicode, + basestring, + bytes, + NUMERIC_TYPES, + range, + deprecated, +) +from openpyxl.utils.units import ( + DEFAULT_ROW_HEIGHT, + DEFAULT_COLUMN_WIDTH +) +from openpyxl.utils.datetime import ( + to_excel, + time_to_days, + timedelta_to_days, + from_excel + ) +from openpyxl.utils.exceptions import ( + IllegalCharacterError +) +from openpyxl.utils.units import points_to_pixels +from openpyxl.utils import ( + get_column_letter, + column_index_from_string, +) +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) +STRING_TYPES = (basestring, unicode, bytes) +KNOWN_TYPES = NUMERIC_TYPES + TIME_TYPES + STRING_TYPES + (bool, type(None)) + +PERCENT_REGEX = re.compile(r'^(?P\-?[0-9]*\.?[0-9]*\s?)\%$') +TIME_REGEX = re.compile(r""" +^(?: # HH:MM and HH:MM:SS +(?P[0-1]{0,1}[0-9]{2}): +(?P[0-5][0-9]):? +(?P[0-5][0-9])?$) +| +^(?: # MM:SS. +([0-5][0-9]): +([0-5][0-9])?\. +(?P\d{1,6})) +""", re.VERBOSE) +NUMBER_REGEX = re.compile(r'^-?([\d]|[\d]+\.[\d]*|\.[\d]+|[1-9][\d]+\.?[\d]*)((E|e)[-+]?[\d]+)?$') +ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]') + +ERROR_CODES = ('#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', + '#N/A') + + +class Cell(StyleableObject): + """Describes cell associated properties. + + Properties of interest include style, type, value, and address. + + """ + __slots__ = ( + 'row', + 'col_idx', + '_value', + 'data_type', + 'parent', + '_hyperlink', + '_comment', + ) + + ERROR_CODES = ERROR_CODES + + 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) + + + def __init__(self, worksheet, column=None, row=None, value=None, col_idx=None, style_array=None): + super(Cell, self).__init__(worksheet, style_array) + self.row = row + """Row 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 + if column is not None: + col_idx = column_index_from_string(column) + self.col_idx = col_idx + """Column number of this cell (1-based)""" + + + @property + def coordinate(self): + """This cell's coordinate (ex. 'A5')""" + return '%s%d' % (self.column, self.row) + + @property + def column(self): + """The letter of this cell's column (ex. 'A')""" + return get_column_letter(self.col_idx) + + @property + def encoding(self): + return self.parent.encoding + + @property + def base_date(self): + return self.parent.parent.excel_base_date + + @property + def guess_types(self): + return getattr(self.parent.parent, 'guess_types', False) + + 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 unicode string + if not isinstance(value, unicode): + value = unicode(value, self.encoding) + value = unicode(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 unicode(value) + except UnicodeDecodeError: + return u'#N/A' + + def set_explicit_value(self, value=None, data_type=TYPE_STRING): + """Coerce values according to their explicit type""" + if data_type not in self.VALID_TYPES: + raise ValueError('Invalid data type: %s' % data_type) + if isinstance(value, STRING_TYPES): + value = self.check_string(value) + self._value = value + self.data_type = data_type + + + def _bind_value(self, value): + """Given a value, infer the correct data type""" + + self.data_type = "n" + + if value is True or value is False: + self.data_type = self.TYPE_BOOL + + elif isinstance(value, NUMERIC_TYPES): + pass + + elif isinstance(value, TIME_TYPES): + value = self._set_time_format(value) + self.data_type = "d" + + elif isinstance(value, STRING_TYPES): + value = self.check_string(value) + self.data_type = self.TYPE_STRING + if len(value) > 1 and value.startswith("="): + self.data_type = self.TYPE_FORMULA + elif value in self.ERROR_CODES: + self.data_type = self.TYPE_ERROR + elif self.guess_types: + value = self._infer_value(value) + + elif value is not None: + raise ValueError("Cannot convert {0!r} to Excel".format(value)) + + self._value = value + + + def _infer_value(self, value): + """Given a string, infer type and formatting options.""" + if not isinstance(value, unicode): + value = str(value) + + # number detection + v = self._cast_numeric(value) + if v is None: + # percentage detection + v = self._cast_percentage(value) + if v is None: + # time detection + v = self._cast_time(value) + if v is not None: + self.data_type = self.TYPE_NUMERIC + return v + + return value + + + def _cast_numeric(self, value): + """Explicity convert a string to a numeric value""" + if NUMBER_REGEX.match(value): + try: + return int(value) + except ValueError: + return float(value) + + def _cast_percentage(self, value): + """Explicitly convert a string to numeric value and format as a + percentage""" + match = PERCENT_REGEX.match(value) + if match: + self.number_format = numbers.FORMAT_PERCENTAGE + return float(match.group('number')) / 100 + + + def _cast_time(self, value): + """Explicitly convert a string to a number and format as datetime or + time""" + match = TIME_REGEX.match(value) + if match: + if match.group("microsecond") is not None: + value = value[:12] + pattern = "%M:%S.%f" + fmt = numbers.FORMAT_DATE_TIME5 + elif match.group('second') is None: + fmt = numbers.FORMAT_DATE_TIME3 + pattern = "%H:%M" + else: + pattern = "%H:%M:%S" + fmt = numbers.FORMAT_DATE_TIME6 + self.number_format = fmt + value = datetime.datetime.strptime(value, pattern) + return value.time() + + + def _set_time_format(self, value): + """Set number format for Python date or time""" + if isinstance(value, datetime.datetime): + #value = to_excel(value, self.base_date) + self.number_format = numbers.FORMAT_DATE_DATETIME + elif isinstance(value, datetime.date): + #value = to_excel(value, self.base_date) + self.number_format = numbers.FORMAT_DATE_YYYYMMDD2 + elif isinstance(value, datetime.time): + #value = time_to_days(value) + self.number_format = numbers.FORMAT_DATE_TIME6 + elif isinstance(value, datetime.timedelta): + #value = timedelta_to_days(value) + self.number_format = numbers.FORMAT_DATE_TIMEDELTA + return 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`) + """ + value = self._value + #if value is not None and self.is_date: + #value = from_excel(value, self.base_date) + return 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 + + +def WriteOnlyCell(ws=None, value=None): + return Cell(worksheet=ws, column='A', row=1, value=value) diff --git a/openpyxl/cell/interface.py b/openpyxl/cell/interface.py new file mode 100644 index 0000000..4c0c6e6 --- /dev/null +++ b/openpyxl/cell/interface.py @@ -0,0 +1,60 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat.abc import ABC +from abc import abstractmethod, abstractproperty + + +class AbstractCell(ABC): + + + def __init__(self, value=None): + self.value = value + + @abstractproperty + def encoding(self): + pass + + @abstractproperty + def coordinate(self): + pass + + @abstractproperty + def base_date(self): + pass + + @abstractproperty + def guess_types(self): + pass + + @abstractproperty + def value(self): + pass + + @abstractproperty + def internal_value(self): + pass + + @abstractmethod + def __repr__(self): + pass + + @abstractmethod + def offset(self, row=0, column=0): + pass + + @abstractproperty + def comment(self): + pass + + @abstractproperty + def style(self): + pass + + @abstractproperty + def number_format(self): + pass + + @abstractproperty + def is_date(self): + pass diff --git a/openpyxl/cell/read_only.py b/openpyxl/cell/read_only.py new file mode 100644 index 0000000..afc8654 --- /dev/null +++ b/openpyxl/cell/read_only.py @@ -0,0 +1,155 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import re + +from openpyxl.compat import unicode, long + +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 + + +FLOAT_REGEX = re.compile(r"\.|[E-e]") + + +def _cast_number(value): + "Convert numbers as string to an int or float" + m = FLOAT_REGEX.search(value) + if m is not None: + return float(value) + return long(value) + + +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 shared_strings(self): + return self.parent.shared_strings + + @property + def base_date(self): + return self.parent.base_date + + @property + def coordinate(self): + column = get_column_letter(self.column) + return "{1}{0}".format(self.row, column) + + @property + def style_array(self): + return self.parent.parent._cell_styles[self._style_id] + + @property + def number_format(self): + _id = self.style_array.numFmtId + if _id < 164: + return BUILTIN_FORMATS.get(_id, "General") + else: + return self.parent.parent._number_formats[_id - 164] + + @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 self.data_type == 'n' and is_date_format(self.number_format) + + @property + def internal_value(self): + return self._value + + @property + def value(self): + if self._value is None: + return + if self.data_type == 'n': + if self.style_array: + if is_date_format(self.number_format): + return from_excel(self._value, self.base_date) + return self._value + if self.data_type == 'b': + return self._value == '1' + elif self.data_type in(Cell.TYPE_INLINE, Cell.TYPE_FORMULA_CACHE_STRING): + return unicode(self._value) + elif self.data_type == 's': + return unicode(self.shared_strings[int(self._value)]) + return self._value + + @value.setter + def value(self, value): + if self._value is not None: + raise AttributeError("Cell is read only") + if value is None: + self.data_type = 'n' + elif self.data_type == 'n': + value = _cast_number(value) + 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/openpyxl/cell/tests/__init__.py b/openpyxl/cell/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/cell/tests/test_cell.py b/openpyxl/cell/tests/test_cell.py new file mode 100644 index 0000000..f7b85dd --- /dev/null +++ b/openpyxl/cell/tests/test_cell.py @@ -0,0 +1,418 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +# Python stdlib imports +from datetime import ( + time, + datetime, + date, +) + +# 3rd party imports +import pytest + +# package imports + +from openpyxl.comments import Comment +from openpyxl.cell.cell import ERROR_CODES + + +@pytest.fixture +def DummyWorksheet(): + from openpyxl.utils.indexed_list import IndexedList + from openpyxl.utils.datetime import CALENDAR_WINDOWS_1900 + from openpyxl.cell import Cell + + class Wb(object): + excel_base_date = CALENDAR_WINDOWS_1900 + _fonts = IndexedList() + _fills = IndexedList() + _borders = IndexedList() + _protections = IndexedList() + _alignments = IndexedList() + _number_formats = IndexedList() + _cell_styles = IndexedList() + + + class Ws(object): + + encoding = 'utf-8' + parent = Wb() + title = "Dummy Worksheet" + _comment_count = 0 + + def cell(self, column, row): + return Cell(self, row=row, col_idx=column) + + return Ws() + + +@pytest.fixture +def Cell(): + from ..cell import Cell + return Cell + + +@pytest.fixture +def dummy_cell(DummyWorksheet, Cell): + ws = DummyWorksheet + cell = Cell(ws, column="A", row=1) + return cell + + +@pytest.fixture(params=[True, False]) +def guess_types(request): + return request.param + + +@pytest.mark.parametrize("value, expected", + [ + ('4.2', 4.2), + ('-42.000', -42), + ( '0', 0), + ('0.9999', 0.9999), + ('99E-02', 0.99), + ('4', 4), + ('-1E3', -1000), + ('2e+2', 200), + ] + ) +def test_cast_numeric(dummy_cell, value, expected): + cell = dummy_cell + result = cell._cast_numeric(value) + assert result == expected + + +@pytest.mark.parametrize("value, expected", + [ + ('-3.1%', -0.031), + ('3.1%', 0.031), + ('4.5 %', 0.045), + ] + ) +def test_cast_percent(dummy_cell, value, expected): + cell = dummy_cell + result = cell._cast_percentage(value) + assert result == expected + + +@pytest.mark.parametrize("value, expected", + [ + ('03:40:16', time(3, 40, 16)), + ('03:40', time(3, 40)), + ('30:33.865633336', time(0, 30, 33, 865633)), + ] + ) +def test_infer_datetime(dummy_cell, value, expected): + cell = dummy_cell + result = cell._cast_time(value) + assert result == expected + + +def test_ctor(dummy_cell): + cell = dummy_cell + assert cell.data_type == 'n' + assert cell.column == 'A' + assert cell.row == 1 + assert cell.coordinate == "A1" + assert cell.value is None + assert cell.comment is None + + +@pytest.mark.parametrize("datatype", ['n', 'd', 's', 'b', 'f', 'e']) +def test_null(dummy_cell, datatype): + cell = dummy_cell + cell.data_type = datatype + assert cell.data_type == datatype + cell.value = None + assert cell.data_type == 'n' + + +@pytest.mark.parametrize("value", ['hello', ".", '0800']) +def test_string(dummy_cell, value): + cell = dummy_cell + cell.value = 'hello' + assert cell.data_type == 's' + + +@pytest.mark.parametrize("value", ['=42', '=if(A1<4;-1;1)']) +def test_formula(dummy_cell, value): + cell = dummy_cell + cell.value = value + assert cell.data_type == 'f' + + +def test_not_formula(dummy_cell): + dummy_cell.value = "=" + assert dummy_cell.data_type == 's' + assert dummy_cell.value == "=" + + +@pytest.mark.parametrize("value", [True, False]) +def test_boolean(dummy_cell, value): + cell = dummy_cell + cell.value = value + assert cell.data_type == 'b' + + +@pytest.mark.parametrize("error_string", ERROR_CODES) +def test_error_codes(dummy_cell, error_string): + cell = dummy_cell + cell.value = error_string + assert cell.data_type == 'e' + + +@pytest.mark.parametrize("value, number_format", + [ + ( + datetime(2010, 7, 13, 6, 37, 41), + "yyyy-mm-dd h:mm:ss" + ), + ( + date(2010, 7, 13), + "yyyy-mm-dd" + ), + ( + time(1, 3), + "h:mm:ss", + ) + ] + ) +def test_insert_date(dummy_cell, value, number_format): + cell = dummy_cell + cell.value = value + assert cell.data_type == 'd' + assert cell.is_date + assert cell.number_format == number_format + + +@pytest.mark.parametrize("value, is_date", + [ + (None, True,), + ("testme", False), + (True, False), + ] + ) +def test_cell_formatted_as_date(dummy_cell, value, is_date): + cell = dummy_cell + cell.value = datetime.today() + cell.value = value + assert cell.is_date == is_date + assert cell.value == value + + +def test_set_bad_type(dummy_cell): + cell = dummy_cell + with pytest.raises(ValueError): + cell.set_explicit_value(1, 'q') + + +def test_illegal_characters(dummy_cell): + from openpyxl.utils.exceptions import IllegalCharacterError + from openpyxl.compat import range + from itertools import chain + cell = dummy_cell + + # The bytes 0x00 through 0x1F inclusive must be manually escaped in values. + + illegal_chrs = chain(range(9), range(11, 13), range(14, 32)) + for i in illegal_chrs: + with pytest.raises(IllegalCharacterError): + cell.value = chr(i) + + with pytest.raises(IllegalCharacterError): + cell.value = "A {0} B".format(chr(i)) + + cell.value = chr(33) + cell.value = chr(9) # Tab + cell.value = chr(10) # Newline + cell.value = chr(13) # Carriage return + cell.value = " Leading and trailing spaces are legal " + + +values = ( + ('30:33.865633336', [('', '', '', '30', '33', '865633')]), + ('03:40:16', [('03', '40', '16', '', '', '')]), + ('03:40', [('03', '40', '', '', '', '')]), + ('55:72:12', []), + ) +@pytest.mark.parametrize("value, expected", + values) +def test_time_regex(value, expected): + from openpyxl.cell.cell import TIME_REGEX + m = TIME_REGEX.findall(value) + assert m == expected + + +#def test_timedelta(dummy_cell): + #cell = dummy_cell + #cell.value = timedelta(days=1, hours=3) + #assert cell.value == 1.125 + #assert cell.data_type == 'n' + #assert cell.is_date is False + #assert cell.number_format == "[hh]:mm:ss" + + +def test_repr(dummy_cell): + cell = dummy_cell + assert repr(cell) == "" + + +def test_repr_object(dummy_cell): + + class Dummy: + + def __str__(self): + return "something" + + cell = dummy_cell + try: + cell._bind_value(Dummy()) + except ValueError as err: + assert "something" not in str(err) + + +def test_comment_assignment(dummy_cell): + assert dummy_cell.comment is None + comm = Comment("text", "author") + dummy_cell.comment = comm + assert dummy_cell.comment == comm + + +def test_only_one_cell_per_comment(dummy_cell): + ws = dummy_cell.parent + comm = Comment('text', 'author') + dummy_cell.comment = comm + + c2 = ws.cell(column=1, row=2) + c2.comment = comm + assert c2.comment.parent is c2 + + +def test_remove_comment(dummy_cell): + comm = Comment('text', 'author') + dummy_cell.comment = comm + dummy_cell.comment = None + assert dummy_cell.comment is None + + +def test_cell_offset(dummy_cell): + cell = dummy_cell + assert cell.offset(2, 1).coordinate == 'B3' + + +class TestEncoding: + + try: + # Python 2 + pound = unichr(163) + except NameError: + # Python 3 + pound = chr(163) + test_string = ('Compound Value (' + pound + ')').encode('latin1') + + def test_bad_encoding(self): + from openpyxl import Workbook + + wb = Workbook() + ws = wb.active + cell = ws['A1'] + with pytest.raises(UnicodeDecodeError): + cell.check_string(self.test_string) + with pytest.raises(UnicodeDecodeError): + cell.value = self.test_string + + def test_good_encoding(self): + from openpyxl import Workbook + + wb = Workbook() + wb.encoding = 'latin1' + ws = wb.active + cell = ws['A1'] + cell.value = self.test_string + + +def test_font(DummyWorksheet, Cell): + from openpyxl.styles import Font + font = Font(bold=True) + ws = DummyWorksheet + ws.parent._fonts.add(font) + + cell = Cell(ws, column='A', row=1) + assert cell.font == font + + +def test_fill(DummyWorksheet, Cell): + from openpyxl.styles import PatternFill + fill = PatternFill(patternType="solid", fgColor="FF0000") + ws = DummyWorksheet + ws.parent._fills.add(fill) + + cell = Cell(ws, column='A', row=1) + assert cell.fill == fill + + +def test_border(DummyWorksheet, Cell): + from openpyxl.styles import Border + border = Border() + ws = DummyWorksheet + ws.parent._borders.add(border) + + cell = Cell(ws, column='A', row=1) + assert cell.border == border + + +def test_number_format(DummyWorksheet, Cell): + ws = DummyWorksheet + ws.parent._number_formats.add("dd--hh--mm") + + cell = Cell(ws, column="A", row=1) + cell.number_format = "dd--hh--mm" + assert cell.number_format == "dd--hh--mm" + + +def test_alignment(DummyWorksheet, Cell): + from openpyxl.styles import Alignment + align = Alignment(wrapText=True) + ws = DummyWorksheet + ws.parent._alignments.add(align) + + cell = Cell(ws, column="A", row=1) + assert cell.alignment == align + + +def test_protection(DummyWorksheet, Cell): + from openpyxl.styles import Protection + prot = Protection(locked=False) + ws = DummyWorksheet + ws.parent._protections.add(prot) + + cell = Cell(ws, column="A", row=1) + assert cell.protection == prot + + +def test_pivot_button(DummyWorksheet, Cell): + ws = DummyWorksheet + + cell = Cell(ws, column="A", row=1) + cell.style_id + cell._style.pivotButton = 1 + assert cell.pivotButton is True + + +def test_quote_prefix(DummyWorksheet, Cell): + ws = DummyWorksheet + + cell = Cell(ws, column="A", row=1) + cell.style_id + cell._style.quotePrefix = 1 + assert cell.quotePrefix is True + + +def test_remove_hyperlink(dummy_cell): + """Remove a cell hyperlink""" + cell = dummy_cell + cell.hyperlink = "http://test.com" + cell.hyperlink = None + assert cell.hyperlink is None diff --git a/openpyxl/cell/tests/test_read_only.py b/openpyxl/cell/tests/test_read_only.py new file mode 100644 index 0000000..8f04064 --- /dev/null +++ b/openpyxl/cell/tests/test_read_only.py @@ -0,0 +1,158 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import datetime +import pytest + +from openpyxl.cell.read_only import ReadOnlyCell +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.styles.styleable import StyleArray + + +@pytest.fixture(scope='module') +def dummy_sheet(): + class DummyWorkbook(object): + shared_styles = IndexedList() + style = StyleArray() + shared_styles.add(style) # Workbooks always have a default style + _cell_styles = IndexedList() + _cell_styles.add(style) + _number_formats = IndexedList() + _fonts = IndexedList() + _fonts.add(None) + + + class DummySheet(object): + base_date = 2415018.5 + style_table = {} + shared_strings = ['Hello world'] + parent = DummyWorkbook() + return DummySheet() + + +def test_ctor(dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, None, None, '10', 'n') + assert cell.value == 10 + + +def test_empty_cell(dummy_sheet): + from openpyxl.cell.read_only import EMPTY_CELL + assert EMPTY_CELL.value is None + assert EMPTY_CELL.data_type == 'n' + + +def test_base_date(dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, None, None, '10', 'n') + assert cell.base_date == 2415018.5 + + +def test_string_table(dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, None, None, '0', 's') + assert cell.shared_strings == ['Hello world'] + assert cell.value == 'Hello world' + + +def test_coordinate(dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, 1, 1, 10, None) + assert cell.coordinate == "A1" + + +@pytest.mark.parametrize("value, expected", + [ + ('1', True), + ('0', False), + ]) +def test_bool(dummy_sheet, value, expected): + cell = ReadOnlyCell(dummy_sheet, None, None, value, 'b') + assert cell.value is expected + + +def test_inline_String(dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, None, None, "Hello World!", 'inlineStr') + assert cell.value == "Hello World!" + + +@pytest.mark.parametrize("value, expected", + [ + ("24555", 24555), + ("1.5", 1.5), + (None, None), + ]) +def test_numeric(dummy_sheet, value, expected): + cell = ReadOnlyCell(dummy_sheet, None, None, value, 'n') + v = cell.value + assert v == expected + assert hasattr(v, 'is_integer') == hasattr(expected, 'is_integer'),\ + "Expected {0}, {1}".format(type(expected), type(v)) + + +@pytest.fixture(scope="class") +def DummyCell(dummy_sheet): + + dummy_sheet.parent._number_formats.add('d-mmm-yy') + style = StyleArray([0,0,0,164,0,0,0,0,0]) + dummy_sheet.parent._cell_styles.add(style) + cell = ReadOnlyCell(dummy_sheet, None, None, "23596", 'n', 1) + return cell + + +class TestDateTime: + + def test_number_format(self, DummyCell): + assert DummyCell.number_format == 'd-mmm-yy' + + def test_is_date(self, DummyCell): + assert DummyCell.is_date is True + + def test_conversion(self, DummyCell): + assert DummyCell.value == datetime.datetime(1964, 8, 7, 0, 0, 0) + + def test_interal_value(self, DummyCell): + assert DummyCell.internal_value == 23596 + + +class TestStyle: + + def test_style_array(self, dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, None, None, None) + assert cell.style_array == StyleArray() + + def test_font(self, dummy_sheet): + cell = ReadOnlyCell(dummy_sheet, None, None, None) + assert cell.font == None + + +def test_read_only(dummy_sheet): + cell = ReadOnlyCell(sheet=dummy_sheet, row=None, column=None, value='1') + assert cell.value == 1 + with pytest.raises(AttributeError): + cell.value = 10 + with pytest.raises(AttributeError): + cell.style = 1 + + +def test_equality(): + c1 = ReadOnlyCell(None, None, 10, None) + c2 = ReadOnlyCell(None, None, 10, None) + assert c1 is not c2 + assert c1 == c2 + c3 = ReadOnlyCell(None, None, 5, None) + assert c3 != c1 + + +@pytest.mark.parametrize("value, expected", + [ + ('4.2', 4.2), + ('-42.000', -42), + ('0', 0), + ('0.9999', 0.9999), + ('99E-02', 0.99), + ('4', 4), + ('-1E3', -1000), + ('1E-3', 0.001), + ('2e+2', 200.0), + ] + ) +def test_number_convesion(value, expected): + from .. read_only import _cast_number + assert _cast_number(value) == expected diff --git a/openpyxl/cell/tests/test_text.py b/openpyxl/cell/tests/test_text.py new file mode 100644 index 0000000..68b1ae5 --- /dev/null +++ b/openpyxl/cell/tests/test_text.py @@ -0,0 +1,184 @@ +from __future__ import absolute_import +# coding=utf8 +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def InlineFont(): + from ..text import InlineFont + return InlineFont + + +class TestInlineFont: + + def test_ctor(self, InlineFont): + font = InlineFont() + xml = tostring(font.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, InlineFont): + src = """ + + """ + node = fromstring(src) + font = InlineFont.from_tree(node) + assert font == InlineFont() + + +@pytest.fixture +def RichText(): + from ..text import RichText + return RichText + + +class TestRichText: + + def test_ctor(self, RichText): + text = RichText() + xml = tostring(text.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RichText): + src = """ + + """ + node = fromstring(src) + text = RichText.from_tree(node) + assert text == RichText() + + +@pytest.fixture +def Text(): + from ..text import Text + return Text + + +class TestText: + + def test_ctor(self, Text): + text = Text() + text.plain = "comment" + xml = tostring(text.to_tree()) + expected = """ + + comment + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + @pytest.mark.parametrize("src, expected", + [ + ("""ID""", "ID"), + (""" + + + + 11 de September de 2014 + + + """, + "11 de September de 2014" + ), + ] + ) + def test_from_xml(self, Text, src, expected): + node = fromstring(src) + text = Text.from_tree(node) + assert text.content == expected + + + def test_empty_element(self, Text): + src = """ + + + Replaced Data + + + + + + + + + + + + + """ + node = fromstring(src) + text = Text.from_tree(node) + assert text.content == "Replaced Data" + + +@pytest.fixture +def PhoneticText(): + from ..text import PhoneticText + return PhoneticText + + +class TestPhoneticText: + + def test_ctor(self, PhoneticText): + text = PhoneticText(sb=9, eb=10, t=u'\u3088') + xml = tostring(text.to_tree()) + expected = b""" + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PhoneticText): + src = b""" + + + + """ + node = fromstring(src) + text = PhoneticText.from_tree(node) + assert text == PhoneticText(sb=9, eb=10, t=u'\u3088') + + +@pytest.fixture +def PhoneticProperties(): + from ..text import PhoneticProperties + return PhoneticProperties + + +class TestPhoneticProperties: + + def test_ctor(self, PhoneticProperties): + props = PhoneticProperties(fontId=0, type="Hiragana") + xml = tostring(props.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PhoneticProperties): + src = """ + + """ + node = fromstring(src) + props = PhoneticProperties.from_tree(node) + assert props == PhoneticProperties(fontId=0, type="noConversion") diff --git a/openpyxl/cell/text.py b/openpyxl/cell/text.py new file mode 100644 index 0000000..cbc94d8 --- /dev/null +++ b/openpyxl/cell/text.py @@ -0,0 +1,186 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +""" +Richtext definition +""" +from openpyxl.compat import unicode + +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=unicode) + 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=unicode, 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=unicode) + 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/openpyxl/chart/_3d.py b/openpyxl/chart/_3d.py new file mode 100644 index 0000000..4ad6c0d --- /dev/null +++ b/openpyxl/chart/_3d.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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 + """ + + 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 diff --git a/openpyxl/chart/__init__.py b/openpyxl/chart/__init__.py new file mode 100644 index 0000000..9d446cd --- /dev/null +++ b/openpyxl/chart/__init__.py @@ -0,0 +1,20 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/chart/_chart.py b/openpyxl/chart/_chart.py new file mode 100644 index 0000000..0919524 --- /dev/null +++ b/openpyxl/chart/_chart.py @@ -0,0 +1,178 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from collections import OrderedDict + +from openpyxl.compat import basestring + +from openpyxl.descriptors import ( + Typed, + Integer, + Alias, + MinMax, + Bool, +) +from openpyxl.descriptors.nested import Nested +from openpyxl.descriptors.sequence import NestedSequence, ValueSequence +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.xml.constants import PACKAGE_CHARTS + +from ._3d import _3DBase +from .data_source import AxDataSource, NumRef +from .layout import Layout +from .legend import Legend +from .reference import Reference +from .series_factory import SeriesFactory +from .series import attribute_mapping +from .shapes import GraphicalProperties +from .title import TitleDescriptor + +class AxId(Serialisable): + + val = Integer() + + def __init__(self, val): + self.val = val + + +def PlotArea(): + from .chartspace import PlotArea + return PlotArea() + + +class ChartBase(Serialisable): + + """ + Base class for all charts + """ + + legend = Typed(expected_type=Legend, allow_none=True) + layout = Typed(expected_type=Layout, allow_none=True) + roundedCorners = Bool(allow_none=True) + axId = ValueSequence(expected_type=int) + visible_cells_only = Bool() + + _series_type = "" + ser = () + series = Alias('ser') + title = TitleDescriptor() + anchor = "E15" # default anchor position + width = 15 # in cm, approx 5 rows + height = 7.5 # in cm, approx 14 rows + _id = 1 + _path = "/xl/charts/chart{0}.xml" + style = MinMax(allow_none=True, min=1, max=48) + mime_type = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" + graphical_properties = Typed(expected_type=GraphicalProperties, allow_none=True) + + __elements__ = () + + + def __init__(self, axId=(), **kw): + self._charts = [self] + self.title = None + self.layout = None + self.roundedCorners = None + self.legend = Legend() + self.graphical_properties = None + self.style = None + self.plot_area = PlotArea() + self.axId = axId + super(ChartBase, self).__init__(**kw) + + def __hash__(self): + """ + Just need to check for identity + """ + return id(self) + + def __iadd__(self, other): + """ + Combine the chart with another one + """ + if not isinstance(other, ChartBase): + raise TypeError("Only other charts can be added") + self._charts.append(other) + return self + + + def to_tree(self, namespace=None, tagname=None, idx=None): + self.axId = [id for id in self._axes] + if self.ser is not None: + for s in self.ser: + s.__elements__ = attribute_mapping[self._series_type] + return super(ChartBase, self).to_tree(tagname, idx) + + + def _write(self): + from .chartspace import ChartSpace, ChartContainer + self.plot_area.layout = self.layout + + idx_base = 0 + for chart in self._charts: + if chart not in self.plot_area._charts: + chart.idx_base = idx_base + idx_base += len(chart.series) + self.plot_area._charts = self._charts + + container = ChartContainer(plotArea=self.plot_area, legend=self.legend, title=self.title) + if isinstance(chart, _3DBase): + container.view3D = chart.view3D + container.floor = chart.floor + container.sideWall = chart.sideWall + container.backWall = chart.backWall + container.plotVisOnly = self.visible_cells_only + cs = ChartSpace(chart=container) + cs.style = self.style + cs.roundedCorners = self.roundedCorners + return cs.to_tree() + + + @property + def _axes(self): + x = getattr(self, "x_axis", None) + y = getattr(self, "y_axis", None) + z = getattr(self, "z_axis", None) + return OrderedDict([(axis.axId, axis) for axis in (x, y, z) if axis]) + + + def set_categories(self, labels): + """ + Set the categories / x-axis values + """ + if not isinstance(labels, Reference): + labels = Reference(range_string=labels) + for s in self.ser: + s.cat = AxDataSource(numRef=NumRef(f=labels)) + + + def add_data(self, data, from_rows=False, titles_from_data=False): + """ + Add a range of data in a single pass. + The default is to treat each column as a data series. + """ + if not isinstance(data, Reference): + data = Reference(range_string=data) + + if from_rows: + values = data.rows + + else: + values = data.cols + + for v in values: + range_string = u"{0}!{1}:{2}".format(data.sheetname, v[0], v[-1]) + series = SeriesFactory(range_string, title_from_data=titles_from_data) + self.ser.append(series) + + + def append(self, value): + """Append a data series to the chart""" + l = self.series[:] + l.append(value) + self.series = l + + + @property + def path(self): + return self._path.format(self._id) diff --git a/openpyxl/chart/area_chart.py b/openpyxl/chart/area_chart.py new file mode 100644 index 0000000..7f23199 --- /dev/null +++ b/openpyxl/chart/area_chart.py @@ -0,0 +1,107 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Set, + Bool, + Integer, + Sequence, + Alias, +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedMinMax, + NestedSet, + NestedBool, +) + +from ._chart import ChartBase +from .descriptors import NestedGapAmount +from .axis import TextAxis, NumericAxis, SeriesAxis, ChartLines +from .label import DataLabelList +from .series import Series + + +class _AreaChartBase(ChartBase): + + grouping = NestedSet(values=(['percentStacked', 'standard', 'stacked'])) + varyColors = NestedBool(nested=True, allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + dropLines = Typed(expected_type=ChartLines, allow_none=True) + + _series_type = "area" + + __elements__ = ('grouping', 'varyColors', 'ser', 'dLbls', 'dropLines') + + def __init__(self, + grouping="standard", + varyColors=None, + ser=(), + dLbls=None, + dropLines=None, + ): + self.grouping = grouping + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.dropLines = dropLines + super(_AreaChartBase, self).__init__() + + +class AreaChart(_AreaChartBase): + + tagname = "areaChart" + + grouping = _AreaChartBase.grouping + varyColors = _AreaChartBase.varyColors + ser = _AreaChartBase.ser + dLbls = _AreaChartBase.dLbls + dropLines = _AreaChartBase.dropLines + + # chart properties actually used by containing classes + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _AreaChartBase.__elements__ + ('axId',) + + def __init__(self, + axId=None, + extLst=None, + **kw + ): + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + super(AreaChart, self).__init__(**kw) + + +class AreaChart3D(AreaChart): + + tagname = "area3DChart" + + grouping = _AreaChartBase.grouping + varyColors = _AreaChartBase.varyColors + ser = _AreaChartBase.ser + dLbls = _AreaChartBase.dLbls + dropLines = _AreaChartBase.dropLines + + gapDepth = NestedGapAmount() + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis, allow_none=True) + + __elements__ = AreaChart.__elements__ + ('gapDepth', ) + + def __init__(self, gapDepth=None, **kw): + self.gapDepth = gapDepth + super(AreaChart3D, self).__init__(**kw) + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() diff --git a/openpyxl/chart/axis.py b/openpyxl/chart/axis.py new file mode 100644 index 0000000..dfd7880 --- /dev/null +++ b/openpyxl/chart/axis.py @@ -0,0 +1,402 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + NoneSet, + Bool, + Integer, + MinMax, + NoneSet, + Set, + String, + Alias, +) + +from openpyxl.descriptors.excel import ( + ExtensionList, + Percentage, + _explicit_none, +) +from openpyxl.descriptors.nested import ( + NestedValue, + NestedSet, + NestedBool, + NestedNoneSet, + NestedFloat, + NestedInteger, + NestedMinMax, +) +from openpyxl.xml.constants import CHART_NS + +from .descriptors import NumberFormatDescriptor +from .layout import Layout +from .text import Text, RichText +from .shapes import GraphicalProperties +from .title import Title, TitleDescriptor + + +class ChartLines(Serialisable): + + tagname = "chartLines" + + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + + def __init__(self, spPr=None): + self.spPr = spPr + + +class Scaling(Serialisable): + + tagname = "scaling" + + logBase = NestedFloat(allow_none=True) + orientation = NestedSet(values=(['maxMin', 'minMax'])) + max = NestedFloat(allow_none=True) + min = NestedFloat(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('logBase', 'orientation', 'max', 'min',) + + def __init__(self, + logBase=None, + orientation="minMax", + max=None, + min=None, + extLst=None, + ): + self.logBase = logBase + self.orientation = orientation + self.max = max + self.min = min + + +class _BaseAxis(Serialisable): + + axId = NestedInteger(expected_type=int) + scaling = Typed(expected_type=Scaling) + delete = NestedBool(allow_none=True) + axPos = NestedSet(values=(['b', 'l', 'r', 't'])) + majorGridlines = Typed(expected_type=ChartLines, allow_none=True) + minorGridlines = Typed(expected_type=ChartLines, allow_none=True) + title = TitleDescriptor() + numFmt = NumberFormatDescriptor() + number_format = Alias("numFmt") + majorTickMark = NestedNoneSet(values=(['cross', 'in', 'out']), to_tree=_explicit_none) + minorTickMark = NestedNoneSet(values=(['cross', 'in', 'out']), to_tree=_explicit_none) + tickLblPos = NestedNoneSet(values=(['high', 'low', 'nextTo'])) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias('txPr') + crossAx = NestedInteger(expected_type=int) # references other axis + crosses = NestedNoneSet(values=(['autoZero', 'max', 'min'])) + crossesAt = NestedFloat(allow_none=True) + + # crosses & crossesAt are mutually exclusive + + __elements__ = ('axId', 'scaling', 'delete', 'axPos', 'majorGridlines', + 'minorGridlines', 'title', 'numFmt', 'majorTickMark', 'minorTickMark', + 'tickLblPos', 'spPr', 'txPr', 'crossAx', 'crosses', 'crossesAt') + + def __init__(self, + axId=None, + scaling=None, + delete=None, + axPos='l', + majorGridlines=None, + minorGridlines=None, + title=None, + numFmt=None, + majorTickMark=None, + minorTickMark=None, + tickLblPos=None, + spPr=None, + txPr= None, + crossAx=None, + crosses=None, + crossesAt=None, + ): + self.axId = axId + if scaling is None: + scaling = Scaling() + self.scaling = scaling + self.delete = delete + self.axPos = axPos + self.majorGridlines = majorGridlines + self.minorGridlines = minorGridlines + self.title = title + self.numFmt = numFmt + self.majorTickMark = majorTickMark + self.minorTickMark = minorTickMark + self.tickLblPos = tickLblPos + self.spPr = spPr + self.txPr = txPr + self.crossAx = crossAx + self.crosses = crosses + self.crossesAt = None + + +class DisplayUnitsLabel(Serialisable): + + tagname = "dispUnitsLbl" + + layout = Typed(expected_type=Layout, allow_none=True) + tx = Typed(expected_type=Text, allow_none=True) + text = Alias("tx") + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + textPropertes = Alias("txPr") + + __elements__ = ('layout', 'tx', 'spPr', 'txPr') + + def __init__(self, + layout=None, + tx=None, + spPr=None, + txPr=None, + ): + self.layout = layout + self.tx = tx + self.spPr = spPr + self.txPr = txPr + + +class DisplayUnitsLabelList(Serialisable): + + tagname = "dispUnits" + + custUnit = NestedFloat(allow_none=True) + builtInUnit = NestedNoneSet(values=(['hundreds', 'thousands', + 'tenThousands', 'hundredThousands', 'millions', 'tenMillions', + 'hundredMillions', 'billions', 'trillions'])) + dispUnitsLbl = Typed(expected_type=DisplayUnitsLabel, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('custUnit', 'builtInUnit', 'dispUnitsLbl',) + + def __init__(self, + custUnit=None, + builtInUnit=None, + dispUnitsLbl=None, + extLst=None, + ): + self.custUnit = custUnit + self.builtInUnit = builtInUnit + self.dispUnitsLbl = dispUnitsLbl + + +class NumericAxis(_BaseAxis): + + tagname = "valAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + crossBetween = NestedNoneSet(values=(['between', 'midCat'])) + majorUnit = NestedFloat(allow_none=True) + minorUnit = NestedFloat(allow_none=True) + dispUnits = Typed(expected_type=DisplayUnitsLabelList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('crossBetween', 'majorUnit', + 'minorUnit', 'dispUnits',) + + + def __init__(self, + crossBetween=None, + majorUnit=None, + minorUnit=None, + dispUnits=None, + extLst=None, + **kw + ): + self.crossBetween = crossBetween + self.majorUnit = majorUnit + self.minorUnit = minorUnit + self.dispUnits = dispUnits + kw.setdefault('majorGridlines', ChartLines()) + kw.setdefault('axId', 100) + kw.setdefault('crossAx', 10) + super(NumericAxis, self).__init__(**kw) + + + @classmethod + def from_tree(cls, node): + """ + Special case value axes with no gridlines + """ + self = super(NumericAxis, cls).from_tree(node) + gridlines = node.find("{%s}majorGridlines" % CHART_NS) + if gridlines is None: + self.majorGridlines = None + return self + + + +class TextAxis(_BaseAxis): + + tagname = "catAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + auto = NestedBool(allow_none=True) + lblAlgn = NestedNoneSet(values=(['ctr', 'l', 'r'])) + lblOffset = NestedMinMax(min=0, max=1000) + tickLblSkip = NestedInteger(allow_none=True) + tickMarkSkip = NestedInteger(allow_none=True) + noMultiLvlLbl = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('auto', 'lblAlgn', 'lblOffset', + 'tickLblSkip', 'tickMarkSkip', 'noMultiLvlLbl') + + def __init__(self, + auto=None, + lblAlgn=None, + lblOffset=100, + tickLblSkip=None, + tickMarkSkip=None, + noMultiLvlLbl=None, + extLst=None, + **kw + ): + self.auto = auto + self.lblAlgn = lblAlgn + self.lblOffset = lblOffset + self.tickLblSkip = tickLblSkip + self.tickMarkSkip = tickMarkSkip + self.noMultiLvlLbl = noMultiLvlLbl + kw.setdefault('axId', 10) + kw.setdefault('crossAx', 100) + super(TextAxis, self).__init__(**kw) + + +class DateAxis(TextAxis): + + tagname = "dateAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + auto = NestedBool(allow_none=True) + lblOffset = NestedInteger(allow_none=True) + baseTimeUnit = NestedNoneSet(values=(['days', 'months', 'years'])) + majorUnit = NestedFloat(allow_none=True) + majorTimeUnit = NestedNoneSet(values=(['days', 'months', 'years'])) + minorUnit = NestedFloat(allow_none=True) + minorTimeUnit = NestedNoneSet(values=(['days', 'months', 'years'])) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('auto', 'lblOffset', + 'baseTimeUnit', 'majorUnit', 'majorTimeUnit', 'minorUnit', + 'minorTimeUnit') + + def __init__(self, + auto=None, + lblOffset=None, + baseTimeUnit=None, + majorUnit=None, + majorTimeUnit=None, + minorUnit=None, + minorTimeUnit=None, + extLst=None, + **kw + ): + self.auto = auto + self.lblOffset = lblOffset + self.baseTimeUnit = baseTimeUnit + self.majorUnit = majorUnit + self.majorTimeUnit = majorTimeUnit + self.minorUnit = minorUnit + self.minorTimeUnit = minorTimeUnit + kw.setdefault('axId', 500) + kw.setdefault('lblOffset', lblOffset) + super(DateAxis, self).__init__(**kw) + + +class SeriesAxis(_BaseAxis): + + tagname = "serAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + tickLblSkip = NestedInteger(allow_none=True) + tickMarkSkip = NestedInteger(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('tickLblSkip', 'tickMarkSkip') + + def __init__(self, + tickLblSkip=None, + tickMarkSkip=None, + extLst=None, + **kw + ): + self.tickLblSkip = tickLblSkip + self.tickMarkSkip = tickMarkSkip + kw.setdefault('axId', 1000) + kw.setdefault('crossAx', 10) + super(SeriesAxis, self).__init__(**kw) diff --git a/openpyxl/chart/bar_chart.py b/openpyxl/chart/bar_chart.py new file mode 100644 index 0000000..7e88fad --- /dev/null +++ b/openpyxl/chart/bar_chart.py @@ -0,0 +1,145 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Integer, + Sequence, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedSet, + NestedBool, + NestedInteger, + NestedMinMax, +) + +from .descriptors import ( + NestedGapAmount, + NestedOverlap, +) +from ._chart import ChartBase +from ._3d import _3DBase +from .axis import TextAxis, NumericAxis, SeriesAxis, ChartLines +from .shapes import GraphicalProperties +from .series import Series +from .legend import Legend +from .label import DataLabelList + + +class _BarChartBase(ChartBase): + + barDir = NestedSet(values=(['bar', 'col'])) + type = Alias("barDir") + grouping = NestedSet(values=(['percentStacked', 'clustered', 'standard', + 'stacked'])) + varyColors = NestedBool(nested=True, allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + + __elements__ = ('barDir', 'grouping', 'varyColors', 'ser', 'dLbls') + + _series_type = "bar" + + def __init__(self, + barDir="col", + grouping="clustered", + varyColors=None, + ser=(), + dLbls=None, + **kw + ): + self.barDir = barDir + self.grouping = grouping + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + super(_BarChartBase, self).__init__(**kw) + + +class BarChart(_BarChartBase): + + tagname = "barChart" + + barDir = _BarChartBase.barDir + grouping = _BarChartBase.grouping + varyColors = _BarChartBase.varyColors + ser = _BarChartBase.ser + dLbls = _BarChartBase.dLbls + + gapWidth = NestedGapAmount() + overlap = NestedOverlap() + serLines = Typed(expected_type=ChartLines, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + # chart properties actually used by containing classes + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + __elements__ = _BarChartBase.__elements__ + ('gapWidth', 'overlap', 'serLines', 'axId') + + def __init__(self, + gapWidth=150, + overlap=None, + serLines=None, + extLst=None, + **kw + ): + self.gapWidth = gapWidth + self.overlap = overlap + self.serLines = serLines + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.legend = Legend() + super(BarChart, self).__init__(**kw) + + +class BarChart3D(_BarChartBase, _3DBase): + + tagname = "bar3DChart" + + barDir = _BarChartBase.barDir + grouping = _BarChartBase.grouping + varyColors = _BarChartBase.varyColors + ser = _BarChartBase.ser + dLbls = _BarChartBase.dLbls + + view3D = _3DBase.view3D + floor = _3DBase.floor + sideWall = _3DBase.sideWall + backWall = _3DBase.backWall + + gapWidth = NestedGapAmount() + gapDepth = NestedGapAmount() + shape = NestedNoneSet(values=(['cone', 'coneToMax', 'box', 'cylinder', 'pyramid', 'pyramidToMax'])) + serLines = Typed(expected_type=ChartLines, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis, allow_none=True) + + __elements__ = _BarChartBase.__elements__ + ('gapWidth', 'gapDepth', 'shape', 'serLines', 'axId') + + def __init__(self, + gapWidth=150, + gapDepth=150, + shape=None, + serLines=None, + extLst=None, + **kw + ): + self.gapWidth = gapWidth + self.gapDepth = gapDepth + self.shape = shape + self.serLines = serLines + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() + + super(BarChart3D, self).__init__(**kw) diff --git a/openpyxl/chart/bubble_chart.py b/openpyxl/chart/bubble_chart.py new file mode 100644 index 0000000..e07143d --- /dev/null +++ b/openpyxl/chart/bubble_chart.py @@ -0,0 +1,68 @@ +from __future__ import absolute_import +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Set, + MinMax, + Bool, + Integer, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedMinMax, + NestedBool, +) + +from ._chart import ChartBase +from .axis import TextAxis, NumericAxis +from .series import XYSeries +from .label import DataLabelList + + +class BubbleChart(ChartBase): + + tagname = "bubbleChart" + + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=XYSeries, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + bubble3D = NestedBool(allow_none=True) + bubbleScale = NestedMinMax(min=0, max=300, allow_none=True) + showNegBubbles = NestedBool(allow_none=True) + sizeRepresents = NestedNoneSet(values=(['area', 'w'])) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=NumericAxis) + y_axis = Typed(expected_type=NumericAxis) + + _series_type = "bubble" + + __elements__ = ('varyColors', 'ser', 'dLbls', 'bubble3D', 'bubbleScale', + 'showNegBubbles', 'sizeRepresents', 'axId') + + def __init__(self, + varyColors=None, + ser=(), + dLbls=None, + bubble3D=None, + bubbleScale=None, + showNegBubbles=None, + sizeRepresents=None, + extLst=None, + **kw + ): + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.bubble3D = bubble3D + self.bubbleScale = bubbleScale + self.showNegBubbles = showNegBubbles + self.sizeRepresents = sizeRepresents + self.x_axis = NumericAxis(axId=10, crossAx=20) + self.y_axis = NumericAxis(axId=20, crossAx=10) + super(BubbleChart, self).__init__(**kw) diff --git a/openpyxl/chart/chartspace.py b/openpyxl/chart/chartspace.py new file mode 100644 index 0000000..6643899 --- /dev/null +++ b/openpyxl/chart/chartspace.py @@ -0,0 +1,268 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +""" +Enclosing chart object. The various chart types are actually child objects. +Will probably need to call this indirectly +""" + +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Bool, + Float, + Typed, + MinMax, + Integer, + NoneSet, + String, + Alias, + Sequence, + Typed, +) +from openpyxl.descriptors.excel import ( + Percentage, + ExtensionList, + Relation +) +from openpyxl.descriptors.nested import ( + NestedBool, + NestedNoneSet, + NestedInteger, + NestedString, + NestedMinMax, + NestedText, +) +from openpyxl.xml.constants import CHART_NS + +from openpyxl.drawing.colors import ColorMapping +from .text import Text, RichText +from .shapes import GraphicalProperties +from .legend import Legend +from .marker import PictureOptions, Marker +from .label import DataLabel +from ._3d import _3DBase, View3D +from .plotarea import PlotArea +from .title import Title +from .print_settings import PrintSettings + + +class PivotFormat(Serialisable): + + tagname = "pivotFmt" + + idx = NestedInteger(nested=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + TextBody = Alias("txPr") + marker = Typed(expected_type=Marker, allow_none=True) + dLbl = Typed(expected_type=DataLabel, allow_none=True) + DataLabel = Alias("dLbl") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('idx', 'spPr', 'txPr', 'marker', 'dLbl') + + def __init__(self, + idx=0, + spPr=None, + txPr=None, + marker=None, + dLbl=None, + extLst=None, + ): + self.idx = idx + self.spPr = spPr + self.txPr = txPr + self.marker = marker + self.dLbl = dLbl + + +class PivotFormatList(Serialisable): + + tagname = "pivotFmts" + + pivotFmt = Sequence(expected_type=PivotFormat, allow_none=True) + + __elements__ = ('pivotFmt',) + + def __init__(self, + pivotFmt=(), + ): + self.pivotFmt = pivotFmt + + +class ChartContainer(Serialisable): + + tagname = "chart" + + title = Typed(expected_type=Title, allow_none=True) + autoTitleDeleted = NestedBool(allow_none=True) + pivotFmts = Typed(expected_type=PivotFormatList, allow_none=True) + view3D = _3DBase.view3D + floor = _3DBase.floor + sideWall = _3DBase.sideWall + backWall = _3DBase.backWall + plotArea = Typed(expected_type=PlotArea, ) + legend = Typed(expected_type=Legend, allow_none=True) + plotVisOnly = NestedBool() + dispBlanksAs = NestedNoneSet(values=(['span', 'gap', 'zero'])) + showDLblsOverMax = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('title', 'autoTitleDeleted', 'pivotFmts', 'view3D', + 'floor', 'sideWall', 'backWall', 'plotArea', 'legend', 'plotVisOnly', + 'dispBlanksAs', 'showDLblsOverMax') + + def __init__(self, + title=None, + autoTitleDeleted=None, + pivotFmts=None, + view3D=None, + floor=None, + sideWall=None, + backWall=None, + plotArea=None, + legend=None, + plotVisOnly=True, + dispBlanksAs="gap", + showDLblsOverMax=None, + extLst=None, + ): + self.title = title + self.autoTitleDeleted = autoTitleDeleted + self.pivotFmts = pivotFmts + self.view3D = view3D + self.floor = floor + self.sideWall = sideWall + self.backWall = backWall + if plotArea is None: + plotArea = PlotArea() + self.plotArea = plotArea + self.legend = legend + self.plotVisOnly = plotVisOnly + self.dispBlanksAs = dispBlanksAs + self.showDLblsOverMax = showDLblsOverMax + + +class Protection(Serialisable): + + tagname = "protection" + + chartObject = NestedBool(allow_none=True) + data = NestedBool(allow_none=True) + formatting = NestedBool(allow_none=True) + selection = NestedBool(allow_none=True) + userInterface = NestedBool(allow_none=True) + + __elements__ = ("chartObject", "data", "formatting", "selection", "userInterface") + + def __init__(self, + chartObject=None, + data=None, + formatting=None, + selection=None, + userInterface=None, + ): + self.chartObject = chartObject + self.data = data + self.formatting = formatting + self.selection = selection + self.userInterface = userInterface + + +class PivotSource(Serialisable): + + tagname = "pivotSource" + + name = NestedText(expected_type=unicode) + fmtId = NestedInteger(expected_type=int) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('name', 'fmtId') + + def __init__(self, + name=None, + fmtId=None, + extLst=None, + ): + self.name = name + self.fmtId = fmtId + + +class ExternalData(Serialisable): + + tagname = "externalData" + + autoUpdate = NestedBool(allow_none=True) + id = String() # Needs namespace + + def __init__(self, + autoUpdate=None, + id=None + ): + self.autoUpdate = autoUpdate + self.id = id + + +class ChartSpace(Serialisable): + + tagname = "chartSpace" + + date1904 = NestedBool(allow_none=True) + lang = NestedString(allow_none=True) + roundedCorners = NestedBool(allow_none=True) + style = NestedMinMax(allow_none=True, min=1, max=48) + clrMapOvr = Typed(expected_type=ColorMapping, allow_none=True) + pivotSource = Typed(expected_type=PivotSource, allow_none=True) + protection = Typed(expected_type=Protection, allow_none=True) + chart = Typed(expected_type=ChartContainer) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias("txPr") + externalData = Typed(expected_type=ExternalData, allow_none=True) + printSettings = Typed(expected_type=PrintSettings, allow_none=True) + userShapes = Relation() + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('date1904', 'lang', 'roundedCorners', 'style', + 'clrMapOvr', 'pivotSource', 'protection', 'chart', 'spPr', 'txPr', + 'externalData', 'printSettings', 'userShapes') + + def __init__(self, + date1904=None, + lang=None, + roundedCorners=None, + style=None, + clrMapOvr=None, + pivotSource=None, + protection=None, + chart=None, + spPr=None, + txPr=None, + externalData=None, + printSettings=None, + userShapes=None, + extLst=None, + ): + self.date1904 = date1904 + self.lang = lang + self.roundedCorners = roundedCorners + self.style = style + self.clrMapOvr = clrMapOvr + self.pivotSource = pivotSource + self.protection = protection + self.chart = chart + self.spPr = spPr + self.txPr = txPr + self.externalData = externalData + self.printSettings = printSettings + self.userShapes = userShapes + + + def to_tree(self, tagname=None, idx=None, namespace=None): + tree = super(ChartSpace, self).to_tree() + tree.set("xmlns", CHART_NS) + return tree diff --git a/openpyxl/chart/data_source.py b/openpyxl/chart/data_source.py new file mode 100644 index 0000000..67cd8c0 --- /dev/null +++ b/openpyxl/chart/data_source.py @@ -0,0 +1,188 @@ +""" +Collection of utility primitives for charts. +""" + +from openpyxl.compat import unicode +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Bool, + Typed, + Alias, + String, + Integer, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedString, + NestedText, + NestedInteger, +) + + +class NumFmt(Serialisable): + + formatCode = String() + sourceLinked = Bool() + + def __init__(self, + formatCode=None, + sourceLinked=False + ): + self.formatCode = formatCode + self.sourceLinked = sourceLinked + + +class NumberValueDescriptor(NestedText): + """ + Data should be numerical but isn't always :-/ + """ + + allow_none = True + + def __set__(self, instance, value): + if value == "#N/A": + self.expected_type = unicode + else: + self.expected_type = float + super(NumberValueDescriptor, self).__set__(instance, value) + + +class NumVal(Serialisable): + + idx = Integer() + formatCode = NestedText(allow_none=True, expected_type=unicode) + v = NumberValueDescriptor() + + def __init__(self, + idx=None, + formatCode=None, + v=None, + ): + self.idx = idx + self.formatCode = formatCode + self.v = v + + +class NumData(Serialisable): + + formatCode = NestedText(expected_type=unicode, allow_none=True) + ptCount = NestedInteger(allow_none=True) + pt = Sequence(expected_type=NumVal) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('formatCode', 'ptCount', 'pt') + + def __init__(self, + formatCode=None, + ptCount=None, + pt=(), + extLst=None, + ): + self.formatCode = formatCode + self.ptCount = ptCount + self.pt = pt + + +class NumRef(Serialisable): + + f = NestedText(expected_type=unicode) + ref = Alias('f') + numCache = Typed(expected_type=NumData, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('f', 'numCache') + + def __init__(self, + f=None, + numCache=None, + extLst=None, + ): + self.f = f + self.numCache = numCache + + +class StrVal(Serialisable): + + tagname = "strVal" + + idx = Integer() + v = NestedText(expected_type=unicode) + + def __init__(self, + idx=0, + v=None, + ): + self.idx = idx + self.v = v + + +class StrData(Serialisable): + + tagname = "strData" + + ptCount = NestedInteger(allow_none=True) + pt = Sequence(expected_type=StrVal) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('ptCount', 'pt') + + def __init__(self, + ptCount=None, + pt=(), + extLst=None, + ): + self.ptCount = ptCount + self.pt = pt + + +class StrRef(Serialisable): + + tagname = "strRef" + + f = NestedText(expected_type=unicode, allow_none=True) + strCache = Typed(expected_type=StrData, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('f', 'strCache') + + def __init__(self, + f=None, + strCache=None, + extLst=None, + ): + self.f = f + self.strCache = strCache + + +class NumDataSource(Serialisable): + + numRef = Typed(expected_type=NumRef, allow_none=True) + numLit = Typed(expected_type=NumData, allow_none=True) + + + def __init__(self, + numRef=None, + numLit=None, + ): + self.numRef = numRef + self.numLit = numLit + + +class AxDataSource(Serialisable): + + numRef = Typed(expected_type=NumRef, allow_none=True) + numLit = Typed(expected_type=NumData, allow_none=True) + strRef = Typed(expected_type=StrRef, allow_none=True) + strLit = Typed(expected_type=StrData, allow_none=True) + + def __init__(self, + numRef=None, + numLit=None, + strRef=None, + strLit=None, + ): + self.numRef = numRef + self.numLit = numLit + self.strRef = strRef + self.strLit = strLit diff --git a/openpyxl/chart/descriptors.py b/openpyxl/chart/descriptors.py new file mode 100644 index 0000000..c512004 --- /dev/null +++ b/openpyxl/chart/descriptors.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat import basestring + +from openpyxl.descriptors.nested import ( + NestedMinMax + ) + +from openpyxl.descriptors import Typed + +from .data_source import NumFmt + +""" +Utility descriptors for the chart module. +For convenience but also clarity. +""" + +class NestedGapAmount(NestedMinMax): + + allow_none = True + min = 0 + max = 500 + + +class NestedOverlap(NestedMinMax): + + allow_none = True + min = -100 + max = 100 + + +class NumberFormatDescriptor(Typed): + """ + Allow direct assignment of format code + """ + + expected_type = NumFmt + allow_none = True + + def __set__(self, instance, value): + if isinstance(value, basestring): + value = NumFmt(value) + super(NumberFormatDescriptor, self).__set__(instance, value) diff --git a/openpyxl/chart/error_bar.py b/openpyxl/chart/error_bar.py new file mode 100644 index 0000000..7818561 --- /dev/null +++ b/openpyxl/chart/error_bar.py @@ -0,0 +1,60 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Set, + Alias +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedSet, + NestedBool, + NestedFloat, +) + +from .data_source import NumDataSource +from .shapes import GraphicalProperties + + +class ErrorBars(Serialisable): + + tagname = "errBars" + + errDir = NestedNoneSet(values=(['x', 'y'])) + direction = Alias("errDir") + errBarType = NestedSet(values=(['both', 'minus', 'plus'])) + style = Alias("errBarType") + errValType = NestedSet(values=(['cust', 'fixedVal', 'percentage', 'stdDev', 'stdErr'])) + size = Alias("errValType") + noEndCap = NestedBool(nested=True, allow_none=True) + plus = Typed(expected_type=NumDataSource, allow_none=True) + minus = Typed(expected_type=NumDataSource, allow_none=True) + val = NestedFloat(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('errDir','errBarType', 'errValType', 'noEndCap','minus', 'plus', 'val', 'spPr') + + + def __init__(self, + errDir=None, + errBarType="both", + errValType="fixedVal", + noEndCap=None, + plus=None, + minus=None, + val=None, + spPr=None, + extLst=None, + ): + self.errDir = errDir + self.errBarType = errBarType + self.errValType = errValType + self.noEndCap = noEndCap + self.plus = plus + self.minus = minus + self.val = val + self.spPr = spPr diff --git a/openpyxl/chart/label.py b/openpyxl/chart/label.py new file mode 100644 index 0000000..5a27c97 --- /dev/null +++ b/openpyxl/chart/label.py @@ -0,0 +1,130 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Integer, + Bool, + Set, + Float, + Sequence, + Alias +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedBool, + NestedString, + NestedInteger, + ) + +from .shapes import GraphicalProperties +from .text import RichText + + +class _DataLabelBase(Serialisable): + + numFmt = NestedString(allow_none=True, attribute="formatCode") + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias('txPr') + dLblPos = NestedNoneSet(values=['bestFit', 'b', 'ctr', 'inBase', 'inEnd', + 'l', 'outEnd', 'r', 't']) + position = Alias('dLblPos') + showLegendKey = NestedBool(allow_none=True) + showVal = NestedBool(allow_none=True) + showCatName = NestedBool(allow_none=True) + showSerName = NestedBool(allow_none=True) + showPercent = NestedBool(allow_none=True) + showBubbleSize = NestedBool(allow_none=True) + showLeaderLines = NestedBool(allow_none=True) + separator = NestedString(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ("numFmt", "spPr", "txPr", "dLblPos", "showLegendKey", + "showVal", "showCatName", "showSerName", "showPercent", "showBubbleSize", + "showLeaderLines", "separator") + + def __init__(self, + numFmt=None, + spPr=None, + txPr=None, + dLblPos=None, + showLegendKey=None, + showVal=None, + showCatName=None, + showSerName=None, + showPercent=None, + showBubbleSize=None, + showLeaderLines=None, + separator=None, + extLst=None, + ): + self.numFmt = numFmt + self.spPr = spPr + self.txPr = txPr + self.dLblPos = dLblPos + self.showLegendKey = showLegendKey + self.showVal = showVal + self.showCatName = showCatName + self.showSerName = showSerName + self.showPercent = showPercent + self.showBubbleSize = showBubbleSize + self.showLeaderLines = showLeaderLines + self.separator = separator + + +class DataLabel(_DataLabelBase): + + tagname = "dLbl" + + idx = NestedInteger() + + numFmt = _DataLabelBase.numFmt + spPr = _DataLabelBase.spPr + txPr = _DataLabelBase.txPr + dLblPos = _DataLabelBase.dLblPos + showLegendKey = _DataLabelBase.showLegendKey + showVal = _DataLabelBase.showVal + showCatName = _DataLabelBase.showCatName + showSerName = _DataLabelBase.showSerName + showPercent = _DataLabelBase.showPercent + showBubbleSize = _DataLabelBase.showBubbleSize + showLeaderLines = _DataLabelBase.showLeaderLines + separator = _DataLabelBase.separator + extLst = _DataLabelBase.extLst + + __elements__ = ("idx",) + _DataLabelBase.__elements__ + + def __init__(self, idx=0, **kw ): + self.idx = idx + super(DataLabel, self).__init__(**kw) + + +class DataLabelList(_DataLabelBase): + + tagname = "dLbls" + + dLbl = Sequence(expected_type=DataLabel, allow_none=True) + + delete = NestedBool(allow_none=True) + numFmt = _DataLabelBase.numFmt + spPr = _DataLabelBase.spPr + txPr = _DataLabelBase.txPr + dLblPos = _DataLabelBase.dLblPos + showLegendKey = _DataLabelBase.showLegendKey + showVal = _DataLabelBase.showVal + showCatName = _DataLabelBase.showCatName + showSerName = _DataLabelBase.showSerName + showPercent = _DataLabelBase.showPercent + showBubbleSize = _DataLabelBase.showBubbleSize + showLeaderLines = _DataLabelBase.showLeaderLines + separator = _DataLabelBase.separator + extLst = _DataLabelBase.extLst + + __elements__ = ("delete", "dLbl",) + _DataLabelBase.__elements__ + + def __init__(self, dLbl=(), delete=None, **kw): + self.dLbl = dLbl + self.delete = delete + super(DataLabelList, self).__init__(**kw) diff --git a/openpyxl/chart/layout.py b/openpyxl/chart/layout.py new file mode 100644 index 0000000..0fa12e0 --- /dev/null +++ b/openpyxl/chart/layout.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + NoneSet, + Float, + Typed, + Alias, +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedFloat + +) + +class ManualLayout(Serialisable): + + tagname = "manualLayout" + + layoutTarget = NestedNoneSet(values=(['inner', 'outer'])) + xMode = NestedNoneSet(values=(['edge', 'factor'])) + yMode = NestedNoneSet(values=(['edge', 'factor'])) + wMode = NestedNoneSet(values=(['edge', 'factor'])) + hMode = NestedNoneSet(values=(['edge', 'factor'])) + x = NestedFloat(allow_none=True) + y = NestedFloat(allow_none=True) + w = NestedFloat(allow_none=True) + width = Alias('w') + h = NestedFloat(allow_none=True) + height = Alias('h') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('layoutTarget', 'xMode', 'yMode', 'wMode', 'hMode', 'x', + 'y', 'w', 'h') + + def __init__(self, + layoutTarget=None, + xMode=None, + yMode=None, + wMode=None, + hMode=None, + x=None, + y=None, + w=None, + h=None, + extLst=None, + ): + self.layoutTarget = layoutTarget + self.xMode = xMode + self.yMode = yMode + self.wMode = wMode + self.hMode = hMode + self.x = x + self.y = y + self.w = w + self.h = h + + +class Layout(Serialisable): + + tagname = "layout" + + manualLayout = Typed(expected_type=ManualLayout, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('manualLayout',) + + def __init__(self, + manualLayout=None, + extLst=None, + ): + self.manualLayout = manualLayout diff --git a/openpyxl/chart/legend.py b/openpyxl/chart/legend.py new file mode 100644 index 0000000..8a0060d --- /dev/null +++ b/openpyxl/chart/legend.py @@ -0,0 +1,73 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedBool, + NestedSet, + NestedInteger +) + +from .layout import Layout +from .shapes import GraphicalProperties +from .text import RichText + + +class LegendEntry(Serialisable): + + tagname = "legendEntry" + + idx = NestedInteger() + delete = NestedBool() + txPr = Typed(expected_type=RichText, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('idx', 'delete', 'txPr') + + def __init__(self, + idx=0, + delete=False, + txPr=None, + extLst=None, + ): + self.idx = idx + self.delete = delete + self.txPr = txPr + + +class Legend(Serialisable): + + tagname = "legend" + + legendPos = NestedSet(values=(['b', 'tr', 'l', 'r', 't'])) + position = Alias('legendPos') + legendEntry = Sequence(expected_type=LegendEntry) + layout = Typed(expected_type=Layout, allow_none=True) + overlay = NestedBool(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias('txPr') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('legendPos', 'legendEntry', 'layout', 'overlay', 'spPr', 'txPr',) + + def __init__(self, + legendPos="r", + legendEntry=(), + layout=None, + overlay=None, + spPr=None, + txPr=None, + extLst=None, + ): + self.legendPos = legendPos + self.legendEntry = legendEntry + self.layout = layout + self.overlay = overlay + self.spPr = spPr + self.txPr = txPr diff --git a/openpyxl/chart/line_chart.py b/openpyxl/chart/line_chart.py new file mode 100644 index 0000000..169bd67 --- /dev/null +++ b/openpyxl/chart/line_chart.py @@ -0,0 +1,132 @@ +from __future__ import absolute_import +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias, + ) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedSet, + NestedBool, + NestedMinMax, +) + +from ._chart import ChartBase +from .updown_bars import UpDownBars +from .descriptors import NestedGapAmount +from .axis import TextAxis, NumericAxis, SeriesAxis, ChartLines, _BaseAxis +from .label import DataLabelList +from .series import Series + + +class _LineChartBase(ChartBase): + + grouping = NestedSet(values=(['percentStacked', 'standard', 'stacked'])) + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + dropLines = Typed(expected_type=ChartLines, allow_none=True) + + _series_type = "line" + + __elements__ = ('grouping', 'varyColors', 'ser', 'dLbls', 'dropLines') + + def __init__(self, + grouping="standard", + varyColors=None, + ser=(), + dLbls=None, + dropLines=None, + **kw + ): + self.grouping = grouping + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.dropLines = dropLines + super(_LineChartBase, self).__init__(**kw) + + +class LineChart(_LineChartBase): + + tagname = "lineChart" + + grouping = _LineChartBase.grouping + varyColors = _LineChartBase.varyColors + ser = _LineChartBase.ser + dLbls = _LineChartBase.dLbls + dropLines =_LineChartBase.dropLines + + hiLowLines = Typed(expected_type=ChartLines, allow_none=True) + upDownBars = Typed(expected_type=UpDownBars, allow_none=True) + marker = NestedBool(allow_none=True) + smooth = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=_BaseAxis) + y_axis = Typed(expected_type=NumericAxis) + + __elements__ = _LineChartBase.__elements__ + ('hiLowLines', 'upDownBars', 'marker', 'smooth', 'axId') + + def __init__(self, + hiLowLines=None, + upDownBars=None, + marker=None, + smooth=None, + extLst=None, + **kw + ): + self.hiLowLines = hiLowLines + self.upDownBars = upDownBars + self.marker = marker + self.smooth = smooth + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + + super(LineChart, self).__init__(**kw) + + +class LineChart3D(_LineChartBase): + + tagname = "line3DChart" + + grouping = _LineChartBase.grouping + varyColors = _LineChartBase.varyColors + ser = _LineChartBase.ser + dLbls = _LineChartBase.dLbls + dropLines =_LineChartBase.dropLines + + gapDepth = NestedGapAmount() + hiLowLines = Typed(expected_type=ChartLines, allow_none=True) + upDownBars = Typed(expected_type=UpDownBars, allow_none=True) + marker = NestedBool(allow_none=True) + smooth = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis) + + __elements__ = _LineChartBase.__elements__ + ('gapDepth', 'hiLowLines', + 'upDownBars', 'marker', 'smooth', 'axId') + + def __init__(self, + gapDepth=None, + hiLowLines=None, + upDownBars=None, + marker=None, + smooth=None, + **kw + ): + self.gapDepth = gapDepth + self.hiLowLines = hiLowLines + self.upDownBars = upDownBars + self.marker = marker + self.smooth = smooth + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() + super(LineChart3D, self).__init__(**kw) diff --git a/openpyxl/chart/marker.py b/openpyxl/chart/marker.py new file mode 100644 index 0000000..82f7613 --- /dev/null +++ b/openpyxl/chart/marker.py @@ -0,0 +1,91 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, +) + +from openpyxl.descriptors.excel import( + ExtensionList, + _explicit_none, +) + +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedMinMax, + NestedNoneSet, +) + +from .layout import Layout +from .picture import PictureOptions +from .shapes import * +from .text import * +from .error_bar import * + + +class Marker(Serialisable): + + tagname = "marker" + + symbol = NestedNoneSet(values=(['circle', 'dash', 'diamond', 'dot', 'picture', + 'plus', 'square', 'star', 'triangle', 'x', 'auto']), + to_tree=_explicit_none) + size = NestedMinMax(min=2, max=72, allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('symbol', 'size', 'spPr') + + def __init__(self, + symbol=None, + size=None, + spPr=None, + extLst=None, + ): + self.symbol = symbol + self.size = size + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + + +class DataPoint(Serialisable): + + tagname = "dPt" + + idx = NestedInteger() + invertIfNegative = NestedBool(allow_none=True) + marker = Typed(expected_type=Marker, allow_none=True) + bubble3D = NestedBool(allow_none=True) + explosion = 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__ = ('idx', 'invertIfNegative', 'marker', 'bubble3D', + 'explosion', 'spPr', 'pictureOptions') + + def __init__(self, + idx=None, + invertIfNegative=None, + marker=None, + bubble3D=None, + explosion=None, + spPr=None, + pictureOptions=None, + extLst=None, + ): + self.idx = idx + self.invertIfNegative = invertIfNegative + self.marker = marker + self.bubble3D = bubble3D + self.explosion = explosion + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + self.pictureOptions = pictureOptions diff --git a/openpyxl/chart/packaging.rst b/openpyxl/chart/packaging.rst new file mode 100644 index 0000000..eff4a2c --- /dev/null +++ b/openpyxl/chart/packaging.rst @@ -0,0 +1,8 @@ +Notes on packaging charts +========================= + +In Excel charts are global objects even though they must be assigned to either a worksheet or a chart sheet. Assignment is further complicated because it is indirect: charts are referred to from drawings (also global objects), which are referred to from worksheets. Drawings can contain other objects. Relations are managed indirectly between worksheets and drawings, and drawings and charts. + +The naive approach too packaging is to collect as groups (charts, images, etc.) and, thus calculate indices. This involves looping over sheets multiple times and is tightly coupled, involving the passing of relation ids around objects. + +A better approach would be to serialise dependent objects sheet by sheet in reverse order of dependency: serialise charts (in /charts) and add the relation to the worksheet drawing relationship mapper; then serialise drawings (not sure if there can be more than one per worksheet, charts are normally bundled) and their relationships and add the relation to the drawing to the worksheet. All dependent objects must be either shadowed or referenced to both in the worksheet and in the workbook so that duplicate filenames can be easily avoided. \ No newline at end of file diff --git a/openpyxl/chart/picture.py b/openpyxl/chart/picture.py new file mode 100644 index 0000000..2fe1604 --- /dev/null +++ b/openpyxl/chart/picture.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.descriptors.nested import ( + NestedBool, + NestedFloat, + NestedMinMax, + NestedNoneSet, +) + +class PictureOptions(Serialisable): + + tagname = "pictureOptions" + + applyToFront = NestedBool(allow_none=True, nested=True) + applyToSides = NestedBool(allow_none=True, nested=True) + applyToEnd = NestedBool(allow_none=True, nested=True) + pictureFormat = NestedNoneSet(values=(['stretch', 'stack', 'stackScale']), nested=True) + pictureStackUnit = NestedFloat(allow_none=True, nested=True) + + __elements__ = ('applyToFront', 'applyToSides', 'applyToEnd', 'pictureFormat', 'pictureStackUnit') + + def __init__(self, + applyToFront=None, + applyToSides=None, + applyToEnd=None, + pictureFormat=None, + pictureStackUnit=None, + ): + self.applyToFront = applyToFront + self.applyToSides = applyToSides + self.applyToEnd = applyToEnd + self.pictureFormat = pictureFormat + self.pictureStackUnit = pictureStackUnit diff --git a/openpyxl/chart/pie_chart.py b/openpyxl/chart/pie_chart.py new file mode 100644 index 0000000..5f215d0 --- /dev/null +++ b/openpyxl/chart/pie_chart.py @@ -0,0 +1,177 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + MinMax, + Integer, + NoneSet, + Float, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList, Percentage +from openpyxl.descriptors.nested import ( + NestedBool, + NestedMinMax, + NestedInteger, + NestedFloat, + NestedNoneSet, + NestedSet, +) +from openpyxl.descriptors.sequence import ValueSequence + +from ._chart import ChartBase +from .axis import ChartLines +from .descriptors import NestedGapAmount +from .series import Series +from .label import DataLabelList + + +class _PieChartBase(ChartBase): + + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + + _series_type = "pie" + + __elements__ = ('varyColors', 'ser', 'dLbls') + + def __init__(self, + varyColors=True, + ser=(), + dLbls=None, + ): + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + super(_PieChartBase, self).__init__() + + + +class PieChart(_PieChartBase): + + tagname = "pieChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + firstSliceAng = NestedMinMax(min=0, max=360) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + ('firstSliceAng', ) + + def __init__(self, + firstSliceAng=0, + extLst=None, + **kw + ): + self.firstSliceAng = firstSliceAng + super(PieChart, self).__init__(**kw) + + +class PieChart3D(_PieChartBase): + + tagname = "pie3DChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + + +class DoughnutChart(_PieChartBase): + + tagname = "doughnutChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + firstSliceAng = NestedMinMax(min=0, max=360) + holeSize = NestedMinMax(min=10, max=90, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + ('firstSliceAng', 'holeSize') + + def __init__(self, + firstSliceAng=0, + holeSize=10, + extLst=None, + **kw + ): + self.firstSliceAng = firstSliceAng + self.holeSize = holeSize + super(DoughnutChart, self).__init__(**kw) + + +class CustomSplit(Serialisable): + + tagname = "custSplit" + + secondPiePt = ValueSequence(expected_type=int) + + __elements__ = ('secondPiePt',) + + def __init__(self, + secondPiePt=(), + ): + self.secondPiePt = secondPiePt + + +class ProjectedPieChart(_PieChartBase): + + """ + From the spec 21.2.2.126 + + This element contains the pie of pie or bar of pie series on this + chart. Only the first series shall be displayed. The splitType element + shall determine whether the splitPos and custSplit elements apply. + """ + + tagname = "ofPieChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + ofPieType = NestedSet(values=(['pie', 'bar'])) + type = Alias('ofPieType') + gapWidth = NestedGapAmount() + splitType = NestedNoneSet(values=(['auto', 'cust', 'percent', 'pos', 'val'])) + splitPos = NestedFloat(allow_none=True) + custSplit = Typed(expected_type=CustomSplit, allow_none=True) + secondPieSize = NestedMinMax(min=5, max=200, allow_none=True) + serLines = Typed(expected_type=ChartLines, allow_none=True) + join_lines = Alias('serLines') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + ('ofPieType', 'gapWidth', + 'splitType', 'splitPos', 'custSplit', 'secondPieSize', 'serLines') + + def __init__(self, + ofPieType="pie", + gapWidth=None, + splitType="auto", + splitPos=None, + custSplit=None, + secondPieSize=75, + serLines=None, + extLst=None, + **kw + ): + self.ofPieType = ofPieType + self.gapWidth = gapWidth + self.splitType = splitType + self.splitPos = splitPos + self.custSplit = custSplit + self.secondPieSize = secondPieSize + if serLines is None: + self.serLines = ChartLines() + super(ProjectedPieChart, self).__init__(**kw) diff --git a/openpyxl/chart/plotarea.py b/openpyxl/chart/plotarea.py new file mode 100644 index 0000000..9aa96b4 --- /dev/null +++ b/openpyxl/chart/plotarea.py @@ -0,0 +1,169 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Typed, + Alias, +) +from openpyxl.descriptors.excel import ( + ExtensionList, +) +from openpyxl.descriptors.sequence import ( + MultiSequence, + MultiSequencePart, +) +from openpyxl.descriptors.nested import ( + NestedBool, + NestedNoneSet, + NestedInteger, + NestedString, + NestedMinMax, + NestedText, +) + +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, ProjectedPieChart, DoughnutChart +from .radar_chart import RadarChart +from .scatter_chart import ScatterChart +from .stock_chart import StockChart +from .surface_chart import SurfaceChart, SurfaceChart3D +from .layout import Layout +from .shapes import GraphicalProperties +from .text import RichText + +from .axis import ( + NumericAxis, + TextAxis, + SeriesAxis, + DateAxis, +) + + +class DataTable(Serialisable): + + tagname = "dTable" + + showHorzBorder = NestedBool(allow_none=True) + showVertBorder = NestedBool(allow_none=True) + showOutline = NestedBool(allow_none=True) + showKeys = NestedBool(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('showHorzBorder', 'showVertBorder', 'showOutline', + 'showKeys', 'spPr', 'txPr') + + def __init__(self, + showHorzBorder=None, + showVertBorder=None, + showOutline=None, + showKeys=None, + spPr=None, + txPr=None, + extLst=None, + ): + self.showHorzBorder = showHorzBorder + self.showVertBorder = showVertBorder + self.showOutline = showOutline + self.showKeys = showKeys + self.spPr = spPr + self.txPr = txPr + + +class PlotArea(Serialisable): + + tagname = "plotArea" + + layout = Typed(expected_type=Layout, allow_none=True) + dTable = Typed(expected_type=DataTable, allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + # at least one chart + _charts = MultiSequence() + areaChart = MultiSequencePart(expected_type=AreaChart, store="_charts") + area3DChart = MultiSequencePart(expected_type=AreaChart3D, store="_charts") + lineChart = MultiSequencePart(expected_type=LineChart, store="_charts") + line3DChart = MultiSequencePart(expected_type=LineChart3D, store="_charts") + stockChart = MultiSequencePart(expected_type=StockChart, store="_charts") + radarChart = MultiSequencePart(expected_type=RadarChart, store="_charts") + scatterChart = MultiSequencePart(expected_type=ScatterChart, store="_charts") + pieChart = MultiSequencePart(expected_type=PieChart, store="_charts") + pie3DChart = MultiSequencePart(expected_type=PieChart3D, store="_charts") + doughnutChart = MultiSequencePart(expected_type=DoughnutChart, store="_charts") + barChart = MultiSequencePart(expected_type=BarChart, store="_charts") + bar3DChart = MultiSequencePart(expected_type=BarChart3D, store="_charts") + ofPieChart = MultiSequencePart(expected_type=ProjectedPieChart, store="_charts") + surfaceChart = MultiSequencePart(expected_type=SurfaceChart, store="_charts") + surface3DChart = MultiSequencePart(expected_type=SurfaceChart3D, store="_charts") + bubbleChart = MultiSequencePart(expected_type=BubbleChart, store="_charts") + + # axes + _axes = MultiSequence() + valAx = MultiSequencePart(expected_type=NumericAxis, store="_axes") + catAx = MultiSequencePart(expected_type=TextAxis, store="_axes") + dateAx = MultiSequencePart(expected_type=DateAxis, store="_axes") + serAx = MultiSequencePart(expected_type=SeriesAxis, store="_axes") + + __elements__ = ('layout', '_charts', '_axes', 'dTable', 'spPr') + + def __init__(self, + layout=None, + dTable=None, + spPr=None, + _charts=(), + _axes=(), + extLst=None, + ): + self.layout = layout + self.dTable = dTable + self.spPr = spPr + self._charts = _charts + self._axes = _axes + + + def to_tree(self, tagname=None, idx=None, namespace=None): + axIds = set((ax.axId for ax in self._axes)) + for chart in self._charts: + for id, axis in chart._axes.items(): + if id not in axIds: + setattr(self, axis.tagname, axis) + axIds.add(id) + + return super(PlotArea, self).to_tree(tagname) + + + @classmethod + def from_tree(cls, node): + self = super(PlotArea, cls).from_tree(node) + axes = dict((axis.axId, axis) for axis in self._axes) + for chart in self._charts: + if isinstance(chart, ScatterChart): + x, y = (axes[axId] for axId in chart.axId) + chart.x_axis = x + chart.y_axis = y + continue + + if isinstance(chart, (BarChart3D, LineChart3D, SurfaceChart3D)): + chart.z_axis = None + + for axId in chart.axId: + if not axId: + continue + axis = axes[axId] + if axis.tagname in ("catAx", "dateAx"): + chart.x_axis = axis + elif axis.tagname == "valAx": + chart.y_axis = axis + elif axis.tagname == "serAx": + chart.z_axis = axis + + return self diff --git a/openpyxl/chart/print_settings.py b/openpyxl/chart/print_settings.py new file mode 100644 index 0000000..824e33b --- /dev/null +++ b/openpyxl/chart/print_settings.py @@ -0,0 +1,58 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Float, + Typed, + Alias, +) + +from openpyxl.worksheet.page import PrintPageSetup +from openpyxl.worksheet.header_footer import HeaderFooter + + +class PageMargins(Serialisable): + """ + Identical to openpyxl.worksheet.page.Pagemargins but element names are different :-/ + """ + tagname = "pageMargins" + + l = Float() + left = Alias('l') + r = Float() + right = Alias('r') + t = Float() + top = Alias('t') + b = Float() + bottom = Alias('b') + header = Float() + footer = Float() + + def __init__(self, l=0.75, r=0.75, t=1, b=1, header=0.5, footer=0.5): + self.l = l + self.r = r + self.t = t + self.b = b + self.header = header + self.footer = footer + + +class PrintSettings(Serialisable): + + tagname = "printSettings" + + headerFooter = Typed(expected_type=HeaderFooter, allow_none=True) + pageMargins = Typed(expected_type=PageMargins, allow_none=True) + pageSetup = Typed(expected_type=PrintPageSetup, allow_none=True) + + __elements__ = ("headerFooter", "pageMargins", "pageMargins") + + def __init__(self, + headerFooter=None, + pageMargins=None, + pageSetup=None, + ): + self.headerFooter = headerFooter + self.pageMargins = pageMargins + self.pageSetup = pageSetup diff --git a/openpyxl/chart/radar_chart.py b/openpyxl/chart/radar_chart.py new file mode 100644 index 0000000..778c82f --- /dev/null +++ b/openpyxl/chart/radar_chart.py @@ -0,0 +1,53 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Typed, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedSet +) + +from ._chart import ChartBase +from .axis import TextAxis, NumericAxis +from .series import Series +from .label import DataLabelList + + +class RadarChart(ChartBase): + + tagname = "radarChart" + + radarStyle = NestedSet(values=(['standard', 'marker', 'filled'])) + type = Alias("radarStyle") + varyColors = NestedBool(nested=True, allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + _series_type = "radar" + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + __elements__ = ('radarStyle', 'varyColors', 'ser', 'dLbls', 'axId') + + def __init__(self, + radarStyle="standard", + varyColors=None, + ser=(), + dLbls=None, + extLst=None, + **kw + ): + self.radarStyle = radarStyle + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + super(RadarChart, self).__init__(**kw) diff --git a/openpyxl/chart/reader.py b/openpyxl/chart/reader.py new file mode 100644 index 0000000..f7799a6 --- /dev/null +++ b/openpyxl/chart/reader.py @@ -0,0 +1,80 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2017 openpyxl +# Copyright (c) 2018 qyou.casia@gmail.com + +""" +Read a chart +""" + +from .chartspace import ChartSpace, PlotArea +from openpyxl.xml.functions import fromstring + +from openpyxl.packaging.relationship import get_rel, get_rels_path, get_dependents +from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing +from openpyxl.drawing.image import Image +from io import BytesIO + +_types = ('areaChart', 'area3DChart', 'lineChart', 'line3DChart', + 'stockChart', 'radarChart', 'scatterChart', 'pieChart', 'pie3DChart', + 'doughnutChart', 'barChart', 'bar3DChart', 'ofPieChart', 'surfaceChart', + 'surface3DChart', 'bubbleChart',) + +_axes = ('valAx', 'catAx', 'dateAx', 'serAx',) + + +def read_chart(chartspace): + cs = chartspace + plot = cs.chart.plotArea + + chart = plot._charts[0] + chart._charts = plot._charts + + chart.title = cs.chart.title + chart.layout = plot.layout + chart.legend = cs.chart.legend + + return chart + + +def find_charts(archive, path): + """ + Given the path to a drawing file extract anchors with charts + """ + + src = archive.read(path) + tree = fromstring(src) + drawing = SpreadsheetDrawing.from_tree(tree) + + rels_path = get_rels_path(path) + deps = [] + if rels_path in archive.namelist(): + deps = get_dependents(archive, rels_path) + + charts = [] + for rel in drawing._chart_rels: + cs = get_rel(archive, deps, rel.id, ChartSpace) + chart = read_chart(cs) + chart.anchor = rel.anchor + charts.append(chart) + + return charts + + +def find_images(archive, path): + src = archive.read(path) + tree = fromstring(src) + drawing = SpreadsheetDrawing.from_tree(tree) + + rels_path = get_rels_path(path) + deps = [] + if rels_path in archive.namelist(): + deps = get_dependents(archive, rels_path) + + images = [] + for rel in drawing._image_rels: + id = rel.embed + path = deps[id].target + image = Image(BytesIO(archive.read(path))) + image.anchor = rel.anchor + images.append(image) + return images diff --git a/openpyxl/chart/reference.py b/openpyxl/chart/reference.py new file mode 100644 index 0000000..2abc74d --- /dev/null +++ b/openpyxl/chart/reference.py @@ -0,0 +1,132 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from itertools import chain + +from openpyxl.compat import unicode +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + MinMax, + Typed, + String, + Strict, +) +from openpyxl.worksheet import Worksheet +from openpyxl.utils import ( + get_column_letter, + range_to_tuple, + quote_sheetname +) + + +class DummyWorksheet: + + + def __init__(self, title): + self.title = title + + +class Reference(Strict): + + """ + Normalise cell range references + """ + + min_row = MinMax(min=1, max=1000000, expected_type=int) + max_row = MinMax(min=1, max=1000000, expected_type=int) + min_col = MinMax(min=1, max=16384, expected_type=int) + max_col = MinMax(min=1, max=16384, expected_type=int) + range_string = String(allow_none=True) + + def __init__(self, + worksheet=None, + min_col=None, + min_row=None, + max_col=None, + max_row=None, + range_string=None + ): + if range_string is not None: + sheetname, boundaries = range_to_tuple(range_string) + min_col, min_row, max_col, max_row = boundaries + worksheet = DummyWorksheet(sheetname) + + self.worksheet = worksheet + self.min_col = min_col + self.min_row = min_row + if max_col is None: + max_col = min_col + self.max_col = max_col + if max_row is None: + max_row = min_row + self.max_row = max_row + + + def __repr__(self): + return unicode(self) + + + def __str__(self): + fmt = u"{0}!${1}${2}:${3}${4}" + if (self.min_col == self.max_col + and self.min_row == self.max_row): + fmt = u"{0}!${1}${2}" + return fmt.format(self.sheetname, + get_column_letter(self.min_col), self.min_row, + get_column_letter(self.max_col), self.max_row + ) + + + __unicode__ = __str__ + + + + def __len__(self): + if self.min_row == self.max_row: + return 1 + self.max_col - self.min_col + return 1 + self.max_row - self.min_row + + + @property + def rows(self): + """ + Return all cells in range by column + """ + for row in range(self.min_row, self.max_row+1): + yield tuple('%s%d' % (get_column_letter(col), row) + for col in range(self.min_col, self.max_col+1)) + + + @property + def cols(self): + """ + Return all cells in range by row + """ + for col in range(self.min_col, self.max_col+1): + yield tuple('%s%d' % (get_column_letter(col), row) + for row in range(self.min_row, self.max_row+1)) + + + @property + def cells(self): + """ + Return a flattened list of all cells (by column) + """ + return chain.from_iterable(self.cols) + + + def pop(self): + """ + Return and remove the first cell + """ + cell = next(self.cells) + if self.min_row == self.max_row: + self.min_col += 1 + else: + self.min_row += 1 + return cell + + + @property + def sheetname(self): + return quote_sheetname(self.worksheet.title) diff --git a/openpyxl/chart/scatter_chart.py b/openpyxl/chart/scatter_chart.py new file mode 100644 index 0000000..ce8dcc6 --- /dev/null +++ b/openpyxl/chart/scatter_chart.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedBool, +) + +from ._chart import ChartBase +from .axis import NumericAxis +from .series import XYSeries +from .label import DataLabelList + + +class ScatterChart(ChartBase): + + tagname = "scatterChart" + + scatterStyle = NestedNoneSet(values=(['line', 'lineMarker', 'marker', 'smooth', 'smoothMarker'])) + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=XYSeries, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=NumericAxis) + y_axis = Typed(expected_type=NumericAxis) + + _series_type = "scatter" + + __elements__ = ('scatterStyle', 'varyColors', 'ser', 'dLbls', 'axId',) + + def __init__(self, + scatterStyle=None, + varyColors=None, + ser=(), + dLbls=None, + extLst=None, + **kw + ): + self.scatterStyle = scatterStyle + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.x_axis = NumericAxis(axId=10, crossAx=20) + self.y_axis = NumericAxis(axId=20, crossAx=10) + super(ScatterChart, self).__init__(**kw) diff --git a/openpyxl/chart/series.py b/openpyxl/chart/series.py new file mode 100644 index 0000000..ec92f7b --- /dev/null +++ b/openpyxl/chart/series.py @@ -0,0 +1,197 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Integer, + Bool, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedBool, + NestedNoneSet, + NestedText, +) + +from .shapes import GraphicalProperties +from .data_source import ( + AxDataSource, + NumDataSource, + NumRef, + StrRef, +) +from .error_bar import ErrorBars +from .label import DataLabelList +from .marker import DataPoint, PictureOptions, Marker +from .trendline import Trendline + +attribute_mapping = { + 'area': ('idx', 'order', 'tx', 'spPr', 'pictureOptions', 'dPt', 'dLbls', 'errBars', + 'trendline', 'cat', 'val',), + 'bar':('idx', 'order','tx', 'spPr', 'invertIfNegative', 'pictureOptions', 'dPt', + 'dLbls', 'trendline', 'errBars', 'cat', 'val', 'shape'), + 'bubble':('idx','order', 'tx', 'spPr', 'invertIfNegative', 'dPt', 'dLbls', + 'trendline', 'errBars', 'xVal', 'yVal', 'bubbleSize', 'bubble3D'), + 'line':('idx', 'order', 'tx', 'spPr', 'marker', 'dPt', 'dLbls', 'trendline', + 'errBars', 'cat', 'val', 'smooth'), + 'pie':('idx', 'order', 'tx', 'spPr', 'explosion', 'dPt', 'dLbls', 'cat', 'val'), + 'radar':('idx', 'order', 'tx', 'spPr', 'marker', 'dPt', 'dLbls', 'cat', 'val'), + 'scatter':('idx', 'order', 'tx', 'spPr', 'marker', 'dPt', 'dLbls', 'trendline', + 'errBars', 'xVal', 'yVal', 'smooth'), + 'surface':('idx', 'order', 'tx', 'spPr', 'cat', 'val'), + } + + +class SeriesLabel(Serialisable): + + tagname = "tx" + + strRef = Typed(expected_type=StrRef, allow_none=True) + v = NestedText(expected_type=unicode, allow_none=True) + value = Alias('v') + + __elements__ = ('strRef', 'v') + + def __init__(self, + strRef=None, + v=None): + self.strRef = strRef + self.v = v + + +class Series(Serialisable): + + """ + Generic series object. Should not be instantiated directly. + User the chart.Series factory instead. + """ + + tagname = "ser" + + idx = NestedInteger() + order = NestedInteger() + tx = Typed(expected_type=SeriesLabel, allow_none=True) + title = Alias('tx') + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + + # area chart + pictureOptions = Typed(expected_type=PictureOptions, allow_none=True) + dPt = Sequence(expected_type=DataPoint, allow_none=True) + data_points = Alias("dPt") + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + labels = Alias("dLbls") + trendline = Typed(expected_type=Trendline, allow_none=True) + errBars = Typed(expected_type=ErrorBars, allow_none=True) + cat = Typed(expected_type=AxDataSource, allow_none=True) + identifiers = Alias("cat") + val = Typed(expected_type=NumDataSource, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + #bar chart + invertIfNegative = NestedBool(allow_none=True) + shape = NestedNoneSet(values=(['cone', 'coneToMax', 'box', 'cylinder', 'pyramid', 'pyramidToMax'])) + + #bubble chart + xVal = Typed(expected_type=AxDataSource, allow_none=True) + yVal = Typed(expected_type=NumDataSource, allow_none=True) + bubbleSize = Typed(expected_type=NumDataSource, allow_none=True) + zVal = Alias("bubbleSize") + bubble3D = NestedBool(allow_none=True) + + #line chart + marker = Typed(expected_type=Marker, allow_none=True) + smooth = NestedBool(allow_none=True) + + #pie chart + explosion = NestedInteger(allow_none=True) + + __elements__ = () + + + def __init__(self, + idx=0, + order=0, + tx=None, + spPr=None, + pictureOptions=None, + dPt=(), + dLbls=None, + trendline=None, + errBars=None, + cat=None, + val=None, + invertIfNegative=None, + shape=None, + xVal=None, + yVal=None, + bubbleSize=None, + bubble3D=None, + marker=None, + smooth=None, + explosion=None, + extLst=None, + ): + self.idx = idx + self.order = order + self.tx = tx + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + self.pictureOptions = pictureOptions + self.dPt = dPt + self.dLbls = dLbls + self.trendline = trendline + self.errBars = errBars + self.cat = cat + self.val = val + self.invertIfNegative = invertIfNegative + self.shape = shape + self.xVal = xVal + self.yVal = yVal + self.bubbleSize = bubbleSize + self.bubble3D = bubble3D + if marker is None: + marker = Marker() + self.marker = marker + self.smooth = smooth + self.explosion = explosion + + def to_tree(self, tagname=None, idx=None): + if idx is not None: + if self.order == self.idx: + self.order = idx + self.idx = idx + return super(Series, self).to_tree(tagname) + + +class XYSeries(Series): + + """Dedicated series for charts that have x and y series""" + + idx = Series.idx + order = Series.order + tx = Series.tx + spPr = Series.spPr + + dPt = Series.dPt + dLbls = Series.dLbls + trendline = Series.trendline + errBars = Series.errBars + xVal = Series.xVal + yVal = Series.yVal + + invertIfNegative = Series.invertIfNegative + + bubbleSize = Series.bubbleSize + bubble3D = Series.bubble3D + + marker = Series.marker + smooth = Series.smooth diff --git a/openpyxl/chart/series_factory.py b/openpyxl/chart/series_factory.py new file mode 100644 index 0000000..31a1674 --- /dev/null +++ b/openpyxl/chart/series_factory.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from .data_source import NumDataSource, NumRef, AxDataSource +from .reference import Reference +from .series import Series, XYSeries, SeriesLabel, StrRef +from openpyxl.utils import rows_from_range, quote_sheetname + + +def SeriesFactory(values, xvalues=None, zvalues=None, title=None, title_from_data=False): + """ + Convenience Factory for creating chart data series. + """ + + if not isinstance(values, Reference): + values = Reference(range_string=values) + + if title_from_data: + cell = values.pop() + title = u"{0}!{1}".format(values.sheetname, cell) + title = SeriesLabel(strRef=StrRef(title)) + elif title is not None: + title = SeriesLabel(v=title) + + source = NumDataSource(numRef=NumRef(f=values)) + if xvalues is not None: + if not isinstance(xvalues, Reference): + xvalues = Reference(range_string=xvalues) + series = XYSeries() + series.yVal = source + series.xVal = AxDataSource(numRef=NumRef(f=xvalues)) + if zvalues is not None: + if not isinstance(zvalues, Reference): + zvalues = Reference(range_string=zvalues) + series.zVal = NumDataSource(NumRef(f=zvalues)) + else: + series = Series() + series.val = source + + if title is not None: + series.title = title + return series diff --git a/openpyxl/chart/shapes.py b/openpyxl/chart/shapes.py new file mode 100644 index 0000000..e81e517 --- /dev/null +++ b/openpyxl/chart/shapes.py @@ -0,0 +1,90 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias +) +from openpyxl.descriptors.nested import ( + EmptyTag +) +from openpyxl.drawing.colors import ColorChoiceDescriptor +from openpyxl.drawing.fill import * +from openpyxl.drawing.line import LineProperties +from openpyxl.drawing.shapes import ( + Shape3D, + Scene3D, + Transform2D, + CustomGeometry2D, + PresetGeometry2D, +) + + +class GraphicalProperties(Serialisable): + + """ + Somewhat vaguely 21.2.2.197 says this: + + This element specifies the formatting for the parent chart element. The + custGeom, prstGeom, scene3d, and xfrm elements are not supported. The + bwMode attribute is not supported. + + This doesn't leave much. And the element is used in different places. + """ + + tagname = "spPr" + + bwMode = NoneSet(values=(['clr', 'auto', 'gray', 'ltGray', 'invGray', + 'grayWhite', 'blackGray', 'blackWhite', 'black', 'white', 'hidden'] + ) + ) + + xfrm = Typed(expected_type=Transform2D, allow_none=True) + transform = Alias('xfrm') + custGeom = Typed(expected_type=CustomGeometry2D, allow_none=True) # either or + prstGeom = Typed(expected_type=PresetGeometry2D, allow_none=True) + + # fills one of + noFill = EmptyTag(namespace=DRAWING_NS) + solidFill = ColorChoiceDescriptor() + gradFill = Typed(expected_type=GradientFillProperties, allow_none=True) + pattFill = Typed(expected_type=PatternFillProperties, allow_none=True) + + ln = Typed(expected_type=LineProperties, allow_none=True) + line = Alias('ln') + scene3d = Typed(expected_type=Scene3D, allow_none=True) + sp3d = Typed(expected_type=Shape3D, allow_none=True) + shape3D = Alias('sp3d') + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ('xfrm', 'prstGeom', 'noFill', 'solidFill', 'gradFill', 'pattFill', + 'ln', 'scene3d', 'sp3d') + + def __init__(self, + bwMode=None, + xfrm=None, + noFill=None, + solidFill=None, + gradFill=None, + pattFill=None, + ln=None, + scene3d=None, + custGeom=None, + prstGeom=None, + sp3d=None, + extLst=None, + ): + self.bwMode = bwMode + self.xfrm = xfrm + self.noFill = noFill + self.solidFill = solidFill + self.gradFill = gradFill + self.pattFill = pattFill + if ln is None: + ln = LineProperties() + self.ln = ln + self.custGeom = custGeom + self.prstGeom = prstGeom + self.scene3d = scene3d + self.sp3d = sp3d diff --git a/openpyxl/chart/stock_chart.py b/openpyxl/chart/stock_chart.py new file mode 100644 index 0000000..0b040e2 --- /dev/null +++ b/openpyxl/chart/stock_chart.py @@ -0,0 +1,55 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList + +from ._chart import ChartBase +from .axis import TextAxis, NumericAxis, ChartLines +from .updown_bars import UpDownBars +from .label import DataLabelList +from .series import Series + + +class StockChart(ChartBase): + + tagname = "stockChart" + + ser = Sequence(expected_type=Series) #min 3, max4 + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias('dLbls') + dropLines = Typed(expected_type=ChartLines, allow_none=True) + hiLowLines = Typed(expected_type=ChartLines, allow_none=True) + upDownBars = Typed(expected_type=UpDownBars, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + _series_type = "line" + + __elements__ = ('ser', 'dLbls', 'dropLines', 'hiLowLines', 'upDownBars', + 'axId') + + def __init__(self, + ser=(), + dLbls=None, + dropLines=None, + hiLowLines=None, + upDownBars=None, + extLst=None, + **kw + ): + self.ser = ser + self.dLbls = dLbls + self.dropLines = dropLines + self.hiLowLines = hiLowLines + self.upDownBars = upDownBars + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + super(StockChart, self).__init__(**kw) diff --git a/openpyxl/chart/surface_chart.py b/openpyxl/chart/surface_chart.py new file mode 100644 index 0000000..09c7f8a --- /dev/null +++ b/openpyxl/chart/surface_chart.py @@ -0,0 +1,120 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + Bool, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedBool, +) + +from ._chart import ChartBase +from ._3d import _3DBase +from .axis import TextAxis, NumericAxis, SeriesAxis +from .shapes import GraphicalProperties +from .series import Series + + +class BandFormat(Serialisable): + + tagname = "bandFmt" + + idx = NestedInteger() + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + + __elements__ = ('idx', 'spPr') + + def __init__(self, + idx=0, + spPr=None, + ): + self.idx = idx + self.spPr = spPr + + +class BandFormatList(Serialisable): + + tagname = "bandFmts" + + bandFmt = Sequence(expected_type=BandFormat, allow_none=True) + + __elements__ = ('bandFmt',) + + def __init__(self, + bandFmt=(), + ): + self.bandFmt = bandFmt + + +class _SurfaceChartBase(ChartBase): + + wireframe = NestedBool(allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + bandFmts = Typed(expected_type=BandFormatList, allow_none=True) + + _series_type = "surface" + + __elements__ = ('wireframe', 'ser', 'bandFmts') + + def __init__(self, + wireframe=None, + ser=(), + bandFmts=None, + **kw + ): + self.wireframe = wireframe + self.ser = ser + self.bandFmts = bandFmts + super(_SurfaceChartBase, self).__init__(**kw) + + +class SurfaceChart3D(_SurfaceChartBase, _3DBase): + + tagname = "surface3DChart" + + wireframe = _SurfaceChartBase.wireframe + ser = _SurfaceChartBase.ser + bandFmts = _SurfaceChartBase.bandFmts + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis) + + __elements__ = _SurfaceChartBase.__elements__ + ('axId',) + + def __init__(self, **kw): + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() + super(SurfaceChart3D, self).__init__(**kw) + + +class SurfaceChart(SurfaceChart3D): + + tagname = "surfaceChart" + + wireframe = _SurfaceChartBase.wireframe + ser = _SurfaceChartBase.ser + bandFmts = _SurfaceChartBase.bandFmts + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = SurfaceChart3D.__elements__ + + def __init__(self, **kw): + super(SurfaceChart, self).__init__(**kw) + self.y_axis.delete = True + self.view3D.x_rotation = 90 + self.view3D.y_rotation = 0 + self.view3D.perspective = False + self.view3D.right_angle_axes = False diff --git a/openpyxl/chart/tests/__init__.py b/openpyxl/chart/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/chart/tests/conftest.py b/openpyxl/chart/tests/conftest.py new file mode 100644 index 0000000..a7e0f7b --- /dev/null +++ b/openpyxl/chart/tests/conftest.py @@ -0,0 +1,12 @@ +# Fixtures (pre-configured objects) for tests +import pytest + + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + from py._path.local import LocalPath + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + return LocalPath(DATADIR) diff --git a/openpyxl/chart/tests/data/chart1.xml b/openpyxl/chart/tests/data/chart1.xml new file mode 100644 index 0000000..abced86 --- /dev/null +++ b/openpyxl/chart/tests/data/chart1.xml @@ -0,0 +1,1198 @@ + + + + + + + + + + + + + + + + + + + + + + + Website Performance + + + + + + + + + + + + + + + + + + + onLoad!$A$2 + + + + Bob + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$2:$L$2 + + General + + + 9436.0 + + + 14389.0 + + + 12502.0 + + + 13691.0 + + + 9337.0 + + + 12471.0 + + + 13671.0 + + + 14200.0 + + + 12755.0 + + + 20353.0 + + + 12489.0 + + + + + + + + + + + + onLoad!$A$3 + + + + Alice + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$3:$L$3 + + General + + + 4382.0 + + + 4628.0 + + + 5439.0 + + + 6077.0 + + + 6849.0 + + + 5164.0 + + + 5611.0 + + + 10239.0 + + + 5391.0 + + + 4200.0 + + + 6908.0 + + + + + + + + + + + + onLoad!$A$4 + + + + Eve + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$4:$L$4 + + General + + + 9404.0 + + + 9277.0 + + + 8842.0 + + + 9193.0 + + + 12849.0 + + + 9408.0 + + + 6985.0 + + + 8376.0 + + + 6589.0 + + + 5326.0 + + + 9380.0 + + + + + + + + + + + + onLoad!$A$5 + + + + Charles + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$5:$L$5 + + General + + + 8669.0 + + + 12775.0 + + + 18881.0 + + + 13008.0 + + + 11672.0 + + + 9092.0 + + + 9427.0 + + + 11328.0 + + + 8308.0 + + + 11606.0 + + + 8685.0 + + + + + + + + + + + + onLoad!$A$6 + + + + Sam + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$6:$L$6 + + General + + + 3126.0 + + + 3451.0 + + + 2007.0 + + + 6498.0 + + + 4327.0 + + + 3840.0 + + + 3420.0 + + + 4437.0 + + + 3700.0 + + + 4167.0 + + + 4065.0 + + + + + + + + + + + + onLoad!$A$7 + + + + June + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$7:$L$7 + + General + + + 12337.0 + + + 13909.0 + + + 12156.0 + + + 11869.0 + + + 11810.0 + + + 14288.0 + + + 12644.0 + + + 12912.0 + + + 12048.0 + + + 13620.0 + + + 14090.0 + + + + + + + + + + + + onLoad!$A$8 + + + + Roger + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$8:$L$8 + + General + + + 10492.0 + + + 7541.0 + + + 6503.0 + + + 57700.0 + + + 9235.0 + + + 10934.0 + + + 12223.0 + + + 9639.0 + + + 42414.0 + + + 11192.0 + + + + + + + + + + + + onLoad!$A$9 + + + + Helen + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$9:$L$9 + + General + + + 19456.0 + + + 16247.0 + + + 18118.0 + + + 17689.0 + + + 15583.0 + + + 16092.0 + + + 16477.0 + + + 18264.0 + + + 16444.0 + + + 16336.0 + + + 15730.0 + + + + + + + + + + + + onLoad!$A$10 + + + + Dave + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$10:$L$10 + + General + + + 8456.0 + + + 9263.0 + + + 9469.0 + + + 7756.0 + + + 8322.0 + + + 10372.0 + + + 8305.0 + + + 10749.0 + + + 10780.0 + + + 16264.0 + + + 11250.0 + + + + + + + + + + + + onLoad!$A$11 + + + + Charlotte + + + + + + + + + + + + + + + onLoad!$B$1:$M$1 + + d\-mmm\-yy + + + 42186.0 + + + 42200.0 + + + 42217.0 + + + 42231.0 + + + 42248.0 + + + 42262.0 + + + 42292.0 + + + 42309.0 + + + 42323.0 + + + 42339.0 + + + 42353.0 + + + + + + + onLoad!$B$11:$L$11 + + General + + + 10411.0 + + + 9473.0 + + + 9610.0 + + + 12045.0 + + + 7571.0 + + + 9756.0 + + + 9004.0 + + + 9871.0 + + + 15782.0 + + + 20767.0 + + + 8120.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Time in seconds + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/chart/tests/data/plotarea.xml b/openpyxl/chart/tests/data/plotarea.xml new file mode 100644 index 0000000..4ca9b19 --- /dev/null +++ b/openpyxl/chart/tests/data/plotarea.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + OXM!$F$10 + + + + + + + + OXM!$B$11:$B$29 + + + + + OXM!$F$11:$F$29 + + + + + + + + + + + + + + + + + + + + + + + + + + OXM!$G$10 + + + + + + + + OXM!$B$11:$B$29 + + + + + OXM!$G$11:$G$29 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <tx> + <rich> + <a:bodyPr rot="-5400000" vert="horz"/> + <a:lstStyle/> + <a:p> + <a:pPr> + <a:defRPr/> + </a:pPr> + <a:r> + <a:rPr lang="en-US"/> + <a:t>Panel Sales</a:t> + </a:r> + </a:p> + </rich> + </tx> + <layout/> + <overlay val="0"/> + + + + + + + + + + + + + + + + + + <tx> + <rich> + <a:bodyPr rot="-5400000" vert="horz"/> + <a:lstStyle/> + <a:p> + <a:pPr> + <a:defRPr/> + </a:pPr> + <a:r> + <a:rPr lang="en-US"/> + <a:t>Revenue ($MM)</a:t> + </a:r> + </a:p> + </rich> + </tx> + <layout/> + <overlay val="0"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/chart/tests/data/sample.xlsx b/openpyxl/chart/tests/data/sample.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f4d4b3ac13fe814904272ee17669eb2acd3996a8 GIT binary patch literal 37202 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/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 + + + + + + + + + + + + + + + + + + + + + C_File_Calculations!$K$56 + + + + + + + + + + + + + + + + + + + + + + + + + C_File_Calculations!$C$58:$C$64 + + + + + C_File_Calculations!$K$58:$K$64 + + + + + + + + + + + + + + + + + + + + + + + + <tx> + <rich> + <a:bodyPr/> + <a:lstStyle/> + <a:p> + <a:pPr> + <a:defRPr/> + </a:pPr> + <a:r> + <a:rPr lang="en-CA"/> + <a:t>Concentration (mg/L)</a:t> + </a:r> + </a:p> + </rich> + </tx> + <layout/> + <overlay val="0"/> + + + + + + + + + + + + + + + + + + + <tx> + <rich> + <a:bodyPr rot="-5400000" vert="horz"/> + <a:lstStyle/> + <a:p> + <a:pPr> + <a:defRPr/> + </a:pPr> + <a:r> + <a:rPr lang="en-CA" baseline="0"/> + <a:t> Absorbance (AU)</a:t> + </a:r> + <a:endParaRPr lang="en-CA"/> + </a:p> + </rich> + </tx> + <layout/> + <overlay val="0"/> + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/chart/tests/test_area_chart.py b/openpyxl/chart/tests/test_area_chart.py new file mode 100644 index 0000000..2385043 --- /dev/null +++ b/openpyxl/chart/tests/test_area_chart.py @@ -0,0 +1,158 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml +from .. import Series + + +@pytest.fixture +def AreaChart(): + from ..area_chart import AreaChart + return AreaChart + + +class TestAreaChart: + + def test_ctor(self, AreaChart): + chart = AreaChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, AreaChart): + src = """ + + + + + + + """ + node = fromstring(src) + chart = AreaChart.from_tree(node) + assert chart == AreaChart(grouping="percentStacked", varyColors=True) + + + def test_write(self, AreaChart): + s1 = Series(values="Sheet1!$A$1:$A$12") + s2 = Series(values="Sheet1!$B$1:$B$12") + chart = AreaChart(ser=[s1, s2]) + xml = tostring(chart._write()) + expected = """ + + + + + + + + + + + + + + + + Sheet1!$A$1:$A$12 + + + + + + + + + + + + + + Sheet1!$B$1:$B$12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def AreaChart3D(): + from ..area_chart import AreaChart3D + return AreaChart3D + + +class TestAreaChart3D: + + def test_ctor(self, AreaChart3D): + chart = AreaChart3D() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, AreaChart3D): + src = """ + + + + + + + """ + node = fromstring(src) + chart = AreaChart3D.from_tree(node) + assert chart == AreaChart3D(gapDepth=150) diff --git a/openpyxl/chart/tests/test_axis.py b/openpyxl/chart/tests/test_axis.py new file mode 100644 index 0000000..e6e040d --- /dev/null +++ b/openpyxl/chart/tests/test_axis.py @@ -0,0 +1,364 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def Scaling(): + from ..axis import Scaling + return Scaling + + +class TestScale: + + + def test_ctor(self, Scaling): + + scale = Scaling() + xml = tostring(scale.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Scaling): + + xml = """ + + + + + """ + node = fromstring(xml) + scale = Scaling.from_tree(node) + assert scale == Scaling(logBase=10) + + +@pytest.fixture +def _BaseAxis(): + from ..axis import _BaseAxis + return _BaseAxis + + +class TestAxis: + + def test_ctor(self, _BaseAxis, Scaling): + axis = _BaseAxis(axId=10, crossAx=100) + xml = tostring(axis.to_tree(tagname="baseAxis")) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + +@pytest.fixture +def TextAxis(): + from ..axis import TextAxis + return TextAxis + + +class TestTextAxis: + + def test_ctor(self, TextAxis): + axis = TextAxis(axId=10, crossAx=100) + xml = tostring(axis.to_tree()) + expected = """ + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def from_xml(self, TextAxis): + src = """ + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + axis = CatAx.from_tree(node) + assert axis.scaling.orientation == "minMax" + assert axis.auto is True + assert axis.majorTickMark == "out" + assert axis.minorTickMark is None + + +@pytest.fixture +def NumericAxis(): + from ..axis import NumericAxis + return NumericAxis + + +class TestValAx: + + def test_ctor(self, NumericAxis): + axis = NumericAxis(axId=100, crossAx=10) + xml = tostring(axis.to_tree()) + expected = """ + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, NumericAxis): + src = """ + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + axis = NumericAxis.from_tree(node) + assert axis.delete is False + assert axis.crossAx == 2065276984 + assert axis.crossBetween == "between" + assert axis.scaling.logBase == 10 + + +@pytest.fixture +def DateAxis(): + from ..axis import DateAxis + return DateAxis + + +class TestDateAx: + + + def test_ctor(self, DateAxis): + axis = DateAxis(axId=500, crossAx=10) + xml = tostring(axis.to_tree()) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DateAxis): + from openpyxl.chart.data_source import NumFmt + + src = """ + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + axis = DateAxis.from_tree(node) + assert axis == DateAxis(axId=20, crossAx=10, axPos="b", delete=False, + numFmt=NumFmt("d-mmm", True), majorTickMark="out", + crosses="autoZero", tickLblPos="nextTo", auto=True, lblOffset=100, + baseTimeUnit="months") + + +@pytest.fixture +def SeriesAxis(): + from ..axis import SeriesAxis + return SeriesAxis + + +class TestSeriesAxis: + + def test_ctor(self, SeriesAxis): + axis = SeriesAxis(axId=1000, crossAx=10) + xml = tostring(axis.to_tree()) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SeriesAxis): + src = """ + + + + + + + + + """ + node = fromstring(src) + axis = SeriesAxis.from_tree(node) + assert axis == SeriesAxis() + + +@pytest.fixture +def DisplayUnitsLabel(): + from ..axis import DisplayUnitsLabel + return DisplayUnitsLabel + + +class TestDispUnitsLabel: + + def test_ctor(self, DisplayUnitsLabel): + axis = DisplayUnitsLabel() + xml = tostring(axis.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DisplayUnitsLabel): + src = """ + + """ + node = fromstring(src) + axis = DisplayUnitsLabel.from_tree(node) + assert axis == DisplayUnitsLabel() + + +@pytest.fixture +def DisplayUnitsLabelList(): + from ..axis import DisplayUnitsLabelList + return DisplayUnitsLabelList + + +class TestDisplayUnitList: + + def test_ctor(self, DisplayUnitsLabelList): + axis = DisplayUnitsLabelList() + xml = tostring(axis.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DisplayUnitsLabelList): + src = """ + + """ + node = fromstring(src) + axis = DisplayUnitsLabelList.from_tree(node) + assert axis == DisplayUnitsLabelList() + + +@pytest.fixture +def ChartLines(): + from ..axis import ChartLines + return ChartLines + + +class TestChartLines: + + def test_ctor(self, ChartLines): + axis = ChartLines() + xml = tostring(axis.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ChartLines): + src = """ + + """ + node = fromstring(src) + axis = ChartLines.from_tree(node) + assert axis == ChartLines() diff --git a/openpyxl/chart/tests/test_bar_chart.py b/openpyxl/chart/tests/test_bar_chart.py new file mode 100644 index 0000000..9abab14 --- /dev/null +++ b/openpyxl/chart/tests/test_bar_chart.py @@ -0,0 +1,188 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def BarChart(): + from ..bar_chart import BarChart + return BarChart + + +class TestBarChart: + + def test_ctor(self, BarChart): + bc = BarChart() + xml = tostring(bc.to_tree()) + expected = """ + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, BarChart): + src = """ + + + + + + + + + """ + node = fromstring(src) + bc = BarChart.from_tree(node) + assert bc == BarChart(varyColors=False,axId=(10, 100)) + assert bc.axId == [10, 100] + assert bc.grouping == "clustered" + + + def test_write(self, BarChart): + chart = BarChart() + xml = tostring(chart._write()) + expected = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_series(self, BarChart): + from .. import Series + s1 = Series(values="Sheet1!$A$1:$A$10") + s2 = Series(values="Sheet1!$B$1:$B$10") + bc = BarChart(ser=[s1, s2]) + xml = tostring(bc.to_tree()) + expected = """ + + + + + + + + + + + + + + Sheet1!$A$1:$A$10 + + + + + + + + + + + + + + Sheet1!$B$1:$B$10 + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def BarChart3D(): + from ..bar_chart import BarChart3D + return BarChart3D + + +class TestBarChart3D: + + def test_ctor(self, BarChart3D): + bc = BarChart3D() + xml = tostring(bc.to_tree()) + expected = """ + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, BarChart3D): + src = """ + + + + + + + + + + """ + node = fromstring(src) + bc = BarChart3D.from_tree(node) + assert bc.axId == [10, 100, 0] diff --git a/openpyxl/chart/tests/test_bubble_chart.py b/openpyxl/chart/tests/test_bubble_chart.py new file mode 100644 index 0000000..6cf3360 --- /dev/null +++ b/openpyxl/chart/tests/test_bubble_chart.py @@ -0,0 +1,39 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def BubbleChart(): + from ..bubble_chart import BubbleChart + return BubbleChart + + +class TestBubbleChart: + + def test_ctor(self, BubbleChart): + bubble_chart = BubbleChart() + xml = tostring(bubble_chart.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, BubbleChart): + src = """ + + + + + """ + node = fromstring(src) + bubble_chart = BubbleChart.from_tree(node) + assert bubble_chart.axId == [10, 20] diff --git a/openpyxl/chart/tests/test_chart.py b/openpyxl/chart/tests/test_chart.py new file mode 100644 index 0000000..66d9730 --- /dev/null +++ b/openpyxl/chart/tests/test_chart.py @@ -0,0 +1,134 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring +from openpyxl.tests.helper import compare_xml +from ..chartspace import PlotArea + +from ..series import Series + +@pytest.fixture +def ChartBase(): + from .._chart import ChartBase + return ChartBase + + +class TestChartBase: + + def test_ctor(self, ChartBase): + chart = ChartBase() + with pytest.raises(NotImplementedError): + xml = tostring(chart.to_tree()) + + + def test_iadd(self, ChartBase): + chart1 = ChartBase() + chart2 = ChartBase() + chart1 += chart2 + assert chart1._charts == [chart1, chart2] + + + def test_invalid_add(self, ChartBase): + chart = ChartBase() + s = Series() + with pytest.raises(TypeError): + chart += s + + + def test_set_catgories(self, ChartBase): + from ..series import Series + s1 = Series() + s1.__elements__ = ('cat',) + chart = ChartBase() + chart.ser = [s1] + chart.set_categories("Sheet!A1:A4") + xml = tostring(s1.to_tree()) + expected = """ + + + + Sheet!$A$1:$A$4 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_add_data_cols(self, ChartBase): + chart = ChartBase() + chart.ser = [] + chart.add_data("Sheet!A1:E4") + assert len(chart.ser) == 5 + assert chart.ser[0].val.numRef.f == "Sheet!$A$1:$A$4" + assert chart.ser[-1].val.numRef.f == "Sheet!$E$1:$E$4" + + + def test_add_data_rows(self, ChartBase): + chart = ChartBase() + chart.ser = [] + chart.add_data("Sheet!A1:E4", from_rows=True) + assert len(chart.ser) == 4 + assert chart.ser[0].val.numRef.f == "Sheet!$A$1:$E$1" + assert chart.ser[-1].val.numRef.f == "Sheet!$A$4:$E$4" + + + def test_hash_function(self, ChartBase): + chart = ChartBase() + assert hash(chart) == hash(id(chart)) + + + def test_path(self, ChartBase): + chart = ChartBase() + assert chart.path == "/xl/charts/chart1.xml" + + + def test_plot_area(self, ChartBase): + chart = ChartBase() + assert type(chart.plot_area) is PlotArea + + + def test_save_twice(self, ChartBase): + ChartBase.tagname = "DummyChart" + chart = ChartBase() + chart._write() + chart._write() + area = chart.plot_area + assert len(area._charts) == 1 + assert area._axes == [] + + + def test_axIds(self, ChartBase): + chart = ChartBase() + assert chart.axId == [] + + + def test_plot_visible_cells(self, ChartBase): + chart = ChartBase() + assert chart.visible_cells_only is True + + + def test_plot_visible_cells(self, ChartBase): + chart = ChartBase() + chart.visible_cells_only = False + tree = chart._write() + expected = """ + + + + + + + + + + + + + """ + xml = tostring(tree) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chart/tests/test_chartspace.py b/openpyxl/chart/tests/test_chartspace.py new file mode 100644 index 0000000..aa6bbba --- /dev/null +++ b/openpyxl/chart/tests/test_chartspace.py @@ -0,0 +1,289 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def ChartContainer(): + from ..chartspace import ChartContainer + return ChartContainer + + +class TestChartContainer: + + def test_ctor(self, ChartContainer): + container = ChartContainer() + xml = tostring(container.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ChartContainer): + src = """ + + + + + """ + node = fromstring(src) + container = ChartContainer.from_tree(node) + assert container == ChartContainer() + + +@pytest.fixture +def Surface(): + from .._3d import Surface + return Surface + + +class TestSurface: + + def test_ctor(self, Surface): + surface = Surface(thickness=0) + xml = tostring(surface.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Surface): + src = """ + + + + """ + node = fromstring(src) + surface = Surface.from_tree(node) + assert surface == Surface(thickness=0) + + +@pytest.fixture +def View3D(): + from .._3d import View3D + return View3D + + +class TestView3D: + + def test_ctor(self, View3D): + view = View3D() + xml = tostring(view.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, View3D): + src = """ + + + + + + + """ + node = fromstring(src) + view = View3D.from_tree(node) + assert view == View3D(rotX=15, rotY=20, rAngAx=False, perspective=30) + + +@pytest.fixture +def PivotFormat(): + from ..chartspace import PivotFormat + return PivotFormat + + +class TestPivotFormat: + + def test_ctor(self, PivotFormat): + fmt = PivotFormat() + xml = tostring(fmt.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PivotFormat): + src = """ + + + + """ + node = fromstring(src) + fmt = PivotFormat.from_tree(node) + assert fmt == PivotFormat() + + +@pytest.fixture +def PivotFormatList(): + from ..chartspace import PivotFormatList + return PivotFormatList + + +class TestPivotFormatList: + + def test_ctor(self, PivotFormatList): + fmt = PivotFormatList() + xml = tostring(fmt.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PivotFormatList): + src = """ + + """ + node = fromstring(src) + fmt = PivotFormatList.from_tree(node) + assert fmt == PivotFormatList() + + +@pytest.fixture +def Protection(): + from ..chartspace import Protection + return Protection + + +class TestProtection: + + def test_ctor(self, Protection): + prot = Protection() + xml = tostring(prot.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Protection): + src = """ + + + + """ + node = fromstring(src) + prot = Protection.from_tree(node) + assert prot == Protection(chartObject=True) + + +@pytest.fixture +def PivotSource(): + from ..chartspace import PivotSource + return PivotSource + + +class TestPivotSource: + + def test_ctor(self, PivotSource): + src = PivotSource(name="pivot source", fmtId=1) + xml = tostring(src.to_tree()) + expected = """ + + pivot source + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PivotSource): + src = """ + + pivot source + + + """ + node = fromstring(src) + src = PivotSource.from_tree(node) + assert src == PivotSource(name="pivot source", fmtId=1) + + +@pytest.fixture +def ExternalData(): + from ..chartspace import ExternalData + return ExternalData + + +class TestExternalData: + + def test_ctor(self, ExternalData): + data = ExternalData(id='rId1') + xml = tostring(data.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ExternalData): + src = """ + + """ + node = fromstring(src) + data = ExternalData.from_tree(node) + assert data == ExternalData(id="rId1") + + +@pytest.fixture +def ChartSpace(): + from ..chartspace import ChartSpace + return ChartSpace + + +class TestChartSpace: + + def test_ctor(self, ChartSpace, ChartContainer): + cs = ChartSpace(chart=ChartContainer()) + xml = tostring(cs.to_tree()) + expected = """ + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ChartSpace, ChartContainer): + src = """ + + + + """ + node = fromstring(src) + cs = ChartSpace.from_tree(node) + assert cs == ChartSpace(chart=ChartContainer()) diff --git a/openpyxl/chart/tests/test_data_source.py b/openpyxl/chart/tests/test_data_source.py new file mode 100644 index 0000000..48e710c --- /dev/null +++ b/openpyxl/chart/tests/test_data_source.py @@ -0,0 +1,154 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def NumRef(): + from ..data_source import NumRef + return NumRef + + +class TestNumRef: + + + def test_from_xml(self, NumRef): + src = """ + + Blatt1!$A$1:$A$12 + + """ + node = fromstring(src) + num = NumRef.from_tree(node) + assert num.ref == "Blatt1!$A$1:$A$12" + + + def test_to_xml(self, NumRef): + num = NumRef(f="Blatt1!$A$1:$A$12") + xml = tostring(num.to_tree("numRef")) + expected = """ + + Blatt1!$A$1:$A$12 + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree_degree_sign(self, NumRef): + + src = b""" + + Hoja1!$A$2:$B$2 + + 0\xc2\xb0 + + + 3 + + + 14 + + + + """ + node = fromstring(src) + numRef = NumRef.from_tree(node) + assert numRef.numCache.formatCode == u"0\xb0" + + +@pytest.fixture +def StrRef(): + from ..data_source import StrRef + return StrRef + + +class TestStrRef: + + def test_ctor(self, StrRef): + data_source = StrRef(f="Sheet1!A1") + xml = tostring(data_source.to_tree()) + expected = """ + + Sheet1!A1 + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, StrRef): + src = """ + + 'Render Start'!$A$2 + + """ + node = fromstring(src) + data_source = StrRef.from_tree(node) + assert data_source == StrRef(f="'Render Start'!$A$2") + + +@pytest.fixture +def StrVal(): + from ..data_source import StrVal + return StrVal + + +class TestStrVal: + + def test_ctor(self, StrVal): + val = StrVal(v="something") + xml = tostring(val.to_tree()) + expected = """ + + something + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, StrVal): + src = """ + + else + + """ + node = fromstring(src) + val = StrVal.from_tree(node) + assert val == StrVal(idx=4, v="else") + + +@pytest.fixture +def StrData(): + from ..data_source import StrData + return StrData + + +class TestStrData: + + def test_ctor(self, StrData): + data_source = StrData(ptCount=1) + xml = tostring(data_source.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, StrData): + src = """ + + + + """ + node = fromstring(src) + data_source = StrData.from_tree(node) + assert data_source == StrData(ptCount=4) diff --git a/openpyxl/chart/tests/test_error_bar.py b/openpyxl/chart/tests/test_error_bar.py new file mode 100644 index 0000000..920876b --- /dev/null +++ b/openpyxl/chart/tests/test_error_bar.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def ErrorBars(): + from ..error_bar import ErrorBars + return ErrorBars + + +class TestErrorBar: + + def test_ctor(self, ErrorBars): + bar = ErrorBars() + xml = tostring(bar.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ErrorBars): + src = """ + + + + + + + + """ + node = fromstring(src) + bar = ErrorBars.from_tree(node) + assert bar == ErrorBars(noEndCap=True, errDir='x', val=10) diff --git a/openpyxl/chart/tests/test_label.py b/openpyxl/chart/tests/test_label.py new file mode 100644 index 0000000..5ad4a36 --- /dev/null +++ b/openpyxl/chart/tests/test_label.py @@ -0,0 +1,80 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def DataLabelList(): + from ..label import DataLabelList + return DataLabelList + + +class TestDataLabeList: + + def test_ctor(self, DataLabelList): + labels = DataLabelList(numFmt="0.0%") + xml = tostring(labels.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DataLabelList): + src = """ + + + + + + + + + """ + node = fromstring(src) + dl = DataLabelList.from_tree(node) + + assert dl.showLegendKey is False + assert dl.showVal is False + assert dl.showCatName is False + assert dl.showSerName is False + assert dl.showPercent is False + assert dl.showBubbleSize is False + + +@pytest.fixture +def DataLabel(): + from ..label import DataLabel + return DataLabel + + +class TestDataLabel: + + def test_ctor(self, DataLabel): + label = DataLabel() + xml = tostring(label.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DataLabel): + src = """ + + + + """ + node = fromstring(src) + label = DataLabel.from_tree(node) + assert label == DataLabel(idx=6) diff --git a/openpyxl/chart/tests/test_layout.py b/openpyxl/chart/tests/test_layout.py new file mode 100644 index 0000000..2d44926 --- /dev/null +++ b/openpyxl/chart/tests/test_layout.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def ManualLayout(): + from ..layout import ManualLayout + return ManualLayout + + +class TestManualLayout: + + def test_ctor(self, ManualLayout): + layout = ManualLayout( + layoutTarget="inner", + xMode="edge", + yMode="factor", + wMode="factor", + hMode="edge", + x=10, + y=50, + w=4, + h=100 + ) + xml = tostring(layout.to_tree()) + expected = """ + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ManualLayout): + src = """ + + + + + + + + + + + + """ + node = fromstring(src) + layout = ManualLayout.from_tree(node) + assert layout == ManualLayout(layoutTarget="inner", xMode="edge", + yMode="factor", wMode="factor", hMode="edge", x=10, y=50, w=4, h=100 + ) + + +class TestLayout: + + def test_ctor(self): + from ..layout import Layout + layout = Layout() + xml = tostring(layout.to_tree()) + diff = compare_xml(xml, "") + assert diff is None, diff diff --git a/openpyxl/chart/tests/test_legend.py b/openpyxl/chart/tests/test_legend.py new file mode 100644 index 0000000..8521f5b --- /dev/null +++ b/openpyxl/chart/tests/test_legend.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import + +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def Legend(): + from ..legend import Legend + return Legend + + +class TestLegend: + + def test_ctor(self, Legend): + legend = Legend() + xml = tostring(legend.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Legend): + src = """ + + + + """ + node = fromstring(src) + legend = Legend.from_tree(node) + assert legend == Legend() + + +@pytest.fixture +def LegendEntry(): + from ..legend import LegendEntry + return LegendEntry + + +class TestLegendEntry: + + def test_ctor(self, LegendEntry): + legend = LegendEntry(idx=0, delete=True) + xml = tostring(legend.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LegendEntry): + src = """ + + + + """ + node = fromstring(src) + legend = LegendEntry.from_tree(node) + assert legend == LegendEntry() diff --git a/openpyxl/chart/tests/test_line_chart.py b/openpyxl/chart/tests/test_line_chart.py new file mode 100644 index 0000000..c5f4f23 --- /dev/null +++ b/openpyxl/chart/tests/test_line_chart.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def LineChart(): + from ..line_chart import LineChart + return LineChart + + +class TestLineChart: + + def test_ctor(self, LineChart): + chart = LineChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LineChart): + src = """ + + + + + + """ + node = fromstring(src) + chart = LineChart.from_tree(node) + assert chart.axId == [10, 100] + assert chart.grouping == "stacked" + + + def test_axes(self, LineChart): + chart = LineChart() + assert set(chart._axes) == set([10, 100]) + + +@pytest.fixture +def LineChart3D(): + from ..line_chart import LineChart3D + return LineChart3D + + +class TestLineChart3D: + + def test_ctor(self, LineChart3D): + line_chart = LineChart3D() + xml = tostring(line_chart.to_tree()) + expected = """ + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LineChart3D): + src = """ + + + + """ + node = fromstring(src) + line_chart = LineChart3D.from_tree(node) + assert line_chart == LineChart3D() diff --git a/openpyxl/chart/tests/test_marker.py b/openpyxl/chart/tests/test_marker.py new file mode 100644 index 0000000..10cfd35 --- /dev/null +++ b/openpyxl/chart/tests/test_marker.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def Marker(): + from ..marker import Marker + return Marker + + +class TestMarker: + + def test_ctor(self, Marker): + marker = Marker(symbol=None, size=5) + xml = tostring(marker.to_tree()) + expected = """ + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Marker): + src = """ + + + + + """ + node = fromstring(src) + marker = Marker.from_tree(node) + assert marker == Marker(symbol="square", size=5) + + +@pytest.fixture +def DataPoint(): + from ..marker import DataPoint + return DataPoint + + +class TestDataPoint: + + def test_ctor(self, DataPoint): + dp = DataPoint(idx=9) + xml = tostring(dp.to_tree()) + expected = """ + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DataPoint): + src = """ + + + + + + + + + """ + node = fromstring(src) + dp = DataPoint.from_tree(node) + assert dp.idx == 9 + assert dp.bubble3D is False diff --git a/openpyxl/chart/tests/test_picture.py b/openpyxl/chart/tests/test_picture.py new file mode 100644 index 0000000..4e3919f --- /dev/null +++ b/openpyxl/chart/tests/test_picture.py @@ -0,0 +1,33 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def PictureOptions(): + from ..picture import PictureOptions + return PictureOptions + + +class TestPictureOptions: + + def test_ctor(self, PictureOptions): + picture = PictureOptions() + xml = tostring(picture.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PictureOptions): + src = """ + + """ + node = fromstring(src) + picture = PictureOptions.from_tree(node) + assert picture == PictureOptions() diff --git a/openpyxl/chart/tests/test_pie_chart.py b/openpyxl/chart/tests/test_pie_chart.py new file mode 100644 index 0000000..ecaf4a1 --- /dev/null +++ b/openpyxl/chart/tests/test_pie_chart.py @@ -0,0 +1,183 @@ +from __future__ import absolute_import + +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def PieChart(): + from ..pie_chart import PieChart + return PieChart + + +class TestPieChart: + + def test_ctor(self, PieChart): + chart = PieChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PieChart): + src = """ + + + + + + """ + node = fromstring(src) + chart = PieChart.from_tree(node) + assert dict(chart) == {} + assert chart.varyColors is True + assert chart.firstSliceAng == 60 + + +@pytest.fixture +def PieChart3D(): + from ..pie_chart import PieChart3D + return PieChart3D + + +class TestPieChart3D: + + def test_ctor(self, PieChart3D): + chart = PieChart3D() + xml = tostring(chart.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def DoughnutChart(): + from ..pie_chart import DoughnutChart + return DoughnutChart + + +class TestDoughnutChart: + + def test_ctor(self, DoughnutChart): + chart = DoughnutChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DoughnutChart): + src = """ + + + + + """ + node = fromstring(src) + chart = DoughnutChart.from_tree(node) + assert dict(chart) == {} + assert chart.firstSliceAng == 0 + assert chart.holeSize == 50 + + +@pytest.fixture +def ProjectedPieChart(): + from ..pie_chart import ProjectedPieChart + return ProjectedPieChart + + +class TestProjectedPieChart: + + def test_ctor(self, ProjectedPieChart): + chart = ProjectedPieChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ProjectedPieChart): + src = """ + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + chart = ProjectedPieChart.from_tree(node) + assert dict(chart) == {} + assert chart.gapWidth == 150 + assert chart.secondPieSize == 75 + + +@pytest.fixture +def CustomSplit(): + from ..pie_chart import CustomSplit + return CustomSplit + + +class TestCustomSplit: + + def test_ctor(self, CustomSplit): + pie_chart = CustomSplit([1, 2, 3]) + xml = tostring(pie_chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, CustomSplit): + src = """ + + + + + """ + node = fromstring(src) + pie_chart = CustomSplit.from_tree(node) + assert pie_chart == CustomSplit([1, 2]) diff --git a/openpyxl/chart/tests/test_plotarea.py b/openpyxl/chart/tests/test_plotarea.py new file mode 100644 index 0000000..de3f33c --- /dev/null +++ b/openpyxl/chart/tests/test_plotarea.py @@ -0,0 +1,148 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +from ..line_chart import LineChart +from ..bar_chart import BarChart + + +@pytest.fixture +def PlotArea(): + from ..plotarea import PlotArea + return PlotArea + + +class TestPlotArea: + + def test_ctor(self, PlotArea): + plot = PlotArea() + xml = tostring(plot.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PlotArea): + src = """ + + """ + node = fromstring(src) + plot = PlotArea.from_tree(node) + assert plot == PlotArea() + + + def test_multi_chart(self, PlotArea): + plot = PlotArea() + plot.lineChart = LineChart() + plot.barChart = BarChart() + plot.lineChart = LineChart() + expected = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + xml = tostring(plot.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_read_multi_chart(self, PlotArea, datadir): + datadir.chdir() + with open("plotarea.xml", "rb") as src: + tree = fromstring(src.read()) + plot = PlotArea.from_tree(tree) + assert len(plot._charts) == 2 + + + def test_read_multi_axes(self, PlotArea, datadir): + datadir.chdir() + with open("plotarea.xml", "rb") as src: + tree = fromstring(src.read()) + plot = PlotArea.from_tree(tree) + assert [ax.tagname for ax in plot._axes] == ["catAx", "valAx", "valAx", "catAx"] + assert plot._charts[0].x_axis == plot._axes[0] + assert plot._charts[0].y_axis == plot._axes[1] + assert plot._charts[1].x_axis == plot._axes[3] + assert plot._charts[1].y_axis == plot._axes[2] + + + def test_read_scatter_chart(self, PlotArea, datadir): + datadir.chdir() + with open("scatterchart_plot_area.xml", "rb") as src: + tree = fromstring(src.read()) + plot = PlotArea.from_tree(tree) + chart = plot._charts[0] + assert chart.axId == [211326240, 211330000] + assert chart.x_axis.axId == 211326240 + assert chart.y_axis.axId == 211330000 + + +@pytest.fixture +def DataTable(): + from ..plotarea import DataTable + return DataTable + + +class TestDataTable: + + def test_ctor(self, DataTable): + table = DataTable() + xml = tostring(table.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DataTable): + src = """ + + """ + node = fromstring(src) + table = DataTable.from_tree(node) + assert table == DataTable() diff --git a/openpyxl/chart/tests/test_print.py b/openpyxl/chart/tests/test_print.py new file mode 100644 index 0000000..e2c1b50 --- /dev/null +++ b/openpyxl/chart/tests/test_print.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def PrintSettings(): + from ..print_settings import PrintSettings + return PrintSettings + + +class TestPrintSettings: + + def test_ctor(self, PrintSettings): + chartspace = PrintSettings() + xml = tostring(chartspace.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PrintSettings): + src = """ + + """ + node = fromstring(src) + chartspace = PrintSettings.from_tree(node) + assert chartspace == PrintSettings() + + +@pytest.fixture +def PageMargins(): + from ..print_settings import PageMargins + return PageMargins + + +class TestPageMargins: + + def test_ctor(self, PageMargins): + pm = PageMargins() + xml = tostring(pm.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PageMargins): + src = """ + + """ + node = fromstring(src) + pm = PageMargins.from_tree(node) + assert pm == PageMargins() diff --git a/openpyxl/chart/tests/test_radar_chart.py b/openpyxl/chart/tests/test_radar_chart.py new file mode 100644 index 0000000..825f91e --- /dev/null +++ b/openpyxl/chart/tests/test_radar_chart.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def RadarChart(): + from ..radar_chart import RadarChart + return RadarChart + + +class TestRadarChart: + + def test_ctor(self, RadarChart): + chart = RadarChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RadarChart): + src = """ + + + + + + + """ + node = fromstring(src) + chart = RadarChart.from_tree(node) + assert dict(chart) == {} + assert chart.type == "marker" + assert chart.axId == [2107159976, 2107207992] diff --git a/openpyxl/chart/tests/test_reader.py b/openpyxl/chart/tests/test_reader.py new file mode 100644 index 0000000..bb1d6f4 --- /dev/null +++ b/openpyxl/chart/tests/test_reader.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from zipfile import ZipFile + +from openpyxl.xml.functions import fromstring + +from .. line_chart import LineChart +from .. axis import NumericAxis, DateAxis +from .. chartspace import ChartSpace + + +def test_read(datadir): + datadir.chdir() + from .. reader import read_chart + + with open("chart1.xml") as src: + xml = src.read() + tree = fromstring(xml) + cs = ChartSpace.from_tree(tree) + chart = read_chart(cs) + + assert isinstance(chart, LineChart) + assert chart.title.tx.rich.p[0].r[0].t == "Website Performance" + + assert isinstance(chart.y_axis, NumericAxis) + assert chart.y_axis.title.tx.rich.p[0].r[0].t == "Time in seconds" + + assert isinstance(chart.x_axis, DateAxis) + assert chart.x_axis.title is None + + assert len(chart.series) == 10 + + +def test_read_drawing(datadir): + datadir.chdir() + + archive = ZipFile("sample.xlsx") + path = "xl/drawings/drawing1.xml" + + from ..reader import find_charts + charts = find_charts(archive, path) + assert len(charts) == 6 diff --git a/openpyxl/chart/tests/test_reference.py b/openpyxl/chart/tests/test_reference.py new file mode 100644 index 0000000..05ecd95 --- /dev/null +++ b/openpyxl/chart/tests/test_reference.py @@ -0,0 +1,102 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.compat import unicode + + +@pytest.fixture +def Reference(): + from ..reference import Reference + return Reference + + +@pytest.fixture +def Worksheet(): + + class DummyWorksheet: + + def __init__(self, title="dummy"): + self.title = title + + return DummyWorksheet + + +class TestReference: + + def test_ctor(self, Reference, Worksheet): + ref = Reference( + worksheet=Worksheet(), + min_col=1, + min_row=1, + max_col=10, + max_row=12 + ) + assert str(ref) == "dummy!$A$1:$J$12" + + + def test_single_cell(self, Reference, Worksheet): + ref = Reference(Worksheet(), min_col=1, min_row=1) + assert str(ref) == "dummy!$A$1" + + + def test_from_string(self, Reference): + ref = Reference(range_string="Sheet1!$A$1:$A$10") + assert (ref.min_col, ref.min_row, ref.max_col, ref.max_row) == (1,1, 1,10) + assert str(ref) == "Sheet1!$A$1:$A$10" + + + def test_cols(self, Reference): + ref = Reference(range_string="Sheet!A1:B2") + assert list(ref.cols) == [ + ('A1', 'A2'), + ('B1', 'B2') + ] + + + def test_rows(self, Reference): + ref = Reference(range_string="Sheet!A1:B2") + assert list(ref.rows) == [ + ('A1', 'B1'), + ('A2', 'B2') + ] + + + @pytest.mark.parametrize("range_string, cells", + [ + ("Sheet!A1:A5", ['A1', 'A2', 'A3', 'A4', 'A5']), + ("Sheet!A1:E1", ['A1', 'B1', 'C1', 'D1', 'E1']), + ] + ) + def test_cells(self, Reference, range_string, cells): + ref = Reference(range_string=range_string) + assert list(ref.cells) == cells + + + @pytest.mark.parametrize("range_string, cell, min_col, min_row", + [ + ("Sheet1!A1:A10", 'A1', 1, 2), + ("Sheet!A1:E1", 'A1', 2, 1), + ] + ) + def test_pop(self, Reference, range_string, cell, min_col, min_row): + ref = Reference(range_string=range_string) + assert cell == ref.pop() + assert (ref.min_col, ref.min_row) == (min_col, min_row) + + + @pytest.mark.parametrize("range_string, length", + [ + ("Sheet1!A1:A10", 10), + ("Sheet!A1:E1", 5), + ] + ) + def test_length(self, Reference, range_string, length): + ref = Reference(range_string=range_string) + assert len(ref) == length + + + def test_repr(self, Reference): + ref = Reference(range_string=b'D\xc3\xbcsseldorf!A1:A10'.decode("utf8")) + assert unicode(ref) == b'D\xc3\xbcsseldorf!$A$1:$A$10'.decode("utf8") diff --git a/openpyxl/chart/tests/test_scatter_chart.py b/openpyxl/chart/tests/test_scatter_chart.py new file mode 100644 index 0000000..652c102 --- /dev/null +++ b/openpyxl/chart/tests/test_scatter_chart.py @@ -0,0 +1,39 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def ScatterChart(): + from ..scatter_chart import ScatterChart + return ScatterChart + + +class TestScatterChart: + + def test_ctor(self, ScatterChart): + chart = ScatterChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ScatterChart): + src = """ + + + + + """ + node = fromstring(src) + chart = ScatterChart.from_tree(node) + assert chart.axId == [10, 20] diff --git a/openpyxl/chart/tests/test_series.py b/openpyxl/chart/tests/test_series.py new file mode 100644 index 0000000..9d25b13 --- /dev/null +++ b/openpyxl/chart/tests/test_series.py @@ -0,0 +1,344 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.tests.helper import compare_xml + + +class TestBarSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.val.numRef.ref == 'Blatt1!$A$1:$A$12' + + ser.__elements__ = attribute_mapping['bar'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + +class TestAreaSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.val.numRef.ref == 'Blatt1!$A$1:$A$12' + + ser.__elements__ = attribute_mapping['area'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + +class TestBubbleSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + + Blatt1!$B$1:$B$12 + + + + + General + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + 1.1 + + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.xVal.numRef.ref == 'Blatt1!$A$1:$A$12' + assert ser.yVal.numRef.ref == 'Blatt1!$B$1:$B$12' + assert ser.bubbleSize.numLit.ptCount == 12 + assert ser.bubbleSize.numLit.pt[0].v == 1.1 + + ser.__elements__ = attribute_mapping['bubble'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + +class TestPieSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.val.numRef.ref == 'Blatt1!$A$1:$A$12' + + ser.__elements__ = attribute_mapping['pie'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + + +class TestRadarSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.val.numRef.ref == 'Blatt1!$A$1:$A$12' + + ser.__elements__ = attribute_mapping['radar'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + +class TestScatterSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + + Blatt1!$B$1:$B$12 + + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.xVal.numRef.ref == 'Blatt1!$A$1:$A$12' + assert ser.yVal.numRef.ref == 'Blatt1!$B$1:$B$12' + + ser.__elements__ = attribute_mapping['scatter'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + +class TestSurfaceSer: + + def test_from_tree(self): + from ..series import Series, attribute_mapping + + src = """ + + + + + + + + + + + Blatt1!$A$1:$A$12 + + + + """ + node = fromstring(src) + ser = Series.from_tree(node) + assert ser.idx == 0 + assert ser.order == 0 + assert ser.val.numRef.ref == 'Blatt1!$A$1:$A$12' + + ser.__elements__ = attribute_mapping['surface'] + xml = tostring(ser.to_tree()) + diff = compare_xml(xml, src) + assert diff is None, diff + + +@pytest.fixture +def SeriesLabel(): + from ..series import SeriesLabel + return SeriesLabel + + +class TestSeriesLabel: + + def test_ctor(self, SeriesLabel): + label = SeriesLabel(v="Label") + xml = tostring(label.to_tree()) + expected = """ + + Label + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SeriesLabel): + src = """ + + Label + + """ + node = fromstring(src) + label = SeriesLabel.from_tree(node) + assert label == SeriesLabel(v="Label") diff --git a/openpyxl/chart/tests/test_series_factory.py b/openpyxl/chart/tests/test_series_factory.py new file mode 100644 index 0000000..23b8e8e --- /dev/null +++ b/openpyxl/chart/tests/test_series_factory.py @@ -0,0 +1,145 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def Series(): + from ..series_factory import SeriesFactory + return SeriesFactory + + +class TestSeriesFactory: + + def test_ctor(self, Series): + series = Series(values="Sheet1!$A$1:$A$10") + series.__elements__ = ('idx', 'order', 'val') + xml = tostring(series.to_tree()) + expected = """ + + + + + + Sheet1!$A$1:$A$10 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_manual_idx(self, Series): + series = Series(values="Sheet1!$A$1:$A$10") + series.__elements__ = ('idx', 'order', 'val') + xml = tostring(series.to_tree(idx=5)) + expected = """ + + + + + + Sheet1!$A$1:$A$10 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_manual_order(self, Series): + series = Series(values="Sheet1!$A$1:$A$10") + series.order = 2 + series.__elements__ = ('idx', 'order', 'val') + xml = tostring(series.to_tree(idx=5)) + expected = """ + + + + + + Sheet1!$A$1:$A$10 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_title(self, Series): + series = Series("Sheet1!A1:A10", title="First Series") + series.__elements__ = ('idx', 'order', 'tx') + xml = tostring(series.to_tree(idx=0)) + expected = """ + + + + + First Series + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_title_from_data(self, Series): + series = Series("Sheet1!A1:A10", title_from_data=True) + series.__elements__ = ('tx', 'val') + xml = tostring(series.to_tree(idx=0)) + expected = """ + + + + Sheet1!A1 + + + + + Sheet1!$A$2:$A$10 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_xy(self, Series): + from ..series import XYSeries + series = Series("Sheet!A1:A10", xvalues="Sheet!B1:B10") + assert isinstance(series, XYSeries) + + + def test_zvalues(self, Series): + series = Series("Sheet!A2:A5", xvalues="Sheet!B2:B5", zvalues="Sheet!C2:C5") + series.__elements__ = ('xVal', 'yVal', 'bubbleSize') + xml = tostring(series.to_tree()) + expected = """ + + + + Sheet!$B$2:$B$5 + + + + + Sheet!$A$2:$A$5 + + + + + Sheet!$C$2:$C$5 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chart/tests/test_shapes.py b/openpyxl/chart/tests/test_shapes.py new file mode 100644 index 0000000..325b1aa --- /dev/null +++ b/openpyxl/chart/tests/test_shapes.py @@ -0,0 +1,49 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def GraphicalProperties(): + from ..shapes import GraphicalProperties + return GraphicalProperties + + +class TestShapeProperties: + + def test_ctor(self, GraphicalProperties): + shapes = GraphicalProperties() + xml = tostring(shapes.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GraphicalProperties): + src = """ + + + + + + + + + + + + + + """ + node = fromstring(src) + shapes = GraphicalProperties.from_tree(node) + assert dict(shapes) == {} diff --git a/openpyxl/chart/tests/test_stock_chart.py b/openpyxl/chart/tests/test_stock_chart.py new file mode 100644 index 0000000..b751a82 --- /dev/null +++ b/openpyxl/chart/tests/test_stock_chart.py @@ -0,0 +1,92 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def StockChart(): + from ..stock_chart import StockChart + return StockChart + + +class TestStockChart: + + def test_ctor(self, StockChart): + from openpyxl.chart.series import Series + + chart = StockChart(ser=[Series(), Series(), Series()]) + xml = tostring(chart.to_tree()) + expected = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, StockChart): + src = """ + + + + + """ + node = fromstring(src) + chart = StockChart.from_tree(node) + assert chart.axId == [10, 100] diff --git a/openpyxl/chart/tests/test_surface_chart.py b/openpyxl/chart/tests/test_surface_chart.py new file mode 100644 index 0000000..29d9d4a --- /dev/null +++ b/openpyxl/chart/tests/test_surface_chart.py @@ -0,0 +1,165 @@ +from __future__ import absolute_import + +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def SurfaceChart(): + from ..surface_chart import SurfaceChart + return SurfaceChart + + +class TestSurfaceChart: + + def test_ctor(self, SurfaceChart): + chart = SurfaceChart() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SurfaceChart): + src = """ + + + + + + + + + + + + + + + + """ + node = fromstring(src) + chart = SurfaceChart.from_tree(node) + assert chart.axId == [2086876920, 2078923400, 2079274408] + + +@pytest.fixture +def SurfaceChart3D(): + from ..surface_chart import SurfaceChart3D + return SurfaceChart3D + + +class TestSurfaceChart3D: + + def test_ctor(self, SurfaceChart3D): + chart = SurfaceChart3D() + xml = tostring(chart.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SurfaceChart3D): + src = """ + + + + + + + + Blatt1!$A$1:$A$12 + + + + + + + + + Blatt1!$B$1:$B$12 + + + + + + + + + """ + node = fromstring(src) + chart = SurfaceChart3D.from_tree(node) + assert len(chart.ser) == 2 + assert chart.axId == [2082935272, 2082938248, 2082941288] + + +@pytest.fixture +def BandFormat(): + from ..surface_chart import BandFormat + return BandFormat + + +class TestBandFormat: + + def test_ctor(self, BandFormat): + fmt = BandFormat() + xml = tostring(fmt.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, BandFormat): + src = """ + + + + """ + node = fromstring(src) + fmt = BandFormat.from_tree(node) + assert fmt == BandFormat(idx=4) + + +@pytest.fixture +def BandFormatList(): + from ..surface_chart import BandFormatList + return BandFormatList + + +class TestBandFormatList: + + def test_ctor(self, BandFormatList): + fmt = BandFormatList() + xml = tostring(fmt.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, BandFormatList): + src = """ + + """ + node = fromstring(src) + fmt = BandFormatList.from_tree(node) + assert fmt == BandFormatList() diff --git a/openpyxl/chart/tests/test_text.py b/openpyxl/chart/tests/test_text.py new file mode 100644 index 0000000..baee94d --- /dev/null +++ b/openpyxl/chart/tests/test_text.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def RichText(): + from ..text import RichText + return RichText + + +class TestRichText: + + def test_ctor(self, RichText): + text = RichText() + xml = tostring(text.to_tree()) + expected = """ + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RichText): + src = """ + + """ + node = fromstring(src) + text = RichText.from_tree(node) + assert text == RichText() diff --git a/openpyxl/chart/tests/test_title.py b/openpyxl/chart/tests/test_title.py new file mode 100644 index 0000000..5b4921e --- /dev/null +++ b/openpyxl/chart/tests/test_title.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def Title(): + from ..title import Title + return Title + + +class TestTitle: + + def test_ctor(self, Title): + title = Title() + xml = tostring(title.to_tree()) + expected = """ + + <tx> + <rich> + <a:bodyPr></a:bodyPr> + <a:p> + <a:r> + <a:t /> + </a:r> + </a:p> + </rich> + </tx> + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Title): + src = """ + + """ + node = fromstring(src) + title = Title.from_tree(node) + assert title == Title() + + +def test_title_maker(): + """ + Create a title element from a string preserving line breaks. + """ + + from ..title import title_maker + text = "Two-line\nText" + title = title_maker(text) + xml = tostring(title.to_tree()) + expected = """ + <title xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"> + <tx> + <rich> + <a:bodyPr /> + <a:p> + <a:r> + <a:t>Two-line</a:t> + </a:r> + </a:p> + <a:p> + <a:r> + <a:t>Text</a:t> + </a:r> + </a:p> + </rich> + </tx> + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chart/tests/test_trendline.py b/openpyxl/chart/tests/test_trendline.py new file mode 100644 index 0000000..c936c12 --- /dev/null +++ b/openpyxl/chart/tests/test_trendline.py @@ -0,0 +1,64 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def TrendlineLabel(): + from ..trendline import TrendlineLabel + return TrendlineLabel + + +class TestTrendlineLabel: + + def test_ctor(self, TrendlineLabel): + trendline = TrendlineLabel() + xml = tostring(trendline.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, TrendlineLabel): + src = """ + + """ + node = fromstring(src) + trendline = TrendlineLabel.from_tree(node) + assert trendline == TrendlineLabel() + + +@pytest.fixture +def Trendline(): + from ..trendline import Trendline + return Trendline + + +class TestTrendline: + + def test_ctor(self, Trendline): + trendline = Trendline() + xml = tostring(trendline.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Trendline): + src = """ + + + + """ + node = fromstring(src) + trendline = Trendline.from_tree(node) + assert trendline == Trendline(trendlineType="log") diff --git a/openpyxl/chart/tests/test_updown_bars.py b/openpyxl/chart/tests/test_updown_bars.py new file mode 100644 index 0000000..1c2e063 --- /dev/null +++ b/openpyxl/chart/tests/test_updown_bars.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import + +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def UpDownBars(): + from ..updown_bars import UpDownBars + return UpDownBars + + +class TestUpDownBars: + + def test_ctor(self, UpDownBars): + bars = UpDownBars(gapWidth=150) + xml = tostring(bars.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, UpDownBars): + src = """ + + + + """ + node = fromstring(src) + bars = UpDownBars.from_tree(node) + assert bars == UpDownBars(gapWidth=156) diff --git a/openpyxl/chart/text.py b/openpyxl/chart/text.py new file mode 100644 index 0000000..ce181d0 --- /dev/null +++ b/openpyxl/chart/text.py @@ -0,0 +1,66 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, + Sequence, +) + + +from openpyxl.drawing.text import ( + RichTextProperties, + ListStyle, + Paragraph, +) + +from .data_source import StrRef + + +class RichText(Serialisable): + + """ + From the specification: 21.2.2.216 + + This element specifies text formatting. The lstStyle element is not supported. + """ + + tagname = "rich" + + bodyPr = Typed(expected_type=RichTextProperties) + properties = Alias("bodyPr") + lstStyle = Typed(expected_type=ListStyle, allow_none=True) + p = Sequence(expected_type=Paragraph) + paragraphs = Alias('p') + + __elements__ = ("bodyPr", "lstStyle", "p") + + def __init__(self, + bodyPr=None, + lstStyle=None, + p=None, + ): + if bodyPr is None: + bodyPr = RichTextProperties() + self.bodyPr = bodyPr + self.lstStyle = lstStyle + if p is None: + p = [Paragraph()] + self.p = p + + +class Text(Serialisable): + + strRef = Typed(expected_type=StrRef, allow_none=True) + rich = Typed(expected_type=RichText, allow_none=True) + + __elements__ = ("strRef", "rich") + + def __init__(self, + strRef=None, + rich=None + ): + self.strRef = strRef + if rich is None: + rich = RichText() + self.rich = rich diff --git a/openpyxl/chart/title.py b/openpyxl/chart/title.py new file mode 100644 index 0000000..dd256b2 --- /dev/null +++ b/openpyxl/chart/title.py @@ -0,0 +1,74 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat import basestring + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import NestedBool + +from .text import Text, RichText +from .layout import Layout +from .shapes import GraphicalProperties + +from openpyxl.drawing.text import ( + Paragraph, + RegularTextRun, + LineBreak +) + + +class Title(Serialisable): + tagname = "title" + + tx = Typed(expected_type=Text, allow_none=True) + text = Alias('tx') + layout = Typed(expected_type=Layout, allow_none=True) + overlay = NestedBool(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + body = Alias('txPr') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('tx', 'layout', 'overlay', 'spPr', 'txPr') + + def __init__(self, + tx=None, + layout=None, + overlay=None, + spPr=None, + txPr=None, + extLst=None, + ): + if tx is None: + tx = Text() + self.tx = tx + self.layout = layout + self.overlay = overlay + self.spPr = spPr + self.txPr = txPr + + +def title_maker(text): + title = Title() + paras = [Paragraph(r=[RegularTextRun(t=s)]) for s in text.split("\n")] + + title.tx.rich.paragraphs = paras + return title + + +class TitleDescriptor(Typed): + + expected_type = Title + allow_none = True + + def __set__(self, instance, value): + if isinstance(value, basestring): + value = title_maker(value) + super(TitleDescriptor, self).__set__(instance, value) diff --git a/openpyxl/chart/trendline.py b/openpyxl/chart/trendline.py new file mode 100644 index 0000000..652461e --- /dev/null +++ b/openpyxl/chart/trendline.py @@ -0,0 +1,96 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Alias +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedFloat, + NestedSet +) + +from .data_source import NumFmt +from .shapes import GraphicalProperties +from .text import RichText, Text +from .layout import Layout + + +class TrendlineLabel(Serialisable): + + tagname = "trendlineLbl" + + layout = Typed(expected_type=Layout, allow_none=True) + tx = Typed(expected_type=Text, allow_none=True) + numFmt = Typed(expected_type=NumFmt, allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias("txPr") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('layout', 'tx', 'numFmt', 'spPr', 'txPr') + + def __init__(self, + layout=None, + tx=None, + numFmt=None, + spPr=None, + txPr=None, + extLst=None, + ): + self.layout = layout + self.tx = tx + self.numFmt = numFmt + self.spPr = spPr + self.txPr = txPr + + +class Trendline(Serialisable): + + tagname = "trendline" + + name = String(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + trendlineType = NestedSet(values=(['exp', 'linear', 'log', 'movingAvg', 'poly', 'power'])) + order = NestedInteger(allow_none=True) + period = NestedInteger(allow_none=True) + forward = NestedFloat(allow_none=True) + backward = NestedFloat(allow_none=True) + intercept = NestedFloat(allow_none=True) + dispRSqr = NestedBool(allow_none=True) + dispEq = NestedBool(allow_none=True) + trendlineLbl = Typed(expected_type=TrendlineLabel, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('name', 'spPr', 'trendlineType', 'order', 'period', + 'forward', 'backward', 'intercept', 'dispRSqr', 'dispEq', 'trendlineLbl') + + def __init__(self, + name=None, + spPr=None, + trendlineType='linear', + order=None, + period=None, + forward=None, + backward=None, + intercept=None, + dispRSqr=None, + dispEq=None, + trendlineLbl=None, + extLst=None, + ): + self.name = name + self.spPr = spPr + self.trendlineType = trendlineType + self.order = order + self.period = period + self.forward = forward + self.backward = backward + self.intercept = intercept + self.dispRSqr = dispRSqr + self.dispEq = dispEq + self.trendlineLbl = trendlineLbl diff --git a/openpyxl/chart/updown_bars.py b/openpyxl/chart/updown_bars.py new file mode 100644 index 0000000..d44566c --- /dev/null +++ b/openpyxl/chart/updown_bars.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import Typed +from openpyxl.descriptors.excel import ExtensionList + +from .shapes import GraphicalProperties +from .axis import ChartLines +from .descriptors import NestedGapAmount + + +class UpDownBars(Serialisable): + + tagname = "upbars" + + gapWidth = NestedGapAmount() + upBars = Typed(expected_type=ChartLines, allow_none=True) + downBars = Typed(expected_type=ChartLines, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('gapWidth', 'upBars', 'downBars') + + def __init__(self, + gapWidth=150, + upBars=None, + downBars=None, + extLst=None, + ): + self.gapWidth = gapWidth + self.upBars = upBars + self.downBars = downBars diff --git a/openpyxl/chartsheet/__init__.py b/openpyxl/chartsheet/__init__.py new file mode 100644 index 0000000..17fc91f --- /dev/null +++ b/openpyxl/chartsheet/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from .chartsheet import Chartsheet diff --git a/openpyxl/chartsheet/chartsheet.py b/openpyxl/chartsheet/chartsheet.py new file mode 100644 index 0000000..627d443 --- /dev/null +++ b/openpyxl/chartsheet/chartsheet.py @@ -0,0 +1,110 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/chartsheet/custom.py b/openpyxl/chartsheet/custom.py new file mode 100644 index 0000000..c9373ed --- /dev/null +++ b/openpyxl/chartsheet/custom.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/chartsheet/properties.py b/openpyxl/chartsheet/properties.py new file mode 100644 index 0000000..30ccad1 --- /dev/null +++ b/openpyxl/chartsheet/properties.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/chartsheet/protection.py b/openpyxl/chartsheet/protection.py new file mode 100644 index 0000000..6d12392 --- /dev/null +++ b/openpyxl/chartsheet/protection.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import +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/openpyxl/chartsheet/publish.py b/openpyxl/chartsheet/publish.py new file mode 100644 index 0000000..7a49db1 --- /dev/null +++ b/openpyxl/chartsheet/publish.py @@ -0,0 +1,59 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/chartsheet/relation.py b/openpyxl/chartsheet/relation.py new file mode 100644 index 0000000..fe9528d --- /dev/null +++ b/openpyxl/chartsheet/relation.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/chartsheet/tests/__init__.py b/openpyxl/chartsheet/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/chartsheet/tests/test_chartsheet.py b/openpyxl/chartsheet/tests/test_chartsheet.py new file mode 100644 index 0000000..daaf547 --- /dev/null +++ b/openpyxl/chartsheet/tests/test_chartsheet.py @@ -0,0 +1,88 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.worksheet.drawing import Drawing +from openpyxl.worksheet.page import PageMargins +from ..views import ChartsheetView, ChartsheetViewList + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml +import pytest + +class DummyWorkbook: + + def __init__(self): + self.sheetnames = [] + self._charts = [] + + +@pytest.fixture +def Chartsheet(): + from ..chartsheet import Chartsheet + + return Chartsheet + +class TestChartsheet: + + def test_ctor(self, Chartsheet): + cs = Chartsheet(parent=DummyWorkbook()) + assert cs.title == "Chart" + + def test_read(self, Chartsheet): + src = """ + + + + + + + + + """ + xml = fromstring(src) + chart = Chartsheet.from_tree(xml) + assert chart.pageMargins.left == 0.7 + assert chart.sheetViews.sheetView[0].tabSelected == True + + def test_write(self, Chartsheet): + + sheetview = ChartsheetView(tabSelected=True, zoomScale=80, workbookViewId=0, zoomToFit=True) + chartsheetViews = ChartsheetViewList(sheetView=[sheetview]) + pageMargins = PageMargins(left=0.7, right=0.7, top=0.75, bottom=0.75, header=0.3, footer=0.3) + drawing = Drawing("rId1") + item = Chartsheet(sheetViews=chartsheetViews, pageMargins=pageMargins, drawing=drawing) + expected = """ + + + + + + + + """ + xml = tostring(item.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_write_charts(self, Chartsheet): + + class DummyChart: + + pass + + cs = Chartsheet(parent=DummyWorkbook()) + cs.add_chart(DummyChart()) + expected = """ + + + + + + + """ + xml = tostring(cs.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chartsheet/tests/test_custom.py b/openpyxl/chartsheet/tests/test_custom.py new file mode 100644 index 0000000..31379a6 --- /dev/null +++ b/openpyxl/chartsheet/tests/test_custom.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.worksheet.page import PageMargins +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def CustomChartsheetView(): + from ..custom import CustomChartsheetView + + return CustomChartsheetView + + +class TestCustomChartsheetView: + def test_read(self, CustomChartsheetView): + src = """ + + + + + + """ + xml = fromstring(src) + customChartsheetView = CustomChartsheetView.from_tree(xml) + assert customChartsheetView.state == 'visible' + assert customChartsheetView.scale == 88 + assert customChartsheetView.pageMargins.left == 0.23622047244094491 + + def test_write(self, CustomChartsheetView): + + pageMargins = PageMargins(left=0.2362204724409449, right=0.2362204724409449, top=0.7480314960629921, + bottom=0.7480314960629921, header=0.3149606299212598, footer=0.3149606299212598) + customChartsheetView = CustomChartsheetView(guid="{C43F44F8-8CE9-4A07-A9A9-0646C7C6B826}", scale=88, + zoomToFit=1, + pageMargins=pageMargins) + expected = """ + + + + """ + + xml = tostring(customChartsheetView.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def CustomChartsheetViews(): + from ..custom import CustomChartsheetViews + + return CustomChartsheetViews + + +class TestCustomChartsheetViews: + def test_read(self, CustomChartsheetViews): + src = """ + + + + + + + + """ + xml = fromstring(src) + customChartsheetViews = CustomChartsheetViews.from_tree(xml) + assert customChartsheetViews.customSheetView[0].state == 'visible' + assert customChartsheetViews.customSheetView[0].scale == 88 + assert customChartsheetViews.customSheetView[0].pageMargins.left == 0.23622047244094491 + + def test_write(self, CustomChartsheetViews): + from ..custom import CustomChartsheetView + + pageMargins = PageMargins(left=0.2362204724409449, right=0.2362204724409449, top=0.7480314960629921, + bottom=0.7480314960629921, header=0.3149606299212598, footer=0.3149606299212598) + customChartsheetView = CustomChartsheetView(guid="{C43F44F8-8CE9-4A07-A9A9-0646C7C6B826}", scale=88, + zoomToFit=1, + pageMargins=pageMargins) + customChartsheetViews = CustomChartsheetViews(customSheetView=[customChartsheetView]) + expected = """ + + + + + + """ + + xml = tostring(customChartsheetViews.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chartsheet/tests/test_properties.py b/openpyxl/chartsheet/tests/test_properties.py new file mode 100644 index 0000000..f8f8527 --- /dev/null +++ b/openpyxl/chartsheet/tests/test_properties.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + + +@pytest.fixture +def ChartsheetProperties(): + from ..properties import ChartsheetProperties + + return ChartsheetProperties + + +class TestChartsheetPr: + def test_read(self, ChartsheetProperties): + src = """ + + + + """ + xml = fromstring(src) + chartsheetPr = ChartsheetProperties.from_tree(xml) + assert chartsheetPr.codeName == "Chart1" + assert chartsheetPr.tabColor.rgb == "FFDCD8F4" + + def test_write(self, ChartsheetProperties): + from openpyxl.styles import Color + + chartsheetPr = ChartsheetProperties() + chartsheetPr.codeName = "Chart Openpyxl" + tabColor = Color(rgb="FFFFFFF4") + chartsheetPr.tabColor = tabColor + expected = """ + + + + """ + xml = tostring(chartsheetPr.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chartsheet/tests/test_protection.py b/openpyxl/chartsheet/tests/test_protection.py new file mode 100644 index 0000000..6e90d65 --- /dev/null +++ b/openpyxl/chartsheet/tests/test_protection.py @@ -0,0 +1,58 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def ChartsheetProtection(): + from ..protection import ChartsheetProtection + + return ChartsheetProtection + + +class TestChartsheetProtection: + def test_read(self, ChartsheetProtection): + src = """ + + """ + xml = fromstring(src) + chartsheetProtection = ChartsheetProtection.from_tree(xml) + assert chartsheetProtection.algorithmName == "SHA-512" + assert chartsheetProtection.saltValue == "Bo89+SCcqbFEcOS/6LcjBw==" + + + def test_write(self, ChartsheetProtection): + chartsheetProtection = ChartsheetProtection() + chartsheetProtection.saltValue = "Bo89+SCcqbFEcOS/6LcjBw==" + chartsheetProtection.content = "1" + chartsheetProtection.objects = "1" + chartsheetProtection.algorithmName = "SHA-512" + chartsheetProtection.spinCount = "100000" + expected = """ + + """ + + xml = tostring(chartsheetProtection.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_password(self, ChartsheetProtection): + prot = ChartsheetProtection() + prot.password = "secret" + assert prot.password == "DAA7" diff --git a/openpyxl/chartsheet/tests/test_publish.py b/openpyxl/chartsheet/tests/test_publish.py new file mode 100644 index 0000000..2c83ff6 --- /dev/null +++ b/openpyxl/chartsheet/tests/test_publish.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def WebPublishItem(): + from ..publish import WebPublishItem + + return WebPublishItem + + +class TestWebPulishItem: + def test_read(self, WebPublishItem): + src = r""" + + """ + xml = fromstring(src) + webPulishItem = WebPublishItem.from_tree(xml) + assert webPulishItem.id == 6433 + assert webPulishItem.sourceObject == "Chart 1" + + def test_write(self, WebPublishItem): + webPublish = WebPublishItem(id=6433, divId="Views_6433", sourceType="chart", sourceRef="", + sourceObject="Chart 1", destinationFile=r"D:\Publish.mht", title="First Chart", + autoRepublish=False) + expected = r""" + + """ + xml = tostring(webPublish.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def WebPublishItems(): + from ..publish import WebPublishItems + + return WebPublishItems + + +class TestWebPublishItems: + def test_read(self, WebPublishItems): + src = r""" + + + + """ + xml = fromstring(src) + webPublishItems = WebPublishItems.from_tree(xml) + assert webPublishItems.count == 1 + assert webPublishItems.webPublishItem[0].sourceObject == "Chart 1" + + def test_write(self, WebPublishItems): + from ..publish import WebPublishItem + + webPublish_6433 = WebPublishItem(id=6433, divId="Views_6433", sourceType="chart", sourceRef="", + sourceObject="Chart 1", destinationFile=r"D:\Publish.mht", title="First Chart", + autoRepublish=False) + webPublish_64487 = WebPublishItem(id=64487, divId="Views_64487", sourceType="chart", sourceRef="Ref_545421", + sourceObject="Chart 15", destinationFile=r"D:\Publish_12.mht", + title="Second Chart", + autoRepublish=True) + webPublishItems = WebPublishItems(webPublishItem=[webPublish_6433, webPublish_64487]) + expected = r""" + + + + + """ + xml = tostring(webPublishItems.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chartsheet/tests/test_relation.py b/openpyxl/chartsheet/tests/test_relation.py new file mode 100644 index 0000000..aa887bc --- /dev/null +++ b/openpyxl/chartsheet/tests/test_relation.py @@ -0,0 +1,58 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def SheetBackgroundPicture(): + from ..chartsheet import SheetBackgroundPicture + + return SheetBackgroundPicture + + +class TestSheetBackgroundPicture: + def test_read(self, SheetBackgroundPicture): + src = """ + + """ + xml = fromstring(src) + sheetBackgroundPicture = SheetBackgroundPicture.from_tree(xml) + assert sheetBackgroundPicture.id == "rId5" + + def test_write(self, SheetBackgroundPicture): + sheetBackgroundPicture = SheetBackgroundPicture(id="rId5") + expected = """ + + """ + xml = tostring(sheetBackgroundPicture.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + +@pytest.fixture +def DrawingHF(): + from ..chartsheet import DrawingHF + + return DrawingHF + + +class TestDrawingHF: + def test_read(self, DrawingHF): + src = """ + + """ + xml = fromstring(src) + drawingHF = DrawingHF.from_tree(xml) + assert drawingHF.lho == 7 + + def test_write(self, DrawingHF): + drawingHF = DrawingHF(lho=7, lhf=6, id='rId3') + expected = """ + + """ + xml = tostring(drawingHF.to_tree("drawingHF")) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chartsheet/tests/test_views.py b/openpyxl/chartsheet/tests/test_views.py new file mode 100644 index 0000000..6a372fe --- /dev/null +++ b/openpyxl/chartsheet/tests/test_views.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def ChartsheetView(): + from ..views import ChartsheetView + + return ChartsheetView + + +class TestChartsheetView: + def test_read(self, ChartsheetView): + src = """ + + """ + xml = fromstring(src) + chart = ChartsheetView.from_tree(xml) + assert chart.tabSelected == True + + def test_write(self, ChartsheetView): + sheetview = ChartsheetView(tabSelected=True, zoomScale=80, workbookViewId=0, zoomToFit=True) + expected = """""" + xml = tostring(sheetview.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def ChartsheetViewList(): + from ..views import ChartsheetViewList + return ChartsheetViewList + + +class TestChartsheetViewList: + + + def test_read(self, ChartsheetViewList): + src = """ + + + + """ + xml = fromstring(src) + views = ChartsheetViewList.from_tree(xml) + assert views.sheetView[0].tabSelected == 1 + + + def test_write(self, ChartsheetViewList): + views = ChartsheetViewList() + + expected = """ + + + + """ + xml = tostring(views.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/chartsheet/views.py b/openpyxl/chartsheet/views.py new file mode 100644 index 0000000..ee81caa --- /dev/null +++ b/openpyxl/chartsheet/views.py @@ -0,0 +1,52 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/comments/__init__.py b/openpyxl/comments/__init__.py new file mode 100644 index 0000000..574de25 --- /dev/null +++ b/openpyxl/comments/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +from .comments import Comment diff --git a/openpyxl/comments/author.py b/openpyxl/comments/author.py new file mode 100644 index 0000000..13f2b26 --- /dev/null +++ b/openpyxl/comments/author.py @@ -0,0 +1,22 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Alias +) + + +class AuthorList(Serialisable): + + tagname = "authors" + + author = Sequence(expected_type=unicode) + authors = Alias("author") + + def __init__(self, + author=(), + ): + self.author = author diff --git a/openpyxl/comments/comment_sheet.py b/openpyxl/comments/comment_sheet.py new file mode 100644 index 0000000..fb5ea2f --- /dev/null +++ b/openpyxl/comments/comment_sheet.py @@ -0,0 +1,232 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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 .author import AuthorList +from .comments import Comment +from .shape_writer import ShapeWriter + + +class ObjectAnchor(Serialisable): + + moveWithCells = Bool(allow_none=True) + sizeWithCells = Bool(allow_none=True) + #z-order = Integer(allow_none=True) needs alias + #from + #to defs from xdr + + def __init__(self, + moveWithCells=None, + sizeWithCells=None, + #z-order=None, + ): + self.moveWithCells = moveWithCells + self.sizeWithCells = sizeWithCells + #self.z-order = z-order + + +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/openpyxl/comments/comments.py b/openpyxl/comments/comments.py new file mode 100644 index 0000000..8aa2225 --- /dev/null +++ b/openpyxl/comments/comments.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/comments/shape_writer.py b/openpyxl/comments/shape_writer.py new file mode 100644 index 0000000..ee2635f --- /dev/null +++ b/openpyxl/comments/shape_writer.py @@ -0,0 +1,117 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.xml.functions import ( + Element, + SubElement, + tostring, + fromstring, +) + +from openpyxl.utils import ( + column_index_from_string, + coordinate_from_string, +) + +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): + col, row = coordinate_from_string(coord) + row -= 1 + column = column_index_from_string(col) - 1 + shape = _shape_factory(row, column, 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/openpyxl/comments/tests/__init__.py b/openpyxl/comments/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/comments/tests/conftest.py b/openpyxl/comments/tests/conftest.py new file mode 100644 index 0000000..3563b4c --- /dev/null +++ b/openpyxl/comments/tests/conftest.py @@ -0,0 +1,16 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) + + +# objects under test diff --git a/openpyxl/comments/tests/data/comments.xlsx b/openpyxl/comments/tests/data/comments.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f59c846493068b8728d7437470a313031b911afb GIT binary patch literal 13202 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 literal 0 HcmV?d00001 diff --git a/openpyxl/comments/tests/data/comments1.xml b/openpyxl/comments/tests/data/comments1.xml new file mode 100644 index 0000000..5895378 --- /dev/null +++ b/openpyxl/comments/tests/data/comments1.xml @@ -0,0 +1,33 @@ + + + author2 + author + author3 + + + + + + + text2 + + + + + + + + text + + + + + + + + text3 + + + + + \ No newline at end of file diff --git a/openpyxl/comments/tests/data/comments2.xml b/openpyxl/comments/tests/data/comments2.xml new file mode 100644 index 0000000..3390bef --- /dev/null +++ b/openpyxl/comments/tests/data/comments2.xml @@ -0,0 +1,81 @@ + + + + Cuke + Not Cuke + + + + + + + + + + + + + Cuke: + + + + + + + + + + First Comment + + + + + + + + + + + + + + Cuke: + + + + + + + + + + Second Comment + + + + + + + + + + + + + + Not Cuke: + + + + + + + + + + Third Comment + + + + + diff --git a/openpyxl/comments/tests/data/commentsDrawing1.vml b/openpyxl/comments/tests/data/commentsDrawing1.vml new file mode 100644 index 0000000..7b74360 --- /dev/null +++ b/openpyxl/comments/tests/data/commentsDrawing1.vml @@ -0,0 +1,54 @@ + + + + + + + + +

+ + + + + False + 6 + 2 + + +
+ + + + + False + 1 + 1 + + +
+ + + + + False + 8 + 3 + + + diff --git a/openpyxl/comments/tests/data/comments_out.xml b/openpyxl/comments/tests/data/comments_out.xml new file mode 100644 index 0000000..5b63690 --- /dev/null +++ b/openpyxl/comments/tests/data/comments_out.xml @@ -0,0 +1,25 @@ + + + + author + author2 + author3 + + + + + text + + + + + text2 + + + + + text3 + + + + diff --git a/openpyxl/comments/tests/data/control+comments.vml b/openpyxl/comments/tests/data/control+comments.vml new file mode 100644 index 0000000..6e7a860 --- /dev/null +++ b/openpyxl/comments/tests/data/control+comments.vml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + +
+ Sheet2 button +
+
+ + + 4, 22, 4, 2, 6, 29, 5, 14 + False + False + [0]!Button1_Click + Center + Center + +
+ + + + +
+ Sheet2 + option +
+
+ + + + 6, 45, 11, 5, 8, 57, 13, 15 + False + False + Center + 1 + + + +
+ + + + + + + +
+ + + + + False + 3 + 1 + + +
+ + + + + False + 7 + 0 + + + diff --git a/openpyxl/comments/tests/data/google_docs_comments.xml b/openpyxl/comments/tests/data/google_docs_comments.xml new file mode 100644 index 0000000..49dfa0d --- /dev/null +++ b/openpyxl/comments/tests/data/google_docs_comments.xml @@ -0,0 +1,14 @@ + + + + + + + + + some comment + -Peter Lustig + + + + diff --git a/openpyxl/comments/tests/data/size+comments.vml b/openpyxl/comments/tests/data/size+comments.vml new file mode 100644 index 0000000..1da5684 --- /dev/null +++ b/openpyxl/comments/tests/data/size+comments.vml @@ -0,0 +1,22 @@ + + + + + +
+ + + + + False + 2 + 3 + + diff --git a/openpyxl/comments/tests/test_author.py b/openpyxl/comments/tests/test_author.py new file mode 100644 index 0000000..f309981 --- /dev/null +++ b/openpyxl/comments/tests/test_author.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def AuthorList(): + from ..author import AuthorList + return AuthorList + + +class TestAuthor: + + def test_ctor(self, AuthorList): + vals = ["Bob", "Alice", "Eve"] + author = AuthorList(author=vals) + xml = tostring(author.to_tree()) + expected = """ + + Bob + Alice + Eve + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, AuthorList): + src = """ + + author2 + author + author3 + + """ + node = fromstring(src) + author = AuthorList.from_tree(node) + assert author.author == ["author2", "author", "author3"] diff --git a/openpyxl/comments/tests/test_comment.py b/openpyxl/comments/tests/test_comment.py new file mode 100644 index 0000000..fac4ff9 --- /dev/null +++ b/openpyxl/comments/tests/test_comment.py @@ -0,0 +1,48 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from copy import copy + +from openpyxl.comments import Comment + +import pytest + +@pytest.fixture +def Comment(): + from ..comments import Comment + return Comment + + +class TestComment: + + def test_ctor(self, Comment): + comment = Comment(author="Charlie", text="A comment") + assert comment.author == "Charlie" + assert comment.text == "A comment" + assert comment.parent is None + assert comment.height == 79 + assert comment.width == 144 + assert repr(comment) == 'Comment: A comment by Charlie' + + + def test_bind(self, Comment): + comment = Comment("", "") + comment.bind("ws") + assert comment.parent == "ws" + + + def test_unbind(self, Comment): + comment = Comment("", "") + comment.bind("ws") + comment.unbind() + assert comment.parent is None + + + def test_copy(self, Comment): + comment = Comment("", "") + clone = copy(comment) + assert clone is not comment + assert comment.text == clone.text + assert comment.author == clone.author + assert comment.height == clone.height + assert comment.width == clone.width diff --git a/openpyxl/comments/tests/test_comment_reader.py b/openpyxl/comments/tests/test_comment_reader.py new file mode 100644 index 0000000..edba575 --- /dev/null +++ b/openpyxl/comments/tests/test_comment_reader.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.reader.excel import load_workbook +from openpyxl.xml.functions import fromstring + +from ..comments import Comment + +import pytest + + +def test_read_comments(datadir): + datadir.chdir() + from .. comment_sheet import CommentSheet + + with open("comments2.xml") as src: + node = fromstring(src.read()) + + sheet = CommentSheet.from_tree(node) + comments = list(sheet.comments) + assert comments == [ + ('A1', Comment('Cuke:\nFirst Comment', 'Cuke')), + ('D1', Comment('Cuke:\nSecond Comment', 'Cuke')), + ('A2', Comment('Not Cuke:\nThird Comment', 'Not Cuke')) + ] + + +def test_comments_cell_association(datadir): + datadir.chdir() + wb = load_workbook('comments.xlsx') + assert wb['Sheet1']["A1"].comment.author == "Cuke" + assert wb['Sheet1']["A1"].comment.text == "Cuke:\nFirst Comment" + assert wb['Sheet2']["A1"].comment is None + assert wb['Sheet1']["D1"].comment.text == "Cuke:\nSecond Comment" + + +def test_comments_with_iterators(datadir): + datadir.chdir() + wb = load_workbook('comments.xlsx', read_only=True) + ws = wb['Sheet1'] + with pytest.raises(AttributeError): + assert ws["A1"].comment.author == "Cuke" diff --git a/openpyxl/comments/tests/test_comment_sheet.py b/openpyxl/comments/tests/test_comment_sheet.py new file mode 100644 index 0000000..baf0887 --- /dev/null +++ b/openpyxl/comments/tests/test_comment_sheet.py @@ -0,0 +1,106 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml +from openpyxl import Workbook +from ..comment_sheet import CommentRecord + + +def _comment_list(): + from ..comments import Comment + wb = Workbook() + ws = wb.active + comment1 = Comment("text", "author") + comment2 = Comment("text2", "author2") + comment3 = Comment("text3", "author3") + ws["B2"].comment = comment1 + ws["C7"].comment = comment2 + ws["D9"].comment = comment3 + + comments = [] + for coord, cell in sorted(ws._cells.items()): + if cell._comment is not None: + comment = CommentRecord.from_cell(cell) + comments.append(comment) + + return comments + + +class TestComment: + + def test_ctor(self): + comment = CommentRecord() + comment.text.t = "Some kind of comment" + xml = tostring(comment.to_tree()) + expected = """ + + + Some kind of comment + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self): + src = """ + + + + """ + node = fromstring(src) + comment = CommentRecord.from_tree(node) + assert comment == CommentRecord(ref="A1") + + +class TestCommentSheet: + + + def test_read_comments(self, datadir): + from ..comment_sheet import CommentSheet + + datadir.chdir() + with open("comments1.xml") as src: + node = fromstring(src.read()) + + comments = CommentSheet.from_tree(node) + assert comments.authors.author == ['author2', 'author', 'author3'] + assert len(comments.commentList) == 3 + + + def test_from_comments(self, datadir): + from .. comment_sheet import CommentSheet + datadir.chdir() + comments = _comment_list() + cs = CommentSheet.from_comments(comments) + xml = tostring(cs.to_tree()) + + with open('comments_out.xml') as src: + expected = src.read() + + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_path(self): + from ..comment_sheet import CommentSheet + from ..author import AuthorList + cs = CommentSheet(authors=AuthorList(), commentList=()) + assert cs.path == '/xl/comments/commentNone.xml' + + +def test_read_google_docs(datadir): + datadir.chdir() + xml = """ + + + some comment + -Peter Lustig + + + """ + node = fromstring(xml) + comment = CommentRecord.from_tree(node) + assert comment.text.t == "some comment\n\t -Peter Lustig" diff --git a/openpyxl/comments/tests/test_shape_writer.py b/openpyxl/comments/tests/test_shape_writer.py new file mode 100644 index 0000000..7ab7c58 --- /dev/null +++ b/openpyxl/comments/tests/test_shape_writer.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +from openpyxl.workbook import Workbook +from openpyxl.tests.helper import compare_xml +from openpyxl.xml.functions import ( + fromstring, + tostring, + Element, +) + +from ..comments import Comment +from ..comment_sheet import CommentRecord +from ..shape_writer import ( + ShapeWriter, + vmlns, + excelns, +) + + +def create_comments(): + wb = Workbook() + ws = wb.active + comment1 = Comment("text", "author") + comment2 = Comment("text2", "author2") + comment3 = Comment("text3", "author3") + ws["B2"].comment = comment1 + ws["C7"].comment = comment2 + ws["D9"].comment = comment3 + + comments = [] + for coord, cell in sorted(ws._cells.items()): + if cell._comment is not None: + comment = CommentRecord.from_cell(cell) + comments.append((cell.coordinate, comment)) + + return comments + + +def test_merge_comments_vml(datadir): + datadir.chdir() + cw = ShapeWriter(create_comments()) + + with open('control+comments.vml', 'rb') as existing: + content = fromstring(cw.write(fromstring(existing.read()))) + assert len(content.findall('{%s}shape' % vmlns)) == 5 + assert len(content.findall('{%s}shapetype' % vmlns)) == 2 + + +def test_write_comments_vml(datadir): + datadir.chdir() + cw = ShapeWriter(create_comments()) + + content = cw.write(Element("xml")) + with open('commentsDrawing1.vml', 'rb') as expected: + correct = fromstring(expected.read()) + check = fromstring(content) + correct_ids = [] + correct_coords = [] + check_ids = [] + check_coords = [] + + for i in correct.findall("{%s}shape" % vmlns): + correct_ids.append(i.attrib["id"]) + row = i.find("{%s}ClientData" % excelns).find("{%s}Row" % excelns).text + col = i.find("{%s}ClientData" % excelns).find("{%s}Column" % excelns).text + correct_coords.append((row,col)) + # blank the data we are checking separately + i.attrib["id"] = "0" + i.find("{%s}ClientData" % excelns).find("{%s}Row" % excelns).text="0" + i.find("{%s}ClientData" % excelns).find("{%s}Column" % excelns).text="0" + + for i in check.findall("{%s}shape" % vmlns): + check_ids.append(i.attrib["id"]) + row = i.find("{%s}ClientData" % excelns).find("{%s}Row" % excelns).text + col = i.find("{%s}ClientData" % excelns).find("{%s}Column" % excelns).text + check_coords.append((row,col)) + # blank the data we are checking separately + i.attrib["id"] = "0" + i.find("{%s}ClientData" % excelns).find("{%s}Row" % excelns).text="0" + i.find("{%s}ClientData" % excelns).find("{%s}Column" % excelns).text="0" + + assert set(correct_coords) == set(check_coords) + assert set(correct_ids) == set(check_ids) + diff = compare_xml(tostring(correct), tostring(check)) + assert diff is None, diff + + +def test_shape(datadir): + from ..shape_writer import _shape_factory + + datadir.chdir() + with open('size+comments.vml', 'rb') as existing: + expected = existing.read() + + shape = _shape_factory(2, 3, 79, 144) + xml = tostring(shape) + + diff = compare_xml(xml, expected) + assert diff is None, diff + + +def test_shape_with_custom_size(datadir): + from ..shape_writer import _shape_factory + + datadir.chdir() + with open('size+comments.vml', 'rb') as existing: + expected = existing.read() + # Change our source document for this test + expected = expected.replace(b'width:144px;', b'width:80px;') + expected = expected.replace(b'height:79px;', b'height:20px;') + + shape = _shape_factory(2, 3, 20, 80) + xml = tostring(shape) + + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/compat/__init__.py b/openpyxl/compat/__init__.py new file mode 100644 index 0000000..3b943db --- /dev/null +++ b/openpyxl/compat/__init__.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +from .strings import ( + basestring, + unicode, + bytes, + file, + tempfile, + safe_string, + safe_repr, + ) +from .numbers import long, NUMERIC_TYPES + + +try: + range = xrange +except NameError: + range = range + +try: + from itertools import accumulate +except ImportError: + from .accumulate import accumulate + +try: + from itertools import izip as zip +except ImportError: + zip = zip + + +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/openpyxl/compat/abc.py b/openpyxl/compat/abc.py new file mode 100644 index 0000000..f7ccd65 --- /dev/null +++ b/openpyxl/compat/abc.py @@ -0,0 +1,9 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +try: + from abc import ABC +except ImportError: + from abc import ABCMeta + ABC = ABCMeta('ABC', (object, ), {}) diff --git a/openpyxl/compat/accumulate.py b/openpyxl/compat/accumulate.py new file mode 100644 index 0000000..7e16af6 --- /dev/null +++ b/openpyxl/compat/accumulate.py @@ -0,0 +1,21 @@ +# Copyright PSF + +""" +Python 2 implementation of the accumulate function in itertools +From the Python documentation https://docs.python.org/3/library/itertools.html#itertool-functions +""" +import operator + +def accumulate(iterable, func=operator.add): + 'Return running totals' + # accumulate([1,2,3,4,5]) --> 1 3 6 10 15 + # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120 + it = iter(iterable) + try: + total = next(it) + except StopIteration: + return + yield total + for element in it: + total = func(total, element) + yield total diff --git a/openpyxl/compat/numbers.py b/openpyxl/compat/numbers.py new file mode 100644 index 0000000..8ed3d18 --- /dev/null +++ b/openpyxl/compat/numbers.py @@ -0,0 +1,31 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +try: + # Python 2 + long = long +except NameError: + # Python 3 + long = int + +from decimal import Decimal + +NUMERIC_TYPES = (int, float, long, Decimal) + + +try: + import numpy + NUMPY = True +except ImportError: + NUMPY = False + + +if NUMPY: + NUMERIC_TYPES = NUMERIC_TYPES + (numpy.bool_, numpy.floating, numpy.integer) + + +try: + import pandas + PANDAS = True +except ImportError: + PANDAS = False diff --git a/openpyxl/compat/singleton.py b/openpyxl/compat/singleton.py new file mode 100644 index 0000000..a839391 --- /dev/null +++ b/openpyxl/compat/singleton.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/compat/strings.py b/openpyxl/compat/strings.py new file mode 100644 index 0000000..f5892d3 --- /dev/null +++ b/openpyxl/compat/strings.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from datetime import datetime +from math import isnan, isinf +import sys + +VER = sys.version_info + +from .numbers import NUMERIC_TYPES + +if VER[0] >= 3: + basestring = str + unicode = str + from io import BufferedReader + file = BufferedReader + from io import BufferedRandom + tempfile = BufferedRandom + bytes = bytes +else: + basestring = basestring + unicode = unicode + file = file + tempfile = file + bytes = str + + +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, basestring): + value = str(value) + return value + + +def safe_repr(value): + """ + Safely convert unicode to ASCII for Python 2 + """ + if VER[0] == 3: + return value + return value.encode("ascii", 'backslashreplace') diff --git a/openpyxl/compat/tests/__init__.py b/openpyxl/compat/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/compat/tests/test_compat.py b/openpyxl/compat/tests/test_compat.py new file mode 100644 index 0000000..87bb58b --- /dev/null +++ b/openpyxl/compat/tests/test_compat.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + + +@pytest.mark.parametrize("value, result", + [ + ('s', 's'), + (2.0/3, '0.6666666666666666'), + (1, '1'), + (None, 'none'), + (float('NaN'), ''), + (float('inf'), ''), + ] + ) +def test_safe_string(value, result): + from openpyxl.compat import safe_string + assert safe_string(value) == result + v = safe_string('s') + assert v == 's' + + +@pytest.mark.numpy_required +def test_numeric_types(): + from ..numbers import NUMERIC_TYPES, numpy, Decimal, long + assert NUMERIC_TYPES == (int, float, long, Decimal, numpy.bool_, + numpy.floating, numpy.integer) + + +@pytest.mark.numpy_required +def test_numpy_tostring(): + from numpy import float_, bool_ + from .. import safe_string + assert safe_string(float_(5.1)) == "5.1" + assert safe_string(int(5)) == "5" + assert safe_string(bool_(True)) == "1" + + +@pytest.mark.skipif("sys.version_info[0]>=3") +def test_safe_repr(): + from ..strings import safe_repr + s = u"D\xfcsseldorf" + assert safe_repr(s) == s.encode("ascii", "backslashreplace") + + +from .. import deprecated + +def test_deprecated_function(recwarn): + + @deprecated("no way") + def fn(): + return "Hello world" + + fn() + w = recwarn.pop() + assert issubclass(w.category, DeprecationWarning) + assert w.filename + assert w.lineno + assert "no way" in str(w.message) + + +def test_deprecated_class(recwarn): + + @deprecated("") + class Simple: + + pass + s = Simple() + w = recwarn.pop() + assert issubclass(w.category, DeprecationWarning) + assert w.filename + assert w.lineno + + +def test_deprecated_method(recwarn): + + class Simple: + + @deprecated("") + def do(self): + return "Nothing" + + s = Simple() + s.do() + w = recwarn.pop() + assert issubclass(w.category, DeprecationWarning) + assert w.filename + assert w.lineno + + +def test_no_deprecation_reason(): + + with pytest.raises(TypeError): + @deprecated + def fn(): + return diff --git a/openpyxl/conftest.py b/openpyxl/conftest.py new file mode 100644 index 0000000..3b922cc --- /dev/null +++ b/openpyxl/conftest.py @@ -0,0 +1,55 @@ +import pytest + +# Global objects under tests + +@pytest.fixture +def Workbook(): + """Workbook Class""" + from openpyxl import Workbook + return Workbook + + +@pytest.fixture +def Worksheet(): + """Worksheet Class""" + from openpyxl.worksheet import Worksheet + return Worksheet + + +# Global fixtures + + +### Markers ### + +def pytest_runtest_setup(item): + if isinstance(item, item.Function): + try: + from PIL import Image + except ImportError: + Image = False + if item.get_marker("pil_required") and Image is False: + pytest.skip("PIL must be installed") + elif item.get_marker("pil_not_installed") and Image: + pytest.skip("PIL is installed") + elif item.get_marker("not_py33"): + pytest.skip("Ordering is not a given in Python 3") + elif item.get_marker("lxml_required"): + from openpyxl import LXML + if not LXML: + pytest.skip("LXML is required for some features such as schema validation") + elif item.get_marker("lxml_buffering"): + from lxml.etree import LIBXML_VERSION + if LIBXML_VERSION < (3, 4, 0, 0): + pytest.skip("LXML >= 3.4 is required") + elif item.get_marker("no_lxml"): + from openpyxl import LXML + if LXML: + pytest.skip("LXML has a different interface") + elif item.get_marker("numpy_required"): + from openpyxl import NUMPY + if not NUMPY: + pytest.skip("Numpy must be installed") + elif item.get_marker("pandas_required"): + from openpyxl import PANDAS + if not PANDAS: + pytest.skip("Pandas must be installed") diff --git a/openpyxl/descriptors/__init__.py b/openpyxl/descriptors/__init__.py new file mode 100644 index 0000000..f583a72 --- /dev/null +++ b/openpyxl/descriptors/__init__.py @@ -0,0 +1,58 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/descriptors/base.py b/openpyxl/descriptors/base.py new file mode 100644 index 0000000..55973c8 --- /dev/null +++ b/openpyxl/descriptors/base.py @@ -0,0 +1,270 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +""" +Based on Python Cookbook 3rd Edition, 8.13 +http://chimera.labs.oreilly.com/books/1230000000393/ch08.html#_discussion_130 +""" + +import datetime +import re + +from openpyxl.compat import basestring, bytes, long +from openpyxl.utils.datetime import from_ISO8601 + +from .namespace import namespaced + +class Descriptor(object): + + def __init__(self, name=None, **kw): + self.name = name + for k, v in kw.items(): + setattr(self, k, v) + + def __set__(self, instance, value): + instance.__dict__[self.name] = value + + +class Typed(Descriptor): + """Values must of a particular type""" + + expected_type = type(None) + allow_none = False + nested = False + + def __init__(self, *args, **kw): + super(Typed, self).__init__(*args, **kw) + self.__doc__ = "Values must be of type {0}".format(self.expected_type) + + def __set__(self, instance, value): + if not isinstance(value, self.expected_type): + if (not self.allow_none + or (self.allow_none and value is not None)): + raise TypeError('expected ' + str(self.expected_type)) + super(Typed, self).__set__(instance, value) + + def __repr__(self): + return self.__doc__ + + +def _convert(expected_type, value): + """ + Check value is of or can be converted to expected type. + """ + if not isinstance(value, expected_type): + try: + value = expected_type(value) + except: + raise TypeError('expected ' + str(expected_type)) + return value + + +class Convertible(Typed): + """Values must be convertible to a particular type""" + + 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) + super(Convertible, self).__set__(instance, value) + + +class Max(Convertible): + """Values must be less than a `max` value""" + + expected_type = float + allow_none = False + + def __init__(self, **kw): + if 'max' not in kw and not hasattr(self, 'max'): + raise TypeError('missing max value') + super(Max, 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.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 = long + + +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 = basestring + + +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, basestring): + try: + value = from_ISO8601(value) + except ValueError: + raise ValueError("Value must be ISO datetime format") + super(DateTime, self).__set__(instance, value) diff --git a/openpyxl/descriptors/excel.py b/openpyxl/descriptors/excel.py new file mode 100644 index 0000000..3de022c --- /dev/null +++ b/openpyxl/descriptors/excel.py @@ -0,0 +1,114 @@ +from __future__ import absolute_import +#copyright openpyxl 2010-2018 + +""" +Excel specific descriptors +""" + +from openpyxl.xml.constants import REL_NS +from openpyxl.compat import safe_string, unicode +from openpyxl.xml.functions import Element + +from . import ( + MatchPattern, + MinMax, + Integer, + String, + Typed, + Sequence, +) +from .serialisable import Serialisable +from openpyxl.utils.cell import RANGE_EXPR + +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, unicode) 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/openpyxl/descriptors/namespace.py b/openpyxl/descriptors/namespace.py new file mode 100644 index 0000000..19ed835 --- /dev/null +++ b/openpyxl/descriptors/namespace.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import +# copyright openpyxl 2010-2015 + + +def namespaced(obj, tagname, namespace=None): + """ + Utility to create a namespaced tag for an object + """ + + namespace = getattr(obj, "namespace", namespace) + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return tagname diff --git a/openpyxl/descriptors/nested.py b/openpyxl/descriptors/nested.py new file mode 100644 index 0000000..916c66a --- /dev/null +++ b/openpyxl/descriptors/nested.py @@ -0,0 +1,131 @@ +from __future__ import absolute_import +#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 + + +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) + 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/openpyxl/descriptors/sequence.py b/openpyxl/descriptors/sequence.py new file mode 100644 index 0000000..4da7953 --- /dev/null +++ b/openpyxl/descriptors/sequence.py @@ -0,0 +1,128 @@ +from __future__ import absolute_import +# 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/openpyxl/descriptors/serialisable.py b/openpyxl/descriptors/serialisable.py new file mode 100644 index 0000000..6bae707 --- /dev/null +++ b/openpyxl/descriptors/serialisable.py @@ -0,0 +1,232 @@ +from __future__ import absolute_import +# copyright openpyxl 2010-2015 +# copyright qyou.casia@gmail.com 2018 + +from copy import deepcopy as 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] + + 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 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:] + 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/openpyxl/descriptors/slots.py b/openpyxl/descriptors/slots.py new file mode 100644 index 0000000..cadc1ef --- /dev/null +++ b/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/openpyxl/descriptors/tests/__init__.py b/openpyxl/descriptors/tests/__init__.py new file mode 100644 index 0000000..ad1d21d --- /dev/null +++ b/openpyxl/descriptors/tests/__init__.py @@ -0,0 +1,2 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl diff --git a/openpyxl/descriptors/tests/test_base.py b/openpyxl/descriptors/tests/test_base.py new file mode 100644 index 0000000..bd17f72 --- /dev/null +++ b/openpyxl/descriptors/tests/test_base.py @@ -0,0 +1,397 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from .. import Strict + +class TestDescriptor: + + from ..base import Descriptor + + class Dummy: + pass + + def test_ctor(self): + d = self.Descriptor('key', size=1) + assert d.name == 'key' + assert d.size == 1 + + def test_setter(self): + d = self.Descriptor('key') + client = self.Dummy() + d.__set__(client, 42) + assert client.key == 42 + + +@pytest.fixture +def boolean(): + + from ..base import Bool + + class Dummy(Strict): + + value = Bool() + + return Dummy() + + +class TestBool: + + def test_valid(self, boolean): + boolean.value = True + assert boolean.value + + @pytest.mark.parametrize("value, expected", + [ + (1, True,), + (0, False), + ('true', True), + ('false', False), + ('0', False), + ('f', False), + ('', False), + ([], False) + ] + ) + def test_cast(self, boolean, value, expected): + boolean.value = value + assert boolean.value == expected + + +def test_nested(): + from ..base import Bool + + class DummyNested(Strict): + + value = Bool(nested=True) + + dummy = DummyNested() + dummy.value = True + assert dummy.__class__.value.nested == True + + +@pytest.fixture +def integer(): + + from ..base import Integer + + class Dummy(Strict): + + value = Integer() + + return Dummy() + + +class TestInt: + + def test_valid(self, integer): + integer.value = 4 + assert integer.value == 4 + + @pytest.mark.parametrize("value", ['a', '4.5', None]) + def test_invalid(self, integer, value): + with pytest.raises(TypeError): + integer.value = value + + @pytest.mark.parametrize("value, expected", + [ + ('4', 4), + (4.5, 4), + ]) + def test_cast(self, integer, value, expected): + integer.value = value + assert integer.value == expected + + +@pytest.fixture +def float(): + + from ..base import Float + + class Dummy(Strict): + + value = Float() + + return Dummy() + + +class TestFloat: + + def test_valid(self, float): + float.value = 4 + assert float.value == 4 + + @pytest.mark.parametrize("value", ['a', None]) + def test_invalid(self, float, value): + with pytest.raises(TypeError): + float.value = value + + @pytest.mark.parametrize("value, expected", + [ + ('4.5', 4.5), + (4.5, 4.5), + (4, 4.0), + ]) + def test_cast(self, float, value, expected): + float.value = value + assert float.value == expected + + +@pytest.fixture +def allow_none(): + + from ..base import Float + + class Dummy(Strict): + + value = Float(allow_none=True) + + return Dummy() + + +class TestAllowNone: + + def test_valid(self, allow_none): + allow_none.value = None + assert allow_none.value is None + + +@pytest.fixture +def maximum(): + from ..base import Max + + class Dummy(Strict): + + value = Max(max=5) + + return Dummy() + + +class TestMax: + + def test_ctor(self): + from ..base import Max + + with pytest.raises(TypeError): + class Dummy(Strict): + value = Max() + + def test_valid(self, maximum): + maximum.value = 4 + assert maximum.value == 4 + + def test_invalid(self, maximum): + with pytest.raises(ValueError): + maximum.value = 6 + + +@pytest.fixture +def minimum(): + from ..base import Min + + class Dummy(Strict): + + value = Min(min=0) + + return Dummy() + + +class TestMin: + + def test_ctor(self): + from ..base import Min + + with pytest.raises(TypeError): + class Dummy(Strict): + value = Min() + + + def test_valid(self, minimum): + minimum.value = 2 + assert minimum.value == 2 + + + def test_invalid(self, minimum): + with pytest.raises(ValueError): + minimum.value = -1 + + +@pytest.fixture +def min_max(): + from ..base import MinMax + + class Dummy(Strict): + + value = MinMax(min=-1, max=1) + + return Dummy() + + +class TestMinMax: + + def test_ctor(self): + from ..base import MinMax + + with pytest.raises(TypeError): + + class Dummy(Strict): + value = MinMax(min=-10) + + with pytest.raises(TypeError): + + class Dummy(Strict): + value = MinMax(max=10) + + + def test_valid(self, min_max): + min_max.value = 1 + assert min_max.value == 1 + + + def test_invalid(self, min_max): + with pytest.raises(ValueError): + min_max.value = 2 + + +@pytest.fixture +def set(): + from ..base import Set + + class Dummy(Strict): + + value = Set(values=[1, 'a', None]) + + return Dummy() + + +class TestValues: + + def test_ctor(self): + from ..base import Set + + with pytest.raises(TypeError): + class Dummy(Strict): + + value = Set() + + + def test_valid(self, set): + set.value = 1 + assert set.value == 1 + + + def test_invalid(self, set): + with pytest.raises(ValueError): + set.value = 2 + + +def test_noneset(): + from ..base import NoneSet + class Dummy(Strict): + + value = NoneSet(values=[1, 2, 3]) + + obj = Dummy() + obj.value = 'none' + assert obj.value is None + with pytest.raises(ValueError): + obj.value = 5 + + +@pytest.fixture +def ascii(): + + from ..base import ASCII + + class Dummy(Strict): + + value = ASCII() + + return Dummy() + + +class TestASCII: + + def test_valid(self, ascii): + ascii.value = b'some text' + assert ascii.value == b'some text' + + value = b'\xc3\xbc'.decode("utf-8") + @pytest.mark.parametrize("value", + [ + value, + 10, + [] + ] + ) + def test_invalid(self, ascii, value): + with pytest.raises(TypeError): + ascii.value = value + + +@pytest.fixture +def string(): + + from ..base import String + + class Dummy(Strict): + + value = String() + + return Dummy() + + +class TestString: + + def test_valid(self, string): + value = b'\xc3\xbc'.decode("utf-8") + string.value = value + assert string.value == value + + def test_invalid(self, string): + with pytest.raises(TypeError): + string.value = 5 + + +@pytest.fixture +def Tuple(): + from ..base import Tuple + + class Dummy(Strict): + + value = Tuple() + + return Dummy() + + +class TestTuple: + + def test_valid(self, Tuple): + Tuple.value = (1, 2) + assert Tuple.value == (1, 2) + + def test_invalid(self, Tuple): + with pytest.raises(TypeError): + Tuple.value = [1, 2, 3] + + +@pytest.fixture +def Length(): + from ..base import Length + + class Dummy(Strict): + + value = Length(length=4) + + return Dummy() + + +class TestLength: + + def test_valid(self, Length): + Length.value = "this" + + def test_invalid(self, Length): + with pytest.raises(ValueError): + Length.value = "2" diff --git a/openpyxl/descriptors/tests/test_excel.py b/openpyxl/descriptors/tests/test_excel.py new file mode 100644 index 0000000..7ceb276 --- /dev/null +++ b/openpyxl/descriptors/tests/test_excel.py @@ -0,0 +1,211 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from .. import Strict + + +@pytest.fixture +def UniversalMeasure(): + from ..excel import UniversalMeasure + + class Dummy(Strict): + + value = UniversalMeasure() + + return Dummy() + + +class TestUniversalMeasure: + + @pytest.mark.parametrize("value", + ["24.73mm", "0cm", "24pt", '999pc', "50pi"] + ) + def test_valid(self, UniversalMeasure, value): + UniversalMeasure.value = value + assert UniversalMeasure.value == value + + @pytest.mark.parametrize("value", + [24.73, '24.73zz', "24.73 mm", None, "-24.73cm"] + ) + def test_invalid(self, UniversalMeasure, value): + with pytest.raises(ValueError): + UniversalMeasure.value = "{0}".format(value) + + +@pytest.fixture +def HexBinary(): + from ..excel import HexBinary + + class Dummy(Strict): + + value = HexBinary() + + return Dummy() + + +class TestHexBinary: + + @pytest.mark.parametrize("value", + ["aa35efd", "AABBCCDD"] + ) + def test_valid(self, HexBinary, value): + HexBinary.value = value + assert HexBinary.value == value + + + @pytest.mark.parametrize("value", + ["GGII", "35.5"] + ) + def test_invalid(self, HexBinary, value): + with pytest.raises(ValueError): + HexBinary.value = value + + +@pytest.fixture +def TextPoint(): + from ..excel import TextPoint + + class Dummy(Strict): + + value = TextPoint() + + return Dummy() + + +class TestTextPoint: + + @pytest.mark.parametrize("value", + [-400000, "400000", 0] + ) + def test_valid(self, TextPoint, value): + TextPoint.value = value + assert TextPoint.value == int(value) + + def test_invalid_value(self, TextPoint): + with pytest.raises(ValueError): + TextPoint.value = -400001 + + def test_invalid_type(self, TextPoint): + with pytest.raises(TypeError): + TextPoint.value = "40pt" + + +@pytest.fixture +def Percentage(): + from ..excel import Percentage + + class Dummy(Strict): + + value = Percentage() + + return Dummy() + + +class TestPercentage: + + @pytest.mark.parametrize("input, value", + [ + ("15%", 15000), + (1500, 1500), + ("15.5%", 15500), + ] + ) + def test_valid(self, Percentage, input, value): + Percentage.value = value + assert Percentage.value == value + + + @pytest.mark.parametrize("value", + ["2000000", "-1000001",] + ) + def test_invalid(self, Percentage, value): + with pytest.raises(ValueError): + Percentage.value = value + + +@pytest.fixture +def Guid(): + from ..excel import Guid + + class Dummy(Strict): + value = Guid() + + return Dummy() + + +class TestGuid(): + @pytest.mark.parametrize("value", + ["{00000000-5BD2-4BC8-9F70-7020E1357FB2}"] + ) + def test_valid(self, Guid, value): + Guid.value = value + assert Guid.value == value + + @pytest.mark.parametrize("value", + ["{00000000-5BD2-4BC8-9F70-7020E1357FB2"] + ) + def test_valid(self, Guid, value): + with pytest.raises(ValueError): + Guid.value = value + + +@pytest.fixture +def Base64Binary(): + from ..excel import Base64Binary + + class Dummy(Strict): + value = Base64Binary() + + return Dummy() + + +class TestBase64Binary(): + @pytest.mark.parametrize("value", + ["9oN7nWkCAyEZib1RomSJTjmPpCY="] + ) + def test_valid(self, Base64Binary, value): + Base64Binary.value = value + assert Base64Binary.value == value + + @pytest.mark.parametrize("value", + ["==0F"] + ) + def test_valid(self, Base64Binary, value): + with pytest.raises(ValueError): + Base64Binary.value = value + + +@pytest.fixture +def CellRange(): + from ..excel import CellRange + + class Dummy(Strict): + value = CellRange() + + return Dummy() + + +class TestCellRange(): + + @pytest.mark.parametrize("value", + ["A1", + "A1:H5", + "A:B", + ] + ) + def test_valid(self, CellRange, value): + CellRange.value = value + assert CellRange.value == value + + + @pytest.mark.parametrize("value", + ["A1:", + "A1:5", + "A1:B4:C7" + ] + ) + def test_invalid(self, CellRange, value): + with pytest.raises(ValueError): + CellRange.value = value diff --git a/openpyxl/descriptors/tests/test_namespace.py b/openpyxl/descriptors/tests/test_namespace.py new file mode 100644 index 0000000..8a93387 --- /dev/null +++ b/openpyxl/descriptors/tests/test_namespace.py @@ -0,0 +1,31 @@ +from __future__ import absolute_import +# copyright openpyxl 2010-2015 + +from ..namespace import namespaced + + +def test_no_namespace(): + obj = object() + + tag = namespaced(obj, "root") + assert tag == "root" + + +def test_object_namespace(): + + class Object: + + namespace = "main" + + obj = Object() + + tag = namespaced(obj, "root") + assert tag == "{main}root" + + +def test_overwrite_namespace(): + + obj = object() + + tag = namespaced(obj, "root", "main") + assert tag == "{main}root" diff --git a/openpyxl/descriptors/tests/test_nested.py b/openpyxl/descriptors/tests/test_nested.py new file mode 100644 index 0000000..a140b1c --- /dev/null +++ b/openpyxl/descriptors/tests/test_nested.py @@ -0,0 +1,363 @@ +from __future__ import absolute_import +#copyright openpyxl 2010-2015 + +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.tests.helper import compare_xml +from ..serialisable import Serialisable + + +import pytest + +@pytest.fixture +def NestedValue(): + from ..nested import NestedValue + + class Simple(Serialisable): + + tagname = "simple" + + size = NestedValue(expected_type=int) + + def __init__(self, size): + self.size = size + + return Simple + + +class TestValue: + + def test_to_tree(self, NestedValue): + + simple = NestedValue(4) + + assert simple.size == 4 + xml = tostring(NestedValue.size.to_tree("size", simple.size)) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, NestedValue): + + xml = """ + + """ + node = fromstring(xml) + simple = NestedValue(size=node) + assert simple.size == 4 + + + def test_tag_mismatch(self, NestedValue): + + xml = """ + + """ + node = fromstring(xml) + with pytest.raises(ValueError): + simple = NestedValue(size=node) + + + def test_nested_to_tree(self, NestedValue): + simple = NestedValue(4) + xml = tostring(simple.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_nested_from_tree(self, NestedValue): + xml = """ + + + + """ + node = fromstring(xml) + obj = NestedValue.from_tree(node) + assert obj.size == 4 + + +@pytest.fixture +def NestedText(): + + from ..nested import NestedText + + class Simple(Serialisable): + + tagname = "simple" + + coord = NestedText(expected_type=int) + + def __init__(self, coord): + self.coord = coord + + return Simple + + +class TestText: + + def test_to_tree(self, NestedText): + + simple = NestedText(4) + + assert simple.coord == 4 + xml = tostring(NestedText.coord.to_tree("coord", simple.coord)) + expected = """ + 4 + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, NestedText): + xml = """ + 4 + """ + node = fromstring(xml) + + simple = NestedText(node) + assert simple.coord == 4 + + + def test_nested_to_tree(self, NestedText): + simple = NestedText(4) + xml = tostring(simple.to_tree()) + expected = """ + + 4 + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_nested_from_tree(self, NestedText): + xml = """ + + 4 + + """ + node = fromstring(xml) + obj = NestedText.from_tree(node) + assert obj.coord == 4 + + +def test_bool_value(): + from ..nested import NestedBool + + class Simple(Serialisable): + + bold = NestedBool() + + def __init__(self, bold): + self.bold = bold + + + xml = """ + + + + """ + node = fromstring(xml) + simple = Simple.from_tree(node) + assert simple.bold is True + + +def test_noneset_value(): + from ..nested import NestedNoneSet + + + class Simple(Serialisable): + + underline = NestedNoneSet(values=('1', '2', '3')) + + def __init__(self, underline): + self.underline = underline + + xml = """ + + + + """ + + node = fromstring(xml) + simple = Simple.from_tree(node) + assert simple.underline == '1' + +def test_min_max_value(): + from ..nested import NestedMinMax + + + class Simple(Serialisable): + + size = NestedMinMax(min=5, max=10) + + def __init__(self, size): + self.size = size + + + xml = """ + + + + """ + + node = fromstring(xml) + simple = Simple.from_tree(node) + assert simple.size == 6 + + +def test_nested_integer(): + from ..nested import NestedInteger + + + class Simple(Serialisable): + + tagname = "font" + + size = NestedInteger() + + def __init__(self, size): + self.size = size + + + simple = Simple('4') + assert simple.size == 4 + + +def test_nested_float(): + from ..nested import NestedFloat + + + class Simple(Serialisable): + + tagname = "font" + + size = NestedFloat() + + def __init__(self, size): + self.size = size + + + simple = Simple('4.5') + assert simple.size == 4.5 + + +def test_nested_string(): + from ..nested import NestedString + + + class Simple(Serialisable): + + tagname = "font" + + name = NestedString() + + def __init__(self, name): + self.name = name + + + simple = Simple('4') + assert simple.name == '4' + + +@pytest.fixture +def Empty(): + from ..nested import EmptyTag + + class Simple(Serialisable): + + tagname = "break" + + height = EmptyTag() + + def __init__(self, height=None): + self.height = height + + return Simple + + +class TestEmptyTag: + + @pytest.mark.parametrize("value, result", + [ + (False, False), + (True, True), + (None, False), + (1, True) + ] + ) + def test_ctor(self, Empty, value, result): + obj = Empty(value) + assert obj.height is result + + + @pytest.mark.parametrize("value, result", + [ + (False, ""), + (True, "") + ] + ) + def test_to_tree(self, Empty, value, result): + obj = Empty(height=value) + xml = tostring(obj.to_tree()) + diff = compare_xml(xml, result) + assert diff is None, diff + + + @pytest.mark.parametrize("value, src", + [ + (False, ""), + (True, "") + ] + ) + def test_from_xml(self, Empty, value, src): + node = fromstring(src) + obj = Empty.from_tree(node) + assert obj.height is value + + +@pytest.fixture +def CustomAttribute(): + from ..nested import NestedValue + + class Simple(Serialisable): + + tagname = "simple" + + size = NestedValue(expected_type=int, attribute="something") + + def __init__(self, size): + self.size = size + + return Simple + + +class TestCustomAttribute: + + def test_to_tree(self, CustomAttribute): + + simple = CustomAttribute(4) + + assert simple.size == 4 + xml = tostring(CustomAttribute.size.to_tree("size", simple.size)) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, CustomAttribute): + + xml = """ + + """ + node = fromstring(xml) + simple = CustomAttribute(size=node) + assert simple.size == 4 diff --git a/openpyxl/descriptors/tests/test_sequence.py b/openpyxl/descriptors/tests/test_sequence.py new file mode 100644 index 0000000..ec86109 --- /dev/null +++ b/openpyxl/descriptors/tests/test_sequence.py @@ -0,0 +1,378 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring, Element +from openpyxl.tests.helper import compare_xml +from ..serialisable import Serialisable +from ..base import Integer + +@pytest.fixture +def Sequence(): + from ..sequence import Sequence + + return Sequence + + +@pytest.fixture +def Dummy(Sequence): + + class Dummy(Serialisable): + + value = Sequence(expected_type=int) + + def __init__(self, value=()): + self.value = value + + return Dummy + + +class TestSequence: + + @pytest.mark.parametrize("value", [list(), tuple()]) + def test_valid_ctor(self, Dummy, value): + dummy = Dummy() + dummy.value = value + assert dummy.value == list(value) + + @pytest.mark.parametrize("value", ["", b"", dict(), 1, None]) + def test_invalid_container(self, Dummy, value): + dummy = Dummy() + with pytest.raises(TypeError): + dummy.value = value + + +class TestPrimitive: + + def test_to_tree(self, Dummy): + + dummy = Dummy([1, '2', 3]) + + root = Element("root") + for node in Dummy.value.to_tree("el", dummy.value, ): + root.append(node) + + xml = tostring(root) + expected = """ + + 1 + 2 + 3 + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Dummy): + src = """ + + 1 + 2 + 3 + + """ + node = fromstring(src) + + dummy = Dummy.from_tree(node) + assert dummy.value == [1, 2, 3] + + +class SomeType(Serialisable): + + value = Integer() + + def __init__(self, value): + self.value = value + + +class TestComplex: + + def test_to_tree(self, Sequence): + + class Dummy: + + vals = Sequence(expected_type=SomeType, name="vals") + + dummy = Dummy() + dummy.vals = [SomeType(1), SomeType(2), SomeType(3)] + + root = Element("root") + for node in Dummy.vals.to_tree("el", dummy.vals): + root.append(node) + + xml = tostring(root) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Sequence): + src = """ + + + + + + """ + node = fromstring(src) + + class Dummy(Serialisable): + + vals = Sequence(expected_type=SomeType) + + def __init__(self, vals): + self.vals = vals + + dummy = Dummy.from_tree(node) + assert dummy.vals == [SomeType(1), SomeType(2), SomeType(3)] + + +@pytest.fixture +def ValueSequence(): + from .. sequence import ValueSequence + return ValueSequence + + +class TestValueSequence: + + def test_to_tree(self, ValueSequence): + + class Dummy(Serialisable): + + tagname = "el" + + size = ValueSequence(expected_type=int) + + dummy = Dummy() + dummy.size = [1, 2, 3] + xml = tostring(dummy.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, ValueSequence): + class Dummy(Serialisable): + + tagname = "el" + + __nested__ = ("size",) + size = ValueSequence(expected_type=int) + + def __init__(self, size): + self.size = size + + src = """ + + + + + + """ + node = fromstring(src) + desc = Dummy.size + vals = desc.from_tree(node) + + dummy = Dummy.from_tree(node) + assert dummy.size == [1, 2, 3] + + +@pytest.fixture +def NestedSequence(): + from ..sequence import NestedSequence + return NestedSequence + + +from openpyxl.styles import Font + +@pytest.fixture +def ComplexObject(NestedSequence): + + + class Complex(Serialisable): + + tagname = "style" + + fonts = NestedSequence(expected_type=Font, count=True) + + def __init__(self, fonts=()): + self.fonts = fonts + + return Complex + + +class TestNestedSequence: + + + def test_ctor(self, ComplexObject): + style = ComplexObject() + ft1 = Font(family=2, sz=11, name="Arial") + ft2 = Font(bold=True) + style.fonts = [ft1, ft2] + + expected = """ + + """ + tree = style.__class__.fonts.to_tree('fonts', style.fonts) + tree = style.to_tree() + xml = tostring(tree) + diff = compare_xml(xml, expected) + + assert diff is None, diff + + + def test_from_tree(self, ComplexObject): + xml = """ + + """ + node = fromstring(xml) + style = ComplexObject.from_tree(node) + assert len(style.fonts) == 2 + assert style.fonts[1].bold is True + + +class Larry(Serialisable): + + tagname = "l" + value = Integer() + + def __init__(self, value): + self.value = value + +class Curly(Serialisable): + + tagname = "c" + hair = Integer() + + def __init__(self, hair): + self.hair = hair + + +class Mo(Serialisable): + + tagname = "m" + cap = Integer() + + def __init__(self, cap): + self.cap = cap + + +@pytest.fixture +def MultiSequence(): + from ..sequence import MultiSequence + return MultiSequence + + +@pytest.fixture +def MultiSequencePart(): + from ..sequence import MultiSequencePart + return MultiSequencePart + + +@pytest.fixture +def Stooge(MultiSequence, MultiSequencePart): + + class Stooge(Serialisable): + + _stooges = MultiSequence(expected_type=SomeType) + l = MultiSequencePart(expected_type=Larry, store="_stooges") + c = MultiSequencePart(expected_type=Curly, store="_stooges") + m = MultiSequencePart(expected_type=Mo, store="_stooges") + + def __init__(self, _stooges=()): + self._stooges = _stooges + + return Stooge + + +class TestMultiSequence: + + + def test_elements(self, Stooge): + + assert Stooge.__elements__ == ("_stooges",) + + + def test_attrs(self, Stooge): + + dummy = Stooge() + + assert Stooge.__attrs__ == () + + + def test_to_tree(self, Stooge): + + dummy = Stooge() + dummy._stooges = [Larry(1), Curly(2), Larry(3), Mo(4)] + + root = Element("root") + for node in Stooge._stooges.to_tree("el", dummy._stooges): + root.append(node) + + tree = dummy.to_tree("root") + xml = tostring(root) + expected = """ + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Stooge): + src = """ + + + + + + + """ + node = fromstring(src) + + dummy = Stooge.from_tree(node) + assert dummy._stooges == [Larry(1), Curly(2), Larry(3), Mo(4)] diff --git a/openpyxl/descriptors/tests/test_serialisable.py b/openpyxl/descriptors/tests/test_serialisable.py new file mode 100644 index 0000000..e47954f --- /dev/null +++ b/openpyxl/descriptors/tests/test_serialisable.py @@ -0,0 +1,209 @@ +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def Serialisable(): + from ..serialisable import Serialisable + return Serialisable + + +@pytest.fixture +def Immutable(Serialisable): + + class Immutable(Serialisable): + + __attrs__ = ('value',) + + def __init__(self, value=None): + self.value = value + + return Immutable + + +class TestSerialisable: + + def test_hash(self, Immutable): + d1 = Immutable() + d2 = Immutable() + assert hash(d1) == hash(d2) + + + def test_add_attrs(self, Immutable): + d1 = Immutable() + d2 = Immutable(value=2) + assert d1 + d2 == d2 + + + def test_str(self, Immutable): + d = Immutable() + assert str(d) == """ +Parameters: +value=None""" + + d2 = Immutable("hello") + assert str(d2) == """ +Parameters: +value='hello'""" + + + def test_eq(self, Immutable): + d1 = Immutable(1) + d2 = Immutable(1) + assert d1 is not d2 + assert d1 == d2 + + + def test_ne(self, Immutable): + d1 = Immutable(1) + d2 = Immutable(2) + assert d1 != d2 + + + def test_copy(self, Immutable): + d1 = Immutable({}) + from copy import copy + d2 = copy(d1) + assert d1.value is not d2.value + + +@pytest.fixture +def Relation(Serialisable): + from ..excel import Relation + + class Dummy(Serialisable): + + tagname = "dummy" + + rId = Relation() + + def __init__(self, rId=None): + self.rId = rId + + return Dummy + + +class TestRelation: + + + def test_binding(self, Relation): + + assert Relation.__namespaced__ == ( + ("rId", "{http://schemas.openxmlformats.org/officeDocument/2006/relationships}rId"), + ) + + + def test_to_tree(self, Relation): + + dummy = Relation("rId1") + + xml = tostring(dummy.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, Relation): + src = """ + + """ + node = fromstring(src) + obj = Relation.from_tree(node) + assert obj.rId == "rId1" + + +@pytest.fixture +def KeywordAttribute(Serialisable): + from ..base import Bool + + class SomeElement(Serialisable): + + tagname = "dummy" + _from = Bool() + + def __init__(self, _from): + self._from = _from + + return SomeElement + + +class TestKeywordAttribute: + + + def test_to_tree(self, KeywordAttribute): + + dummy = KeywordAttribute(_from=True) + + xml = tostring(dummy.to_tree()) + expected = """""" + + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, KeywordAttribute): + src = """""" + + el = fromstring(src) + dummy = KeywordAttribute.from_tree(el) + assert dummy._from is True + + +@pytest.fixture +def Node(Serialisable): + + from ..base import Bool + + class SomeNode(Serialisable): + + tagname = "from" + val = Bool() + + def __init__(self, val): + self.val = val + + return SomeNode + + +@pytest.fixture +def KeywordNode(Serialisable, Node): + + from ..base import Typed + + class SomeElement(Serialisable): + + tagname = "dummy" + _from = Typed(expected_type=Node) + + def __init__(self, _from): + self._from = _from + + return SomeElement + + +class TestKeywordNode: + + + def test_to_tree(self, KeywordNode, Node): + + n = Node(val=True) + dummy = KeywordNode(_from=n) + + xml = tostring(dummy.to_tree()) + + expected = """""" + + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, KeywordNode): + src = """""" + + el = fromstring(src) + dummy = KeywordNode.from_tree(el) + assert dummy._from.val is True diff --git a/openpyxl/develop/__init__.py b/openpyxl/develop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/develop/classify.py b/openpyxl/develop/classify.py new file mode 100644 index 0000000..2f79ad8 --- /dev/null +++ b/openpyxl/develop/classify.py @@ -0,0 +1,328 @@ +from __future__ import absolute_import, print_function +# Copyright (c) 2010-2018 openpyxl + +""" +Generate Python classes from XML Schema +Disclaimer: this is really shabby, "works well enough" code. + +The spyne library does a much better job of interpreting the schema. +""" + +import argparse +import re +import logging + +logging.basicConfig(filename="classify.log", level=logging.DEBUG) + +from openpyxl.tests.schema import ( + sheet_src, + chart_src, + drawing_main_src, + drawing_src, + shared_src, + ) +from openpyxl.descriptors.serialisable import KEYWORDS + +from lxml.etree import parse + + +XSD = "http://www.w3.org/2001/XMLSchema" + +simple_mapping = { + 'xsd:boolean':'Bool', + 'xsd:unsignedInt':'Integer', + 'xsd:unsignedShort':'Integer', + 'xsd:int':'Integer', + 'xsd:double':'Float', + 'xsd:string':'String', + 'xsd:unsignedByte':'Integer', + 'xsd:byte':'Integer', + 'xsd:long':'Float', + 'xsd:token':'String', + 'xsd:dateTime':'DateTime', + 'xsd:hexBinary':'HexBinary', + 's:ST_Panose':'HexBinary', + 's:ST_Lang':'String', + 'ST_Percentage':'String', + 'ST_PositivePercentage':'Percentage', + 'ST_TextPoint':'TextPoint', + 'ST_UniversalMeasure':'UniversalMeasure', + 'ST_Coordinate32':'Coordinate', + 'ST_Coordinate':'Coordinate', + 'ST_Coordinate32Unqualified':'Coordinate', + 's:ST_Xstring':'String', + 'ST_Angle':'Integer', +} + +complex_mapping = { + 'Boolean':'Bool', + 'Double':'Float', + 'Long':'Integer', +} + + +ST_REGEX = re.compile("(?P[a-z]:)?(?PST_[A-Za-z]+)") + + +def get_attribute_group(schema, tagname): + node = schema.find("{%s}attributeGroup[@name='%s']" % (XSD, tagname)) + attrs = node.findall("{%s}attribute" % XSD) + return attrs + + +def get_element_group(schema, tagname): + node = schema.find("{%s}group[@name='%s']" % (XSD, tagname)) + return node.findall(".//{%s}element" % XSD) + + +def convert_default(value): + """ + Convert attribute defaults into Python + """ + if value == "false": + value = False + elif value == "true": + value = True + elif value.isdigit(): + value = int(value) + return value + + +def classify(tagname, src=sheet_src, schema=None): + """ + Generate a Python-class based on the schema definition + """ + if schema is None: + schema = parse(src) + node = schema.find("{%s}complexType[@name='%s']" % (XSD, tagname)) + if node is None: + pass + raise ValueError("Tag {0} not found".format(tagname)) + + types = set() + + clsname = tagname[3:] + tgname = clsname[0].lower() + clsname[1:] + s = """\n\nclass {0}(Serialisable): + + tagname = "{1}"\n\n""".format(clsname, tgname) + attrs = [] + header = [] + sig = [] + body = [] + + node = derived(node) + node = extends(node) + + # attributes + attributes = node.findall("{%s}attribute" % XSD) + _group = node.find("{%s}attributeGroup" % XSD) + if _group is not None: + s += " #Using attribute group{0}\n".format(_group.get('ref')) + attributes.extend(get_attribute_group(schema, _group.get('ref'))) + for el in attributes: + attr = dict(el.attrib) + if attr.get('name', '') in KEYWORDS: + attr['name'] = "_" + attr['name'] + if 'ref' in attr: + continue + attrs.append(attr) + + # XML attributes are optional by default + if attr.get("use") != "required": + attr["use"] = "allow_none=True" + else: + attr["use"] = "" + default = attr.get('default', None) + if default: + default = convert_default(default) + attr['default'] = default + + if attr.get("type").startswith("ST_"): + attr['type'] = simple(attr.get("type"), schema, attr['use']) + types.add(attr['type'].split("(")[0]) + defn = "{name} = {type}" + else: + if attr['type'] in simple_mapping: + attr['type'] = simple_mapping[attr['type']] + types.add(attr['type']) + defn = "{name} = {type}({use})" + else: + defn = "{name} = Typed(expected_type={type}, {use})" + header.append(defn.format(**attr)) + + children = [] + element_names = [] + elements = node.findall(".//{%s}element" % XSD) + choice = node.findall("{%s}choice" % XSD) + if choice: + s += """ # some elements are choice\n""" + + groups = node.findall("{%s}sequence/{%s}group" % (XSD, XSD)) + for group in groups: + ref = group.get("ref") + s += """ # uses element group {0}\n""".format(ref) + elements.extend(get_element_group(schema, ref)) + + els = [] + header_els = [] + for el in elements: + attr = dict(el.attrib) + attr['default'] = None + + typename = el.get("type") + if typename is None: + logging.log(logging.DEBUG, "Cannot resolve {0}".format(el.tag)) + continue + + match = ST_REGEX.match(typename) + if typename.startswith("xsd:"): + attr['type'] = simple_mapping[typename] + types.add(attr['type']) + elif match is not None: + src = srcs_mapping.get(match.group('schema')) + if src is not None: + schema = parse(src) + typename = match.group('typename') + attr['type'] = simple(typename, schema) + else: + if (typename.startswith("a:") + or typename.startswith("s:") + ): + attr['type'] = typename[5:] + else: + attr['type'] = typename[3:] + children.append(typename) + element_names.append(attr['name']) + + attr['use'] = "" + if el.get("minOccurs") == "0" or el in choice: + attr['use'] = "allow_none=True" + els.append(attr) + if attr['type'] in complex_mapping: + attr['type'] = complex_mapping[attr['type']] + defn = "{name} = {type}(nested=True, {use})" + else: + defn = "{name} = Typed(expected_type={type}, {use})" + max = attr.get("maxOccurs") + if max and max != "1": + defn = "{name} = Sequence(expected_type={type})" + attr['default'] = () + header_els.append(defn.format(**attr)) + + header = header_els + header + + s += " " + "\n ".join(header) + "\n\n" + + if element_names: + names = (c for c in element_names) + s += " __elements__ = {0}\n\n".format(tuple(names)) + + attrs = els + attrs # elements first + + if attrs: + s += " def __init__(self,\n" + for attr in attrs: + s += " {name}={default},\n".format(**attr) + s += " ):\n" + else: + s += " pass" + for attr in attrs: + s += " self.{name} = {name}\n".format(**attr) + + return s, types, children + + +def derived(node): + base = node.find("{%s}simpleContent" % XSD) + return base or node + + +def extends(node): + base = node.find("{%s}extension" % XSD) + return base or node + + +def simple(tagname, schema, use=""): + + node = schema.find("{%s}simpleType[@name='%s']" % (XSD, tagname)) + constraint = node.find("{%s}restriction" % XSD) + if constraint is None: + return "unknown defintion for {0}".format(tagname) + + typ = constraint.get("base") + typ = "{0}()".format(simple_mapping.get(typ, typ)) + values = constraint.findall("{%s}enumeration" % XSD) + values = [v.get('value') for v in values] + if values: + s = "Set" + if "none" in values: + idx = values.index("none") + del values[idx] + s = "NoneSet" + typ = s + "(values=({0}))".format(values) + return typ + +srcs_mapping = {'a:':drawing_main_src, 's:':shared_src} + +class ClassMaker: + """ + Generate + """ + + def __init__(self, tagname, src=sheet_src, classes=set()): + self.schema=parse(src) + self.types = set() + self.classes = classes + self.body = "" + self.create(tagname) + + def create(self, tagname): + body, types, children = classify(tagname, schema=self.schema) + self.body = body + self.body + self.types = self.types.union(types) + for child in children: + if (child.startswith("a:") + or child.startswith("s:") + ): + src = srcs_mapping[child[:2]] + tagname = child[2:] + if tagname not in self.classes: + cm = ClassMaker(tagname, src=src, classes=self.classes) + self.body = cm.body + self.body # prepend dependent types + self.types.union(cm.types) + self.classes.add(tagname) + self.classes.union(cm.classes) + continue + if child not in self.classes: + self.create(child) + self.classes.add(child) + + def __str__(self): + s = """#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import (\n Typed,""" + for t in self.types: + s += "\n {0},".format(t) + s += (")\n") + s += self.body + return s + + +def make(element, schema=sheet_src): + cm = ClassMaker(element, schema) + print(cm) + + +commands = argparse.ArgumentParser(description="Generate Python classes for a specific scheme element") +commands.add_argument('element', help='The XML type to be converted') +commands.add_argument('--schema', + help='The relevant schema. The default is for worksheets', + choices=["sheet_src", "chart_src", "shared_src", "drawing_src", "drawing_main_src"], + default="sheet_src", + ) + +if __name__ == "__main__": + args = commands.parse_args() + schema = globals().get(args.schema) + make(args.element, schema) diff --git a/openpyxl/develop/stub.py b/openpyxl/develop/stub.py new file mode 100644 index 0000000..35f3d9f --- /dev/null +++ b/openpyxl/develop/stub.py @@ -0,0 +1,53 @@ +""" +Generate test stubs for Serialisable classes +""" +import argparse + + +stub = ''' +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def {clsname}(): + from ..{module} import {clsname} + return {clsname} + + +class Test{clsname}: + + def test_ctor(self, {clsname}): + {module} = {clsname}() + xml = tostring({module}.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, {clsname}): + src = """ + + """ + node = fromstring(src) + {module} = {clsname}.from_tree(node) + assert {module} == {clsname}() +''' + + +def generate(clsname, module): + return stub.format(clsname=clsname, module=module) + +commands = argparse.ArgumentParser(description="Generate stub tests for Serialisable classes") +commands.add_argument('clsname', help='Name of the class to be tested') +commands.add_argument('--module', help='Name of the module to be tested', default="fut") + + +if __name__ == "__main__": + args = commands.parse_args() + print(generate(args.clsname, args.module)) diff --git a/openpyxl/develop/tests/__init__.py b/openpyxl/develop/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/develop/tests/data/defined_name.xsd b/openpyxl/develop/tests/data/defined_name.xsd new file mode 100644 index 0000000..70d694a --- /dev/null +++ b/openpyxl/develop/tests/data/defined_name.xsd @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/develop/tests/test_classify.py b/openpyxl/develop/tests/test_classify.py new file mode 100644 index 0000000..eabdcd7 --- /dev/null +++ b/openpyxl/develop/tests/test_classify.py @@ -0,0 +1,142 @@ +import pytest +import py + +from lxml.etree import parse +import os.path + +from openpyxl.tests.schema import parse +from openpyxl.tests.schema import drawing_main_src +from ..classify import XSD + + +@pytest.fixture +def datadir(): + here = os.path.split(__file__)[0] + return py.path.local(here).join("data") + + +@pytest.fixture +def schema(): + return parse(drawing_main_src) + + +def test_attribute_group(schema): + from ..classify import get_attribute_group + attrs = get_attribute_group(schema, "AG_Locking") + assert [a.get('name') for a in attrs] == ['noGrp', 'noSelect', 'noRot', + 'noChangeAspect', 'noMove', 'noResize', 'noEditPoints', 'noAdjustHandles', + 'noChangeArrowheads', 'noChangeShapeType'] + + +def test_element_group(schema): + from ..classify import get_element_group + els = get_element_group(schema, "EG_FillProperties") + assert [el.get('name') for el in els] == ['noFill', 'solidFill', 'gradFill', 'blipFill', 'pattFill', 'grpFill'] + + +def test_class_no_deps(schema): + from ..classify import classify + cls = classify("CT_FileRecoveryPr") + assert cls[0] == """ + +class FileRecoveryPr(Serialisable): + + tagname = "fileRecoveryPr" + + autoRecover = Bool(allow_none=True) + crashSave = Bool(allow_none=True) + dataExtractLoad = Bool(allow_none=True) + repairLoad = Bool(allow_none=True) + + def __init__(self, + autoRecover=True, + crashSave=False, + dataExtractLoad=False, + repairLoad=False, + ): + self.autoRecover = autoRecover + self.crashSave = crashSave + self.dataExtractLoad = dataExtractLoad + self.repairLoad = repairLoad +""" + + +def test_derived(datadir): + from ..classify import derived + datadir.chdir() + node = parse("defined_name.xsd").find("{%s}complexType" % XSD) + assert derived(node).tag == "{%s}simpleContent" % XSD + + +def test_extends(datadir): + from ..classify import derived, extends + datadir.chdir() + node = parse("defined_name.xsd").find("{%s}complexType" % XSD) + node = extends(node) + assert derived(node).tag == "{%s}simpleContent" % XSD + + +def test_simple_content(schema): + from ..classify import classify + cls = classify("CT_DefinedName")[0] + assert cls == """ + +class DefinedName(Serialisable): + + tagname = "definedName" + + name = String() + 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) + + def __init__(self, + name=None, + comment=None, + customMenu=None, + description=None, + help=None, + statusBar=None, + localSheetId=None, + hidden=False, + function=False, + vbProcedure=False, + xlm=False, + functionGroupId=None, + shortcutKey=None, + publishToServer=False, + workbookParameter=False, + ): + 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 +""" + + +def test_simpleType(schema): + from ..classify import simple + typ = simple("ST_FontCollectionIndex", schema) + assert typ == "NoneSet(values=(['major', 'minor']))" diff --git a/openpyxl/drawing/__init__.py b/openpyxl/drawing/__init__.py new file mode 100644 index 0000000..30e4f5e --- /dev/null +++ b/openpyxl/drawing/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +from .drawing import Drawing diff --git a/openpyxl/drawing/colors.py b/openpyxl/drawing/colors.py new file mode 100644 index 0000000..ffdc821 --- /dev/null +++ b/openpyxl/drawing/colors.py @@ -0,0 +1,438 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat import basestring, unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Integer, + Set, + MinMax, +) +from openpyxl.descriptors.excel import Percentage +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedValue, + NestedInteger, + EmptyTag, +) + +from openpyxl.styles.colors import RGB +from openpyxl.xml.constants import DRAWING_NS + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +PRESET_COLORS = [ + 'aliceBlue', 'antiqueWhite', 'aqua', 'aquamarine', + 'azure', 'beige', 'bisque', 'black', 'blanchedAlmond', 'blue', + 'blueViolet', 'brown', 'burlyWood', 'cadetBlue', 'chartreuse', + 'chocolate', 'coral', 'cornflowerBlue', 'cornsilk', 'crimson', 'cyan', + 'darkBlue', 'darkCyan', 'darkGoldenrod', 'darkGray', 'darkGrey', + 'darkGreen', 'darkKhaki', 'darkMagenta', 'darkOliveGreen', 'darkOrange', + 'darkOrchid', 'darkRed', 'darkSalmon', 'darkSeaGreen', 'darkSlateBlue', + 'darkSlateGray', 'darkSlateGrey', 'darkTurquoise', 'darkViolet', + 'dkBlue', 'dkCyan', 'dkGoldenrod', 'dkGray', 'dkGrey', 'dkGreen', + 'dkKhaki', 'dkMagenta', 'dkOliveGreen', 'dkOrange', 'dkOrchid', 'dkRed', + 'dkSalmon', 'dkSeaGreen', 'dkSlateBlue', 'dkSlateGray', 'dkSlateGrey', + 'dkTurquoise', 'dkViolet', 'deepPink', 'deepSkyBlue', 'dimGray', + 'dimGrey', 'dodgerBlue', 'firebrick', 'floralWhite', 'forestGreen', + 'fuchsia', 'gainsboro', 'ghostWhite', 'gold', 'goldenrod', 'gray', + 'grey', 'green', 'greenYellow', 'honeydew', 'hotPink', 'indianRed', + 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderBlush', 'lawnGreen', + 'lemonChiffon', 'lightBlue', 'lightCoral', 'lightCyan', + 'lightGoldenrodYellow', 'lightGray', 'lightGrey', 'lightGreen', + 'lightPink', 'lightSalmon', 'lightSeaGreen', 'lightSkyBlue', + 'lightSlateGray', 'lightSlateGrey', 'lightSteelBlue', 'lightYellow', + 'ltBlue', 'ltCoral', 'ltCyan', 'ltGoldenrodYellow', 'ltGray', 'ltGrey', + 'ltGreen', 'ltPink', 'ltSalmon', 'ltSeaGreen', 'ltSkyBlue', + 'ltSlateGray', 'ltSlateGrey', 'ltSteelBlue', 'ltYellow', 'lime', + 'limeGreen', 'linen', 'magenta', 'maroon', 'medAquamarine', 'medBlue', + 'medOrchid', 'medPurple', 'medSeaGreen', 'medSlateBlue', + 'medSpringGreen', 'medTurquoise', 'medVioletRed', 'mediumAquamarine', + 'mediumBlue', 'mediumOrchid', 'mediumPurple', 'mediumSeaGreen', + 'mediumSlateBlue', 'mediumSpringGreen', 'mediumTurquoise', + 'mediumVioletRed', 'midnightBlue', 'mintCream', 'mistyRose', 'moccasin', + 'navajoWhite', 'navy', 'oldLace', 'olive', 'oliveDrab', 'orange', + 'orangeRed', 'orchid', 'paleGoldenrod', 'paleGreen', 'paleTurquoise', + 'paleVioletRed', 'papayaWhip', 'peachPuff', 'peru', 'pink', 'plum', + 'powderBlue', 'purple', 'red', 'rosyBrown', 'royalBlue', 'saddleBrown', + 'salmon', 'sandyBrown', 'seaGreen', 'seaShell', 'sienna', 'silver', + 'skyBlue', 'slateBlue', 'slateGray', 'slateGrey', 'snow', 'springGreen', + 'steelBlue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', + 'wheat', 'white', 'whiteSmoke', 'yellow', 'yellowGreen' + ] + + +SCHEME_COLORS= ['bg1', 'tx1', 'bg2', 'tx2', 'accent1', 'accent2', 'accent3', + 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink', 'phClr', 'dk1', 'lt1', + 'dk2', 'lt2' + ] + + +class Transform(Serialisable): + + pass + + +class SystemColor(Serialisable): + + tagname = "sysClr" + namespace = DRAWING_NS + + # color transform options + tint = NestedInteger(allow_none=True) + shade = NestedInteger(allow_none=True) + comp = Typed(expected_type=Transform, allow_none=True) + inv = Typed(expected_type=Transform, allow_none=True) + gray = Typed(expected_type=Transform, allow_none=True) + alpha = NestedInteger(allow_none=True) + alphaOff = NestedInteger(allow_none=True) + alphaMod = NestedInteger(allow_none=True) + hue = NestedInteger(allow_none=True) + hueOff = NestedInteger(allow_none=True) + hueMod = NestedInteger(allow_none=True) + sat = NestedInteger(allow_none=True) + satOff = NestedInteger(allow_none=True) + satMod = NestedInteger(allow_none=True) + lum = NestedInteger(allow_none=True) + lumOff = NestedInteger(allow_none=True) + lumMod = NestedInteger(allow_none=True) + red = NestedInteger(allow_none=True) + redOff = NestedInteger(allow_none=True) + redMod = NestedInteger(allow_none=True) + green = NestedInteger(allow_none=True) + greenOff = NestedInteger(allow_none=True) + greenMod = NestedInteger(allow_none=True) + blue = NestedInteger(allow_none=True) + blueOff = NestedInteger(allow_none=True) + blueMod = NestedInteger(allow_none=True) + gamma = Typed(expected_type=Transform, allow_none=True) + invGamma = Typed(expected_type=Transform, allow_none=True) + + val = Set(values=( ['scrollBar', 'background', 'activeCaption', + 'inactiveCaption', 'menu', 'window', 'windowFrame', 'menuText', + 'windowText', 'captionText', 'activeBorder', 'inactiveBorder', + 'appWorkspace', 'highlight', 'highlightText', 'btnFace', 'btnShadow', + 'grayText', 'btnText', 'inactiveCaptionText', 'btnHighlight', + '3dDkShadow', '3dLight', 'infoText', 'infoBk', 'hotLight', + 'gradientActiveCaption', 'gradientInactiveCaption', 'menuHighlight', + 'menuBar'] ) + ) + lastClr = RGB(allow_none=True) + + __elements__ = ('tint', 'shade', 'comp', 'inv', 'gray', "alpha", + "alphaOff", "alphaMod", "hue", "hueOff", "hueMod", "hueOff", "sat", + "satOff", "satMod", "lum", "lumOff", "lumMod", "red", "redOff", "redMod", + "green", "greenOff", "greenMod", "blue", "blueOff", "blueMod", "gamma", + "invGamma") + + def __init__(self, + val="windowText", + lastClr=None, + tint=None, + shade=None, + comp=None, + inv=None, + gray=None, + alpha=None, + alphaOff=None, + alphaMod=None, + hue=None, + hueOff=None, + hueMod=None, + sat=None, + satOff=None, + satMod=None, + lum=None, + lumOff=None, + lumMod=None, + red=None, + redOff=None, + redMod=None, + green=None, + greenOff=None, + greenMod=None, + blue=None, + blueOff=None, + blueMod=None, + gamma=None, + invGamma=None + ): + self.val = val + self.lastClr = lastClr + self.tint = tint + self.shade = shade + self.comp = comp + self.inv = inv + self.gray = gray + self.alpha = alpha + self.alphaOff = alphaOff + self.alphaMod = alphaMod + self.hue = hue + self.hueOff = hueOff + self.hueMod = hueMod + self.sat = sat + self.satOff = satOff + self.satMod = satMod + self.lum = lum + self.lumOff = lumOff + self.lumMod = lumMod + self.red = red + self.redOff = redOff + self.redMod = redMod + self.green = green + self.greenOff = greenOff + self.greenMod = greenMod + self.blue = blue + self.blueOff = blueOff + self.blueMod = blueMod + self.gamma = gamma + self.invGamma = invGamma + + +class HSLColor(Serialisable): + + tagname = "hslClr" + + hue = Integer() + sat = MinMax(min=0, max=100) + lum = MinMax(min=0, max=100) + + #TODO add color transform options + + def __init__(self, + hue=None, + sat=None, + lum=None, + ): + self.hue = hue + self.sat = sat + self.lum = lum + + + +class RGBPercent(Serialisable): + + tagname = "rgbClr" + + r = MinMax(min=0, max=100) + g = MinMax(min=0, max=100) + b = MinMax(min=0, max=100) + + #TODO add color transform options + + def __init__(self, + r=None, + g=None, + b=None, + ): + self.r = r + self.g = g + self.b = b + + +class SchemeColor(Serialisable): + + tagname = "schemeClr" + namespace = DRAWING_NS + + tint = NestedInteger(allow_none=True) + shade = NestedInteger(allow_none=True) + comp = EmptyTag(allow_none=True) + inv = NestedInteger(allow_none=True) + gray = NestedInteger(allow_none=True) + alpha = NestedInteger(allow_none=True) + alphaOff = NestedInteger(allow_none=True) + alphaMod = NestedInteger(allow_none=True) + hue = NestedInteger(allow_none=True) + hueOff = NestedInteger(allow_none=True) + hueMod = NestedInteger(allow_none=True) + sat = NestedInteger(allow_none=True) + satOff = NestedInteger(allow_none=True) + satMod = NestedInteger(allow_none=True) + lum = NestedInteger(allow_none=True) + lumOff = NestedInteger(allow_none=True) + lumMod = NestedInteger(allow_none=True) + red = NestedInteger(allow_none=True) + redOff = NestedInteger(allow_none=True) + redMod = NestedInteger(allow_none=True) + green = NestedInteger(allow_none=True) + greenOff = NestedInteger(allow_none=True) + greenMod = NestedInteger(allow_none=True) + blue = NestedInteger(allow_none=True) + blueOff = NestedInteger(allow_none=True) + blueMod = NestedInteger(allow_none=True) + gamma = EmptyTag(allow_none=True) + invGamma = EmptyTag(allow_none=True) + val = Set(values=(['bg1', 'tx1', 'bg2', 'tx2', 'accent1', 'accent2', + 'accent3', 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink', 'phClr', + 'dk1', 'lt1', 'dk2', 'lt2'])) + + __elements__ = ('tint', 'shade', 'comp', 'inv', 'gray', 'alpha', + 'alphaOff', 'alphaMod', 'hue', 'hueOff', 'hueMod', 'sat', 'satOff', + 'satMod', 'lum', 'lumMod', 'lumOff', 'red', 'redOff', 'redMod', 'green', + 'greenOff', 'greenMod', 'blue', 'blueOff', 'blueMod', 'gamma', + 'invGamma') + + def __init__(self, + tint=None, + shade=None, + comp=None, + inv=None, + gray=None, + alpha=None, + alphaOff=None, + alphaMod=None, + hue=None, + hueOff=None, + hueMod=None, + sat=None, + satOff=None, + satMod=None, + lum=None, + lumOff=None, + lumMod=None, + red=None, + redOff=None, + redMod=None, + green=None, + greenOff=None, + greenMod=None, + blue=None, + blueOff=None, + blueMod=None, + gamma=None, + invGamma=None, + val=None, + ): + self.tint = tint + self.shade = shade + self.comp = comp + self.inv = inv + self.gray = gray + self.alpha = alpha + self.alphaOff = alphaOff + self.alphaMod = alphaMod + self.hue = hue + self.hueOff = hueOff + self.hueMod = hueMod + self.sat = sat + self.satOff = satOff + self.satMod = satMod + self.lum = lum + self.lumOff = lumOff + self.lumMod = lumMod + self.red = red + self.redOff = redOff + self.redMod = redMod + self.green = green + self.greenOff = greenOff + self.greenMod = greenMod + self.blue = blue + self.blueOff = blueOff + self.blueMod = blueMod + self.gamma = gamma + self.invGamma = invGamma + self.val = val + +class ColorChoice(Serialisable): + + tagname = "colorChoice" + namespace = DRAWING_NS + + scrgbClr = Typed(expected_type=RGBPercent, allow_none=True) + RGBPercent = Alias('scrgbClr') + srgbClr = NestedValue(expected_type=unicode, allow_none=True) # needs pattern and can have transform + RGB = Alias('srgbClr') + hslClr = Typed(expected_type=HSLColor, allow_none=True) + sysClr = Typed(expected_type=SystemColor, allow_none=True) + schemeClr = Typed(expected_type=SchemeColor, allow_none=True) + prstClr = NestedNoneSet(values=PRESET_COLORS) + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + scrgbClr=None, + srgbClr=None, + hslClr=None, + sysClr=None, + schemeClr=None, + prstClr=None, + ): + self.scrgbClr = scrgbClr + self.srgbClr = srgbClr + self.hslClr = hslClr + self.sysClr = sysClr + self.schemeClr = schemeClr + self.prstClr = prstClr + +_COLOR_SET = ('dk1', 'lt1', 'dk2', 'lt2', 'accent1', 'accent2', 'accent3', + 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink') + + +class ColorMapping(Serialisable): + + tagname = "clrMapOvr" + + bg1 = Set(values=_COLOR_SET) + tx1 = Set(values=_COLOR_SET) + bg2 = Set(values=_COLOR_SET) + tx2 = Set(values=_COLOR_SET) + accent1 = Set(values=_COLOR_SET) + accent2 = Set(values=_COLOR_SET) + accent3 = Set(values=_COLOR_SET) + accent4 = Set(values=_COLOR_SET) + accent5 = Set(values=_COLOR_SET) + accent6 = Set(values=_COLOR_SET) + hlink = Set(values=_COLOR_SET) + folHlink = Set(values=_COLOR_SET) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + bg1="lt1", + tx1="dk1", + bg2="lt2", + tx2="dk2", + accent1="accent1", + accent2="accent2", + accent3="accent3", + accent4="accent4", + accent5="accent5", + accent6="accent6", + hlink="hlink", + folHlink="folHlink", + extLst=None, + ): + self.bg1 = bg1 + self.tx1 = tx1 + self.bg2 = bg2 + self.tx2 = tx2 + self.accent1 = accent1 + self.accent2 = accent2 + self.accent3 = accent3 + self.accent4 = accent4 + self.accent5 = accent5 + self.accent6 = accent6 + self.hlink = hlink + self.folHlink = folHlink + self.extLst = extLst + + +class ColorChoiceDescriptor(Typed): + """ + Objects can choose from 7 different kinds of color system. + Assume RGBHex if a string is passed in. + """ + + expected_type = ColorChoice + allow_none = True + + def __set__(self, instance, value): + if isinstance(value, basestring): + value = ColorChoice(srgbClr=value) + else: + if hasattr(self, "namespace") and value is not None: + value.namespace = self.namespace + super(ColorChoiceDescriptor, self).__set__(instance, value) diff --git a/openpyxl/drawing/drawing.py b/openpyxl/drawing/drawing.py new file mode 100644 index 0000000..92d2223 --- /dev/null +++ b/openpyxl/drawing/drawing.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import +from __future__ import division +# Copyright (c) 2010-2018 openpyxl + +import math + +from openpyxl.compat import deprecated + +from openpyxl.styles.colors import Color, BLACK, WHITE +from openpyxl.utils.units import ( + pixels_to_EMU, + EMU_to_pixels, + short_color, +) + + +class Drawing(object): + """ a drawing object - eg container for shapes or charts + we assume user specifies dimensions in pixels; units are + converted to EMU in the drawing part + """ + + count = 0 + + def __init__(self): + + self.name = '' + self.description = '' + self.coordinates = ((1, 2), (16, 8)) + self.left = 0 + self.top = 0 + self._width = 21 # default in px + self._height = 192 #default in px + self.resize_proportional = False + self.rotation = 0 + self.anchortype = "absolute" + self.anchorcol = 0 # left cell + self.anchorrow = 0 # top row + + + @property + def width(self): + return self._width + + @width.setter + def width(self, w): + if self.resize_proportional and w: + ratio = self._height / self._width + self._height = round(ratio * w) + self._width = w + + @property + def height(self): + return self._height + + @height.setter + def height(self, h): + if self.resize_proportional and h: + ratio = self._width / self._height + self._width = round(ratio * h) + self._height = h + + def set_dimension(self, w=0, h=0): + + xratio = w / self._width + yratio = h / self._height + + if self.resize_proportional and w and h: + if (xratio * self._height) < h: + self._height = math.ceil(xratio * self._height) + self._width = w + else: + self._width = math.ceil(yratio * self._width) + self._height = h + + @deprecated("Private method used when serialising") + def get_emu_dimensions(self): + """ return (x, y, w, h) in EMU """ + + return (pixels_to_EMU(self.left), pixels_to_EMU(self.top), + pixels_to_EMU(self._width), pixels_to_EMU(self._height)) + + + @property + def anchor(self): + from .spreadsheet_drawing import ( + OneCellAnchor, + TwoCellAnchor, + AbsoluteAnchor) + if self.anchortype == "absolute": + anchor = AbsoluteAnchor() + anchor.pos.x = pixels_to_EMU(self.left) + anchor.pos.y = pixels_to_EMU(self.top) + + elif self.anchortype == "oneCell": + anchor = OneCellAnchor() + anchor._from.col = self.anchorcol + anchor._from.row = self.anchorrow + + anchor.ext.width = pixels_to_EMU(self._width) + anchor.ext.height = pixels_to_EMU(self._height) + + return anchor diff --git a/openpyxl/drawing/effect.py b/openpyxl/drawing/effect.py new file mode 100644 index 0000000..6e05f56 --- /dev/null +++ b/openpyxl/drawing/effect.py @@ -0,0 +1,404 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Set, + Bool, + Integer, + NoneSet, + Float,) + + +from .colors import ColorChoice + + +class TintEffect(Serialisable): + + hue = Integer() + amt = Integer() + + def __init__(self, + hue=None, + amt=None, + ): + self.hue = hue + self.amt = amt + + +class LuminanceEffect(Serialisable): + + bright = Integer() + contrast = Integer() + + def __init__(self, + bright=None, + contrast=None, + ): + self.bright = bright + self.contrast = contrast + + +class HSLEffect(Serialisable): + + hue = Integer() + sat = Integer() + lum = Integer() + + def __init__(self, + hue=None, + sat=None, + lum=None, + ): + self.hue = hue + self.sat = sat + self.lum = lum + + +class GrayscaleEffect(Serialisable): + + pass + +class FillOverlayEffect(Serialisable): + + blend = Set(values=(['over', 'mult', 'screen', 'darken', 'lighten'])) + + def __init__(self, + blend=None, + ): + self.blend = blend + + +class DuotoneEffect(Serialisable): + + pass + +class ColorReplaceEffect(Serialisable): + + pass + +class Color(Serialisable): + + pass + +class ColorChangeEffect(Serialisable): + + useA = Bool(allow_none=True) + clrFrom = Typed(expected_type=Color, ) + clrTo = Typed(expected_type=Color, ) + + def __init__(self, + useA=None, + clrFrom=None, + clrTo=None, + ): + self.useA = useA + self.clrFrom = clrFrom + self.clrTo = clrTo + + +class BlurEffect(Serialisable): + + rad = Float() + grow = Bool(allow_none=True) + + def __init__(self, + rad=None, + grow=None, + ): + self.rad = rad + self.grow = grow + + +class BiLevelEffect(Serialisable): + + thresh = Integer() + + def __init__(self, + thresh=None, + ): + self.thresh = thresh + + +class AlphaReplaceEffect(Serialisable): + + a = Integer() + + def __init__(self, + a=None, + ): + self.a = a + + +class AlphaModulateFixedEffect(Serialisable): + + amt = Integer() + + def __init__(self, + amt=None, + ): + self.amt = amt + + +class EffectContainer(Serialisable): + + type = Set(values=(['sib', 'tree'])) + name = String(allow_none=True) + + def __init__(self, + type=None, + name=None, + ): + self.type = type + self.name = name + + +class AlphaModulateEffect(Serialisable): + + cont = Typed(expected_type=EffectContainer, ) + + def __init__(self, + cont=None, + ): + self.cont = cont + + +class AlphaInverseEffect(Serialisable): + + pass + +class AlphaFloorEffect(Serialisable): + + pass + +class AlphaCeilingEffect(Serialisable): + + pass + +class AlphaBiLevelEffect(Serialisable): + + thresh = Integer() + + def __init__(self, + thresh=None, + ): + self.thresh = thresh + + +class GlowEffect(ColorChoice): + + rad = Float() + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + rad=None, + **kw + ): + self.rad = rad + super(GlowEffect, self).__init__(**kw) + + +class InnerShadowEffect(ColorChoice): + + blurRad = Float() + dist = Float() + dir = Integer() + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + blurRad=None, + dist=None, + dir=None, + **kw + ): + self.blurRad = blurRad + self.dist = dist + self.dir = dir + super(InnerShadowEffect, self).__init__(**kw) + + +class OuterShadow(ColorChoice): + + tagname = "outerShdw" + + blurRad = Float(allow_none=True) + dist = Float(allow_none=True) + dir = Integer(allow_none=True) + sx = Integer(allow_none=True) + sy = Integer(allow_none=True) + kx = Integer(allow_none=True) + ky = Integer(allow_none=True) + algn = Set(values=['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br']) + rotWithShape = Bool(allow_none=True) + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + blurRad=None, + dist=None, + dir=None, + sx=None, + sy=None, + kx=None, + ky=None, + algn=None, + rotWithShape=None, + **kw + ): + self.blurRad = blurRad + self.dist = dist + self.dir = dir + self.sx = sx + self.sy = sy + self.kx = kx + self.ky = ky + self.algn = algn + self.rotWithShape = rotWithShape + super(OuterShadow, self).__init__(**kw) + + +class PresetShadowEffect(ColorChoice): + + prst = Set(values=(['shdw1', 'shdw2', 'shdw3', 'shdw4', 'shdw5', 'shdw6', + 'shdw7', 'shdw8', 'shdw9', 'shdw10', 'shdw11', 'shdw12', 'shdw13', + 'shdw14', 'shdw15', 'shdw16', 'shdw17', 'shdw18', 'shdw19', 'shdw20'])) + dist = Float() + dir = Integer() + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + prst=None, + dist=None, + dir=None, + **kw + ): + self.prst = prst + self.dist = dist + self.dir = dir + super(PresetShadowEffect, self).__init__(**kw) + + +class ReflectionEffect(Serialisable): + + blurRad = Float() + stA = Integer() + stPos = Integer() + endA = Integer() + endPos = Integer() + dist = Float() + dir = Integer() + fadeDir = Integer() + sx = Integer() + sy = Integer() + kx = Integer() + ky = Integer() + algn = Set(values=(['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br'])) + rotWithShape = Bool(allow_none=True) + + def __init__(self, + blurRad=None, + stA=None, + stPos=None, + endA=None, + endPos=None, + dist=None, + dir=None, + fadeDir=None, + sx=None, + sy=None, + kx=None, + ky=None, + algn=None, + rotWithShape=None, + ): + self.blurRad = blurRad + self.stA = stA + self.stPos = stPos + self.endA = endA + self.endPos = endPos + self.dist = dist + self.dir = dir + self.fadeDir = fadeDir + self.sx = sx + self.sy = sy + self.kx = kx + self.ky = ky + self.algn = algn + self.rotWithShape = rotWithShape + + +class SoftEdgesEffect(Serialisable): + + rad = Float() + + def __init__(self, + rad=None, + ): + self.rad = rad + + +class EffectList(Serialisable): + + blur = Typed(expected_type=BlurEffect, allow_none=True) + fillOverlay = Typed(expected_type=FillOverlayEffect, allow_none=True) + glow = Typed(expected_type=GlowEffect, allow_none=True) + innerShdw = Typed(expected_type=InnerShadowEffect, allow_none=True) + outerShdw = Typed(expected_type=OuterShadow, allow_none=True) + prstShdw = Typed(expected_type=PresetShadowEffect, allow_none=True) + reflection = Typed(expected_type=ReflectionEffect, allow_none=True) + softEdge = Typed(expected_type=SoftEdgesEffect, allow_none=True) + + __elements__ = ('blur', 'fillOverlay', 'glow', 'innerShdw', 'outerShdw', + 'prstShdw', 'reflection', 'softEdge') + + def __init__(self, + blur=None, + fillOverlay=None, + glow=None, + innerShdw=None, + outerShdw=None, + prstShdw=None, + reflection=None, + softEdge=None, + ): + self.blur = blur + self.fillOverlay = fillOverlay + self.glow = glow + self.innerShdw = innerShdw + self.outerShdw = outerShdw + self.prstShdw = prstShdw + self.reflection = reflection + self.softEdge = softEdge diff --git a/openpyxl/drawing/fill.py b/openpyxl/drawing/fill.py new file mode 100644 index 0000000..1789375 --- /dev/null +++ b/openpyxl/drawing/fill.py @@ -0,0 +1,392 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat import unicode +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Bool, + Integer, + Set, + NoneSet, + Typed, + MinMax, + Sequence, +) +from openpyxl.descriptors.excel import ( + Relation, + Percentage, +) +from openpyxl.descriptors.nested import NestedNoneSet, NestedValue +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.xml.constants import DRAWING_NS + +from .colors import ( + ColorChoice, + HSLColor, + SystemColor, + SchemeColor, + RGBPercent, + PRESET_COLORS, +) + + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from .effect import * + +""" +Fill elements from drawing main schema +""" + +class PatternFillProperties(Serialisable): + + tagname = "pattFill" + namespace = DRAWING_NS + + prst = NoneSet(values=(['pct5', 'pct10', 'pct20', 'pct25', 'pct30', 'pct40', + 'pct50', 'pct60', 'pct70', 'pct75', 'pct80', 'pct90', 'horz', 'vert', + 'ltHorz', 'ltVert', 'dkHorz', 'dkVert', 'narHorz', 'narVert', 'dashHorz', + 'dashVert', 'cross', 'dnDiag', 'upDiag', 'ltDnDiag', 'ltUpDiag', + 'dkDnDiag', 'dkUpDiag', 'wdDnDiag', 'wdUpDiag', 'dashDnDiag', + 'dashUpDiag', 'diagCross', 'smCheck', 'lgCheck', 'smGrid', 'lgGrid', + 'dotGrid', 'smConfetti', 'lgConfetti', 'horzBrick', 'diagBrick', + 'solidDmnd', 'openDmnd', 'dotDmnd', 'plaid', 'sphere', 'weave', 'divot', + 'shingle', 'wave', 'trellis', 'zigZag'])) + preset = Alias("prst") + fgClr = Typed(expected_type=ColorChoice, allow_none=True) + foreground = Alias("fgClr") + bgClr = Typed(expected_type=ColorChoice, allow_none=True) + background = Alias("bgClr") + + __elements__ = ("fgClr", "bgClr") + + def __init__(self, + prst=None, + fgClr=None, + bgClr=None, + ): + self.prst = prst + self.fgClr = fgClr + self.bgClr = bgClr + + +class RelativeRect(Serialisable): + + tagname = "rect" + namespace = DRAWING_NS + + l = Percentage(allow_none=True) + left = Alias('l') + t = Percentage(allow_none=True) + top = Alias('t') + r = Percentage(allow_none=True) + right = Alias('r') + b = Percentage(allow_none=True) + bottom = Alias('b') + + def __init__(self, + l=None, + t=None, + r=None, + b=None, + ): + self.l = l + self.t = t + self.r = r + self.b = b + + +class StretchInfoProperties(Serialisable): + + tagname = "stretch" + namespace = DRAWING_NS + + fillRect = Typed(expected_type=RelativeRect, allow_none=True) + + def __init__(self, + fillRect=RelativeRect(), + ): + self.fillRect = fillRect + + +class GradientStop(Serialisable): + + tagname = "gradStop" + + pos = MinMax(min=0, max=100000, allow_none=True) + # Color Choice Group + + def __init__(self, + pos=None, + ): + self.pos = pos + + +class GradientStopList(Serialisable): + + tagname = "gradStopLst" + + gs = Sequence(expected_type=GradientStop) + + def __init__(self, + gs=None, + ): + if gs is None: + gs = [GradientStop(), GradientStop()] + self.gs = gs + + +class LinearShadeProperties(Serialisable): + + ang = Integer() + scaled = Bool(allow_none=True) + + def __init__(self, + ang=None, + scaled=None, + ): + self.ang = ang + self.scaled = scaled + + +class PathShadeProperties(Serialisable): + + path = Set(values=(['shape', 'circle', 'rect'])) + fillToRect = Typed(expected_type=RelativeRect, allow_none=True) + + def __init__(self, + path=None, + fillToRect=None, + ): + self.path = path + self.fillToRect = fillToRect + + +class GradientFillProperties(Serialisable): + + tagname = "gradFill" + + flip = NoneSet(values=(['x', 'y', 'xy'])) + rotWithShape = Bool(allow_none=True) + + gsLst = Typed(expected_type=GradientStopList, allow_none=True) + stop_list = Alias("gsLst") + + lin = Typed(expected_type=LinearShadeProperties, allow_none=True) + linear = Alias("lin") + path = Typed(expected_type=PathShadeProperties, allow_none=True) + + tileRect = Typed(expected_type=RelativeRect, allow_none=True) + + __elements__ = ('gsLst', 'lin', 'path', 'tileRect') + + def __init__(self, + flip=None, + rotWithShape=None, + gsLst=None, + lin=None, + path=None, + tileRect=None, + ): + self.flip = flip + self.rotWithShape = rotWithShape + self.gsLst = gsLst + self.lin = lin + self.path = path + self.tileRect = tileRect + + +class SolidColorFillProperties(Serialisable): + + tagname = "solidFill" + + # uses element group EG_ColorChoice + scrgbClr = Typed(expected_type=RGBPercent, allow_none=True) + RGBPercent = Alias('scrgbClr') + srgbClr = NestedValue(expected_type=unicode, allow_none=True) # needs pattern and can have transform + RGB = Alias('srgbClr') + hslClr = Typed(expected_type=HSLColor, allow_none=True) + sysClr = Typed(expected_type=SystemColor, allow_none=True) + schemeClr = Typed(expected_type=SchemeColor, allow_none=True) + prstClr = NestedNoneSet(values=PRESET_COLORS) + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + scrgbClr=None, + srgbClr=None, + hslClr=None, + sysClr=None, + schemeClr=None, + prstClr=None, + ): + self.scrgbClr = scrgbClr + self.srgbClr = srgbClr + self.hslClr = hslClr + self.sysClr = sysClr + self.schemeClr = schemeClr + self.prstClr = prstClr + + +class Blip(Serialisable): + + tagname = "blip" + namespace = DRAWING_NS + + #Using attribute groupAG_Blob + cstate = NoneSet(values=(['email', 'screen', 'print', 'hqprint'])) + embed = Relation() #rId + link = Relation() #hyperlink + noGrp = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noRot = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + noEditPoints = Bool(allow_none=True) + noAdjustHandles = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noChangeShapeType = Bool(allow_none=True) + # some elements are choice + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + alphaBiLevel = Typed(expected_type=AlphaBiLevelEffect, allow_none=True) + alphaCeiling = Typed(expected_type=AlphaCeilingEffect, allow_none=True) + alphaFloor = Typed(expected_type=AlphaFloorEffect, allow_none=True) + alphaInv = Typed(expected_type=AlphaInverseEffect, allow_none=True) + alphaMod = Typed(expected_type=AlphaModulateEffect, allow_none=True) + alphaModFix = Typed(expected_type=AlphaModulateFixedEffect, allow_none=True) + alphaRepl = Typed(expected_type=AlphaReplaceEffect, allow_none=True) + biLevel = Typed(expected_type=BiLevelEffect, allow_none=True) + blur = Typed(expected_type=BlurEffect, allow_none=True) + clrChange = Typed(expected_type=ColorChangeEffect, allow_none=True) + clrRepl = Typed(expected_type=ColorReplaceEffect, allow_none=True) + duotone = Typed(expected_type=DuotoneEffect, allow_none=True) + fillOverlay = Typed(expected_type=FillOverlayEffect, allow_none=True) + grayscl = Typed(expected_type=GrayscaleEffect, allow_none=True) + hsl = Typed(expected_type=HSLEffect, allow_none=True) + lum = Typed(expected_type=LuminanceEffect, allow_none=True) + tint = Typed(expected_type=TintEffect, allow_none=True) + + __elements__ = ('alphaBiLevel', 'alphaCeiling', 'alphaFloor', 'alphaInv', + 'alphaMod', 'alphaModFix', 'alphaRepl', 'biLevel', 'blur', 'clrChange', + 'clrRepl', 'duotone', 'fillOverlay', 'grayscl', 'hsl', 'lum', 'tint') + + def __init__(self, + cstate=None, + embed=None, + link=None, + noGrp=None, + noSelect=None, + noRot=None, + noChangeAspect=None, + noMove=None, + noResize=None, + noEditPoints=None, + noAdjustHandles=None, + noChangeArrowheads=None, + noChangeShapeType=None, + extLst=None, + alphaBiLevel=None, + alphaCeiling=None, + alphaFloor=None, + alphaInv=None, + alphaMod=None, + alphaModFix=None, + alphaRepl=None, + biLevel=None, + blur=None, + clrChange=None, + clrRepl=None, + duotone=None, + fillOverlay=None, + grayscl=None, + hsl=None, + lum=None, + tint=None, + ): + self.cstate = cstate + self.embed = embed + self.link = link + self.noGrp = noGrp + self.noSelect = noSelect + self.noRot = noRot + self.noChangeAspect = noChangeAspect + self.noMove = noMove + self.noResize = noResize + self.noEditPoints = noEditPoints + self.noAdjustHandles = noAdjustHandles + self.noChangeArrowheads = noChangeArrowheads + self.noChangeShapeType = noChangeShapeType + self.extLst = extLst + self.alphaBiLevel = alphaBiLevel + self.alphaCeiling = alphaCeiling + self.alphaFloor = alphaFloor + self.alphaInv = alphaInv + self.alphaMod = alphaMod + self.alphaModFix = alphaModFix + self.alphaRepl = alphaRepl + self.biLevel = biLevel + self.blur = blur + self.clrChange = clrChange + self.clrRepl = clrRepl + self.duotone = duotone + self.fillOverlay = fillOverlay + self.grayscl = grayscl + self.hsl = hsl + self.lum = lum + self.tint = tint + + +class TileInfoProperties(Serialisable): + + tx = Integer(allow_none=True) + ty = Integer(allow_none=True) + sx = Integer(allow_none=True) + sy = Integer(allow_none=True) + flip = NoneSet(values=(['x', 'y', 'xy'])) + algn = Set(values=(['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br'])) + + def __init__(self, + tx=None, + ty=None, + sx=None, + sy=None, + flip=None, + algn=None, + ): + self.tx = tx + self.ty = ty + self.sx = sx + self.sy = sy + self.flip = flip + self.algn = algn + + +class BlipFillProperties(Serialisable): + + tagname = "blipFill" + + dpi = Integer(allow_none=True) + rotWithShape = Bool(allow_none=True) + + blip = Typed(expected_type=Blip, allow_none=True) + srcRect = Typed(expected_type=RelativeRect, allow_none=True) + tile = Typed(expected_type=TileInfoProperties, allow_none=True) + stretch = Typed(expected_type=StretchInfoProperties, allow_none=True) + + __elements__ = ("blip", "srcRect", "tile", "stretch") + + def __init__(self, + dpi=None, + rotWithShape=None, + blip=None, + tile=None, + stretch=StretchInfoProperties(), + srcRect=None, + ): + self.dpi = dpi + self.rotWithShape = rotWithShape + self.blip = blip + self.tile = tile + self.stretch = stretch + self.srcRect = srcRect diff --git a/openpyxl/drawing/graphic.py b/openpyxl/drawing/graphic.py new file mode 100644 index 0000000..70ba05b --- /dev/null +++ b/openpyxl/drawing/graphic.py @@ -0,0 +1,599 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.xml.functions import NS_REGEX, Element +from openpyxl.xml.constants import CHART_NS, REL_NS, DRAWING_NS + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + NoneSet, + Integer, + Set, + String, + Alias, +) +from openpyxl.descriptors.excel import Relation +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +from openpyxl.chart.shapes import GraphicalProperties +from openpyxl.chart.text import RichText + +from .effect import * +from .fill import RelativeRect, BlipFillProperties +from .text import Hyperlink, EmbeddedWAVAudioFile +from .shapes import ( + Transform2D, + Point2D, + PositiveSize2D, + Scene3D, + ShapeStyle, +) + +class GroupTransform2D(Serialisable): + + tagname = "xfrm" + + rot = Integer(allow_none=True) + flipH = Bool(allow_none=True) + flipV = Bool(allow_none=True) + off = Typed(expected_type=Point2D, allow_none=True) + ext = Typed(expected_type=PositiveSize2D, allow_none=True) + chOff = Typed(expected_type=Point2D, allow_none=True) + chExt = Typed(expected_type=PositiveSize2D, allow_none=True) + + def __init__(self, + rot=0, + flipH=None, + flipV=None, + off=None, + ext=None, + chOff=None, + chExt=None, + ): + self.rot = rot + self.flipH = flipH + self.flipV = flipV + self.off = off + self.ext = ext + self.chOff = chOff + self.chExt = chExt + + +class GroupShapeProperties(Serialisable): + + tagname = "grpSpPr" + + bwMode = NoneSet(values=(['clr', 'auto', 'gray', 'ltGray', 'invGray', + 'grayWhite', 'blackGray', 'blackWhite', 'black', 'white', 'hidden'])) + xfrm = Typed(expected_type=GroupTransform2D, allow_none=True) + scene3d = Typed(expected_type=Scene3D, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + bwMode=None, + xfrm=None, + scene3d=None, + extLst=None, + ): + self.bwMode = bwMode + self.xfrm = xfrm + self.scene3d = scene3d + self.extLst = extLst + + +class GroupLocking(Serialisable): + + noGrp = Bool(allow_none=True) + noUngrp = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noRot = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noEditPoints = Bool(allow_none=True) + noAdjustHandles = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noChangeShapeType = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + noGrp=None, + noUngrp=None, + noSelect=None, + noRot=None, + noChangeAspect=None, + noChangeArrowheads=None, + noMove=None, + noResize=None, + noEditPoints=None, + noAdjustHandles=None, + noChangeShapeType=None, + extLst=None, + ): + self.noGrp = noGrp + self.noUngrp = noUngrp + self.noSelect = noSelect + self.noRot = noRot + self.noChangeAspect = noChangeAspect + self.noChangeArrowheads = noChangeArrowheads + self.noMove = noMove + self.noResize = noResize + + +class NonVisualGroupDrawingShapeProps(Serialisable): + + grpSpLocks = Typed(expected_type=GroupLocking, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + grpSpLocks=None, + extLst=None, + ): + self.grpSpLocks = grpSpLocks + self.extLst = extLst + + +class NonVisualDrawingShapeProps(Serialisable): + + tagname = "cNvSpPr" + + spLocks = Typed(expected_type=GroupLocking, allow_none=True) + txBax = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + spLocks=None, + txBox=None, + extLst=None, + ): + self.spLocks = spLocks + self.txBox = txBox + self.extLst = extLst + + +class NonVisualDrawingProps(Serialisable): + + tagname = "cNvPr" + + id = Integer() + name = String() + descr = String(allow_none=True) + hidden = Bool(allow_none=True) + title = String(allow_none=True) + hlinkClick = Typed(expected_type=Hyperlink, allow_none=True) + hlinkHover = Typed(expected_type=Hyperlink, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + id=None, + name=None, + descr=None, + hidden=None, + title=None, + hlinkClick=None, + hlinkHover=None, + extLst=None, + ): + self.id = id + self.name = name + self.descr = descr + self.hidden = hidden + self.title = title + self.hlinkClick = hlinkClick + self.hlinkHover = hlinkHover + self.extLst = extLst + + +class NonVisualGroupShape(Serialisable): + + cNvPr = Typed(expected_type=NonVisualDrawingProps, ) + cNvGrpSpPr = Typed(expected_type=NonVisualGroupDrawingShapeProps, ) + + def __init__(self, + cNvPr=None, + cNvGrpSpPr=None, + ): + self.cNvPr = cNvPr + self.cNvGrpSpPr = cNvGrpSpPr + + +class GroupShape(Serialisable): + + nvGrpSpPr = Typed(expected_type=NonVisualGroupShape, ) + grpSpPr = Typed(expected_type=GroupShapeProperties, ) + + def __init__(self, + nvGrpSpPr=None, + grpSpPr=None, + ): + self.nvGrpSpPr = nvGrpSpPr + self.grpSpPr = grpSpPr + + +class GraphicFrameLocking(Serialisable): + + noGrp = Bool(allow_none=True) + noDrilldown = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + noGrp=None, + noDrilldown=None, + noSelect=None, + noChangeAspect=None, + noMove=None, + noResize=None, + extLst=None, + ): + self.noGrp = noGrp + self.noDrilldown = noDrilldown + self.noSelect = noSelect + self.noChangeAspect = noChangeAspect + self.noMove = noMove + self.noResize = noResize + self.extLst = extLst + + +class NonVisualGraphicFrameProperties(Serialisable): + + tagname = "cNvGraphicFramePr" + + graphicFrameLocks = Typed(expected_type=GraphicFrameLocking, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + graphicFrameLocks=None, + extLst=None, + ): + self.graphicFrameLocks = graphicFrameLocks + self.extLst = extLst + + +class NonVisualGraphicFrame(Serialisable): + + tagname = "nvGraphicFramePr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps) + cNvGraphicFramePr = Typed(expected_type=NonVisualGraphicFrameProperties) + + __elements__ = ('cNvPr', 'cNvGraphicFramePr') + + def __init__(self, + cNvPr=None, + cNvGraphicFramePr=None, + ): + if cNvPr is None: + cNvPr = NonVisualDrawingProps(id=0, name="Chart 0") + self.cNvPr = cNvPr + if cNvGraphicFramePr is None: + cNvGraphicFramePr = NonVisualGraphicFrameProperties() + self.cNvGraphicFramePr = cNvGraphicFramePr + + +class ChartRelation(Serialisable): + + tagname = "chart" + namespace = CHART_NS + + id = Relation() + + def __init__(self, id): + self.id = id + + +class GraphicData(Serialisable): + + tagname = "graphicData" + namespace = DRAWING_NS + + uri = String() + chart = Typed(expected_type=ChartRelation, allow_none=True) + + + def __init__(self, + uri=CHART_NS, + chart=None, + ): + self.uri = uri + self.chart = chart + + +class GraphicObject(Serialisable): + + tagname = "graphic" + namespace = DRAWING_NS + + graphicData = Typed(expected_type=GraphicData) + + def __init__(self, + graphicData=None, + ): + if graphicData is None: + graphicData = GraphicData() + self.graphicData = graphicData + + +class GraphicFrame(Serialisable): + + tagname = "graphicFrame" + + nvGraphicFramePr = Typed(expected_type=NonVisualGraphicFrame) + xfrm = Typed(expected_type=Transform2D) + graphic = Typed(expected_type=GraphicObject) + macro = String(allow_none=True) + fPublished = Bool(allow_none=True) + + __elements__ = ('nvGraphicFramePr', 'xfrm', 'graphic', 'macro', 'fPublished') + + def __init__(self, + nvGraphicFramePr=None, + xfrm=None, + graphic=None, + macro=None, + fPublished=None, + ): + if nvGraphicFramePr is None: + nvGraphicFramePr = NonVisualGraphicFrame() + self.nvGraphicFramePr = nvGraphicFramePr + if xfrm is None: + xfrm = Transform2D() + self.xfrm = xfrm + if graphic is None: + graphic = GraphicObject() + self.graphic = graphic + self.macro = macro + self.fPublished = fPublished + + +class Connection(Serialisable): + + id = Integer() + idx = Integer() + + def __init__(self, + id=None, + idx=None, + ): + self.id = id + self.idx = idx + + +class ConnectorLocking(Serialisable): + + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + extLst=None, + ): + self.extLst = extLst + + +class NonVisualConnectorProperties(Serialisable): + + cxnSpLocks = Typed(expected_type=ConnectorLocking, allow_none=True) + stCxn = Typed(expected_type=Connection, allow_none=True) + endCxn = Typed(expected_type=Connection, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + cxnSpLocks=None, + stCxn=None, + endCxn=None, + extLst=None, + ): + self.cxnSpLocks = cxnSpLocks + self.stCxn = stCxn + self.endCxn = endCxn + self.extLst = extLst + + +class ConnectorNonVisual(Serialisable): + + cNvPr = Typed(expected_type=NonVisualDrawingProps, ) + cNvCxnSpPr = Typed(expected_type=NonVisualConnectorProperties, ) + + __elements__ = ("cNvPr", "cNvCxnSpPr",) + + def __init__(self, + cNvPr=None, + cNvCxnSpPr=None, + ): + self.cNvPr = cNvPr + self.cNvCxnSpPr = cNvCxnSpPr + + +class ConnectorShape(Serialisable): + + tagname = "cxnSp" + + nvCxnSpPr = Typed(expected_type=ConnectorNonVisual, ) + spPr = Typed(expected_type=GraphicalProperties) + style = Typed(expected_type=ShapeStyle, allow_none=True) + macro = String(allow_none=True) + fPublished = Bool(allow_none=True) + + def __init__(self, + nvCxnSpPr=None, + spPr=None, + style=None, + macro=None, + fPublished=None, + ): + self.nvCxnSpPr = nvCxnSpPr + self.spPr = spPr + self.style = style + self.macro = macro + self.fPublished = fPublished + + +class ShapeMeta(Serialisable): + + tagname = "nvSpPr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps) + cNvSpPr = Typed(expected_type=NonVisualDrawingShapeProps) + + def __init__(self, cNvPr=None, cNvSpPr=None): + self.cNvPr = cNvPr + self.cNvSpPr = cNvSpPr + + +class Shape(Serialisable): + + macro = String(allow_none=True) + textlink = String(allow_none=True) + fPublished = Bool(allow_none=True) + nvSpPr = Typed(expected_type=ShapeMeta, allow_none=True) + meta = Alias("nvSpPr") + spPr = Typed(expected_type=GraphicalProperties) + graphicalProperties = Alias("spPr") + style = Typed(expected_type=ShapeStyle, allow_none=True) + txBody = Typed(expected_type=RichText, allow_none=True) + + def __init__(self, + macro=None, + textlink=None, + fPublished=None, + nvSpPr=None, + spPr=None, + style=None, + txBody=None, + ): + self.macro = macro + self.textlink = textlink + self.fPublished = fPublished + self.nvSpPr = nvSpPr + self.spPr = spPr + self.style = style + self.txBody = txBody + + +class PictureLocking(Serialisable): + + tagname = "picLocks" + namespace = DRAWING_NS + + #Using attribute group AG_Locking + noCrop = Bool(allow_none=True) + noGrp = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noRot = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + noEditPoints = Bool(allow_none=True) + noAdjustHandles = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noChangeShapeType = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + noCrop=None, + noGrp=None, + noSelect=None, + noRot=None, + noChangeAspect=None, + noMove=None, + noResize=None, + noEditPoints=None, + noAdjustHandles=None, + noChangeArrowheads=None, + noChangeShapeType=None, + extLst=None, + ): + self.noCrop = noCrop + self.noGrp = noGrp + self.noSelect = noSelect + self.noRot = noRot + self.noChangeAspect = noChangeAspect + self.noMove = noMove + self.noResize = noResize + self.noEditPoints = noEditPoints + self.noAdjustHandles = noAdjustHandles + self.noChangeArrowheads = noChangeArrowheads + self.noChangeShapeType = noChangeShapeType + + +class NonVisualPictureProperties(Serialisable): + + tagname = "cNvPicPr" + + preferRelativeResize = Bool(allow_none=True) + picLocks = Typed(expected_type=PictureLocking, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ("picLocks",) + + def __init__(self, + preferRelativeResize=None, + picLocks=None, + extLst=None, + ): + self.preferRelativeResize = preferRelativeResize + self.picLocks = picLocks + + +class PictureNonVisual(Serialisable): + + tagname = "nvPicPr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps, ) + cNvPicPr = Typed(expected_type=NonVisualPictureProperties, ) + + __elements__ = ("cNvPr", "cNvPicPr") + + def __init__(self, + cNvPr=None, + cNvPicPr=None, + ): + if cNvPr is None: + cNvPr = NonVisualDrawingProps(id=0, name="Image 1", descr="Name of file") + self.cNvPr = cNvPr + if cNvPicPr is None: + cNvPicPr = NonVisualPictureProperties() + self.cNvPicPr = cNvPicPr + + +class PictureFrame(Serialisable): + + tagname = "pic" + + macro = String(allow_none=True) + fPublished = Bool(allow_none=True) + nvPicPr = Typed(expected_type=PictureNonVisual, ) + blipFill = Typed(expected_type=BlipFillProperties, ) + spPr = Typed(expected_type=GraphicalProperties, ) + graphicalProperties = Alias('spPr') + style = Typed(expected_type=ShapeStyle, allow_none=True) + + __elements__ = ("nvPicPr", "blipFill", "spPr", "style") + + def __init__(self, + macro=None, + fPublished=None, + nvPicPr=None, + blipFill=None, + spPr=None, + style=None, + ): + self.macro = macro + self.fPublished = fPublished + if nvPicPr is None: + nvPicPr = PictureNonVisual() + self.nvPicPr = nvPicPr + if blipFill is None: + blipFill = BlipFillProperties() + self.blipFill = blipFill + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + self.style = style diff --git a/openpyxl/drawing/image.py b/openpyxl/drawing/image.py new file mode 100644 index 0000000..903b2e1 --- /dev/null +++ b/openpyxl/drawing/image.py @@ -0,0 +1,80 @@ +from __future__ import absolute_import +from __future__ import division +# Copyright (c) 2010-2018 openpyxl + +from io import BytesIO + + +def bounding_box(bw, bh, w, h): + """ + Returns a tuple (new_width, new_height) which has the property + that it fits within box_width and box_height and has (close to) + the same aspect ratio as the original size + """ + new_width, new_height = w, h + if bw and new_width > bw: + new_width = bw + new_height = new_width / (w / h) + if bh and new_height > bh: + new_height = bh + new_width = new_height * (w / h) + return (new_width, new_height) + + +def _import_image(img): + try: + try: + import Image as PILImage + except ImportError: + from PIL import Image as PILImage + except ImportError: + raise ImportError('You must install PIL to fetch image objects') + + if not isinstance(img, PILImage.Image): + img = PILImage.open(img) + + return img + + +class Image(object): + """Image in a spreadsheet""" + + _id = 1 + _path = "/xl/media/image{0}.{1}" + anchor = "A1" + + def __init__(self, img): + + self.ref = img + + # don't keep the image open + image = _import_image(img) + self.width = image.size[0] + self.height = image.size[1] + try: + self.format = image.format.lower() + except AttributeError: + self.format = "png" + + + def _data(self): + """ + Open image and write it to a buffer when saving the workbook + """ + img = _import_image(self.ref) + fp = None + # don't convert these file formats + if self.format in ['gif', 'jpeg', 'png']: + if img.fp: + img.fp.seek(0) + fp = img.fp + if not fp: + fp = BytesIO() + img.save(fp, format=self.format) + + return fp.read() + + + @property + def path(self): + return self._path.format(self._id, self.format) diff --git a/openpyxl/drawing/line.py b/openpyxl/drawing/line.py new file mode 100644 index 0000000..6a673f8 --- /dev/null +++ b/openpyxl/drawing/line.py @@ -0,0 +1,164 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Bool, + MinMax, + Set, + NoneSet, + String, + Alias, + Sequence +) +from openpyxl.descriptors.excel import Coordinate, Percentage + +from openpyxl.descriptors.nested import ( + NestedSet, + NestedNoneSet, + EmptyTag, +) +from openpyxl.compat import safe_string +from openpyxl.xml.constants import DRAWING_NS +from openpyxl.xml.functions import Element + +from .colors import ColorChoiceDescriptor +from .fill import GradientFillProperties, PatternFillProperties +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +""" +Line elements from drawing main schema +""" + +class LineEndProperties(Serialisable): + + tagname = "end" + namespace = DRAWING_NS + + type = NoneSet(values=(['none', 'triangle', 'stealth', 'diamond', 'oval', 'arrow'])) + w = NoneSet(values=(['sm', 'med', 'lg'])) + len = NoneSet(values=(['sm', 'med', 'lg'])) + + def __init__(self, + type=None, + w=None, + len=None, + ): + self.type = type + self.w = w + self.len = len + + +class DashStop(Serialisable): + + tagname = "ds" + namespace = DRAWING_NS + + d = Integer() + length = Alias('d') + sp = Integer() + space = Alias('sp') + + def __init__(self, + d=0, + sp=0, + ): + self.d = d + self.sp = sp + + +class DashStopList(Serialisable): + + ds = Sequence(expected_type=DashStop, allow_none=True) + + def __init__(self, + ds=None, + ): + self.ds = ds + + +class LineJoinMiterProperties(Serialisable): + + tagname = "miter" + namespace = DRAWING_NS + + lim = Integer(allow_none=True) + + def __init__(self, + lim=None, + ): + self.lim = lim + + +class LineProperties(Serialisable): + + tagname = "ln" + namespace = DRAWING_NS + + w = MinMax(min=0, max=20116800, allow_none=True) # EMU + width = Alias('w') + cap = NoneSet(values=(['rnd', 'sq', 'flat'])) + cmpd = NoneSet(values=(['sng', 'dbl', 'thickThin', 'thinThick', 'tri'])) + algn = NoneSet(values=(['ctr', 'in'])) + + noFill = EmptyTag() + solidFill = ColorChoiceDescriptor() + gradFill = Typed(expected_type=GradientFillProperties, allow_none=True) + pattFill = Typed(expected_type=PatternFillProperties, allow_none=True) + + prstDash = NestedNoneSet(values=(['solid', 'dot', 'dash', 'lgDash', 'dashDot', + 'lgDashDot', 'lgDashDotDot', 'sysDash', 'sysDot', 'sysDashDot', + 'sysDashDotDot']), namespace=namespace) + dashStyle = Alias('prstDash') + + custDash = Typed(expected_type=DashStop, allow_none=True) + + round = EmptyTag() + bevel = EmptyTag() + miter = Typed(expected_type=LineJoinMiterProperties, allow_none=True) + + headEnd = Typed(expected_type=LineEndProperties, allow_none=True) + tailEnd = Typed(expected_type=LineEndProperties, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ('noFill', 'solidFill', 'gradFill', 'pattFill', + 'prstDash', 'custDash', 'round', 'bevel', 'mitre', 'headEnd', 'tailEnd') + + def __init__(self, + w=None, + cap=None, + cmpd=None, + algn=None, + noFill=None, + solidFill=None, + gradFill=None, + pattFill=None, + prstDash=None, + custDash=None, + round=None, + bevel=None, + miter=None, + headEnd=None, + tailEnd=None, + extLst=None, + ): + self.w = w + self.cap = cap + self.cmpd = cmpd + self.algn = algn + self.noFill = noFill + self.solidFill = solidFill + self.gradFill = gradFill + self.pattFill = pattFill + if prstDash is None: + prstDash = "solid" + self.prstDash = prstDash + self.custDash = custDash + self.round = round + self.bevel = bevel + self.mitre = bevel + self.headEnd = headEnd + self.tailEnd = tailEnd diff --git a/openpyxl/drawing/shape.py b/openpyxl/drawing/shape.py new file mode 100644 index 0000000..288560a --- /dev/null +++ b/openpyxl/drawing/shape.py @@ -0,0 +1,416 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.styles.colors import Color, BLACK, WHITE + +from openpyxl.utils.units import ( + pixels_to_EMU, + EMU_to_pixels, + short_color, +) + +from openpyxl.compat import deprecated +from openpyxl.xml.functions import Element, SubElement, tostring +from openpyxl.xml.constants import ( + DRAWING_NS, + SHEET_DRAWING_NS, + CHART_NS, + CHART_DRAWING_NS, + PKG_REL_NS +) +from openpyxl.compat.strings import safe_string + + +class Shape(object): + """ a drawing inside a chart + coordiantes are specified by the user in the axis units + """ + + MARGIN_LEFT = 6 + 13 + 1 + MARGIN_BOTTOM = 17 + 11 + + FONT_WIDTH = 7 + FONT_HEIGHT = 8 + + ROUND_RECT = 'roundRect' + RECT = 'rect' + + # other shapes to define : + ''' + "line" + "lineInv" + "triangle" + "rtTriangle" + "diamond" + "parallelogram" + "trapezoid" + "nonIsoscelesTrapezoid" + "pentagon" + "hexagon" + "heptagon" + "octagon" + "decagon" + "dodecagon" + "star4" + "star5" + "star6" + "star7" + "star8" + "star10" + "star12" + "star16" + "star24" + "star32" + "roundRect" + "round1Rect" + "round2SameRect" + "round2DiagRect" + "snipRoundRect" + "snip1Rect" + "snip2SameRect" + "snip2DiagRect" + "plaque" + "ellipse" + "teardrop" + "homePlate" + "chevron" + "pieWedge" + "pie" + "blockArc" + "donut" + "noSmoking" + "rightArrow" + "leftArrow" + "upArrow" + "downArrow" + "stripedRightArrow" + "notchedRightArrow" + "bentUpArrow" + "leftRightArrow" + "upDownArrow" + "leftUpArrow" + "leftRightUpArrow" + "quadArrow" + "leftArrowCallout" + "rightArrowCallout" + "upArrowCallout" + "downArrowCallout" + "leftRightArrowCallout" + "upDownArrowCallout" + "quadArrowCallout" + "bentArrow" + "uturnArrow" + "circularArrow" + "leftCircularArrow" + "leftRightCircularArrow" + "curvedRightArrow" + "curvedLeftArrow" + "curvedUpArrow" + "curvedDownArrow" + "swooshArrow" + "cube" + "can" + "lightningBolt" + "heart" + "sun" + "moon" + "smileyFace" + "irregularSeal1" + "irregularSeal2" + "foldedCorner" + "bevel" + "frame" + "halfFrame" + "corner" + "diagStripe" + "chord" + "arc" + "leftBracket" + "rightBracket" + "leftBrace" + "rightBrace" + "bracketPair" + "bracePair" + "straightConnector1" + "bentConnector2" + "bentConnector3" + "bentConnector4" + "bentConnector5" + "curvedConnector2" + "curvedConnector3" + "curvedConnector4" + "curvedConnector5" + "callout1" + "callout2" + "callout3" + "accentCallout1" + "accentCallout2" + "accentCallout3" + "borderCallout1" + "borderCallout2" + "borderCallout3" + "accentBorderCallout1" + "accentBorderCallout2" + "accentBorderCallout3" + "wedgeRectCallout" + "wedgeRoundRectCallout" + "wedgeEllipseCallout" + "cloudCallout" + "cloud" + "ribbon" + "ribbon2" + "ellipseRibbon" + "ellipseRibbon2" + "leftRightRibbon" + "verticalScroll" + "horizontalScroll" + "wave" + "doubleWave" + "plus" + "flowChartProcess" + "flowChartDecision" + "flowChartInputOutput" + "flowChartPredefinedProcess" + "flowChartInternalStorage" + "flowChartDocument" + "flowChartMultidocument" + "flowChartTerminator" + "flowChartPreparation" + "flowChartManualInput" + "flowChartManualOperation" + "flowChartConnector" + "flowChartPunchedCard" + "flowChartPunchedTape" + "flowChartSummingJunction" + "flowChartOr" + "flowChartCollate" + "flowChartSort" + "flowChartExtract" + "flowChartMerge" + "flowChartOfflineStorage" + "flowChartOnlineStorage" + "flowChartMagneticTape" + "flowChartMagneticDisk" + "flowChartMagneticDrum" + "flowChartDisplay" + "flowChartDelay" + "flowChartAlternateProcess" + "flowChartOffpageConnector" + "actionButtonBlank" + "actionButtonHome" + "actionButtonHelp" + "actionButtonInformation" + "actionButtonForwardNext" + "actionButtonBackPrevious" + "actionButtonEnd" + "actionButtonBeginning" + "actionButtonReturn" + "actionButtonDocument" + "actionButtonSound" + "actionButtonMovie" + "gear6" + "gear9" + "funnel" + "mathPlus" + "mathMinus" + "mathMultiply" + "mathDivide" + "mathEqual" + "mathNotEqual" + "cornerTabs" + "squareTabs" + "plaqueTabs" + "chartX" + "chartStar" + "chartPlus" + ''' + + @deprecated("Chart Drawings need a complete rewrite") + def __init__(self, + chart, + coordinates=((0, 0), (1, 1)), + text=None, + scheme="accent1"): + self.chart = chart + self.coordinates = coordinates # in axis units + self.text = text + self.scheme = scheme + self.style = Shape.RECT + self.border_width = 0 + self.border_color = BLACK # "F3B3C5" + self.color = WHITE + self.text_color = BLACK + + @property + def border_color(self): + return self._border_color + + @border_color.setter + def border_color(self, color): + self._border_color = short_color(color) + + @property + def color(self): + return self._color + + @color.setter + def color(self, color): + self._color = short_color(color) + + @property + def text_color(self): + return self._text_color + + @text_color.setter + def text_color(self, color): + self._text_color = short_color(color) + + @property + def border_width(self): + return self._border_width + + @border_width.setter + def border_width(self, w): + self._border_width = w + + @property + def coordinates(self): + """Return coordindates in axis units""" + return self._coordinates + + @coordinates.setter + def coordinates(self, coords): + """ set shape coordinates in percentages (left, top, right, bottom) + """ + # this needs refactoring to reflect changes in charts + self.axis_coordinates = coords + (x1, y1), (x2, y2) = coords # bottom left, top right + drawing_width = pixels_to_EMU(self.chart.drawing.width) + drawing_height = pixels_to_EMU(self.chart.drawing.height) + plot_width = drawing_width * self.chart.width + plot_height = drawing_height * self.chart.height + + margin_left = self.chart._get_margin_left() * drawing_width + xunit = plot_width / self.chart.get_x_units() + + margin_top = self.chart._get_margin_top() * drawing_height + yunit = self.chart.get_y_units() + + x_start = (margin_left + (float(x1) * xunit)) / drawing_width + y_start = ((margin_top + + plot_height + - (float(y1) * yunit)) + / drawing_height) + + x_end = (margin_left + (float(x2) * xunit)) / drawing_width + y_end = ((margin_top + + plot_height + - (float(y2) * yunit)) + / drawing_height) + + # allow user to specify y's in whatever order + # excel expect y_end to be lower + if y_end < y_start: + y_end, y_start = y_start, y_end + + self._coordinates = ( + self._norm_pct(x_start), self._norm_pct(y_start), + self._norm_pct(x_end), self._norm_pct(y_end) + ) + + @staticmethod + def _norm_pct(pct): + """ force shapes to appear by truncating too large sizes """ + if pct > 1: + return 1 + elif pct < 0: + return 0 + return pct + + +class ShapeWriter(object): + """ one file per shape """ + + def __init__(self, shapes): + + self._shapes = shapes + + def write(self, shape_id): + + root = Element('{%s}userShapes' % CHART_NS) + + for shape in self._shapes: + anchor = SubElement(root, '{%s}relSizeAnchor' % CHART_DRAWING_NS) + + xstart, ystart, xend, yend = shape.coordinates + + _from = SubElement(anchor, '{%s}from' % CHART_DRAWING_NS) + SubElement(_from, '{%s}x' % CHART_DRAWING_NS).text = str(xstart) + SubElement(_from, '{%s}y' % CHART_DRAWING_NS).text = str(ystart) + + _to = SubElement(anchor, '{%s}to' % CHART_DRAWING_NS) + SubElement(_to, '{%s}x' % CHART_DRAWING_NS).text = str(xend) + SubElement(_to, '{%s}y' % CHART_DRAWING_NS).text = str(yend) + + sp = SubElement(anchor, '{%s}sp' % CHART_DRAWING_NS, {'macro':'', 'textlink':''}) + nvspr = SubElement(sp, '{%s}nvSpPr' % CHART_DRAWING_NS) + SubElement(nvspr, '{%s}cNvPr' % CHART_DRAWING_NS, {'id':str(shape_id), 'name':'shape %s' % shape_id}) + SubElement(nvspr, '{%s}cNvSpPr' % CHART_DRAWING_NS) + + sppr = SubElement(sp, '{%s}spPr' % CHART_DRAWING_NS) + frm = SubElement(sppr, '{%s}xfrm' % DRAWING_NS,) + # no transformation + SubElement(frm, '{%s}off' % DRAWING_NS, {'x':'0', 'y':'0'}) + SubElement(frm, '{%s}ext' % DRAWING_NS, {'cx':'0', 'cy':'0'}) + + prstgeom = SubElement(sppr, '{%s}prstGeom' % DRAWING_NS, {'prst':str(shape.style)}) + SubElement(prstgeom, '{%s}avLst' % DRAWING_NS) + + fill = SubElement(sppr, '{%s}solidFill' % DRAWING_NS, ) + SubElement(fill, '{%s}srgbClr' % DRAWING_NS, {'val':shape.color}) + + border = SubElement(sppr, '{%s}ln' % DRAWING_NS, {'w':str(shape._border_width)}) + sf = SubElement(border, '{%s}solidFill' % DRAWING_NS) + SubElement(sf, '{%s}srgbClr' % DRAWING_NS, {'val':shape.border_color}) + + self._write_style(sp) + self._write_text(sp, shape) + + shape_id += 1 + + return tostring(root) + + def _write_text(self, node, shape): + """ write text in the shape """ + + tx_body = SubElement(node, '{%s}txBody' % CHART_DRAWING_NS) + SubElement(tx_body, '{%s}bodyPr' % DRAWING_NS, {'vertOverflow':'clip'}) + SubElement(tx_body, '{%s}lstStyle' % DRAWING_NS) + p = SubElement(tx_body, '{%s}p' % DRAWING_NS) + if shape.text: + r = SubElement(p, '{%s}r' % DRAWING_NS) + rpr = SubElement(r, '{%s}rPr' % DRAWING_NS, {'lang':'en-US'}) + fill = SubElement(rpr, '{%s}solidFill' % DRAWING_NS) + SubElement(fill, '{%s}srgbClr' % DRAWING_NS, {'val':shape.text_color}) + + SubElement(r, '{%s}t' % DRAWING_NS).text = shape.text + else: + SubElement(p, '{%s}endParaRPr' % DRAWING_NS, {'lang':'en-US'}) + + def _write_style(self, node): + """ write style theme """ + + style = SubElement(node, '{%s}style' % CHART_DRAWING_NS) + + ln_ref = SubElement(style, '{%s}lnRef' % DRAWING_NS, {'idx':'2'}) + scheme_clr = SubElement(ln_ref, '{%s}schemeClr' % DRAWING_NS, {'val':'accent1'}) + SubElement(scheme_clr, '{%s}shade' % DRAWING_NS, {'val':'50000'}) + + fill_ref = SubElement(style, '{%s}fillRef' % DRAWING_NS, {'idx':'1'}) + SubElement(fill_ref, '{%s}schemeClr' % DRAWING_NS, {'val':'accent1'}) + + effect_ref = SubElement(style, '{%s}effectRef' % DRAWING_NS, {'idx':'0'}) + SubElement(effect_ref, '{%s}schemeClr' % DRAWING_NS, {'val':'accent1'}) + + font_ref = SubElement(style, '{%s}fontRef' % DRAWING_NS, {'idx':'minor'}) + SubElement(font_ref, '{%s}schemeClr' % DRAWING_NS, {'val':'lt1'}) diff --git a/openpyxl/drawing/shapes.py b/openpyxl/drawing/shapes.py new file mode 100644 index 0000000..72ea936 --- /dev/null +++ b/openpyxl/drawing/shapes.py @@ -0,0 +1,544 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Bool, + MinMax, + Set, + NoneSet, + String, + Alias, +) +from openpyxl.descriptors.excel import Coordinate, Percentage + +from openpyxl.descriptors.nested import ( + EmptyTag + ) + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from .colors import ColorChoiceDescriptor +from .fill import ( + GradientFillProperties, + BlipFillProperties, + PatternFillProperties, + ) +from .line import LineProperties + +from openpyxl.styles.colors import Color +from openpyxl.xml.constants import DRAWING_NS + + +class Point2D(Serialisable): + + x = Coordinate() + y = Coordinate() + + def __init__(self, + x=None, + y=None, + ): + self.x = x + self.y = y + + +class PositiveSize2D(Serialisable): + + """ + Dimensions in EMUs + """ + + cx = Integer() + width = Alias('cx') + cy = Integer() + height = Alias('cy') + + def __init__(self, + cx=None, + cy=None, + ): + self.cx = cx + self.cy = cy + + +class Transform2D(Serialisable): + + tagname = "xfrm" + + rot = Integer(allow_none=True) + flipH = Bool(allow_none=True) + flipV = Bool(allow_none=True) + off = Typed(expected_type=Point2D, allow_none=True) + ext = Typed(expected_type=PositiveSize2D, allow_none=True) + + __elements__ = ('off', 'ext') + + def __init__(self, + rot=None, + flipH=None, + flipV=None, + off=None, + ext=None, + ): + self.rot = rot + self.flipH = flipH + self.flipV = flipV + self.off = off + self.ext = ext + + +class SphereCoords(Serialisable): + + lat = Integer() + lon = Integer() + rev = Integer() + + def __init__(self, + lat=None, + lon=None, + rev=None, + ): + self.lat = lat + self.lon = lon + self.rev = rev + + +class Camera(Serialisable): + + tagname = "camera" + + prst = Set(values=[ + 'legacyObliqueTopLeft', 'legacyObliqueTop', 'legacyObliqueTopRight', 'legacyObliqueLeft', + 'legacyObliqueFront', 'legacyObliqueRight', 'legacyObliqueBottomLeft', + 'legacyObliqueBottom', 'legacyObliqueBottomRight', 'legacyPerspectiveTopLeft', + 'legacyPerspectiveTop', 'legacyPerspectiveTopRight', 'legacyPerspectiveLeft', + 'legacyPerspectiveFront', 'legacyPerspectiveRight', 'legacyPerspectiveBottomLeft', + 'legacyPerspectiveBottom', 'legacyPerspectiveBottomRight', 'orthographicFront', + 'isometricTopUp', 'isometricTopDown', 'isometricBottomUp', 'isometricBottomDown', + 'isometricLeftUp', 'isometricLeftDown', 'isometricRightUp', 'isometricRightDown', + 'isometricOffAxis1Left', 'isometricOffAxis1Right', 'isometricOffAxis1Top', + 'isometricOffAxis2Left', 'isometricOffAxis2Right', 'isometricOffAxis2Top', + 'isometricOffAxis3Left', 'isometricOffAxis3Right', 'isometricOffAxis3Bottom', + 'isometricOffAxis4Left', 'isometricOffAxis4Right', 'isometricOffAxis4Bottom', + 'obliqueTopLeft', 'obliqueTop', 'obliqueTopRight', 'obliqueLeft', 'obliqueRight', + 'obliqueBottomLeft', 'obliqueBottom', 'obliqueBottomRight', 'perspectiveFront', + 'perspectiveLeft', 'perspectiveRight', 'perspectiveAbove', 'perspectiveBelow', + 'perspectiveAboveLeftFacing', 'perspectiveAboveRightFacing', + 'perspectiveContrastingLeftFacing', 'perspectiveContrastingRightFacing', + 'perspectiveHeroicLeftFacing', 'perspectiveHeroicRightFacing', + 'perspectiveHeroicExtremeLeftFacing', 'perspectiveHeroicExtremeRightFacing', + 'perspectiveRelaxed', 'perspectiveRelaxedModerately']) + fov = Typed(expected_type=Integer, allow_none=True) + zoom = Typed(expected_type=Percentage, allow_none=True) + rot = Typed(expected_type=SphereCoords, allow_none=True) + + + def __init__(self, + prst=None, + fov=None, + zoom=None, + rot=None, + ): + self.prst = prst + self.fov = fov + self.zoom = zoom + self.rot = rot + + +class LightRig(Serialisable): + + tagname = "lightRig" + + rig = Set(values=['legacyFlat1', 'legacyFlat2', 'legacyFlat3', 'legacyFlat4', 'legacyNormal1', + 'legacyNormal2', 'legacyNormal3', 'legacyNormal4', 'legacyHarsh1', + 'legacyHarsh2', 'legacyHarsh3', 'legacyHarsh4', 'threePt', 'balanced', + 'soft', 'harsh', 'flood', 'contrasting', 'morning', 'sunrise', 'sunset', + 'chilly', 'freezing', 'flat', 'twoPt', 'glow', 'brightRoom'] + ) + dir = Set(values=(['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'])) + rot = Typed(expected_type=SphereCoords, allow_none=True) + + def __init__(self, + rig=None, + dir=None, + rot=None, + ): + self.rig = rig + self.dir = dir + self.rot = rot + + +class Vector3D(Serialisable): + + dx = Typed(expected_type=Coordinate, ) + dy = Typed(expected_type=Coordinate, ) + dz = Typed(expected_type=Coordinate, ) + + def __init__(self, + dx=None, + dy=None, + dz=None, + ): + self.dx = dx + self.dy = dy + self.dz = dz + + +class Point3D(Serialisable): + + x = Typed(expected_type=Coordinate, ) + y = Typed(expected_type=Coordinate, ) + z = Typed(expected_type=Coordinate, ) + + def __init__(self, + x=None, + y=None, + z=None, + ): + self.x = x + self.y = y + self.z = z + + +class Backdrop(Serialisable): + + anchor = Typed(expected_type=Point3D, ) + norm = Typed(expected_type=Vector3D, ) + up = Typed(expected_type=Vector3D, ) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + anchor=None, + norm=None, + up=None, + extLst=None, + ): + self.anchor = anchor + self.norm = norm + self.up = up + self.extLst = extLst + + +class Scene3D(Serialisable): + + camera = Typed(expected_type=Camera, ) + lightRig = Typed(expected_type=LightRig, ) + backdrop = Typed(expected_type=Backdrop, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + camera=None, + lightRig=None, + backdrop=None, + extLst=None, + ): + self.camera = camera + self.lightRig = lightRig + self.backdrop = backdrop + self.extLst = extLst + + +class Bevel(Serialisable): + + tagname = "bevel" + + w = Integer() + h = Integer() + prst = NoneSet(values= + ['relaxedInset', 'circle', 'slope', 'cross', 'angle', + 'softRound', 'convex', 'coolSlant', 'divot', 'riblet', + 'hardEdge', 'artDeco'] + ) + + def __init__(self, + w=None, + h=None, + prst=None, + ): + self.w = w + self.h = h + self.prst = prst + + +class Shape3D(Serialisable): + + namespace = DRAWING_NS + + z = Typed(expected_type=Coordinate, allow_none=True) + extrusionH = Integer(allow_none=True) + contourW = Integer(allow_none=True) + prstMaterial = NoneSet(values=[ + 'legacyMatte','legacyPlastic', 'legacyMetal', 'legacyWireframe', 'matte', 'plastic', + 'metal', 'warmMatte', 'translucentPowder', 'powder', 'dkEdge', + 'softEdge', 'clear', 'flat', 'softmetal'] + ) + bevelT = Typed(expected_type=Bevel, allow_none=True) + bevelB = Typed(expected_type=Bevel, allow_none=True) + extrusionClr = Typed(expected_type=Color, allow_none=True) + contourClr = Typed(expected_type=Color, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + z=None, + extrusionH=None, + contourW=None, + prstMaterial=None, + bevelT=None, + bevelB=None, + extrusionClr=None, + contourClr=None, + extLst=None, + ): + self.z = z + self.extrusionH = extrusionH + self.contourW = contourW + self.prstMaterial = prstMaterial + self.bevelT = bevelT + self.bevelB = bevelB + self.extrusionClr = extrusionClr + self.contourClr = contourClr + self.extLst = extLst + + +class Path2D(Serialisable): + + w = Float() + h = Float() + fill = NoneSet(values=(['norm', 'lighten', 'lightenLess', 'darken', 'darkenLess'])) + stroke = Bool(allow_none=True) + extrusionOk = Bool(allow_none=True) + + def __init__(self, + w=None, + h=None, + fill=None, + stroke=None, + extrusionOk=None, + ): + self.w = w + self.h = h + self.fill = fill + self.stroke = stroke + self.extrusionOk = extrusionOk + + +class Path2DList(Serialisable): + + path = Typed(expected_type=Path2D, allow_none=True) + + def __init__(self, + path=None, + ): + self.path = path + + +class GeomRect(Serialisable): + + l = Coordinate() + t = Coordinate() + r = Coordinate() + b = Coordinate() + + def __init__(self, + l=None, + t=None, + r=None, + b=None, + ): + self.l = l + self.t = t + self.r = r + self.b = b + + +class AdjPoint2D(Serialisable): + + x = Coordinate() + y = Coordinate() + + def __init__(self, + x=None, + y=None, + ): + self.x = x + self.y = y + + +class ConnectionSite(Serialisable): + + ang = MinMax(min=0, max=360) # guess work, can also be a name + pos = Typed(expected_type=AdjPoint2D, ) + + def __init__(self, + ang=None, + pos=None, + ): + self.ang = ang + self.pos = pos + + +class ConnectionSiteList(Serialisable): + + cxn = Typed(expected_type=ConnectionSite, allow_none=True) + + def __init__(self, + cxn=None, + ): + self.cxn = cxn + + +class AdjustHandleList(Serialisable): + + pass + +class GeomGuide(Serialisable): + + name = String() + fmla = String() + + def __init__(self, + name=None, + fmla=None, + ): + self.name = name + self.fmla = fmla + + +class GeomGuideList(Serialisable): + + gd = Typed(expected_type=GeomGuide, allow_none=True) + + def __init__(self, + gd=None, + ): + self.gd = gd + + +class CustomGeometry2D(Serialisable): + + avLst = Typed(expected_type=GeomGuideList, allow_none=True) + gdLst = Typed(expected_type=GeomGuideList, allow_none=True) + ahLst = Typed(expected_type=AdjustHandleList, allow_none=True) + cxnLst = Typed(expected_type=ConnectionSiteList, allow_none=True) + rect = Typed(expected_type=GeomRect, allow_none=True) + pathLst = Typed(expected_type=Path2DList, ) + + def __init__(self, + avLst=None, + gdLst=None, + ahLst=None, + cxnLst=None, + rect=None, + pathLst=None, + ): + self.avLst = avLst + self.gdLst = gdLst + self.ahLst = ahLst + self.cxnLst = cxnLst + self.rect = rect + self.pathLst = pathLst + + +class PresetGeometry2D(Serialisable): + + namespace = DRAWING_NS + + prst = Set(values=( + ['line', 'lineInv', 'triangle', 'rtTriangle', 'rect', + 'diamond', 'parallelogram', 'trapezoid', 'nonIsoscelesTrapezoid', + 'pentagon', 'hexagon', 'heptagon', 'octagon', 'decagon', 'dodecagon', + 'star4', 'star5', 'star6', 'star7', 'star8', 'star10', 'star12', + 'star16', 'star24', 'star32', 'roundRect', 'round1Rect', + 'round2SameRect', 'round2DiagRect', 'snipRoundRect', 'snip1Rect', + 'snip2SameRect', 'snip2DiagRect', 'plaque', 'ellipse', 'teardrop', + 'homePlate', 'chevron', 'pieWedge', 'pie', 'blockArc', 'donut', + 'noSmoking', 'rightArrow', 'leftArrow', 'upArrow', 'downArrow', + 'stripedRightArrow', 'notchedRightArrow', 'bentUpArrow', + 'leftRightArrow', 'upDownArrow', 'leftUpArrow', 'leftRightUpArrow', + 'quadArrow', 'leftArrowCallout', 'rightArrowCallout', 'upArrowCallout', + 'downArrowCallout', 'leftRightArrowCallout', 'upDownArrowCallout', + 'quadArrowCallout', 'bentArrow', 'uturnArrow', 'circularArrow', + 'leftCircularArrow', 'leftRightCircularArrow', 'curvedRightArrow', + 'curvedLeftArrow', 'curvedUpArrow', 'curvedDownArrow', 'swooshArrow', + 'cube', 'can', 'lightningBolt', 'heart', 'sun', 'moon', 'smileyFace', + 'irregularSeal1', 'irregularSeal2', 'foldedCorner', 'bevel', 'frame', + 'halfFrame', 'corner', 'diagStripe', 'chord', 'arc', 'leftBracket', + 'rightBracket', 'leftBrace', 'rightBrace', 'bracketPair', 'bracePair', + 'straightConnector1', 'bentConnector2', 'bentConnector3', + 'bentConnector4', 'bentConnector5', 'curvedConnector2', + 'curvedConnector3', 'curvedConnector4', 'curvedConnector5', 'callout1', + 'callout2', 'callout3', 'accentCallout1', 'accentCallout2', + 'accentCallout3', 'borderCallout1', 'borderCallout2', 'borderCallout3', + 'accentBorderCallout1', 'accentBorderCallout2', 'accentBorderCallout3', + 'wedgeRectCallout', 'wedgeRoundRectCallout', 'wedgeEllipseCallout', + 'cloudCallout', 'cloud', 'ribbon', 'ribbon2', 'ellipseRibbon', + 'ellipseRibbon2', 'leftRightRibbon', 'verticalScroll', + 'horizontalScroll', 'wave', 'doubleWave', 'plus', 'flowChartProcess', + 'flowChartDecision', 'flowChartInputOutput', + 'flowChartPredefinedProcess', 'flowChartInternalStorage', + 'flowChartDocument', 'flowChartMultidocument', 'flowChartTerminator', + 'flowChartPreparation', 'flowChartManualInput', + 'flowChartManualOperation', 'flowChartConnector', 'flowChartPunchedCard', + 'flowChartPunchedTape', 'flowChartSummingJunction', 'flowChartOr', + 'flowChartCollate', 'flowChartSort', 'flowChartExtract', + 'flowChartMerge', 'flowChartOfflineStorage', 'flowChartOnlineStorage', + 'flowChartMagneticTape', 'flowChartMagneticDisk', + 'flowChartMagneticDrum', 'flowChartDisplay', 'flowChartDelay', + 'flowChartAlternateProcess', 'flowChartOffpageConnector', + 'actionButtonBlank', 'actionButtonHome', 'actionButtonHelp', + 'actionButtonInformation', 'actionButtonForwardNext', + 'actionButtonBackPrevious', 'actionButtonEnd', 'actionButtonBeginning', + 'actionButtonReturn', 'actionButtonDocument', 'actionButtonSound', + 'actionButtonMovie', 'gear6', 'gear9', 'funnel', 'mathPlus', 'mathMinus', + 'mathMultiply', 'mathDivide', 'mathEqual', 'mathNotEqual', 'cornerTabs', + 'squareTabs', 'plaqueTabs', 'chartX', 'chartStar', 'chartPlus'])) + avLst = Typed(expected_type=GeomGuideList, allow_none=True) + + def __init__(self, + prst=None, + avLst=None, + ): + self.prst = prst + self.avLst = avLst + + +class FontReference(Serialisable): + + idx = NoneSet(values=(['major', 'minor'])) + + def __init__(self, + idx=None, + ): + self.idx = idx + + +class StyleMatrixReference(Serialisable): + + idx = Integer() + + def __init__(self, + idx=None, + ): + self.idx = idx + + +class ShapeStyle(Serialisable): + + lnRef = Typed(expected_type=StyleMatrixReference, ) + fillRef = Typed(expected_type=StyleMatrixReference, ) + effectRef = Typed(expected_type=StyleMatrixReference, ) + fontRef = Typed(expected_type=FontReference, ) + + def __init__(self, + lnRef=None, + fillRef=None, + effectRef=None, + fontRef=None, + ): + self.lnRef = lnRef + self.fillRef = fillRef + self.effectRef = effectRef + self.fontRef = fontRef diff --git a/openpyxl/drawing/spreadsheet_drawing.py b/openpyxl/drawing/spreadsheet_drawing.py new file mode 100644 index 0000000..742b029 --- /dev/null +++ b/openpyxl/drawing/spreadsheet_drawing.py @@ -0,0 +1,375 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2017 openpyxl +# Copyright (c) 2018 qyou.casia@gmail.com + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + NoneSet, + Integer, + Sequence, + Alias, +) +from openpyxl.descriptors.nested import ( + NestedText, + NestedNoneSet, +) +from openpyxl.descriptors.excel import Relation + +from openpyxl.packaging.relationship import ( + Relationship, + RelationshipList, +) +from openpyxl.utils import coordinate_to_tuple +from openpyxl.utils.units import ( + cm_to_EMU, + pixels_to_EMU, +) +from openpyxl.drawing.image import Image + +from openpyxl.xml.constants import SHEET_DRAWING_NS + +from openpyxl.chart._chart import ChartBase +from .shapes import ( + Point2D, + PositiveSize2D, + PresetGeometry2D, +) +from .fill import Blip +from .graphic import ( + GroupShape, + GraphicFrame, + Shape, + PictureFrame, + ChartRelation, + Shape, + ) + + +class AnchorClientData(Serialisable): + + fLocksWithSheet = Bool(allow_none=True) + fPrintsWithSheet = Bool(allow_none=True) + + def __init__(self, + fLocksWithSheet=None, + fPrintsWithSheet=None, + ): + self.fLocksWithSheet = fLocksWithSheet + self.fPrintsWithSheet = fPrintsWithSheet + + +class AnchorMarker(Serialisable): + + tagname = "marker" + + col = NestedText(expected_type=int) + colOff = NestedText(expected_type=int) + row = NestedText(expected_type=int) + rowOff = NestedText(expected_type=int) + + def __init__(self, + col=0, + colOff=0, + row=0, + rowOff=0, + ): + self.col = col + self.colOff = colOff + self.row = row + self.rowOff = rowOff + + +class _AnchorBase(Serialisable): + + #one of + sp = Typed(expected_type=Shape, allow_none=True) + shape = Alias("sp") + grpSp = Typed(expected_type=GroupShape, allow_none=True) + groupShape = Alias("grpSp") + graphicFrame = Typed(expected_type=GraphicFrame, allow_none=True) + cxnSp = Typed(expected_type=Shape, allow_none=True) + connectionShape = Alias("cxnSp") + pic = Typed(expected_type=PictureFrame, allow_none=True) + contentPart = Relation() + + clientData = Typed(expected_type=AnchorClientData) + + __elements__ = ('sp', 'grpSp', 'graphicFrame', + 'cxnSp', 'pic', 'contentPart', 'clientData') + + def __init__(self, + clientData=None, + sp=None, + grpSp=None, + graphicFrame=None, + cxnSp=None, + pic=None, + contentPart=None + ): + if clientData is None: + clientData = AnchorClientData() + self.clientData = clientData + self.sp = sp + self.grpSp = grpSp + self.graphicFrame = graphicFrame + self.cxnSp = cxnSp + self.pic = pic + self.contentPart = contentPart + + +class AbsoluteAnchor(_AnchorBase): + + tagname = "absoluteAnchor" + + pos = Typed(expected_type=Point2D) + ext = Typed(expected_type=PositiveSize2D) + + sp = _AnchorBase.sp + grpSp = _AnchorBase.grpSp + graphicFrame = _AnchorBase.graphicFrame + cxnSp = _AnchorBase.cxnSp + pic = _AnchorBase.pic + contentPart = _AnchorBase.contentPart + clientData = _AnchorBase.clientData + + __elements__ = ('pos', 'ext') + _AnchorBase.__elements__ + + def __init__(self, + pos=None, + ext=None, + **kw + ): + if pos is None: + pos = Point2D(0, 0) + self.pos = pos + if ext is None: + ext = PositiveSize2D(0, 0) + self.ext = ext + super(AbsoluteAnchor, self).__init__(**kw) + + +class OneCellAnchor(_AnchorBase): + + tagname = "oneCellAnchor" + + _from = Typed(expected_type=AnchorMarker) + ext = Typed(expected_type=PositiveSize2D) + + sp = _AnchorBase.sp + grpSp = _AnchorBase.grpSp + graphicFrame = _AnchorBase.graphicFrame + cxnSp = _AnchorBase.cxnSp + pic = _AnchorBase.pic + contentPart = _AnchorBase.contentPart + clientData = _AnchorBase.clientData + + __elements__ = ('_from', 'ext') + _AnchorBase.__elements__ + + + def __init__(self, + _from=None, + ext=None, + **kw + ): + if _from is None: + _from = AnchorMarker() + self._from = _from + if ext is None: + ext = PositiveSize2D(0, 0) + self.ext = ext + super(OneCellAnchor, self).__init__(**kw) + + +class TwoCellAnchor(_AnchorBase): + + tagname = "twoCellAnchor" + + editAs = NoneSet(values=(['twoCell', 'oneCell', 'absolute'])) + _from = Typed(expected_type=AnchorMarker) + to = Typed(expected_type=AnchorMarker) + + sp = _AnchorBase.sp + grpSp = _AnchorBase.grpSp + graphicFrame = _AnchorBase.graphicFrame + cxnSp = _AnchorBase.cxnSp + pic = _AnchorBase.pic + contentPart = _AnchorBase.contentPart + clientData = _AnchorBase.clientData + + __elements__ = ('_from', 'to') + _AnchorBase.__elements__ + + def __init__(self, + editAs=None, + _from=None, + to=None, + **kw + ): + self.editAs = editAs + if _from is None: + _from = AnchorMarker() + self._from = _from + if to is None: + to = AnchorMarker() + self.to = to + super(TwoCellAnchor, self).__init__(**kw) + + +def _check_anchor(obj): + """ + Check whether an object has an existing Anchor object + If not create a OneCellAnchor using the provided coordinate + """ + anchor = obj.anchor + if not isinstance(anchor, _AnchorBase): + row, col = coordinate_to_tuple(anchor) + anchor = OneCellAnchor() + anchor._from.row = row -1 + anchor._from.col = col -1 + if isinstance(obj, ChartBase): + anchor.ext.width = cm_to_EMU(obj.width) + anchor.ext.height = cm_to_EMU(obj.height) + elif isinstance(obj, Image): + anchor.ext.width = pixels_to_EMU(obj.width) + anchor.ext.height = pixels_to_EMU(obj.height) + return anchor + + +class SpreadsheetDrawing(Serialisable): + + tagname = "wsDr" + mime_type = "application/vnd.openxmlformats-officedocument.drawing+xml" + _rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" + _path = PartName="/xl/drawings/drawing{0}.xml" + _id = None + + twoCellAnchor = Sequence(expected_type=TwoCellAnchor, allow_none=True) + oneCellAnchor = Sequence(expected_type=OneCellAnchor, allow_none=True) + absoluteAnchor = Sequence(expected_type=AbsoluteAnchor, allow_none=True) + + __elements__ = ("twoCellAnchor", "oneCellAnchor", "absoluteAnchor") + + def __init__(self, + twoCellAnchor=(), + oneCellAnchor=(), + absoluteAnchor=(), + ): + self.twoCellAnchor = twoCellAnchor + self.oneCellAnchor = oneCellAnchor + self.absoluteAnchor = absoluteAnchor + self.charts = [] + self.images = [] + self._rels = [] + + + def __hash__(self): + """ + Just need to check for identity + """ + return id(self) + + + def __bool__(self): + return bool(self.charts) or bool(self.images) + + __nonzero__ = __bool__ + + + def _write(self): + """ + create required structure and the serialise + """ + anchors = [] + for idx, obj in enumerate(self.charts + self.images, 1): + anchor = _check_anchor(obj) + if isinstance(obj, ChartBase): + rel = Relationship(type="chart", Target=obj.path) + anchor.graphicFrame = self._chart_frame(idx) + elif isinstance(obj, Image): + rel = Relationship(type="image", Target=obj.path) + anchor.pic = self._picture_frame(idx) + + anchors.append(anchor) + self._rels.append(rel) + + for a in anchors: + if isinstance(a, OneCellAnchor): + self.oneCellAnchor.append(a) + elif isinstance(a, TwoCellAnchor): + self.twoCellAnchor.append(a) + else: + self.absoluteAnchor.append(a) + + tree = self.to_tree() + tree.set('xmlns', SHEET_DRAWING_NS) + return tree + + + def _chart_frame(self, idx): + chart_rel = ChartRelation("rId%s" % idx) + frame = GraphicFrame() + nv = frame.nvGraphicFramePr.cNvPr + nv.id = idx + nv.name = "Chart {0}".format(idx) + frame.graphic.graphicData.chart = chart_rel + return frame + + + def _picture_frame(self, idx): + pic = PictureFrame() + pic.nvPicPr.cNvPr.descr = "Picture" + pic.nvPicPr.cNvPr.id = idx + pic.nvPicPr.cNvPr.name = "Image {0}".format(idx) + + pic.blipFill.blip = Blip() + pic.blipFill.blip.embed = "rId{0}".format(idx) + pic.blipFill.blip.cstate = "print" + + pic.spPr.prstGeom = PresetGeometry2D(prst="rect") + pic.spPr.ln = None + return pic + + + def _write_rels(self): + rels = RelationshipList() + rels.Relationship = self._rels + return rels.to_tree() + + + @property + def path(self): + return self._path.format(self._id) + + + @property + def _chart_rels(self): + """ + Get relationship information for each chart and bind anchor to it + """ + rels = [] + anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor + for anchor in anchors: + if anchor.graphicFrame is not None: + graphic = anchor.graphicFrame.graphic + rel = graphic.graphicData.chart + if rel is not None: + rel.anchor = anchor + rel.anchor.graphicFrame = None + rels.append(rel) + + return rels + + @property + def _image_rels(self): + rels = [] + anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor + for anchor in anchors: + if anchor.pic is not None: + pic = anchor.pic + rel = pic.blipFill.blip + if rel is not None: + rel.anchor = anchor + rel.anchor.pic = None + rels.append(rel) + return rels diff --git a/openpyxl/drawing/tests/__init__.py b/openpyxl/drawing/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/drawing/tests/conftest.py b/openpyxl/drawing/tests/conftest.py new file mode 100644 index 0000000..fe55808 --- /dev/null +++ b/openpyxl/drawing/tests/conftest.py @@ -0,0 +1,10 @@ +import pytest + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) diff --git a/openpyxl/drawing/tests/data/plain.png b/openpyxl/drawing/tests/data/plain.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad05c3c2198f86c6ce2950bd68287550113ca0d GIT binary patch literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^WgslT#=yX6Wzc;D$YCjV@(d9Gf~@B+?SXvG0*}aI z1_nK45N51cY7f-G*zf7$7*cWT?cK&q21Nl^$E~8TH~impjq9BQi)h(0#+!Tov^Y3r zxx7zynQ}O4=aIv5-!`<{zIo_>b-OEn^$jq0eSNgg_lad2nq}W4uv_0a$OmRVSibb< z#|=M4%2qhbz6xl!y0Vb36g%_9s*?RVFO4M<1;s$9)it68jG%nMFetx86fA%9VgDu_ zFpCJqq$m;lA)fI-+c056!iI)}Ol;h2+!8u;=Gd$Kl2*E9d{8M97+(yYu6{1-oD!M< DKA)+s literal 0 HcmV?d00001 diff --git a/openpyxl/drawing/tests/data/spreadsheet_drawing_with_chart.xml b/openpyxl/drawing/tests/data/spreadsheet_drawing_with_chart.xml new file mode 100644 index 0000000..7637be5 --- /dev/null +++ b/openpyxl/drawing/tests/data/spreadsheet_drawing_with_chart.xml @@ -0,0 +1,25 @@ + + + + + 1 + 0 + 28 + 0 + + + + + + + + + + + + + + + + + diff --git a/openpyxl/drawing/tests/data/text_box_drawing.xml b/openpyxl/drawing/tests/data/text_box_drawing.xml new file mode 100644 index 0000000..d7e7862 --- /dev/null +++ b/openpyxl/drawing/tests/data/text_box_drawing.xml @@ -0,0 +1,64 @@ + + + + 1 + 142875 + 0 + 19050 + + + 2 + 600075 + 4 + 76200 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + charlie clark: + + + + + This is a comment + + + + + + + diff --git a/openpyxl/drawing/tests/test_color.py b/openpyxl/drawing/tests/test_color.py new file mode 100644 index 0000000..cdab6c1 --- /dev/null +++ b/openpyxl/drawing/tests/test_color.py @@ -0,0 +1,179 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def ColorChoice(): + from ..colors import ColorChoice + return ColorChoice + + +class TestColorChoice: + + def test_ctor(self, ColorChoice): + color = ColorChoice() + color.RGB = "000000" + xml = tostring(color.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ColorChoice): + src = """ + + """ + node = fromstring(src) + color = ColorChoice.from_tree(node) + assert color == ColorChoice() + + +@pytest.fixture +def SystemColor(): + from ..colors import SystemColor + return SystemColor + + +class TestSystemColor: + + def test_ctor(self, SystemColor): + colors = SystemColor() + xml = tostring(colors.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SystemColor): + src = """ + + """ + node = fromstring(src) + colors = SystemColor.from_tree(node) + assert colors == SystemColor(val="windowText") + + +@pytest.fixture +def HSLColor(): + from ..colors import HSLColor + return HSLColor + + +class TestHSLColor: + + def test_ctor(self, HSLColor): + colors = HSLColor(hue=50, sat=10, lum=90) + xml = tostring(colors.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, HSLColor): + src = """ + + """ + node = fromstring(src) + colors = HSLColor.from_tree(node) + assert colors == HSLColor(hue=0, sat=20, lum=70) + + +@pytest.fixture +def RGBPercent(): + from ..colors import RGBPercent + return RGBPercent + + +class TestRGBPercent: + + def test_ctor(self, RGBPercent): + colors = RGBPercent(r=30, g=40, b=20) + xml = tostring(colors.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RGBPercent): + src = """ + + """ + node = fromstring(src) + colors = RGBPercent.from_tree(node) + assert colors == RGBPercent(r=30, g=40, b=20) + + +@pytest.fixture +def ColorMapping(): + from ..colors import ColorMapping + return ColorMapping + + +class TestColorMapping: + + def test_ctor(self, ColorMapping): + colors = ColorMapping() + xml = tostring(colors.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ColorMapping): + src = """ + + """ + node = fromstring(src) + colors = ColorMapping.from_tree(node) + assert colors == ColorMapping() + + +@pytest.fixture +def SchemeColor(): + from ..colors import SchemeColor + return SchemeColor + + +class TestSchemeColor: + + def test_ctor(self, SchemeColor): + sclr = SchemeColor(val="tx1") + xml = tostring(sclr.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SchemeColor): + src = """ + + """ + node = fromstring(src) + sclr = SchemeColor.from_tree(node) + assert sclr == SchemeColor(val="tx1") diff --git a/openpyxl/drawing/tests/test_descriptors.py b/openpyxl/drawing/tests/test_descriptors.py new file mode 100644 index 0000000..0421e33 --- /dev/null +++ b/openpyxl/drawing/tests/test_descriptors.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +def test_color_descriptor(): + from ..colors import ColorChoiceDescriptor + + class DummyStyle(object): + + value = ColorChoiceDescriptor('value') + + style = DummyStyle() + style.value = "efefef" + style.value.RGB == "efefef" diff --git a/openpyxl/drawing/tests/test_drawing.py b/openpyxl/drawing/tests/test_drawing.py new file mode 100644 index 0000000..5793a8c --- /dev/null +++ b/openpyxl/drawing/tests/test_drawing.py @@ -0,0 +1,106 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring + +from openpyxl.tests.helper import compare_xml + + +class TestDrawing(object): + + def setup(self): + from ..drawing import Drawing + self.drawing = Drawing() + + def test_ctor(self): + d = self.drawing + assert d.coordinates == ((1, 2), (16, 8)) + assert d.width == 21 + assert d.height == 192 + assert d.left == 0 + assert d.top == 0 + assert d.count == 0 + assert d.rotation == 0 + assert d.resize_proportional is False + assert d.description == "" + assert d.name == "" + + def test_width(self): + d = self.drawing + d.width = 100 + d.height = 50 + assert d.width == 100 + + def test_proportional_width(self): + d = self.drawing + d.resize_proportional = True + d.width = 100 + d.height = 50 + assert (d.width, d.height) == (5, 50) + + def test_height(self): + d = self.drawing + d.height = 50 + d.width = 100 + assert d.height == 50 + + def test_proportional_height(self): + d = self.drawing + d.resize_proportional = True + d.height = 50 + d.width = 100 + assert (d.width, d.height) == (100, 1000) + + def test_set_dimension(self): + d = self.drawing + d.resize_proportional = True + d.set_dimension(100, 50) + assert d.width == 6 + assert d.height == 50 + + d.set_dimension(50, 500) + assert d.width == 50 + assert d.height == 417 + + def test_get_emu(self): + d = self.drawing + dims = d.get_emu_dimensions() + assert dims == (0, 0, 200025, 1828800) + + + @pytest.mark.pil_required + def test_absolute_anchor(self): + node = self.drawing.anchor + xml = tostring(node.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + @pytest.mark.pil_required + def test_onecell_anchor(self): + self.drawing.anchortype = "oneCell" + node = self.drawing.anchor + xml = tostring(node.to_tree()) + expected = """ + + + 0 + 0 + 0 + 0 + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/drawing/tests/test_effect.py b/openpyxl/drawing/tests/test_effect.py new file mode 100644 index 0000000..220c0ee --- /dev/null +++ b/openpyxl/drawing/tests/test_effect.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def OuterShadow(): + from ..effect import OuterShadow + return OuterShadow + + +class TestOuterShadow: + + def test_ctor(self, OuterShadow): + shadow = OuterShadow(algn="tl", srgbClr="000000") + xml = tostring(shadow.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, OuterShadow): + src = """ + + + + + """ + node = fromstring(src) + shadow = OuterShadow.from_tree(node) + assert shadow == OuterShadow(algn="tl", blurRad=38100, dist=38100, dir=2700000, srgbClr="000000") diff --git a/openpyxl/drawing/tests/test_fill.py b/openpyxl/drawing/tests/test_fill.py new file mode 100644 index 0000000..048ca3e --- /dev/null +++ b/openpyxl/drawing/tests/test_fill.py @@ -0,0 +1,243 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def PatternFillProperties(): + from ..fill import PatternFillProperties + return PatternFillProperties + + +class TestPatternFillProperties: + + def test_ctor(self, PatternFillProperties): + fill = PatternFillProperties(prst="cross",) + xml = tostring(fill.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PatternFillProperties): + src = """ + + """ + node = fromstring(src) + fill = PatternFillProperties.from_tree(node) + assert fill == PatternFillProperties("dashHorz") + +@pytest.fixture +def RelativeRect(): + from ..fill import RelativeRect + return RelativeRect + + +class TestRelativeRect: + + def test_ctor(self, RelativeRect): + fill = RelativeRect(10, 15, 20, 25) + xml = tostring(fill.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RelativeRect): + src = """ + + """ + node = fromstring(src) + fill = RelativeRect.from_tree(node) + assert fill == RelativeRect(10, 15, 20, 25) + + + def test_from_src_rect(self, RelativeRect): + src = """ + + """ + node = fromstring(src) + fill = RelativeRect.from_tree(node) + assert fill == RelativeRect(l=71321, t=10170, r=4935, b=80270) + + +@pytest.fixture +def StretchInfoProperties(): + from ..fill import StretchInfoProperties + return StretchInfoProperties + + +class TestStretchInfoProperties: + + def test_ctor(self, StretchInfoProperties): + fill = StretchInfoProperties() + xml = tostring(fill.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, StretchInfoProperties): + src = """ + + """ + node = fromstring(src) + fill = StretchInfoProperties.from_tree(node) + assert fill == StretchInfoProperties() + + +@pytest.fixture +def GradientStop(): + from ..fill import GradientStop + return GradientStop + + +class TestGradientStop: + + def test_ctor(self, GradientStop): + fill = GradientStop() + xml = tostring(fill.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GradientStop): + src = """ + + """ + node = fromstring(src) + fill = GradientStop.from_tree(node) + assert fill == GradientStop() + + +@pytest.fixture +def GradientStopList(): + from ..fill import GradientStopList + return GradientStopList + + +class TestGradientStopList: + + def test_ctor(self, GradientStopList): + fill = GradientStopList() + xml = tostring(fill.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GradientStopList): + src = """ + + + + + """ + node = fromstring(src) + fill = GradientStopList.from_tree(node) + assert fill == GradientStopList() + + +@pytest.fixture +def GradientFillProperties(): + from ..fill import GradientFillProperties + return GradientFillProperties + + +class TestGradientFillProperties: + + def test_ctor(self, GradientFillProperties): + fill = GradientFillProperties(flip="xy") + xml = tostring(fill.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GradientFillProperties): + src = """ + + """ + node = fromstring(src) + fill = GradientFillProperties.from_tree(node) + assert fill == GradientFillProperties() + + +@pytest.fixture +def Blip(): + from ..fill import Blip + return Blip + + +class TestBlip: + + def test_ctor(self, Blip): + fill = Blip(embed="rId1") + xml = tostring(fill.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Blip): + src = """ + + """ + node = fromstring(src) + fill = Blip.from_tree(node) + assert fill == Blip() + + +@pytest.fixture +def BlipFillProperties(): + from ..fill import BlipFillProperties + return BlipFillProperties + + +class TestBlipFillProperties: + + def test_ctor(self, BlipFillProperties): + fill = BlipFillProperties() + xml = tostring(fill.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, BlipFillProperties): + src = """ + + """ + node = fromstring(src) + fill = BlipFillProperties.from_tree(node) + assert fill == BlipFillProperties() diff --git a/openpyxl/drawing/tests/test_graphic.py b/openpyxl/drawing/tests/test_graphic.py new file mode 100644 index 0000000..1d913ca --- /dev/null +++ b/openpyxl/drawing/tests/test_graphic.py @@ -0,0 +1,537 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def NonVisualGraphicFrameProperties(): + from ..graphic import NonVisualGraphicFrameProperties + return NonVisualGraphicFrameProperties + + +class TestNonVisualGraphicFrameProperties: + + def test_ctor(self, NonVisualGraphicFrameProperties): + graphic = NonVisualGraphicFrameProperties() + xml = tostring(graphic.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, NonVisualGraphicFrameProperties): + src = """ + + """ + node = fromstring(src) + graphic = NonVisualGraphicFrameProperties.from_tree(node) + assert graphic == NonVisualGraphicFrameProperties() + + +@pytest.fixture +def NonVisualDrawingProps(): + from ..graphic import NonVisualDrawingProps + return NonVisualDrawingProps + + +class TestNonVisualDrawingProps: + + def test_ctor(self, NonVisualDrawingProps): + graphic = NonVisualDrawingProps(id=2, name="Chart 1") + xml = tostring(graphic.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, NonVisualDrawingProps): + src = """ + + """ + node = fromstring(src) + graphic = NonVisualDrawingProps.from_tree(node) + assert graphic == NonVisualDrawingProps(id=3, name="Chart 2") + + +@pytest.fixture +def NonVisualGraphicFrame(): + from ..graphic import NonVisualGraphicFrame + return NonVisualGraphicFrame + + +class TestNonVisualGraphicFrame: + + def test_ctor(self, NonVisualGraphicFrame): + graphic = NonVisualGraphicFrame() + xml = tostring(graphic.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, NonVisualGraphicFrame): + src = """ + + + + + """ + node = fromstring(src) + graphic = NonVisualGraphicFrame.from_tree(node) + assert graphic == NonVisualGraphicFrame() + + +@pytest.fixture +def GraphicData(): + from ..graphic import GraphicData + return GraphicData + + +class TestGraphicData: + + def test_ctor(self, GraphicData): + graphic = GraphicData() + xml = tostring(graphic.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GraphicData): + src = """ + + """ + node = fromstring(src) + graphic = GraphicData.from_tree(node) + assert graphic == GraphicData() + + + def test_contains_chart(self, GraphicData): + src = """ + + + + """ + node = fromstring(src) + graphic = GraphicData.from_tree(node) + assert graphic.chart is not None + + +@pytest.fixture +def GraphicObject(): + from ..graphic import GraphicObject + return GraphicObject + + +class TestGraphicObject: + + def test_ctor(self, GraphicObject): + graphic = GraphicObject() + xml = tostring(graphic.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GraphicObject): + src = """ + + + """ + node = fromstring(src) + graphic = GraphicObject.from_tree(node) + assert graphic == GraphicObject() + + +@pytest.fixture +def GraphicFrame(): + from ..graphic import GraphicFrame + return GraphicFrame + + +class TestGraphicFrame: + + def test_ctor(self, GraphicFrame): + graphic = GraphicFrame() + xml = tostring(graphic.to_tree()) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GraphicFrame): + src = """ + + + + + + + + + + + """ + node = fromstring(src) + graphic = GraphicFrame.from_tree(node) + assert graphic == GraphicFrame() + + +@pytest.fixture +def ChartRelation(): + from ..graphic import ChartRelation + return ChartRelation + + +class TestChartRelation: + + def test_ctor(self, ChartRelation): + rel = ChartRelation('rId1') + xml = tostring(rel.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ChartRelation): + src = """ + + """ + node = fromstring(src) + rel = ChartRelation.from_tree(node) + assert rel == ChartRelation("rId1") + + +@pytest.fixture +def PictureLocking(): + from ..graphic import PictureLocking + return PictureLocking + + +class TestPictureLocking: + + def test_ctor(self, PictureLocking): + graphic = PictureLocking(noChangeAspect=True) + xml = tostring(graphic.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PictureLocking): + src = """ + + """ + node = fromstring(src) + graphic = PictureLocking.from_tree(node) + assert graphic == PictureLocking(noRot=1) + + +@pytest.fixture +def NonVisualPictureProperties(): + from ..graphic import NonVisualPictureProperties + return NonVisualPictureProperties + + +class TestNonVisualPictureProperties: + + def test_ctor(self, NonVisualPictureProperties): + graphic = NonVisualPictureProperties() + xml = tostring(graphic.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, NonVisualPictureProperties): + src = """ + + """ + node = fromstring(src) + graphic = NonVisualPictureProperties.from_tree(node) + assert graphic == NonVisualPictureProperties() + + +@pytest.fixture +def PictureNonVisual(): + from ..graphic import PictureNonVisual + return PictureNonVisual + + +class TestPictureNonVisual: + + def test_ctor(self, PictureNonVisual): + graphic = PictureNonVisual() + xml = tostring(graphic.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PictureNonVisual): + src = """ + + + + + """ + node = fromstring(src) + graphic = PictureNonVisual.from_tree(node) + assert graphic == PictureNonVisual() + + +@pytest.fixture +def PictureFrame(): + from ..graphic import PictureFrame + return PictureFrame + + +class TestPicture: + + def test_ctor(self, PictureFrame): + graphic = PictureFrame() + xml = tostring(graphic.to_tree()) + expected = """ + + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PictureFrame): + src = """ + + """ + node = fromstring(src) + graphic = PictureFrame.from_tree(node) + assert graphic == PictureFrame() + + +@pytest.fixture +def ConnectorShape(): + from ..graphic import ConnectorShape + return ConnectorShape + + +class TestConnectorShape: + + + @pytest.mark.xfail + def test_ctor(self, ConnectorShape): + fut = ConnectorShape() + xml = tostring(fut.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ConnectorShape): + src = """ + + + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + cnx = ConnectorShape.from_tree(node) + assert cnx.nvCxnSpPr.cNvPr.id == 3 + + + +@pytest.fixture +def GroupTransform2D(): + from ..graphic import GroupTransform2D + return GroupTransform2D + + +class TestGroupTransform2D: + + def test_ctor(self, GroupTransform2D): + xfrm = GroupTransform2D(rot=0) + xml = tostring(xfrm.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GroupTransform2D): + src = """ + + + + + + + """ + node = fromstring(src) + xfrm = GroupTransform2D.from_tree(node) + assert xfrm.off.y == 394447 + + +@pytest.fixture +def GroupShape(): + from ..graphic import GroupShape + return GroupShape + + +class TestGroupShape: + + @pytest.mark.xfail + def test_ctor(self, GroupShape): + grp = GroupShape() + xml = tostring(grp.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + @pytest.mark.xfail + def test_from_xml(self, GroupShape): + src = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + grp = GroupShape.from_tree(node) + assert grp == GroupShape() diff --git a/openpyxl/drawing/tests/test_image.py b/openpyxl/drawing/tests/test_image.py new file mode 100644 index 0000000..28d6859 --- /dev/null +++ b/openpyxl/drawing/tests/test_image.py @@ -0,0 +1,45 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + + +def test_bounding_box(): + from ..image import bounding_box + w, h = bounding_box(80, 80, 90, 100) + assert w == 72 + assert h == 80 + + +@pytest.fixture +def Image(): + from ..image import Image + return Image + + +class TestImage: + + @pytest.mark.pil_not_installed + def test_import(self, Image, datadir): + from ..image import _import_image + datadir.chdir() + with pytest.raises(ImportError): + _import_image("plain.png") + + + @pytest.mark.pil_required + def test_ctor(self, Image, datadir): + datadir.chdir() + i = Image(img="plain.png") + assert i.format == "png" + assert i.width == 118 + assert i.height == 118 + assert i.anchor == "A1" + + + @pytest.mark.pil_required + def test_write_image(self, Image, datadir): + datadir.chdir() + i = Image("plain.png") + with open("plain.png", "rb") as src: + assert i._data() == src.read() diff --git a/openpyxl/drawing/tests/test_line.py b/openpyxl/drawing/tests/test_line.py new file mode 100644 index 0000000..125a2c0 --- /dev/null +++ b/openpyxl/drawing/tests/test_line.py @@ -0,0 +1,134 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def LineProperties(): + from ..line import LineProperties + return LineProperties + + +class TestLineProperties: + + def test_ctor(self, LineProperties): + line = LineProperties(w=10) + xml = tostring(line.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_color(self, LineProperties): + line = LineProperties(w=10) + line.solidFill = "FF0000" + xml = tostring(line.to_tree()) + expected = """ + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LineProperties): + src = """ + + + + """ + node = fromstring(src) + line = LineProperties.from_tree(node) + assert line == LineProperties(w=38100, cmpd="sng") + + +@pytest.fixture +def LineEndProperties(): + from ..line import LineEndProperties + return LineEndProperties + + +class TestLineEndProperties: + + def test_ctor(self, LineEndProperties): + line = LineEndProperties() + xml = tostring(line.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LineEndProperties): + src = """ + + """ + node = fromstring(src) + line = LineEndProperties.from_tree(node) + assert line == LineEndProperties() + + +@pytest.fixture +def DashStop(): + from ..line import DashStop + return DashStop + + +class TestDashStop: + + def test_ctor(self, DashStop): + line = DashStop() + xml = tostring(line.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DashStop): + src = """ + + """ + node = fromstring(src) + line = DashStop.from_tree(node) + assert line == DashStop(d=10, sp=15) + + +@pytest.fixture +def LineJoinMiterProperties(): + from ..line import LineJoinMiterProperties + return LineJoinMiterProperties + + +class TestLineJoinMiterProperties: + + def test_ctor(self, LineJoinMiterProperties): + line = LineJoinMiterProperties() + xml = tostring(line.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LineJoinMiterProperties): + src = """ + + """ + node = fromstring(src) + line = LineJoinMiterProperties.from_tree(node) + assert line == LineJoinMiterProperties() diff --git a/openpyxl/drawing/tests/test_shape.py b/openpyxl/drawing/tests/test_shape.py new file mode 100644 index 0000000..98f0cf6 --- /dev/null +++ b/openpyxl/drawing/tests/test_shape.py @@ -0,0 +1,187 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.xml.constants import CHART_DRAWING_NS +from openpyxl.xml.functions import Element, fromstring, tostring + +from openpyxl.tests.helper import compare_xml + +class DummyDrawing(object): + + """Shapes need charts which need drawings""" + + width = 10 + height = 20 + + +class DummyChart(object): + + """Shapes need a chart to calculate their coordinates""" + + width = 100 + height = 100 + + def __init__(self): + self.drawing = DummyDrawing() + + def _get_margin_left(self): + return 10 + + def _get_margin_top(self): + return 5 + + def get_x_units(self): + return 25 + + def get_y_units(self): + return 15 + + +class TestShape(object): + + def setup(self): + from ..shape import Shape + self.shape = Shape(chart=DummyChart()) + + def test_ctor(self): + s = self.shape + assert s.axis_coordinates == ((0, 0), (1, 1)) + assert s.text is None + assert s.scheme == "accent1" + assert s.style == "rect" + assert s.border_color == "000000" + assert s.color == "FFFFFF" + assert s.text_color == "000000" + assert s.border_width == 0 + + def test_border_color(self): + s = self.shape + s.border_color = "BBBBBB" + assert s.border_color == "BBBBBB" + + def test_color(self): + s = self.shape + s.color = "000000" + assert s.color == "000000" + + def test_text_color(self): + s = self.shape + s.text_color = "FF0000" + assert s.text_color == "FF0000" + + def test_border_width(self): + s = self.shape + s.border_width = 50 + assert s.border_width == 50 + + def test_coordinates(self): + s = self.shape + s.coordinates = ((0, 0), (60, 80)) + assert s.axis_coordinates == ((0, 0), (60, 80)) + assert s.coordinates == (1, 1, 1, 1) + + def test_pct(self): + s = self.shape + assert s._norm_pct(10) == 1 + assert s._norm_pct(0.5) == 0.5 + assert s._norm_pct(-10) == 0 + + +class TestShapeWriter(object): + + def setup(self): + from ..shape import ShapeWriter + from ..shape import Shape + chart = DummyChart() + self.shape = Shape(chart=chart, text="My first chart") + self.sw = ShapeWriter(shapes=[self.shape]) + + def test_write(self): + xml = self.sw.write(0) + tree = fromstring(xml) + expected = """ + + + + 1 + 1 + + + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + My first chart + + + + + + +""" + diff = compare_xml(xml, expected) + assert diff is None, diff + + def test_write_text(self): + root = Element("{%s}test" % CHART_DRAWING_NS) + self.sw._write_text(root, self.shape) + xml = tostring(root) + expected = """My first chart""" + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_write_style(self): + root = Element("{%s}test" % CHART_DRAWING_NS) + self.sw._write_style(root) + xml = tostring(root) + expected = """""" + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/drawing/tests/test_shapes.py b/openpyxl/drawing/tests/test_shapes.py new file mode 100644 index 0000000..e6722ec --- /dev/null +++ b/openpyxl/drawing/tests/test_shapes.py @@ -0,0 +1,142 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def GradientFillProperties(): + from ..fill import GradientFillProperties + return GradientFillProperties + + +class TestGradientFillProperties: + + def test_ctor(self, GradientFillProperties): + fill = GradientFillProperties() + xml = tostring(fill.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, GradientFillProperties): + src = """ + + """ + node = fromstring(src) + fill = GradientFillProperties.from_tree(node) + assert fill == GradientFillProperties() + + +@pytest.fixture +def Transform2D(): + from ..shapes import Transform2D + return Transform2D + + +class TestTransform2D: + + def test_ctor(self, Transform2D): + shapes = Transform2D() + xml = tostring(shapes.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Transform2D): + src = """ + + """ + node = fromstring(src) + shapes = Transform2D.from_tree(node) + assert shapes == Transform2D() + + +@pytest.fixture +def Camera(): + from ..shapes import Camera + return Camera + + +class TestCamera: + + def test_ctor(self, Camera): + cam = Camera(prst="legacyObliqueFront") + xml = tostring(cam.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Camera): + src = """ + + """ + node = fromstring(src) + cam = Camera.from_tree(node) + assert cam == Camera(prst="orthographicFront") + + +@pytest.fixture +def LightRig(): + from ..shapes import LightRig + return LightRig + + +class TestLightRig: + + def test_ctor(self, LightRig): + rig = LightRig(rig="threePt", dir="t") + xml = tostring(rig.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, LightRig): + src = """ + + """ + node = fromstring(src) + rig = LightRig.from_tree(node) + assert rig == LightRig(rig="threePt", dir="t") + + +@pytest.fixture +def Bevel(): + from ..shapes import Bevel + return Bevel + + +class TestBevel: + + def test_ctor(self, Bevel): + bevel = Bevel(w=10, h=20) + xml = tostring(bevel.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Bevel): + src = """ + + """ + node = fromstring(src) + bevel = Bevel.from_tree(node) + assert bevel == Bevel( w=101600, h=101600) diff --git a/openpyxl/drawing/tests/test_spreadsheet_drawing.py b/openpyxl/drawing/tests/test_spreadsheet_drawing.py new file mode 100644 index 0000000..3c1b78f --- /dev/null +++ b/openpyxl/drawing/tests/test_spreadsheet_drawing.py @@ -0,0 +1,364 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml +from openpyxl.drawing.image import Image +from openpyxl.chart import BarChart + + +@pytest.fixture +def TwoCellAnchor(): + from ..spreadsheet_drawing import TwoCellAnchor + return TwoCellAnchor + + +class TestTwoCellAnchor: + + def test_ctor(self, TwoCellAnchor): + chart_drawing = TwoCellAnchor() + xml = tostring(chart_drawing.to_tree()) + expected = """ + + + 0 + 0 + 0 + 0 + + + 0 + 0 + 0 + 0 + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, TwoCellAnchor): + src = """ + + + 0 + 0 + 0 + 0 + + + 0 + 0 + 0 + 0 + + + + """ + node = fromstring(src) + chart_drawing = TwoCellAnchor.from_tree(node) + assert chart_drawing == TwoCellAnchor() + + +@pytest.fixture +def OneCellAnchor(): + from ..spreadsheet_drawing import OneCellAnchor + return OneCellAnchor + + +class TestOneCellAnchor: + + def test_ctor(self, OneCellAnchor): + chart_drawing = OneCellAnchor() + xml = tostring(chart_drawing.to_tree()) + expected = """ + + + 0 + 0 + 0 + 0 + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, OneCellAnchor): + src = """ + + + 0 + 0 + 0 + 0 + + + + + """ + node = fromstring(src) + chart_drawing = OneCellAnchor.from_tree(node) + assert chart_drawing == OneCellAnchor() + + +@pytest.fixture +def AbsoluteAnchor(): + from ..spreadsheet_drawing import AbsoluteAnchor + return AbsoluteAnchor + + +class TestAbsoluteAnchor: + + def test_ctor(self, AbsoluteAnchor): + chart_drawing = AbsoluteAnchor() + xml = tostring(chart_drawing.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, AbsoluteAnchor): + src = """ + + + + + + """ + node = fromstring(src) + chart_drawing = AbsoluteAnchor.from_tree(node) + assert chart_drawing == AbsoluteAnchor() + + +@pytest.fixture +def SpreadsheetDrawing(): + from ..spreadsheet_drawing import SpreadsheetDrawing + return SpreadsheetDrawing + + +class TestSpreadsheetDrawing: + + def test_ctor(self, SpreadsheetDrawing): + from ..spreadsheet_drawing import ( + OneCellAnchor, + TwoCellAnchor, + AbsoluteAnchor + ) + a = [AbsoluteAnchor(), AbsoluteAnchor()] + o = [OneCellAnchor()] + t = [TwoCellAnchor(), TwoCellAnchor()] + chart_drawing = SpreadsheetDrawing(absoluteAnchor=a, oneCellAnchor=o, + twoCellAnchor=t) + xml = tostring(chart_drawing.to_tree()) + expected = """ + + + + 0 + 0 + 0 + 0 + + + 0 + 0 + 0 + 0 + + + + + + 0 + 0 + 0 + 0 + + + 0 + 0 + 0 + 0 + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_write_chart(self, SpreadsheetDrawing): + from openpyxl.chart._chart import ChartBase + + class Chart(ChartBase): + + anchor = "E15" + width = 15 + height = 7.5 + + drawing = SpreadsheetDrawing() + drawing.charts.append(Chart()) + xml = tostring(drawing._write()) + expected = """ + + + + 4 + 0 + 14 + 0 + + + + + + + + + + + + + + + + + + """ + diff = compare_xml (xml, expected) + assert diff is None, diff + + + def test_hash_function(self, SpreadsheetDrawing): + drawing = SpreadsheetDrawing() + assert hash(drawing) == hash(id(drawing)) + + + def test_write_picture(self, SpreadsheetDrawing): + drawing = SpreadsheetDrawing() + pic = drawing._picture_frame(4) + xml = tostring(pic.to_tree()) + expected = """ + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_read_chart(self, SpreadsheetDrawing, datadir): + datadir.chdir() + with open("spreadsheet_drawing_with_chart.xml") as src: + xml = src.read() + node = fromstring(xml) + + drawing = SpreadsheetDrawing.from_tree(node) + chart_rels = drawing._chart_rels + assert len(chart_rels) == 1 + assert chart_rels[0].anchor is not None + + + def test_write_rels(self, SpreadsheetDrawing): + from openpyxl.packaging.relationship import Relationship + rel = Relationship(type="drawing", Target="../file.xml") + drawing = SpreadsheetDrawing() + drawing._rels.append(rel) + xml = tostring(drawing._write_rels()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_path(self, SpreadsheetDrawing): + drawing = SpreadsheetDrawing() + assert drawing.path == "/xl/drawings/drawingNone.xml" + + + def test_empty(self, SpreadsheetDrawing): + drawing = SpreadsheetDrawing() + assert bool(drawing) is False + + + @pytest.mark.parametrize("attr", ['charts', 'images']) + def test_bool(self, SpreadsheetDrawing, attr): + drawing = SpreadsheetDrawing() + getattr(drawing, attr).append(1) + assert bool(drawing) is True + + +def test_check_anchor_chart(): + from ..spreadsheet_drawing import _check_anchor + c = BarChart() + anc = _check_anchor(c) + assert anc._from.row == 14 + assert anc._from.col == 4 + assert anc.ext.width == 5400000 + assert anc.ext.height == 2700000 + + +@pytest.mark.pil_required +def test_check_anchor_image(datadir): + datadir.chdir() + from ..spreadsheet_drawing import _check_anchor + from PIL.Image import Image as PILImage + im = Image(PILImage()) + anc = _check_anchor(im) + assert anc._from.row == 0 + assert anc._from.col == 0 + assert anc.ext.height == 0 + assert anc.ext.width == 0 diff --git a/openpyxl/drawing/tests/test_text.py b/openpyxl/drawing/tests/test_text.py new file mode 100644 index 0000000..fcb5f87 --- /dev/null +++ b/openpyxl/drawing/tests/test_text.py @@ -0,0 +1,197 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def Paragraph(): + from ..text import Paragraph + return Paragraph + + +class TestParagraph: + + + def test_ctor(self, Paragraph): + text = Paragraph() + xml = tostring(text.to_tree()) + expected = """ +

+ + + +

+ """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Paragraph): + src = """ +

+ """ + node = fromstring(src) + text = Paragraph.from_tree(node) + assert text == Paragraph() + + + def test_multiline(self, Paragraph): + src = """ +

+ + Adjusted Absorbance vs. + + + Concentration + +

+ """ + node = fromstring(src) + para = Paragraph.from_tree(node) + assert len(para.text) == 2 + + +@pytest.fixture +def ParagraphProperties(): + from ..text import ParagraphProperties + return ParagraphProperties + + +class TestParagraphProperties: + + def test_ctor(self, ParagraphProperties): + text = ParagraphProperties() + xml = tostring(text.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ParagraphProperties): + src = """ + + """ + node = fromstring(src) + text = ParagraphProperties.from_tree(node) + assert text == ParagraphProperties() + + +from ..spreadsheet_drawing import SpreadsheetDrawing + + +class TestTextBox: + + def test_from_xml(self, datadir): + datadir.chdir() + with open("text_box_drawing.xml") as src: + xml = src.read() + node = fromstring(xml) + drawing = SpreadsheetDrawing.from_tree(node) + anchor = drawing.twoCellAnchor[0] + box = anchor.sp + meta = box.nvSpPr + graphic = box.graphicalProperties + text = box.txBody + assert len(text.p) == 2 + + +@pytest.fixture +def CharacterProperties(): + from ..text import CharacterProperties + return CharacterProperties + + +class TestCharacterProperties: + + def test_ctor(self, CharacterProperties): + from ..text import Font + normal_font = Font(typeface='Arial') + text = CharacterProperties(latin=normal_font, sz=900, b=False, solidFill='FFC000') + + xml = tostring(text.to_tree()) + expected = (""" + + + + + + + """) + + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, CharacterProperties): + src = """ + + """ + node = fromstring(src) + text = CharacterProperties.from_tree(node) + assert text == CharacterProperties(sz=110) + + +@pytest.fixture +def Font(): + from ..text import Font + return Font + + +class TestFont: + + def test_ctor(self, Font): + fut = Font("Arial") + xml = tostring(fut.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Font): + src = """ + + """ + node = fromstring(src) + fut = Font.from_tree(node) + assert fut == Font(typeface="Arial", pitchFamily=40) + + + +@pytest.fixture +def Hyperlink(): + from ..text import Hyperlink + return Hyperlink + + +class TestHyperlink: + + def test_ctor(self, Hyperlink): + link = Hyperlink() + xml = tostring(link.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Hyperlink): + src = """ + + """ + node = fromstring(src) + link = Hyperlink.from_tree(node) + assert link == Hyperlink(tooltip="Select/de-select all") + + diff --git a/openpyxl/drawing/text.py b/openpyxl/drawing/text.py new file mode 100644 index 0000000..dc58d5f --- /dev/null +++ b/openpyxl/drawing/text.py @@ -0,0 +1,711 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Set, + NoneSet, + Sequence, + String, + Bool, + MinMax, + Integer +) +from openpyxl.descriptors.excel import ( + HexBinary, + TextPoint, + Coordinate, + ExtensionList, + Relation, +) +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedString, + NestedText, + NestedValue, + EmptyTag +) +from openpyxl.xml.constants import DRAWING_NS + + +from .colors import ColorChoiceDescriptor +from .effect import * +from .fill import * +from .shapes import ( + LineProperties, + Color, + Scene3D +) + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from openpyxl.descriptors.nested import NestedBool + + +class EmbeddedWAVAudioFile(Serialisable): + + name = String(allow_none=True) + + def __init__(self, + name=None, + ): + self.name = name + + +class Hyperlink(Serialisable): + + tagname = "hlinkClick" + namespace = DRAWING_NS + + invalidUrl = String(allow_none=True) + action = String(allow_none=True) + tgtFrame = String(allow_none=True) + tooltip = String(allow_none=True) + history = Bool(allow_none=True) + highlightClick = Bool(allow_none=True) + endSnd = Bool(allow_none=True) + snd = Typed(expected_type=EmbeddedWAVAudioFile, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + id = Relation(allow_none=True) + + __elements__ = ('snd',) + + def __init__(self, + invalidUrl=None, + action=None, + tgtFrame=None, + tooltip=None, + history=None, + highlightClick=None, + endSnd=None, + snd=None, + extLst=None, + id=None, + ): + self.invalidUrl = invalidUrl + self.action = action + self.tgtFrame = tgtFrame + self.tooltip = tooltip + self.history = history + self.highlightClick = highlightClick + self.endSnd = endSnd + self.snd = snd + self.id = id + + +class Font(Serialisable): + + tagname = "latin" + namespace = DRAWING_NS + + typeface = String() + panose = HexBinary(allow_none=True) + pitchFamily = MinMax(min=0, max=52, allow_none=True) + charset = Integer(allow_none=True) + + def __init__(self, + typeface=None, + panose=None, + pitchFamily=None, + charset=None, + ): + self.typeface = typeface + self.panose = panose + self.pitchFamily = pitchFamily + self.charset = charset + + +class CharacterProperties(Serialisable): + + tagname = "defRPr" + namespace = DRAWING_NS + + kumimoji = Bool(allow_none=True) + lang = String(allow_none=True) + altLang = String(allow_none=True) + sz = MinMax(allow_none=True, min=100, max=400000) # 100ths of a point + b = Bool(allow_none=True) + i = Bool(allow_none=True) + u = NoneSet(values=(['words', 'sng', 'dbl', 'heavy', 'dotted', + 'dottedHeavy', 'dash', 'dashHeavy', 'dashLong', 'dashLongHeavy', + 'dotDash', 'dotDashHeavy', 'dotDotDash', 'dotDotDashHeavy', 'wavy', + 'wavyHeavy', 'wavyDbl'])) + strike = NoneSet(values=(['noStrike', 'sngStrike', 'dblStrike'])) + kern = Integer(allow_none=True) + cap = NoneSet(values=(['small', 'all'])) + spc = Integer(allow_none=True) + normalizeH = Bool(allow_none=True) + baseline = Integer(allow_none=True) + noProof = Bool(allow_none=True) + dirty = Bool(allow_none=True) + err = Bool(allow_none=True) + smtClean = Bool(allow_none=True) + smtId = Integer(allow_none=True) + bmk = String(allow_none=True) + ln = Typed(expected_type=LineProperties, allow_none=True) + highlight = Typed(expected_type=Color, allow_none=True) + latin = Typed(expected_type=Font, allow_none=True) + ea = Typed(expected_type=Font, allow_none=True) + cs = Typed(expected_type=Font, allow_none=True) + sym = Typed(expected_type=Font, allow_none=True) + hlinkClick = Typed(expected_type=Hyperlink, allow_none=True) + hlinkMouseOver = Typed(expected_type=Hyperlink, allow_none=True) + rtl = NestedBool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + # uses element group EG_FillProperties + noFill = EmptyTag(namespace=DRAWING_NS) + solidFill = ColorChoiceDescriptor() + gradFill = Typed(expected_type=GradientFillProperties, allow_none=True) + blipFill = Typed(expected_type=BlipFillProperties, allow_none=True) + pattFill = Typed(expected_type=PatternFillProperties, allow_none=True) + grpFill = EmptyTag(namespace=DRAWING_NS) + # uses element group EG_EffectProperties + effectLst = Typed(expected_type=EffectList, allow_none=True) + effectDag = Typed(expected_type=EffectContainer, allow_none=True) + # uses element group EG_TextUnderlineLine + uLnTx = EmptyTag() + uLn = Typed(expected_type=LineProperties, allow_none=True) + # uses element group EG_TextUnderlineFill + uFillTx = EmptyTag() + uFill = EmptyTag() + + __elements__ = ('ln', 'noFill', 'solidFill', 'gradFill', 'blipFill', + 'pattFill', 'grpFill', 'effectLst', 'effectDag', 'highlight','uLnTx', + 'uLn', 'uFillTx', 'uFill', 'latin', 'ea', 'cs', 'sym', 'hlinkClick', + 'hlinkMouseOver', 'rtl', ) + + def __init__(self, + kumimoji=None, + lang=None, + altLang=None, + sz=None, + b=None, + i=None, + u=None, + strike=None, + kern=None, + cap=None, + spc=None, + normalizeH=None, + baseline=None, + noProof=None, + dirty=None, + err=None, + smtClean=None, + smtId=None, + bmk=None, + ln=None, + highlight=None, + latin=None, + ea=None, + cs=None, + sym=None, + hlinkClick=None, + hlinkMouseOver=None, + rtl=None, + extLst=None, + noFill=None, + solidFill=None, + gradFill=None, + blipFill=None, + pattFill=None, + grpFill=None, + effectLst=None, + effectDag=None, + uLnTx=None, + uLn=None, + uFillTx=None, + uFill=None, + ): + self.kumimoji = kumimoji + self.lang = lang + self.altLang = altLang + self.sz = sz + self.b = b + self.i = i + self.u = u + self.strike = strike + self.kern = kern + self.cap = cap + self.spc = spc + self.normalizeH = normalizeH + self.baseline = baseline + self.noProof = noProof + self.dirty = dirty + self.err = err + self.smtClean = smtClean + self.smtId = smtId + self.bmk = bmk + self.ln = ln + self.highlight = highlight + self.latin = latin + self.ea = ea + self.cs = cs + self.sym = sym + self.hlinkClick = hlinkClick + self.hlinkMouseOver = hlinkMouseOver + self.rtl = rtl + self.noFill = noFill + self.solidFill = solidFill + self.gradFill = gradFill + self.blipFill = blipFill + self.pattFill = pattFill + self.grpFill = grpFill + self.effectLst = effectLst + self.effectDag = effectDag + self.uLnTx = uLnTx + self.uLn = uLn + self.uFillTx = uFillTx + self.uFill = uFill + + +class TabStop(Serialisable): + + pos = Typed(expected_type=Coordinate, allow_none=True) + algn = Typed(expected_type=Set(values=(['l', 'ctr', 'r', 'dec']))) + + def __init__(self, + pos=None, + algn=None, + ): + self.pos = pos + self.algn = algn + + +class TabStopList(Serialisable): + + tab = Typed(expected_type=TabStop, allow_none=True) + + def __init__(self, + tab=None, + ): + self.tab = tab + + +class Spacing(Serialisable): + + spcPct = NestedInteger(allow_none=True) + spcPts = NestedInteger(allow_none=True) + + __elements__ = ('spcPct', 'spcPts') + + def __init__(self, + spcPct=None, + spcPts=None, + ): + self.spcPct = spcPct + self.spcPts = spcPts + + +class AutonumberBullet(Serialisable): + + type = Set(values=(['alphaLcParenBoth', 'alphaUcParenBoth', + 'alphaLcParenR', 'alphaUcParenR', 'alphaLcPeriod', 'alphaUcPeriod', + 'arabicParenBoth', 'arabicParenR', 'arabicPeriod', 'arabicPlain', + 'romanLcParenBoth', 'romanUcParenBoth', 'romanLcParenR', 'romanUcParenR', + 'romanLcPeriod', 'romanUcPeriod', 'circleNumDbPlain', + 'circleNumWdBlackPlain', 'circleNumWdWhitePlain', 'arabicDbPeriod', + 'arabicDbPlain', 'ea1ChsPeriod', 'ea1ChsPlain', 'ea1ChtPeriod', + 'ea1ChtPlain', 'ea1JpnChsDbPeriod', 'ea1JpnKorPlain', 'ea1JpnKorPeriod', + 'arabic1Minus', 'arabic2Minus', 'hebrew2Minus', 'thaiAlphaPeriod', + 'thaiAlphaParenR', 'thaiAlphaParenBoth', 'thaiNumPeriod', + 'thaiNumParenR', 'thaiNumParenBoth', 'hindiAlphaPeriod', + 'hindiNumPeriod', 'hindiNumParenR', 'hindiAlpha1Period'])) + startAt = Integer() + + def __init__(self, + type=None, + startAt=None, + ): + self.type = type + self.startAt = startAt + + +class ParagraphProperties(Serialisable): + + tagname = "pPr" + namespace = DRAWING_NS + + marL = Integer(allow_none=True) + marR = Integer(allow_none=True) + lvl = Integer(allow_none=True) + indent = Integer(allow_none=True) + algn = NoneSet(values=(['l', 'ctr', 'r', 'just', 'justLow', 'dist', 'thaiDist'])) + defTabSz = Integer(expected_type=Coordinate, allow_none=True) + rtl = Bool(allow_none=True) + eaLnBrk = Bool(allow_none=True) + fontAlgn = NoneSet(values=(['auto', 't', 'ctr', 'base', 'b'])) + latinLnBrk = Bool(allow_none=True) + hangingPunct = Bool(allow_none=True) + + # uses element group EG_TextBulletColor + # uses element group EG_TextBulletSize + # uses element group EG_TextBulletTypeface + # uses element group EG_TextBullet + lnSpc = Typed(expected_type=Spacing, allow_none=True) + spcBef = Typed(expected_type=Spacing, allow_none=True) + spcAft = Typed(expected_type=Spacing, allow_none=True) + tabLst = Typed(expected_type=TabStopList, allow_none=True) + defRPr = Typed(expected_type=CharacterProperties, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + buClrTx = EmptyTag() + buClr = Typed(expected_type=Color, allow_none=True) + buSzTx = EmptyTag() + buSzPct = NestedInteger(allow_none=True) + buSzPts = NestedInteger(allow_none=True) + buFontTx = EmptyTag() + buFont = Typed(expected_type=Font, allow_none=True) + buNone = EmptyTag() + buAutoNum = EmptyTag() + buChar = NestedValue(expected_type=unicode, attribute="char", allow_none=True) + buBlip = NestedValue(expected_type=Blip, attribute="blip", allow_none=True) + + __elements__ = ('lnSpc', 'spcBef', 'spcAft', 'tabLst', 'defRPr', + 'buClrTx', 'buClr', 'buSzTx', 'buSzPct', 'buSzPts', 'buFontTx', 'buFont', + 'buNone', 'buAutoNum', 'buChar', 'buBlip') + + def __init__(self, + marL=None, + marR=None, + lvl=None, + indent=None, + algn=None, + defTabSz=None, + rtl=None, + eaLnBrk=None, + fontAlgn=None, + latinLnBrk=None, + hangingPunct=None, + lnSpc=None, + spcBef=None, + spcAft=None, + tabLst=None, + defRPr=None, + extLst=None, + buClrTx=None, + buClr=None, + buSzTx=None, + buSzPct=None, + buSzPts=None, + buFontTx=None, + buFont=None, + buNone=None, + buAutoNum=None, + buChar=None, + buBlip=None, + ): + self.marL = marL + self.marR = marR + self.lvl = lvl + self.indent = indent + self.algn = algn + self.defTabSz = defTabSz + self.rtl = rtl + self.eaLnBrk = eaLnBrk + self.fontAlgn = fontAlgn + self.latinLnBrk = latinLnBrk + self.hangingPunct = hangingPunct + self.lnSpc = lnSpc + self.spcBef = spcBef + self.spcAft = spcAft + self.tabLst = tabLst + self.defRPr = defRPr + self.buClrTx = buClrTx + self.buClr = buClr + self.buSzTx = buSzTx + self.buSzPct = buSzPct + self.buSzPts = buSzPts + self.buFontTx = buFontTx + self.buFont = buFont + self.buNone = buNone + self.buAutoNum = buAutoNum + self.buChar = buChar + self.buBlip = buBlip + self.defRPr = defRPr + + +class ListStyle(Serialisable): + + tagname = "lstStyle" + namespace = DRAWING_NS + + defPPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl1pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl2pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl3pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl4pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl5pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl6pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl7pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl8pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl9pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ("defPPr", "lvl1pPr", "lvl2pPr", "lvl3pPr", "lvl4pPr", + "lvl5pPr", "lvl6pPr", "lvl7pPr", "lvl8pPr", "lvl9pPr") + + def __init__(self, + defPPr=None, + lvl1pPr=None, + lvl2pPr=None, + lvl3pPr=None, + lvl4pPr=None, + lvl5pPr=None, + lvl6pPr=None, + lvl7pPr=None, + lvl8pPr=None, + lvl9pPr=None, + extLst=None, + ): + self.defPPr = defPPr + self.lvl1pPr = lvl1pPr + self.lvl2pPr = lvl2pPr + self.lvl3pPr = lvl3pPr + self.lvl4pPr = lvl4pPr + self.lvl5pPr = lvl5pPr + self.lvl6pPr = lvl6pPr + self.lvl7pPr = lvl7pPr + self.lvl8pPr = lvl8pPr + self.lvl9pPr = lvl9pPr + + +class RegularTextRun(Serialisable): + + tagname = "r" + namespace = DRAWING_NS + + rPr = Typed(expected_type=CharacterProperties, allow_none=True) + properties = Alias("rPr") + t = NestedText(expected_type=unicode) + value = Alias("t") + + __elements__ = ('rPr', 't') + + def __init__(self, + rPr=None, + t="", + ): + self.rPr = rPr + self.t = t + + +class LineBreak(Serialisable): + + rPr = Typed(expected_type=CharacterProperties, allow_none=True) + + __elements__ = ('rPr',) + + def __init__(self, + rPr=None, + ): + self.rPr = rPr + + +class TextField(Serialisable): + + id = String() + type = String(allow_none=True) + rPr = Typed(expected_type=CharacterProperties, allow_none=True) + pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + t = Typed(expected_type=String, allow_none=True) + + __elements__ = ('rPr', 'pPr') + + def __init__(self, + id=None, + type=None, + rPr=None, + pPr=None, + t=None, + ): + self.id = id + self.type = type + self.rPr = rPr + self.pPr = pPr + self.t = t + + +class Paragraph(Serialisable): + + tagname = "p" + namespace = DRAWING_NS + + # uses element group EG_TextRun + pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + properties = Alias("pPr") + endParaRPr = Typed(expected_type=CharacterProperties, allow_none=True) + r = Sequence(expected_type=RegularTextRun) + text = Alias('r') + br = Typed(expected_type=LineBreak, allow_none=True) + fld = Typed(expected_type=TextField, allow_none=True) + + __elements__ = ('pPr', 'r', 'br', 'fld', 'endParaRPr') + + def __init__(self, + pPr=None, + endParaRPr=None, + r=None, + br=None, + fld=None, + ): + self.pPr = pPr + self.endParaRPr = endParaRPr + if r is None: + r = [RegularTextRun()] + self.r = r + self.br = br + self.fld = fld + + +class GeomGuide(Serialisable): + + name = Typed(expected_type=String()) + fmla = Typed(expected_type=String()) + + def __init__(self, + name=None, + fmla=None, + ): + self.name = name + self.fmla = fmla + + +class GeomGuideList(Serialisable): + + gd = Sequence(expected_type=GeomGuide, allow_none=True) + + def __init__(self, + gd=None, + ): + self.gd = gd + + +class PresetTextShape(Serialisable): + + prst = Typed(expected_type=Set(values=( + ['textNoShape', 'textPlain','textStop', 'textTriangle', 'textTriangleInverted', 'textChevron', + 'textChevronInverted', 'textRingInside', 'textRingOutside', 'textArchUp', + 'textArchDown', 'textCircle', 'textButton', 'textArchUpPour', + 'textArchDownPour', 'textCirclePour', 'textButtonPour', 'textCurveUp', + 'textCurveDown', 'textCanUp', 'textCanDown', 'textWave1', 'textWave2', + 'textDoubleWave1', 'textWave4', 'textInflate', 'textDeflate', + 'textInflateBottom', 'textDeflateBottom', 'textInflateTop', + 'textDeflateTop', 'textDeflateInflate', 'textDeflateInflateDeflate', + 'textFadeRight', 'textFadeLeft', 'textFadeUp', 'textFadeDown', + 'textSlantUp', 'textSlantDown', 'textCascadeUp', 'textCascadeDown' + ] + ))) + avLst = Typed(expected_type=GeomGuideList, allow_none=True) + + def __init__(self, + prst=None, + avLst=None, + ): + self.prst = prst + self.avLst = avLst + + +class TextNormalAutofit(Serialisable): + + fontScale = Integer() + lnSpcReduction = Integer() + + def __init__(self, + fontScale=None, + lnSpcReduction=None, + ): + self.fontScale = fontScale + self.lnSpcReduction = lnSpcReduction + + +class RichTextProperties(Serialisable): + + tagname = "bodyPr" + namespace = DRAWING_NS + + rot = Integer(allow_none=True) + spcFirstLastPara = Bool(allow_none=True) + vertOverflow = NoneSet(values=(['overflow', 'ellipsis', 'clip'])) + horzOverflow = NoneSet(values=(['overflow', 'clip'])) + vert = NoneSet(values=(['horz', 'vert', 'vert270', 'wordArtVert', + 'eaVert', 'mongolianVert', 'wordArtVertRtl'])) + wrap = NoneSet(values=(['none', 'square'])) + lIns = Integer(allow_none=True) + tIns = Integer(allow_none=True) + rIns = Integer(allow_none=True) + bIns = Integer(allow_none=True) + numCol = Integer(allow_none=True) + spcCol = Integer(allow_none=True) + rtlCol = Bool(allow_none=True) + fromWordArt = Bool(allow_none=True) + anchor = NoneSet(values=(['t', 'ctr', 'b', 'just', 'dist'])) + anchorCtr = Bool(allow_none=True) + forceAA = Bool(allow_none=True) + upright = Bool(allow_none=True) + compatLnSpc = Bool(allow_none=True) + prstTxWarp = Typed(expected_type=PresetTextShape, allow_none=True) + scene3d = Typed(expected_type=Scene3D, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + noAutofit = EmptyTag() + normAutofit = EmptyTag() + spAutoFit = EmptyTag() + flatTx = NestedInteger(attribute="z", allow_none=True) + + __elements__ = ('prstTxWarp', 'scene3d', 'noAutofit', 'normAutofit', 'spAutoFit') + + def __init__(self, + rot=None, + spcFirstLastPara=None, + vertOverflow=None, + horzOverflow=None, + vert=None, + wrap=None, + lIns=None, + tIns=None, + rIns=None, + bIns=None, + numCol=None, + spcCol=None, + rtlCol=None, + fromWordArt=None, + anchor=None, + anchorCtr=None, + forceAA=None, + upright=None, + compatLnSpc=None, + prstTxWarp=None, + scene3d=None, + extLst=None, + noAutofit=None, + normAutofit=None, + spAutoFit=None, + flatTx=None, + ): + self.rot = rot + self.spcFirstLastPara = spcFirstLastPara + self.vertOverflow = vertOverflow + self.horzOverflow = horzOverflow + self.vert = vert + self.wrap = wrap + self.lIns = lIns + self.tIns = tIns + self.rIns = rIns + self.bIns = bIns + self.numCol = numCol + self.spcCol = spcCol + self.rtlCol = rtlCol + self.fromWordArt = fromWordArt + self.anchor = anchor + self.anchorCtr = anchorCtr + self.forceAA = forceAA + self.upright = upright + self.compatLnSpc = compatLnSpc + self.prstTxWarp = prstTxWarp + self.scene3d = scene3d + self.noAutofit = noAutofit + self.normAutofit = normAutofit + self.spAutoFit = spAutoFit + self.flatTx = flatTx diff --git a/openpyxl/formatting/__init__.py b/openpyxl/formatting/__init__.py new file mode 100644 index 0000000..d5ed2a4 --- /dev/null +++ b/openpyxl/formatting/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from .rule import Rule diff --git a/openpyxl/formatting/formatting.py b/openpyxl/formatting/formatting.py new file mode 100644 index 0000000..e454bc7 --- /dev/null +++ b/openpyxl/formatting/formatting.py @@ -0,0 +1,115 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from collections import OrderedDict + +from openpyxl.compat import basestring +from openpyxl.descriptors import ( + Bool, + String, + Sequence, + Alias, + Convertible, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + +from .rule import Rule + +from openpyxl.worksheet.cell_range import MultiCellRange + +class ConditionalFormatting(Serialisable): + + tagname = "conditionalFormatting" + + sqref = Convertible(expected_type=MultiCellRange) + cells = Alias("sqref") + pivot = Bool(allow_none=True) + cfRule = Sequence(expected_type=Rule) + rules = Alias("cfRule") + + + def __init__(self, sqref=(), pivot=None, cfRule=(), extLst=None): + self.sqref = sqref + self.pivot = pivot + self.cfRule = cfRule + + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + return self.sqref == other.sqref + + + def __hash__(self): + return hash(str(self.sqref)) + + + def __repr__(self): + return "<{cls} {cells}>".format(cls=self.__class__.__name__, cells=self.sqref) + + + def __contains__(self, coord): + """ + Check whether a certain cell is affected by the formatting + """ + return coord in self.sqref + + +class ConditionalFormattingList(object): + """Conditional formatting rules.""" + + + def __init__(self): + self._cf_rules = OrderedDict() + self.max_priority = 0 + + + def add(self, range_string, cfRule): + """Add a rule such as ColorScaleRule, FormulaRule or CellIsRule + + The priority will be added automatically. + """ + cf = range_string + if isinstance(range_string, basestring): + cf = ConditionalFormatting(range_string) + if not isinstance(cfRule, Rule): + raise ValueError("Only instances of openpyxl.formatting.rule.Rule may be added") + rule = cfRule + self.max_priority += 1 + if not rule.priority: + rule.priority = self.max_priority + + self._cf_rules.setdefault(cf, []).append(rule) + + + def __bool__(self): + return bool(self._cf_rules) + + __nonzero = __bool__ + + + def __len__(self): + return len(self._cf_rules) + + + def __iter__(self): + for cf, rules in self._cf_rules.items(): + cf.rules = rules + yield cf + + + def __getitem__(self, key): + """ + Get the rules for a cell range + """ + if isinstance(key, basestring): + key = ConditionalFormatting(sqref=key) + return self._cf_rules[key] + + + def __setitem__(self, key, rule): + """ + Add a rule for a cell range + """ + self.add(key, rule) diff --git a/openpyxl/formatting/rule.py b/openpyxl/formatting/rule.py new file mode 100644 index 0000000..d64c9e4 --- /dev/null +++ b/openpyxl/formatting/rule.py @@ -0,0 +1,294 @@ +from __future__ import absolute_import +# copyright openpyxl 2010-2015 + +from openpyxl.compat import basestring, unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Sequence, + Bool, + NoneSet, + Set, + Integer, + Float, +) +from openpyxl.descriptors.excel import HexBinary, ExtensionList +from openpyxl.styles.colors import Color, ColorDescriptor +from openpyxl.styles.differential import DifferentialStyle + +from openpyxl.utils.cell import COORD_RE + + +class ValueDescriptor(Float): + """ + Expected type depends upon type attribue of parent :-( + + Most values should be numeric BUT they can also be cell references + """ + + def __set__(self, instance, value): + ref = None + if value is not None and isinstance(value, basestring): + ref = COORD_RE.match(value) + if instance.type == "formula" or ref: + self.expected_type = basestring + else: + self.expected_type = float + super(ValueDescriptor, self).__set__(instance, value) + + +class FormatObject(Serialisable): + + tagname = "cfvo" + + type = Set(values=(['num', 'percent', 'max', 'min', 'formula', 'percentile'])) + val = ValueDescriptor(allow_none=True) + gte = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + type, + val=None, + gte=None, + extLst=None, + ): + self.type = type + self.val = val + self.gte = gte + + +class RuleType(Serialisable): + + cfvo = Sequence(expected_type=FormatObject) + + +class IconSet(RuleType): + + tagname = "iconSet" + + iconSet = NoneSet(values=(['3Arrows', '3ArrowsGray', '3Flags', + '3TrafficLights1', '3TrafficLights2', '3Signs', '3Symbols', '3Symbols2', + '4Arrows', '4ArrowsGray', '4RedToBlack', '4Rating', '4TrafficLights', + '5Arrows', '5ArrowsGray', '5Rating', '5Quarters'])) + showValue = Bool(allow_none=True) + percent = Bool(allow_none=True) + reverse = Bool(allow_none=True) + + __elements__ = ("cfvo",) + + def __init__(self, + iconSet=None, + showValue=None, + percent=None, + reverse=None, + cfvo=None, + ): + self.iconSet = iconSet + self.showValue = showValue + self.percent = percent + self.reverse = reverse + self.cfvo = cfvo + + +class DataBar(RuleType): + + tagname = "dataBar" + + minLength = Integer(allow_none=True) + maxLength = Integer(allow_none=True) + showValue = Bool(allow_none=True) + color = ColorDescriptor() + + __elements__ = ('cfvo', 'color') + + def __init__(self, + minLength=None, + maxLength=None, + showValue=None, + cfvo=None, + color=None, + ): + self.minLength = minLength + self.maxLength = maxLength + self.showValue = showValue + self.cfvo = cfvo + self.color = color + + +class ColorScale(RuleType): + + tagname = "colorScale" + + color = Sequence(expected_type=Color) + + __elements__ = ('cfvo', 'color') + + def __init__(self, + cfvo=None, + color=None, + ): + self.cfvo = cfvo + self.color = color + + +class Rule(Serialisable): + + tagname = "cfRule" + + type = Set(values=(['expression', 'cellIs', 'colorScale', 'dataBar', + 'iconSet', 'top10', 'uniqueValues', 'duplicateValues', 'containsText', + 'notContainsText', 'beginsWith', 'endsWith', 'containsBlanks', + 'notContainsBlanks', 'containsErrors', 'notContainsErrors', 'timePeriod', + 'aboveAverage'])) + dxfId = Integer(allow_none=True) + priority = Integer() + stopIfTrue = Bool(allow_none=True) + aboveAverage = Bool(allow_none=True) + percent = Bool(allow_none=True) + bottom = Bool(allow_none=True) + operator = NoneSet(values=(['lessThan', 'lessThanOrEqual', 'equal', + 'notEqual', 'greaterThanOrEqual', 'greaterThan', 'between', 'notBetween', + 'containsText', 'notContains', 'beginsWith', 'endsWith'])) + text = String(allow_none=True) + timePeriod = NoneSet(values=(['today', 'yesterday', 'tomorrow', 'last7Days', + 'thisMonth', 'lastMonth', 'nextMonth', 'thisWeek', 'lastWeek', + 'nextWeek'])) + rank = Integer(allow_none=True) + stdDev = Integer(allow_none=True) + equalAverage = Bool(allow_none=True) + formula = Sequence(expected_type=unicode) + colorScale = Typed(expected_type=ColorScale, allow_none=True) + dataBar = Typed(expected_type=DataBar, allow_none=True) + iconSet = Typed(expected_type=IconSet, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + dxf = Typed(expected_type=DifferentialStyle, allow_none=True) + + __elements__ = ('colorScale', 'dataBar', 'iconSet', 'formula') + __attrs__ = ('type', 'rank', 'priority', 'equalAverage', 'operator', + 'aboveAverage', 'dxfId', 'stdDev', 'stopIfTrue', 'timePeriod', 'text', + 'percent', 'bottom') + + + def __init__(self, + type, + dxfId=None, + priority=0, + stopIfTrue=None, + aboveAverage=None, + percent=None, + bottom=None, + operator=None, + text=None, + timePeriod=None, + rank=None, + stdDev=None, + equalAverage=None, + formula=(), + colorScale=None, + dataBar=None, + iconSet=None, + extLst=None, + dxf=None, + ): + self.type = type + self.dxfId = dxfId + self.priority = priority + self.stopIfTrue = stopIfTrue + self.aboveAverage = aboveAverage + self.percent = percent + self.bottom = bottom + self.operator = operator + self.text = text + self.timePeriod = timePeriod + self.rank = rank + self.stdDev = stdDev + self.equalAverage = equalAverage + self.formula = formula + self.colorScale = colorScale + self.dataBar = dataBar + self.iconSet = iconSet + self.dxf = dxf + + +def ColorScaleRule(start_type=None, + start_value=None, + start_color=None, + mid_type=None, + mid_value=None, + mid_color=None, + end_type=None, + end_value=None, + end_color=None): + + """Backwards compatibility""" + formats = [] + if start_type is not None: + formats.append(FormatObject(type=start_type, val=start_value)) + if mid_type is not None: + formats.append(FormatObject(type=mid_type, val=mid_value)) + if end_type is not None: + formats.append(FormatObject(type=end_type, val=end_value)) + colors = [] + for v in (start_color, mid_color, end_color): + if v is not None: + if not isinstance(v, Color): + v = Color(v) + colors.append(v) + cs = ColorScale(cfvo=formats, color=colors) + rule = Rule(type="colorScale", colorScale=cs) + return rule + + +def FormulaRule(formula=None, stopIfTrue=None, font=None, border=None, + fill=None): + """ + Conditional formatting with custom differential style + """ + rule = Rule(type="expression", formula=formula, stopIfTrue=stopIfTrue) + rule.dxf = DifferentialStyle(font=font, border=border, fill=fill) + return rule + + +def CellIsRule(operator=None, formula=None, stopIfTrue=None, font=None, border=None, fill=None): + """ + Conditional formatting rule based on cell contents. + """ + # Excel doesn't use >, >=, etc, but allow for ease of python development + expand = {">": "greaterThan", ">=": "greaterThanOrEqual", "<": "lessThan", "<=": "lessThanOrEqual", + "=": "equal", "==": "equal", "!=": "notEqual"} + + operator = expand.get(operator, operator) + + rule = Rule(type='cellIs', operator=operator, formula=formula, stopIfTrue=stopIfTrue) + rule.dxf = DifferentialStyle(font=font, border=border, fill=fill) + + return rule + + +def IconSetRule(icon_style=None, type=None, values=None, showValue=None, percent=None, reverse=None): + """ + Convenience function for creating icon set rules + """ + cfvo = [] + for val in values: + cfvo.append(FormatObject(type, val)) + icon_set = IconSet(iconSet=icon_style, cfvo=cfvo, showValue=showValue, + percent=percent, reverse=reverse) + rule = Rule(type='iconSet', iconSet=icon_set) + + return rule + + +def DataBarRule(start_type=None, start_value=None, end_type=None, + end_value=None, color=None, showValue=None, minLength=None, maxLength=None): + start = FormatObject(start_type, start_value) + end = FormatObject(end_type, end_value) + data_bar = DataBar(cfvo=[start, end], color=color, showValue=showValue, + minLength=minLength, maxLength=maxLength) + rule = Rule(type='dataBar', dataBar=data_bar) + + return rule diff --git a/openpyxl/formatting/tests/__init__.py b/openpyxl/formatting/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/formatting/tests/conftest.py b/openpyxl/formatting/tests/conftest.py new file mode 100644 index 0000000..fd1f673 --- /dev/null +++ b/openpyxl/formatting/tests/conftest.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) + + +# objects under test + + +@pytest.fixture +def FormatRule(): + """Formatting rule class""" + from openpyxl.formatting.rules import FormatRule + return FormatRule diff --git a/openpyxl/formatting/tests/data/conditional-formatting.xlsx b/openpyxl/formatting/tests/data/conditional-formatting.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1f63669009914773e3a9203b365a0fc56dd0dc99 GIT binary patch literal 50266 zcmeEucUV))x9CPdlq$W5#D+)*1wmR=1O%i=?-7s^dX)fySm;tznjl4`H$l1xL;gA>?^RkT8_h(LmJTtpl{gkC?G52lx%aiL>g|{ze)fa78SpYGVpQ{qper-Su0B?L(i=vv-aZXy2KZgT0NW<87(b~XtRrq!rV%3KyKV|p?KG?Vn%l% zVKR`fPbd6Eo*mNef+Z@F5E6HXnn=N@$UeFNeO)G~sMurGM(-vN$MBWHKe7`)?{4^wl7P0aVFJ zP~+%8m4tcOxp<3<9o_%GD*g}FzrPK=_@$OMM&eZH?w#*VsX=puq}p#lXB%RTamQR8B@0`)Ew4es(^rtfk># zH4AO1xHxl%`P%UI@P25(-MxD*RdFIk-!8w=>VSIi*=ZiP#VU+hjD5NmWwt2Sn> zj5JL0Qy>mKa(dTMt#RX9;pzL^{fn98cijy92CtrLaynv_FVU$xh!?l@>9V{@L-vf~ zKV^Lx`rJ!7X~p7{AcaygFcwVf~*n+Yk7bZ6bKjb z-xbKu-OJg=-QD@frTt5pC_ujkYTEzotN2B)2IWoqw)H3L5D-s#zSC1>HdVS?1DZ3MUy>oPk=Jo>DYZ_8miupoG$h!NE@6G9h>3>pichRP z-+J28Kd`Ddq{icx{E{+b!{z8K=f}rLK$Fyn7RWZJ{Hw^SiZI@VZ>H zp{de+(i+8fO5WHtj*aSq&#MdB$>iNwS9wH0EJ@;)3Gt?RU9ieiu2kMceH3 z*fr=8-1fd&T>a0NXdb+%9fETID((1uC2qf8p9i)te@r3J>+s}13CNZ@mgOrr>C2#i zFo81<3dr9@BU*35Z9;-R2vUYRwe{=Ei@9P37fw?#lSkHBV+AN{t zl9Ed>R=J#d**;&qzs$M~(FSq$XgX`uTV!*c#JGxfe{7j<!9CPI*jnED?4U`W>7ycf*6Hg_PSdo^mFK0jMsHt* zZeucSxzs01`&R~N(<%8M=JIJ@?VO_-K3GNG6d9rURKb()j}rG^YD!_j z`ROg*jA|l3PI>Q$OvtTPCX$Gq5F5h1k+-3Y8@{wS@71Tz7q5Lrxare2ea=wQIRnrk zC=D_}}<`%v3f|#tn$xrW9E-*vWIC010dx4c|$WNIbHk$JING4-J-soECdOt>~ z>6;6jHzmX3lnkg%hU86WA?seLXEuV8`iQUiCy384b@{5sZ?D|h9F83@)P}vM9G!!* zj=q%t>B`FS-RTO`>7)==NiSx<1^cO#!oE5CpNPQmtSlRczBE<7KDs5(wP=f)%Kg7h zJnbLKeGW*r)4hpI0Dz-kPO$WWyREL5yN9>9kE5@vjhi*xMa;>=&Vl@mJOiB4P*qn2 zC@3g^JKzsMo~5Z(S5}7U>glMe-&FxC0Dvan+QY-;*jWH@b@TDkQ@eH6#MJC8%@`Oc zbAx`H2cWaI_4c@_t9$o|f+O6c=f7V&Bfp!ULD7G%|99;F@J9E*&f69MD2@Vo_Xpm# zt{{v60BRRo4=*18IQ|1Hm-X}U0AU(l5N7iN2?Sw@BiP}0`1TQO^E+(v8;*gVGKh0T zE2FKW^#c$_g0P6q-^v~S2LDb2hynJaX6I(-1-CtW#0!wK_HY-w-zt8;{TJ1L!~EZY zt}eddn18$Yz&pLSv%V(yynHlvN3T12f52~StyT0v_##;T#lz>;QTZhhUiEc0ya~cW z0C0@O-b>Z+cl=`t0geX7Aj|=lKY;tF8~jlo;AW$(1Hxe2WAB{ZHIMo^!uewBtqk%K zI1j>8j&|xta}3f$2|Td7tpdU#Ak63Jd*_e-DK)$w>K~Q=)=z+=vNi~V{ZM*2S!?Km zFb@bvc)9BxeG8sMf>Pm;CQJR?Yw^*BiJY9l8>VS zD5pnlsjm5W8T`?f%FG_Fs{RK)(!=F9Sp;dL%J%ZrJE9HjpQ_K=OGOQY!G5SV?A#2G z_y)q%To0^oAMsWUgp~jz3Twa)a0hI_yDi`b90F$nJwOe(1wK6hFR;cQfCDaIi5*yL z2bRDo7yxIm?4R*;f5Z8`&0iP}zqMXEY7KtnpK*Tw!tx(D?o^yq5>!{gUwNv#REkux zRM&yCR8XoLR7zC0!P4tg@_$mG`!_nz0uI13Na^qWcmlrQmyhT$0&YF$@xAsI2=gLU!!#Ys~+s<&k=((yZ@a7fH%m2e~nJ`?|f=U)^q(?b9R`j)?$#p9SP|C zSNx^^LA^vh4Bp46zyH;qdV=~p_%jCF1ZTkIH;Po?4)uYxALAqT)y6t@I z5TNe>0A+U%e=oR$qtDqZ5)v|JZ-TDO?yS0-t(eGJYZsTZzg2mk^|JG}^Ll9aKnytY zgGcQEU{mim|3<;l`&XHd762&z0&P~`Uu9;#08s1=x@D=q$}WR8kv$UtYOmS)dOiHT zKhW`Z1Aw>v)hoZfQ_$Z6ZPOH)yb0PXnwJ1@_<~G6ct<85W`X^G2Y}BmWGvvLf`Hot zfR@&g)d$mFR1`b_H3(6Fze*IR|AbX3E`tXAsQefJgu?-llU9TT^@v4p2lasU@oLt;I{FekS3kpd|Uz3r&E~k83MOE#N zx`w`ip^-5-y|xeR>>V88PToGg5B(7S0Z&6h!@?sXqh7`*Bqk-lN=bc}m7SBDmtRm= zUQt<9T~qt9t{KzP+SdN1qjO+zXn17w+t@gEcJ9ag!s62M3VvgAYkOyxu(y9SuA_1O z>-xuWflQz{Mny$QMRPPRier99181h9J|}sc<>oyaYfskmS00~WyY=Gjho+MkuIk~~ zZM^zvIW9_J`SC|X`)y?Zy@5UXKN{Je1N&=S;{ZJ+D3r{U%m5T1W09k5i6F%Y?K2e zEHy7&LC`aYFxA!0$B(DK2Va^0g@j_et)@nACz^7M>}nbhxo&eue~@o1u!djM}o_PitOt5X7wiZ4F^CUk6)4YmVkr2O4q3(QG-n{NnEXNW)l2W zB3gBtAiDVAiVFMMSH~uMj~j}b|IN>IWZ~%ys>#5U*VDnhHF6G5II$F?KIzB1nk0zw zguo(Lw{t$J8&6c@iC443PM{*{lrkYP_v@23XARdQs+cIpc6pBk9AOm|mkC+JhWPldEl#aMc22M_jlYtlcl${EKxGwDEXsi3P0K48N z3gSxXIb%e*plfZGtU518nyMHCOq2FX&N|Y2NPjPDip!VNYhv$oThE4v>)&QWzpWKNYuwpEX3I6rzl5jt6m zyG*$`x5M@cC(sth09U$1h?|=}j) zTQQXr3bLV3FG&*A*X!M9Ep5bEQ(_yRovSZP0+{-y|6zo{KX5`J4-3IS!vo$Y&s~=m zr$U&KG@((`tXrRqKlO`<@l9(FB5e!JmUkp7%Oqn*I!9#J*6!a`qrd#3t?&&kuQlPnx3{3c>Us6=Yo=*T05>6vy6Q@C7vGgD|? zbXFO<#X{CY`fgiM3zKxq-$gzG;ecXrS4AnuCDSX!z)? z=zeFY@*(qb&2DyY!8a>YQ!>y#-@>Bi*8E6OW+wm#YwkUbynxG#SSZIqTI9QihDOLh z%T6h-rWJ2t)l@XCtQbeU2%}dJ#6qc@TytD3rE~j{&^+n`nPfyfET_BWGvDAp$T32E zO%wO?vlY+b>qa?3IA)3wHx>DQ2~CUiE?DMiOV0W>=457{<$(RUwXg0m zMM*r*o#&NP78_YzR7{YCMlsJYyU$39biDRGwUaQ3uAF2cHaxD#m`=Q80de;X3{ESZ zZ;)7E{5o9JWE@$td2yBuxbDjl++QMUK#&&4#BAfud+A<=$nxw5ZbLIRPn`cl<&=el zpco5n2)nAnFwSRAKOU>w<=``*Z=nI- ziQ)f^cCDhCB-)P$PRrv#Yx{%=jUlt1k-)^V!Qe9lvpL-cM;kcDNGDBoB!6A49rKMi zkvju&;Yre63}s|M6UL0^ijv}kHNEmRD;BIHL|=kyhq7P4TvwG_VJbWD1UdLJIyLIy zvj8OvztvwjbLyw9HY0wsR-)Ll?V@5aR>?0;vmdVHoYd0A3??a`=&3ULD5$C*V(}v( zkNu$<&Y46TcyJGCH&`!@j}kvU{>{Qk>3EGCizVT7^R?aT0Hzq2+NisWwD6s?4dpWL z7HwmW z2Yf3|3?&N6X&8LRp4>nO2u?y4+Xpbs+SE9|aN?O+7$e?7C+aidb;6>50}{$P(p5Ak z;p2e_=4-j{m~Us|BFbYphirWNVNC<0AJHpo=Q6 z0c9Y0u6LgUH%ekzfDrM}XJlZ6*MWF|Ap^wEz(_&XLOIpo>!wG5{2ifi5agQbU7B_9n>yo$%esXg5&4IGeAx=c39t zqoA{PgiTUty);<(F`Nu2C?8t4p?d3~Bnr?YC()4sCc4A-(50MZG(j1mMg|fSP%T~f z#X~xTGw7+knMjE6eM~eN$V8xs$3T%5Q2;$;CXpSwlTXl@VPZwz9wyoamcp&r2d52& zD#-vnQ649~P_lC(*!fdS)czT{Zc&%BES6JRia&0O&U&6o#lC$4)92s6iFll0|4N}S zmNP`6>O++wkF;mPRXLxa;>yr#%X30Q-bKQ<(rmSj%zMLn3TyIXd=@q+ZeF(Ld(OIxByQK*IG<=)*_B5iv0uNyyfpUZuw34i~7 zAC(8W&Garjd`gL+X&fhuzBZeNz8220m_m#BA^ZIk)8qYIQ)SVDTUmKAo%rpB`uXU> z!mf!8^~+w(*ZjZ;g-Q1ic=<1 z_Ae8aL7mT4Ap?DQP(YgW$bicae|{JQVfWy*`|I;WC0xG$LxzF{+4AoPob#3~YrOQd z(En(}?*C^KcJZI=UmW2zngzO~-wY!tr;vfoZ_wGa3|cbKuQm%Iow*`T3XkP%%fy*X zPp~#|2Gds$DOkD(XGr6N6KA?KyT!5XPZuf%e9zcr$e&Jka(?0Dx|)+FLhEX@vGyaV z!?CYb;X|N5bRe*WxRT_q7sm8BUm;VtWnma@Qrx@e{4XS;ei)t@sObIqBnNkRzXd$^N;m25mOi1b>J|HFioZe$#o3Qog+RPrG(e4He_0BwB+ z3ux@4*O4fj)M?{z#stuJykDvbA{;{@*bWxM$#;Yadn`SO=dac&fc4 zP&Utacg&dD{MTv?MnC^K8v3uB#ho2R&lJI`)LNgRqcV>v=*&v-xD(F>+*RUFTKufU zHdw516I&W_kMLT}eUTECgtWaCng^TUFEuGa6CWtdz zerLIU0xDl8ECY&Pi^EJ)jhPUc@wpWa&e$N9sZpa!JSXN^pryu%N&<`UPlPmt+jJVk z&`BZXIb}BS{k*K{J2h&xeR@>v;&bFxbpNW79MPGOI1@*MRKYv6#BtrkcGJyfhT0H1 za562|l+N(62B}UokNEL%4c0sFfL`>w<1^p)@E!F)A&sB@`Iv`d4lz96DHk#cLg*-> z^{mcVoYtUkX=vjmoaI|W3HIcu$w(ENYI>{^4-Xa?vpFzZ9*H>0XjZkv1F>g?r z@1VixW!9NkGFWi{hA5Ernw=Q&9|gN$pzxAu^&@B@vSF{u0A30{1_sPH$^+Hp4TFO$ zGSES@V*xsd0r74ycakc#N&+7ghgq6?LP4OY{&;#IhT2=^Tma)!EuM!c6cHfx=MWQ> zz$ke`)$Kzzw^X{in6&r1&pR@j*GgTz*>l=bm_u8Vag&x!iY4{c&r;?x7P#+6K3Diq zcPuaKq@Wsmc%p)oXCk|k8<$yGx)UNr@Vyypj(T&+o#EggZ?!$s5>lfRQXM$#;xGnX z_`Z_J7Q3OmkQUWkr5Fu1rx~n}o?fediK#)3Mh?~FouMLakLY(fvK^N)RSskcSCw&0 zkA1-SiU2eHG+KW(8=h*H8NMw%sXc94o2(vT85DJ{VBCDde9C;{v)$me~>w5ZJS419T~F@Ok3Bo2OXu?U-H`f^xgn`34+11gU0s_@Z;oa{!a3o-82svc?cDy48p;0tO00@Z~C2RUC`y)=VGnr^n)h4*XSDY- zeB#H-VNp@7`KdRqm9ApbYx^M|bWlM*TKW&mcO1`EKXLw)-ssy>HqpcuFJJG`5`8Mz zu4U8k>UEu$Z2koW2SWEeb97RDAr*PtJ$ljSaLrfJe!P3>75@pfp1Il&+R;eQmPvEc zHDgYB{&$V6=N}roPE<{7JMhC4^Za&EP0&7f$9ghQ`s&_quT1~NOUgnfGw~cYlS}4l zNxk}PkUpl|kt9AqRK|YuX>iu=|1pfz%vRrt5#!W6kgpl){uKT~MD$7JJ9UNr^J{cl zNX66zZjJ60yFItl60zb!Ei;mvEi*;4bD>>$&9Pbgbe^Fk$oQo!p>45uL#_@*1UKm~ zUP>2|8SnLY*9YHIIF5SSeI*qkf}wky0loIQ!>Cy=!Bfh)i*q$QZGzWy;jDz-0e_`32-v2^vO9=9v*sf>6HjZ(UHO|cKP1@gH z&i!v;g#%0@EsmJUPE?ysmv0S7Tz@9r0KqhJj?^nE)#O<$%W0?NO~R&@Ab9?oa-P^U z{P&gp;UO7LPBAy_qM#QdHe;@h&$v_VS+VsMf+b1T`F5{d+~gro+S_R&aw1!8K1g8t z+GV2C{dM2x=+HgQlfCtF$C*wIn_T6q#Ke&J;1!?btd&SshCSEUg=)`dTo;b3F5&=)AOkU)E8rhyvEbQiJIy+H*() zC{EHfqS0_L)tJT4aq0C(!Q9S`#|fFvX9q|eri3~jW_SQ&>C7Hd@Li;W{;Zbj!dz>k zG~RVL%6Bx#!pza^QfA(=(MwAwfbS?=w8Zy5RnR(+T$b|-M;c?-yog9MTxdu6K-Dw4 zgSYM+akEt~ZK*3a?wpUwzOi28l&pK_OKEfC%V#91Y4*n9E6_vOeE2q`uQg{Yc80F9 zVd~Q41POk3o!Gkw?Myz1*)!fVLDeJiBuCKI+)*e%)lMf;^gSce{rI%zK{+2t>DEFjXrSXU@-2taina0EThep9N z%=)dfULw;>Ni!S8y%HtKvjsC{+{0?TdF)Z?+-;$}ZMtuxGKc2xwNK>h76|C1sLH^L zAmxis#7Sq6l7mS8!fzAs{hW~0#DyaE#P6I*SDp5EBcywoojFG?jcO0Lth)Fz7#r2s zm-oA@+En$G2t2)06BtoEG7IbbaJ_+dH=^R}Ad^{+i!@?Bp=jBL$yZmT3OzNi)aCgtCuM4tp7awvusb8|dl|p`6v=}d2+dY2 zd4l;Y(+PykE!@L~VnleyfSu0i!-VBJ?YR8x9l2kha@2ydoU8Q+MLGlT-I6g-*K&@B z`#CK#qh6ZsaM76cK95Eyb&`XLMX+_`SBY;(acQ58Vf_i?7Cn!mu;x*p4_r<~)MoW( z@fkf?1#%LJvX^^2i*%4uSnW!O>)JvxetE$T{KYoo31&~>-YXMk{C>V)SL9Cixe&$Q zn89CuGk4^p$5n?OjS&K$!z?i8g?01n_s+DG* zXd96vuGD5TuRo#5uJ`_uVM7$=06yAcTq2ig9G%C*yOBS?YJ0scagQxiw0E~+$JyxV zzMReXm75w?{GLAPYsdG>KC&+S@Iuy1jXoUz)dm}LLoH6B4z>lrMHoyB^+sttnRp0h zPad}uF{?Gu&2{MEK4`lm-zZjz?;Yrn0m`_&@V_^@5$FWl+{q--KsK4mzk`t&=)!ha zbV!#T?F^w8*Puj3=mJ8mohe*NkPy;%e88voBq7keuNY~X>)a^(X?UG6aaL29iD{)y z2@U3IH4e8F4U113jZ_wKj;`NN>cK6`!3T@e-Fz<&N%hvw)NfL}Q25Cn`PEy5<65<8 zM3Y@YStXx)3OiT{PifE5=`WM3Exq51s8wy?O=-M z1C*G*H`k`bI;5b9-^8L~%g|V` zIi$o)ogHS(#2#pQ4T$Pk;i*#JXWKrzSyhSNFFr1Kp8%8Z8ooQl9qTM-z{g@Dx9wGx z+~0B^U+%LO5h`awG!mv$F>E!!Xd8HZGgM8S67m)F)$6)?r>}@d+}>s#i|a!^HW}QA89({sW>m5dbq)4v0J(rg>ESzT}Lh7 zB?HCL?@IU+=GBLCQ@cD)=E+VZ{VfHKCjU^In-?vJEFo23`dvEs}y3$pBgpg_okK&LIO( zIOtJ4$-vci5(If2Zvd@~t-+r*!DWW~9EvTfRp63CwxLXuRKxC-2QkK~Bht^NIlnQ` zj?C$e`UdIi)Mz?K`kEKHGO72wx{73_E}9Jea{hHyVUt3MrgMY-l5L8Oq|_!&s{JdZ zFSjO>YJ(hCu^3CNvFV%;hUsZD_UDkVAcjFZSBCXjfl+0a4qaF-nkNV2AHxToW0d^r zH3ez9FXYj4;o=v3iq*9=&sS;EX0PbopsX2KGEh;r*=jHQ-HIg*ZkQq0!25|hxi2Ry zq{tS(4cmQ;%lq7TqLE-XbGmDq24VE;*J86A)vk=O72kkBZEdR7waM&M|M?1kyChZq z40Z1GZ%N?(7I9*Vp-bH;Wiy}f1nB*N_sp8eC67 z7Ez-bm-~q?C*K4CX_kw7`aa9jzFnig{+x7s3)RqS<)o$=8n2z4Kb9_k512vC6i%_? zsv{4i9sN?p!|I{gRx+*Vlb=TetYY#^EM7h6tG5`q!{A>Q;j15Jro-BY3gQ>P*7o z)=JM+h4oMc)>a4Ed|Ph?G0!zs*}8bV=PSq()|&Z0E9Slq^nbgupSX}>>nwU0 z6FR?YhD_$E2}c?_aV)MbsGrwj{I#%f+TZm}Qb(@L2x*#eu-ULj!T?X;`54BS4(oQS7tlK)1hwvWLFnM*0B{eNeKY2{YOh~MlZMm+@Z6HeTp;k_}-DRiJ zocT{<2?}(xfpI)UC4&2$CR*B!XpNiuoFVv$)in3D%sBBTHeJT(UC zoVnKBS^DDLYW$8#UM2arzmm24uK(I@Yjt@>vi2*xl38q}zeMeK!xLr|p)!VEO=d-T zT0?~I=cmC_7oJduV#UlyuR5Kbf-iVJ;HVjpMW%?ll)jtlT-2%E&jfedVvzoaJ*VRe zh`oe9ia&~gKi+A;97knu3{oF=&{Ss}jTD`YsDy7pSO`vwru`;xKN%|l-2C)2qImYt zq=!64Z*wibJ9B!%$78;7j9imz54CjjJs~zfYQs_B?7f>tk!ZI!1&{39Tt^^>uG%citVH-&F z^-gb-nViSnUVWvQKvXf`T{F5;CANLbc=8`GIt)pDyCk26RV zsv?@bL1bD|F-mi{m)@e$yS_iV_Wg7z?GNaVIOxc7Kno5{cN+{CI-|+6;1C8)wi6cCUUg$zH ziK>?#zaCkC6FVIRJ1JVMpzMZ}pGC7mL%@8X0grDh{^f47lJv+{w}EMOYH9+&Ndsols9joM2n0vYvg_j@``7|y27<9RdVme`1)@Sz57^L zxFV_RvBSEs7`J@);@&+k)Nr2nVgEV|!%ha`%t?)4mUdu_Gz@N;e8-`87{Kipt=sXQ z+t3s;!1HC9p?!$tKy(DY2k9)J+8+J!O4*{oG{?j~9y}W0{`gnDp=$|2rbX$*h=NYe zQuU%{T_(nCHw0-uqsX^EwY?^le&s;Tw1j9dJ2i5kkH4GxwaC}!B5oEQ=}>Ukc2%US z5D|D-q9c~UD&6GpO60fzyZ%p!SSU@({9c<-qK3wM0cJH8tiD*B!m{Yc6M5%@MS$gIRR_1OWc#Z%J410He9;nuAV1A(%AY<32r{YQb5bO<<;|R$lsF#R2xHNGvfSOh& zUS?Y3neJi&BbZmH2NS3Oor5J*D3}B_-?QF@ZeH;{yo$N!7*O9RaE%7@e7Ppx89kxt zEcRR~|Gt0eYyDU}U;paM$%}!BwMHRrCH2gyD@`J{ZPvEQ^|#tJ8ZOU}iiBRY9NO#O zdzY^fZ^&*8X26fu~j;q8)WMmfw`xze+hf58@M@_pzF!DSjo^Mj_&G{$)#5n`XS z#(sNtE-*Ki65AeIf1wnqghNM>xV}Au9Cv=cqPw)i$-~yjdkrM7Vdt5*OC4QPUU!ba zTw>yu{kWzHmkZN;elW^=rjf42xTm&Md={w{kn-MAUobIXOLFK#@>dYT%YqYb-B84x=6JKlHnRFOlj^xdX3r>T4@^(Tfhp&IN0&hTKfv|hZG}cmvEW7@Z6B$N_CO_r ze$zNZ`ATYNY{?se$d?O&55agpT&M57ZG&9W2SrRVk6l$&WzEa)8Y}>vG^qcf?9*Uq9i1b(Laf-eROH0t=am;H zEZPsU;zVo|3k{-3UU=n8SDAJ#h`xaqXKgT5~<|q?rSv@(~ zyED2IxKP`vbj+r}t6!V%EdEyVM}hSqV}8AZpy5OFp%oOCV-Sr7qk9x5=tD;r)po^q zdahPMh{s^WSEy#uJXG)^i6l-2PQQUxd#2}uVLA(#@WYUpUF8pUp}Tk#VUpsoB6>{? zJnR6@>m>;88GL#gR4FvLa%x**T1#_qf)c3Edsn}ZM68f!a6AM7L&6JJS3kc(#587# z19va;*3%ZblF~?`X5qKxhsJd_babB@E=`Rr1YWPQ^kQ{&&;4B1?dui3ifxP!_v$&@ zjjk#iJL9xfaIf>jE8=K8w+J8Kdm74H@6YFmDDNa-Bhv4v7JCp(h2l$PE;lnxJO7Za zcvdcw{^QmA^Ud>wXcd=5iboxm7BxS=6>`T~Kf_=Pj4^?Ug;-nd?4@4jXK@vwT&hW@8aFl5#8sWbxRJm*_;b+ z(GBGqDxmc<$WjnlFs_mD8bJ+XUn8XkVZfY|T9)AaQ_@@6T ziQO8#7Y)U_B9ci#Tct!Aa4Y*qA#@mtCI#D|aV{t@G1$V072l`pLF0-T$iPrGaCMKNd;Pf?;Dku?| z7dLrJ&a_Q^@hFjdMSm9?(SE2JQqWliExFvEXxhUz$FI_Qt9VXOIo0B20_#&(OFl-K zW;CP6^&PEv8_5(*kN2xN4hnsQQ$?r=l_-_fKwG>u1z}ZJtV?NGB;2zbMp7V{dXsGSWYfiX5%coddO&%XLyuuDktmI*x4W*5qf!`$Smx9YC@jIL*nSw zIfbw!ht`kREJM1iTX5LAvG?#u%zWC*QWcefJWb9Ny=X(E7GD56e`t}F;j(UvZa$XQ zcUc29LW6OS+K){JV{bDb7%l0Nljk=uD5=s?)9EvErkfAfc}zW=HtopgT1nVE8xPdG z6yN9(KFotQU2Rc1O%RwF#l*$(2@yWw7H9HWT`#orOovo4(Gud6KR9qkn>+Kq;ZYK7{G#E3F;clmeKVY3Sh>Yo*!l4v#OCY4)3=TonktqF3jU5N{V zt3qD&sY{NO0#DMVVEYs-sPogb&K=x-r;3P&f4>l{dKLB`7rv-|v1~YE@AGEjYF3N6 za09|Y7WVoVcw)%Tf((FK7W8}k@%m>a3Op9{sFZXETK*mdYHVXD#r8OK8k}e2Bkwn6 z3|&P{-+Hva23ZGfs_<{ywMCgb(Dk=Bf*!{0f|(L9obBN4jjpH8A`CX~kA~KpyPjDO z+kUN6@slH$w5V;mb|F>x-AgM|3$yPSu9qjqZzVGqy{q*475YPUy>pY^&G41AH+^+p zdAqffH?~m7;AgF2f}QqCqKQGsJiO|3vzB^+>Y#e5xIE^^-iUEb*VQP!lBC&XSzE!n zDv1??EPk18vluhObEjLcn`P_A%n7$v5@DJ+XlNrXK^&98(C1zye@O2XEHNTdD$SrB z6tLkOcZv|Z-V%_UlHV&bpvmjE--5qIm!|sYOYikmb!Z`y{cR@xmqVL>_R`F<%~Bs0 zdF_1KIUP!E3Cw*@1g{J~J6HB3FHgd1WkSP*a*{AH_VDePy-xF(+LIL3d*+-GpE1m{ z_Se!~tj?Q^4;G{>D9FFE5syfl7i^QpOP{0bsN#BWKi@5QmBeBxd@u^xokusLQDh(j z)dJ}X0nJ`MWI=6o*$+jaBLhcjn5;qCZa>^}$7PYuf#wk<2Br&X-#}nF;90#HaU$k# z{lpQ%i8qP0;9mhe3JOm411A@~$wW~1DP6ZfO@nbOcxVfPr`f z?2RNr2!43w<_dyC8%CGZd8OOi)!R8A<~Yg|oO3O?z!A5?vHQ^P+NbxTYrf9Y=Y`{? zKO~N*Jsy`59(z19CD`?|Se5&J;_ZY7d1f(pdQuB$V_BjH%M%Kk*UXHxU`9UH6%OEk zRMB^I z{gPIcHj+ODvA-Y<1pyV`MUZrDb>CNVbWR~MiWA`SR=KAy#jC5p?Z=Im0%eHjQ%iTj zbr^b?Bu$9BuyHWG2bsv6ATVyDa0XyL6^dmWMt!0I4<_i~vtH~4trMRfpeUwFNwk@J zG=FO>6fDV%up$EW1{I70-%Pdd=&&R7TWffbp5xQz6IQJSE5Xct1529j zieEi%mB9Jyu@@nR7>j!u9;2hSiGj2Uk_km>+_xHOlf)7la$&FRz?~nx=tT{K%l5K0 zS_RFF`oaC$y!}eT&&nGaXFu9)_Bh#Io-H$6(xi;Q*x*fsy2k8H-5lhYW8}*6jAWy= zjh`A`FX(xvYt^Zno*isJ>eE$s#ebTOW|`}L*18|1G?@6E%Cza%)hTpEz3h=?2qA8` zTm_f4J>%Q9W>p$}Xnxh4(@T36Hi@HZxp{G;<0-!etH%oxF~bfnWSro+m)R<#t)AbI`m!i%^C zz4i1w7K${Ab9j>K?F8@TrvKQgO5AhZc=e0z3;w%W68?;-abDW|wzbvkoY+$Bj7w4; z+)1!o>B#d%t9jZnBE8(Q8-v>VX?x$Z^a?Y#O7aNChOSDV*PgyUeFYl)lw|pCTQMl; zC-G}b)1d=v%|UB$DJi)3%c0rz9SRlU;enq7y+E1a^V5G50qNhxhy4)!XdN`5)H=kc zBxWt+gEG|g7Pzjz4oL zu^UVT6=~S7CNSz9Bs%^Gh6REGMVkKXR|Fn7%Ok11CSE*zh2#cvyW<+Yvo+y73lnX1 zt2J`k4S7uLQ&Vt17D86bKHtZJmCdCZnNeob>0a+_c%N#&3U#qjB(Lu0$LA)tK9;gC zB6`?zFyDr6AF3uQ+%}@2iE7rxw6kj`oH5f4yHuIeD>#&>uA%qZD;*5F zx;Gl>yCfC#TM<03I{iz6Aw5fTWj{HiO2XsYJ7dL}qmo!k%wFV}c{le*dFVdLO*lu* z&W0|5h6J?5b2d+hf6y$1_jf^_)+@YJ9tCEM-sL8@zr*wgbJvQmDf;y$T#<=POPWU8h4Qy8BXX zxWE_@z1}Xds;*fVM5F1WGn{eg>||m}qX-$`P$vULt7W8!eL>LM?apK#l#+p;GOsg3 z2uk4_(j>#oRrHl#jo{g&b5EC07jAfq{RG|R6E3h)ism0G{MTDE>6lIfP;f;bsjn zyKx+&I)nEIMLBvg6^PWpIJ%~M%)72@5p^4c09Wa%%`A<5+n-E`o$#*vSqE`TW09X1 zu1`mr)aN47w0TOD7<`2dlg<|v#Kh8E2~#p~h>U7gxX`9r*Ho-wIKr)OC~XY=0*Wy8 zldl_75S%>a>wXLt?<0grD8Sc`6&~ z{Gkirl1`D8bO4!y4U;-ciTj{dJ<=q`ZQf7-yIp*Tnzki6?iivLhjqZ!;tz_y@YTve zMN%q69W52lGF^f0EoJV4ArOyZB5ZFPG~Ph+9>v@=Z#_RW7RhiJXKjrtf=xKr8C6-s zb1jybInaX~Ypa>M;Vtn^+0)Cy-Xf@R4%WuKEa8hc>d(dqHWy!<2pNFqA#~m!%B(H) zstLEKF+}aerwO(W6lm+`7s9zGUcVU7_RJqZ#+xhde%QA=IB)*bhzw8>6n~kI0KiyXuX{7dK&x(1EvJgeY$~5U-Z&^yCpl(-%g6L%%LF`;~EIa~%RI=k?+aPN5T)_-BAT2n~Cn;hWtQf_- zG!lDzW#{<6UcIZW9NV#^+Gv|N>mbD1V$h^1f@!N3pwO$b2!=50Nw1hC@o0xy&^tuE zm6jJayq7tTS`&^Vz@|ARxR?l9RTgds-wsD)0_RE_P?iX9)HYMdI(kQphg4JGy_E`W z%6a&D$K#VZEYWJp_#2+~K>5a-W8f*N4OfTe{jX^DdYReoOP{Oi{}*p>9u8&u|BdTb zDbgg_Wm+terLqfimk?sI?YTCdP6jWDi+pWDhge3p3O2 z>~nv<&-Z&g&mYfv{QhwquDRwi<6Pd$YdNh_>>iNbFCKUIbEnUzRI?->;S8R1G6`;F z2G}Bd=`_LEu)(aEPq&=S8j@vo1!rExDBGodvMA8YRJODTaFf+0xH6=BZ{|z6t+pbO z(Wstv+4xHZZMJ$LPnsrPUf8(s`kjJ+EZ+@r-_a9(uQkwzH1MM~m_8d~$N`y`3q*(T{su6`;<*qM#QHKH_O(q)LuKS3mT0{M@|(LMg&Eki%cQEh4=Zh~ zCG4bR#lNSsKpD)*Exz-3hUmvg~%_~~kx`=pTRK$~pa}4RP_rC;Aimp__VYX;@3^yEQ z76%>2Emh*ZW3(}Jd|GxV-T%)hJ!rEpz|8wi>A1FA+lL`ktpaTt( z(0Y@dou`;2t>#65vHi93M9DNMQk3%bQmL?gL0-k0tpoBdY^ybdgzKl$cIA(EUU)sp zd*`*4Gg$KcmoH@N@LD1h3eA#d4t3S(1$h0;ma3Y4y~c3QxGN;UIrpi-R)0y2anLcM zEG)M_Bc$rqj)w{7%FVZvgp9|n+=|pPQS_WCeVdHOJzmMz+8oMvr@|sxA`Jr%mN)k9 z3_j3Qq+VBgG_cXucU((jTbIb+v1^rmFPd-Wy={>1Pi#Lto8J`IQ5nxqt17r@;TA^Q z7SW4wv@3%hdBmmF9L(9t*JYO?DZ15c_Wpap0P)cW)Q`61echgOBT~=M42zRpK$SNfyX>4rP_^3b zM&Q2(y3`4XuAA#XSd#_1Um-P6@6QEnPaz7DhchBLUQ01HuzSm-)_6A~R)`sxBM(6j z>`%>h#9R&_MtHH5f;CXWLw9AK4jBw*a>`4J2uilgKnIq&$Y6+IQl&yawQ7RtLGy_-0nX(*8N*QfP$nRwV$-W*@JXQkF~f4MK+I)e zx0ex3PqvZ~b3Z_@Lmm6-zuo>uD-XG|0|->8SrJ|U6#rri?FUeeyE!Wq33ynkJNVCjIG!dZ>hKysh%Ife#d@kYP@vX1=#f^uv?TkP9{7#D51H1A0Du-#-t zf2(kHHg{OVxYh0$d6>t&EBWIcCht?oO@VquGRT}y!&@l4AjSEiQjtZG5zJ_5W8E%! z{fNVSyGfP^C+&o^VezV72K{D0U=;R1tAo+&AFqg6S;Cf9{PmiT&I@Vy#2RjjaaKEW zs>y14NC^$M6uY`Ny%a7nQ*$r$rh2Z4Y?a=`SK(W$*w`AhhXmO3#ja0}k_w5JL4_v} zhWLpD^QNy5{#8gb;{$Pj=BlimKzvZC`>B4mADAijxxr zsApw$cXIpkO>l#KF*LCf852Ohc2j-;NrYvY9Y{6jv|#?|ti>#_Ww?q&tn zLeX8=nvjymak-8*QxCahvqi7b)=XlGq^*b^SKuR$UPdyL^B4)B08>ORMqy{V#eP`t zYptzPYY$R)S3B=)i0#~HvZ)TRUoNENCE7*lR>_X?E%&9>L|eA zmSwO#?VynJ1v}q2{CYQHZwpWPGuRNnz6RJuq%SH`jP-aHGatGs)|by<02-dcy0$(u zOIs5fZXs2>#j^O(c$3=x4LSCf3E4iP$h(n+-M|q!CG0*@6CEC@sT=&`U4>V`w6vyY zU=XDy#ky}(@D$v zn>!a4IMrEK9d)RqHP$b{Po3+vR6?W+s}4UWJ=-8m*Ig-~*EOR!?rR3py>*~S{>Vaf zlG)%U92dH;Z^JA2Ds82}ek`P(Sgo`<>s9^}hx+N6kok&F)^J}e_@0kd+<^l#$7 zBr2N>{ojc++ZAT2J98NHRY&7==RlzS3@f7|(h$%e7a4yll?dnz2H1~r>zh@TS7cG= zko{h8b5i}&n~`26{+p*JI%ST(on+ZPv-lw@79%Na6JyDFqOd8}kl=jCs>ISX&%|p& zvB0)$yj9Mz=jV#A%eu!a2QeugyNrqx1*RE`rg>K^3#>R5(s=T>z9$bWjMmrU)8JM3 zW^YcYvkA_LLDFWqq7mFZ?-@p4T?nK;#0QBQ5-qL{aBHjyj%t)saXmH_17141uUyfN z(|4VayW4xpA7%IyVIt)Y`WQV$ZX+_r!Q*DDMu$bErULJ@w25Wng9oKz{xWt}84r>O zD$I4rSGRjZ!IRWwRNkLqcgL}_DCh_lbmqhXje-PY?!u?_ z89FQxfaGHIStmLf&zAr;^%^^M@t+=Ck~xck*q+D6!s#%`oAs0qhs+h$U?s!9;oc{0 zpi6Vy7`aCS!=BAj!Zsr)q+O0?Eae92Z|xe?PAZEXzNWr<0!{Zn$-iVaw4AkPggdw< z;w7Pc^{=-E54U+oD@L|F1D@ukx$QWQmii3%+!YmQdUZtZhLzOexPq&Ax}{JzF=u~9 zp86F9N~3Ov-(y{miF~W0=r5M&-fX)}jSnR8dCJkQt! zCrUpcQ=q44SfZ(7$(fSaXJ8`d={V9L! z&Wr++^_YFn6V3o*L37agFSkEoje=FHj9>_WL408i7DN`vNG9h1NL%rXtqa7-1I=_~ zY^WM!O!GAARR%5aU3i4Ie-Od*dWo{50@)m$HmoyP8lZLlTHm|6#0x!nSXnE^gNf=#kM;eSz{YAMkM3n@iI`Dz;ex96`vX8nU3+D<1 z^Xt5g2AJaV;d)-tpp^|saFfgG8^b5tM2Y|-0wc@jDtP9-)Ag8k24 zj_1ZN!}Vwle~qfdwMzSrnxwUDcMXjEiCjX>uAFDifi;F&!Y;JKV~`qcGRfA;S~7!1 z+n#v_@-<2%3STd1sJtX??=1a+{m%hJ{O5ImqyHbw4c;~kf)1l#uqWkN0t=&(q&$XM z1&x(GEzvNbsVtF&FF^-3Jhwq2vMuyw$5vVTRd2 zXd}>W_Al?c;i}SOHP-*`pNd~rQS77&il+_TuA?MSBC{?P_hyU3wr+y{Qn&eMZHcc$ zDLNMwP#5f*;6fPlX@!WzL#_1fNR{lPIPK`_jPW&J`5nFYH~A zyZc~Q%UFbcn~NuBwiN)5SSkOD?IUR$#5}tvyPHSX_EG_m=(ae@UxLl^NYMycUl7<% z^J0RI{2%&CyOuLu#pPoj56KEE?^1w?)kkXTwy);tJ3EI%`Wte!x2S?N^Q&nBwCN2 zkZ${FM(&@<)`Q}^>Z2CU9*s+>b zea2z1$l~!pk$PA4j}cl4wp3~-?qA{<(Ya`%mO!+oY9ZvixI2xk7&;x*WA>5YYLMGE zv3PLc=e4*$qqNLgj|xA^Cbau{I?cZ_2Wn zC&BJ6UnSB>8cC!Yox2q`frxC7tBE_+BoXP{PgLVqijh7neA-f%?SN_35l*m+liXdU zfJ6n3`w`=EfKqm-^`f1gjM^9GxfXv@D+7Nk#DGF}saIGN+9*0NA`|H@f1$fj&+8`@Xtc#(gz| zE);H!l8uuz{xmgzd(qUUk|bnSQY5hbqbJ7t=GKEIaT0Y5#>S1s-Yl-IpzpyU}Ys zd_{c;HR>c*^bo<7U)yn^eRr>I)9uh;Sg7FNMdo$+*0N+10V__1hTsZu7VE2M9A$`$bYp zlHK)zZeB3AXdq7}S-U@0LY33H`Y*o+xwSQiN^$WK9qzaNzSOCeIQeB+Kl^e45*3-* zvY-pSD5tmFJ&a1T9O923nEE+w7YQq&W71MbC##s}Ft!0Zt#Vb=dW-(7SlXnOzcN{W(asv0Hq&-y^C2 z|7;q7yk---GsnRMPycytOL1+FyoZ%%J;pL>_Lz-fq%tSmh6SAq>yS`kCT2Y}3S7`#Y*UhI{%iGy_vcBS7ALI;;_Ud!a8<^9vpJ%=!6I|A% zeQ!MsN`do6Cgq4;wOBxA_2r2VWJ{lG%`xac=38K_kr0JdZ-F!)xscU|IOK8Ks(h)` zb2p<(WRA}jncO7Gw0T6TP=tb$5^tuPCV8$!Ppdw|xYE|ACL7_L(8|E5XG)e7wEkX4 zH1n9AwX?duv?+8| z{$dlW7;)7#=Qck~(UMkP7Uh@XN~X&T@CpZb0%-u%y>P&_GpePmY$XW8kcI8Qic$)I^-7G{s;6j* za5|S&xPg4C>Z26G+IQ#F>2=L_f({MGGf~S&1pIX%ZwRe%NbE^iS=5a^^Yk z42>qZThjJaO7x6-OIp8Qu}XUBVq%tWCV059)@R!p-5#9KciA6dx%Dr|Lzy9ly}ter zuq*O!;EeqrcnpAs#Z| zA4m0LGd` zo2-^vK10sISke!RdQCAi;tsOy?rSq-CDC^5pQ$-N>bSu4W{f}H-Tcnlo;oqSpY|cR zICXzk&7k?PQ$sy6`TS2<=|Ah1gARY8>4>ER`S$q_giB~YqxdUPvo3+h8-I+Aq5I@a ze;l2BP+c2t>}jGh#BVtIi0~rGs&ip0T)`6-)kHqmOmWk8(6x3lbtp?)OX2A!)VJ#y z#_Z%HCd`IM7Sz{6Ei*vTe(W~!db{FxWwHA?97jIz6>93L^>6u?GYpU?)%QC2^a$#A zZFG&eanVh84X)Zg^J}{41NhQjhwb;cbaw1nf7T*hK!<(L;}@F>NDstFOe}y!^Y3CW zQ0;cRr7T%=R2fqX|GEIwtiM6g;+UY+0FXd5a>p~lQf@Hb_oc3k#5!~zyHdkkyLZ%e zJOmWNI^dZBq}8PVAOnP2L5QiqR=}8L4Nq1Lw<1p5TlJ>3wTkgUpFDFWx{B=_?V=s+ z6rL?jwJYU^o0l&7E%LJt(M5M#anyU(dQgbptUa20sK5E~@F>ZlEb9)sl$UY?&t+?* z)HuE7KJ+X{ttTz^#=^9vrNmEZz2j|^X}t!%z57inS=#I5n=_2y7@FFyEgVi=(of|! z4_k*?P2OgNFb=?7+tq{DjZM=tJTBi%y_Dl>2EP6qcsSxf=!qrxQ6j8xG4v1G%u3FC z$o^V=1My>doWV-zia#WgMRn68CU$&TX)C&@%z6iZg6G0;4k>7w?>VCDro7`>C!m8% zSD*h0jZqD9zE+_)9Tu|K%V|v6a#%JDde1;%Q<)E@Er+WB)?vHwxD`*0 z6|ku6^U@6qTYSS&%o@XTHHbl%DU(k&L61-W+Sg4fs4Ul(u!6t)m&s5ibh!;Fdtf+8 zH2%nq&aW>~h76qrk+SFCZ9@Xu0|DQ0%OvN})cIjA zegpe&?CQ6yh~F{wyJ$}xpn!z~`0M?LpAA$01=7+@b=~Ip7p&!$in&jRM>9^+(%UA^ z(GF(jE@oZ&%FqhHzOnTTv`W!%NKI(xB-R89Y}dNR$;mlU*8~-m<+O*^W+y~0M=xD3 z6{8~DaOJk4h|8T9g7ORp9tvhyNZ&4WUg*wqJuI!1Eo`x5;jnI+FWr=E+aEJyrc`Js z18b!MIVXG4G(E^)da%_rTfH~zaY8;ay*MM^EJNWykS@^29Zxh(zApKL1d{lBq$usv z5Sf-Ul^?5Vd_VHNLJa91f^&4y^qZZ#50rk5W*xuGkORg)N=$_5;Kmqgd0Ex4hWJ{k z3t+@X!DZTBmng*&)$Gh9X4;Ml@9A!$e<#iSLG)+HG&WhvEYJEy^Iaj-aTgy!DYFt$ zEnB8tNzhe!f^fh~Q5QRz!en48SeAd#JErGS{2`xj^J~MqJrS>*?!3)$%Ho12I7{C) z3o`_Rjx~vTKdw=kFRgBvQJ?WPKF*VAiRk(KFD!lCJE}o^7$Z$VZ+aalp7JRgwO>r# zj&@Pqa*ErkeX*^&HS{D6^ba1GV7Th{yu!Pfu#XUXFYsnfVst@MS)<)&Fh;V?n}Z&VvnkzKP6Y4EoRXZ)yd% zrL9zA^NDvvM7qq$Vw?@1D#zp(j@kG4h+h$x_$ogvK?Zo9X3(c8q@vMD*Z`3>2Zl{=;s~KYU>ds{l+J-ht~9Ks6FG%|AthFT|=z5D*Q$% z^u>ciW&abx`rm&cG>|n3ufn#%Mye+%t#!0ca@s+{+eIC_B%AV&mA%T|$Ivw`k63b$ zYc`}qsZP#8LFwdOBEQpxevBn`M;>ED@r3hw+Nsp!Ko7nxoawRn=nnGjr)W<}s`RXq zq6}evF#o5ea6+<&|AmjX&VDiwv9-hleP35f^W+VA9vxU@mT;_xa>b>s?;F$JysBR+ zKB`mdV=}T;RVBK==big_uHSUlxG-bQxW%B=P`;0Jp5=<2dJ|C*Iz}R#xOe3`>jL`q zv6x$h=?`I|z6a6MeWZLeJL`HDKiD#~Mb=-l5+HkNBsq&Sqv|CTtnf0aejv|qWqUYq z*lDh9aI*@ZA`THQd*)_3U$3_l)YQ6P!x!;#MtH#4&Z{%+Li7t7J;y|QPD0<& zqHt_0>fuZ0dxol{IicYbZb5+}cRroe)H*z4`|hkl(-l1>8LCGc?~CpVp%j&(-S0lH zuFoWWzEz)Sqw&$fSX4JqgI-wBg3VRMoq7QUl6RsMfr3a5o)`s)i6wOj0O?n+;6d_4 zczG;=NY1+fwN+bRo?fYNCc-Th$sQi20o^x74~9#0^0cd)zrf1Sxh}R1OtCJL zty1Nbn}Rl`JI!C~S(HDJkr|W_pDi>r?zrfkI!-iCVp{jRKxGt#r@Mi1%i7|TUD@~G zuwsUeKv)$F=fpQiXz43fLYH=D=64o(q*>N8zw;-Q+j^H3H_xgB-L-PTZ^}ZvvpXaY zxywzHv?ohjD4u{idqgz;XebZHldc+3HTi53wwyy#eWtB}&NrgzlTkbjqjc8sLkeOc zel4}N3yN3Xv{CKrwa;^-EQvN7ou~cOIK2061EUqW_-C5?6k@c+b*NnuEE)F%Zh82j z1r6Sk5t>_E78j|;_WC*xXYTu3t?ioQUwoe?8)@A(y)_Qribhv zi44#jC=R^yGj zaM~1W%fI6lh>w#Yo)wT);mi_Yk1mqEk=NtFCn{}xo|M-0v4=mW16{N-XOh2H zZy)kx`Y=;>oD`*7&=egJr#P8+XJM2q7kwdIky$ld=*5?Z4C^TJ?E;Gu!&o=r^5oni zvwW*en}K{Y9?<&r57AhUQXdj&$}kao`Q9wYJnlj~OygmmHO!vd8h399=p`2|-eowP zc)QT;KXVv3vrUWlbQgk=gROu22idQ)g;FUtrG&1d>w3e=UPd}}{->0gUh8Ym>@(eL zhn2c??E8;~@#h41dIxxe0@ouo(L+Gd)?P1#xBW;ZJG=2Zn)r)NF80RVqouq;k-#$q zlKrDeR@6hKJHEj!8xwK+3s42 zPo5{~gr;NBLgghT1&NgGx9F>jBe`MawDuXpw>B9MqWDCKetPkfV=Az%4-WW^Rq$Cc?2=A%D>ml4VY3IRdJSh(Q0Q5{G6T>yQe#e(z{dAmdn=#UIzF z{ytxYi02)~sf zlt+;?fmWd2@T3P)QBg|c_EyycM0E=6DD=EVOgNkxqUHpB*hUVX`G_MQ9TLvqhX?9B z{TMb}_B1EDb86{k`}ZKl!y%|kWm&h1X4S16aRnwB1;Z`WbIuj=GCAS=nRX_ZTI3)5 z7EpWC$iNk|BiTTFse@4w+MUT6m1)<#ta@nT(jyax%IF!-*KIoipxAvT*)!(1m5p3s zIML70eAV_YOvN4AI9q~6(qN1PO$DHy#;DFIGSAbh9VSF+4s|rvzQtk|NA2VM@jz|u zvQq60Fxxm&w-Xk&tOM6t{4^guTKi5^v08JSxKW<%$5&Ein;0}Hayv4}?bPucSs5O` zj9VGz886KYLX&faVBue7R)UX}&l{!)#{iS@s0@dUbEaW) zR>jMmCS24c!v)7IpL~od?QUKnM-HUv_R*1%w3UK$Xg_~eOy%g;!t;a(An%)MhA3tI9MMjboBRl`L^M$m3mP< zZ9!R`FyRZ$H}kAV3J2YZy6Q;gDNT)NkU6g6AV9t2k9nN6>je~ml{_qZ1ZbB>P3}H& z+nI^XXdpxlvu;;tAr_cAF1L3>-&I_kOT{)GY}sHM@9P2R%roxjh21pvf6c}qfi`3e z>libuK@c5Hos28iw8T;YK+XeM1K^b60X#1p{2|x8fnKV&gqshBWQn(~^Y=_2-Qw zkrH@oC1j86XPl+8w;=ZWPjb0~b1t|=nlE_3ik9r!$aVOWXkW6WMr%U8&G+j8HdWj@ zmX5sUDxk%mKx_3v3N($xU8X2`9uKnj55;nK}@(QXI{;hSSGizPwX%x4$wucX~S{ zzd4rO2IBIhZ8c4|39Hl>VpMTW1tpC%EEN`2wIq^0&g0)1U5EauWb-rNy{Y)ivHH@S zE%)wrGq-raYOsor(pF8{a-|($J*U->)7qL*(E@g;=d5FxV-O!*@;qu}vTe%?prhMA zm+11HRBQ5~Qwdc@I9kfbw9MREWR&u`qdELFc^<00qa*Itc$eBLw(XwLZE7G+(`RZG zS1XBgr9x%;EygO_kLsq7f{@4GWbY}wSmX&0?Nhx5k#Rf;RWTG2-K-9mmy8>s0hQb)}e}!s=wueeUbJOQDL#L_Yp~AGE zq(hq^w5rrfq`oLw-L%&-vZ9loG>9*-WE>wa+1p5_UZ>;93LN95@$xZVW&W=MLN2)v zZk}6}ZI}!H*nvAbbv8WQRMtt_4ec<2j(WA~W|$|ecBE6L=o$LnP2Z@pr(G3acbk1g zX~pxFF?P8N0}%M=XKYurZcC);z;JQ)k&XR1qKe7)tV$x9ZZA+nitw_@vL+l{3T`qF zE=3gx6c3~V9Rh*lrx(3-kDSHPA1VeJ0fbdV(>P>$H5PT8CB8VpP5ZP@)0A$6>LrZK zRMQ5-y0d-WqC@w2D8 z@x6%SE2|q=$pvZf2Kep1){#7S67j z_dNR<{_yOkv_4mK6BRFi=2EkcKqT2kLgo>fMfIl(+L^_UUt3u35A>Zw4Y)CQI;S|A z@lO#Izzjd9)rp;+8m8RC2`xEY4BNwx|!XB=Q5E_MCKA!Juhce~as8WyElf287;I zj(h?9q3jYXan*5=YWSh7LoV7mO|PSSL@yOTQjJu+;nzf6#l0_}o(}WLkRIG-rYgA| zGGI8wTNfcLNfco^jJXduG8}x&x(IkYu2d-|kAo3do5j{(#fP>t2}$5^ft~}u*q&42 zv{*Eb^#q8<*|p&GBgAWffqp&`OB;*=5H6q+0W3K?i|ZE~2Vi3T=W`KMAvA#EuUQ`l z-t?BD@ zx+6X!RCz*^#G~vz>UM*^*&Rts>^By!X(}77qpCg$J;%5~(@Ww8UZzjqE^URLrSTEU z!Zh7xQP{ipY8&$1gR?yXpY`0VW~!v{#?dw(4qjmK(KT#19n`Jag?n2EwrumcW*|n5 zT&v~F3;vmQ$H~)qvQ|fXEj|14)u_@sSs5%JBl(A>S$14kCh7=3;Ih;%+12C zlcMCLuxDo`px9Ep!EQ?z`wn2V{%=bgp9#_Nr3%dzoQ8#r+D`s^tc6b19cv~ zc4);j=xudvI;Hhf03WgMv_+czt#XkUF_~S5vUpGJV4AxrrtN;NYM*9ocCHuzvI%F! z%fvu;gZ{+mkkQZ>G9R@j9E|RQGq7-$;afB-;8b3`i?-WXuy|<~X3%RB1*l_^xs>J8 zUy?s1_wdms8OQNkqhD7v<+kFVpiX)hR|zuq18NaPyWn1c=W6)So}FfiBo)PUOtr73G=UWx?ud)~O%Don-gXbIto09`6w!ip?sUP!>+JgjAwLbvtQ&zvv5JK74T!_IIECc1V5xdy#IDQvXtYdUd?LHtuhh95bBg zI%N~8#IPP9qKctb9twOE+Y-t{GcJ2}t2b4TzP+sIJr(OWeY8NbG%YJ9xX4`IW6pw^DTXRJh2)Q!U z%>|5PquJ36K<4T#+BI2M+WkoOJ zy#J8)nE{1TqDq3zmD~yWg15@`o#UEEK7jLwY@Q1Gx><^=?#iI0{!Wt$V=0b7Zmh$> z!S5L=P%BFovPx5lq|>;q4HEoAv^%F1q8db&5~0>f7H>j6^p_LEm=q2V5-M^F+lA^QZSIG{Q2*qubZh7Xis>|yzmNq3DGB*+#7Z7; zq$zf%4PsN~wod^+`vYtk>*+lO0o^@|Of!X}4D>*Q@FMoZb~By}N;jiDAc$-8Rnud# zq-c8e6eH}R5t!hD?}FInPc&Babnqcfoji9dRkGzkRkZZx-_uzWo^WEP+tXBzJ)zql zeN)_xqJ4{<_~S0agj}Sa+T0*>$7HD#HCo2ruNBPD`y;m;kiKvqa_oHG?6?WzMdHKd zd`=Wz4fix>*NnfA=Vak?R4^c7jyy<~IkwyEsA(Q1d*iItiEQyjGm{TiGkepl!$JbF zKUv%iwY~<41#S6W?Njsc{g{iCNuh7(6{<+jiFls*OAEDDMFEAV#v;$?0>b8aaq2^l z_(DhI`?0Q*<7Dx*b^t(_Fi&Qo5Q4<(CCOHew=aD+F^_Dy>&5tO;6i*Pb_ zOy1F@5l%CQ3!%T+E@>+YhgV|vls&P00h2t9@1b2!Qq;Cos}q0xXN}FwIDEF}b{e7k z^?ThAH_tbpxA^_oV59K|p2;bHf8G;=vC|8VU+!!O+38|?^bg|?)?&lIL@A4x`%z6N zEFvk!zN(dwHx?X8%34MtGn8wd6V5xD>(o}zMW6%~cJ8D??SL|rF{sG9l+a?E)D z+{|b7YG7j#Q?G|Zm1!Ky>TZZ|tP7TpA;-*>pj$`Cc{7CGVvGo1Lq*-~WL7i^+eCowHmSy zbiW)Hev3|Rx~F-Wulc-Wy{BKXT=H^Sx@V`+lDh=qC3`zHiOSCEg;#5T3ZCkT*FZK+ ziiLLI4|p@w3mI1joQY7NGhKvihHGR02wicBdvD0)|91J%zSfT`5R`D`I z_F~L)I%H?jnRb)N+VZiFvdH64*f=_dYG>jjFzS1}o?^$ohQb#ol$dWBBDBCZF*WF_ zGCoq@rksX~{^`$cM8!8dspj}2}X&b>Q2{TX{q%!bufxR_d9_nHzD zc}?5H>-0$-73{HHJ#0IkN^(f2^<0|U$=Oig5{Vc6QKFJ&8>S!5CKs9HVQZch@g(~+i&JB{2+7cvhn3M)My~Dtq0B= zzbCV3K+rmKkS*ka<6kq-m+%yi>Gazb71WqY@vBJZr}4DTwE(i ztu-~zEod9NqL&=xwf>4RcY%D@@Zg?4nB&Q{yxx?j_O(SX{LG0()>>NgO^c;MOGJ=?#g?ULK8MsUfKLv=UI1|n zKbH2}oa;v|YZ{SBW*vhc2DKJ2u9$k11Hzy`IFOi~g4jKPXB-{C?#P2ZT9r7+5R(MSqC9|Q|9ZH&|^**g<3k_YV)KZEJ7IDg5dPn z%?g`Z)+g7BRZ_e>Eup+=g%b-YPA6#CaE63Z)M#o%)!um@s^^%uf+^cx4aMq7gG9ev z0A2)r8Y4pOvTnp44Z!n4Dq4@1Y>zdimE=l6+x*5+#* z%X2M%6h5FVaiHR}to*AdNHe|&(W#A7@Wf#_0oD=@1x+!1$)B<84_Gp&&`}Z()^(}p z`Dkbj{jBvs!`TI=1JD9kMSB)R$h9QX-XK+8r7t)|925Bkp$Cu(`x( zoxT!fHSRYY;QkjF9!hkyqkCSzks!mG8o|p{y(w}VXImSmyz?OZ(RHRhbpJH zigDvx+NlcR+8Tp*`%lsG$;gP|+APw0$PWbsOGHIP1Ow~!ZeI9NWbhP;dw%ptgLoy0 zK%WZ_IR6*S#v8KP!dCv!z(xLm3`!XaRgjN+a1q+i5;C5ijL=Bl4qL8zRVw~rK=blj zsBj8O`C)fT)oMsXD2mei!~QzOyqtY*Db|Qmn&7;53HpvvW2S>o#&Zku@uIP}r64|F zqa=EX;*-af?H&Cn4GH{tu&9RdDXw}?)bOo=;^LALXJaYL`gDsy`x{6Nh%H!Q85nzd z7+WGJci zMCIaOh5B2{4besF`RVLe-2hoqkZqn?(ibr)d5n(YduaJ^^Wl1!drNPI>`>?0B;Unv z4yYq{irBBl!4Dsy)`rPgNQK`l;8ZINpvWl3s+!y^F4XgRh#Zxkw8v+I>Y6^xJU|4Y zFWjnf;1Ac_`{gCYO?U%3KL#~urSfAihi+sYhdplC@`lkuBC z@tJjw7BgAZwuSxaI9(PIDvocKG8w>_Qt&)We=Rf!4HDq+Q3z{XIi&0|ZLGjL&J8ES zTnor5m97O>d_35+J0Q~lX4yM?4?MRrY9E|!(TNN4d5=D03~L(AjK}5DEVskMdS1jm zf7e!Ohjnw;xtj82#BRs5SfymYf%pB29shkxsQ>dq|*`iJ?f!%)9M zGdDX06-kIc;Rxf@o8&G9|B?t579Xvb-V@n>wUTb!ac_I5wmL6&^yJk6_6=OqG4z+w zrBIUs$_w_EtaHbMYWa&!yN6c>%xRpUiw56zzvL48Ttl8O_pjI=?8SSCIMTcv1;LOa z#IxMz+5RWLvxh@zp>4GF)*2LVK24>Fwl+!DXl_vM12XQ3Gjy+%97^toSBqP!+5#1W zgy2aY{Q$Sw&;CYQ3Z``foza(r&lkMA?!ZpP;wo7!Lr${H6hc|}Bv&XG+S>uD2RZRI zA~U7o%stWvcr@{Rc1?Hy=^!*mop?G`#*Mk|$ZPVI!A<4DAJnA_v{p|ZRp$1*I$ZoS zz4`xZ@2jKYT9&Ym01=#X;tdG`8PTD>|*Qtbek(wGftFy^G_lBo&maZt1JLF=+`%Kp4<*3MUil<0({ z=-{N>x((Z5dbQv&d5)wte!cG;1$>WVzQ&5V>KiSFQsx5 zZfB4Ywyb#*;=jum6=j{{0E+|1eT;Ts?YW-rbPFhV(z< z_Dwr3pTT4jbur8`LB@J7`|!2dc6~Ui|3tBKEu$^0?C=p8HpySfs0(y>B)Ycjz7_iF z(Pg03aeLWNW>crzQ;llVUSouK?J6AdHqjklSKMY!|8w46grDif7bC3q&i83|w1sNS zQnHp`{5^!+gvN1#%1X>3C+hSnc|=@Bl{U$|*bmJXd(zc;DpDniI44f7rs2JQ_5?fP zOo$O3jGUbU@hMXVbPVdML25BOs*0G4PHGCg!sbV9l}4YMG4F7F`CzHqZAs}{nxGF6 zCwjXnuq}3S=0)>{*G)aQy|ER(Z&%|`J-t}Vg{W4*n8KBA`JRHEJf{nDJESC)3F^*A(77tw2nLRd3UX4cl+t;+c*LPEo^Y4E z9jvfm_^o)Jwh~Yw5iB9CwGXWAj|kT;D<4?9h2K<7UBtb*PR4}4HX*~PZCHy@;EHnj z<{71?2L#uYOO<~}egddAG&aWK>b#&wDuXG>l#j!1r{`VzAUwa{$ak*cBvF`bH#}pbqxwLdr6_d46^S zTliU2-U;}!p9Fj|z4lwrnrpn%AE$ZvcCU7)s6F%5oyp<;S{~$J)t? z&$$O<4D^c!vaZXy?!x$W1$J)qcVrlzp~rF!RSiv$?~}%W2Uh6tA#AgzV%?-^(+SPb z4{mEpSbJEXviHG_Fnf=BeM2oMaXgj5(@c|o#+AI> zj4$WpJtuhApZGe0HB?CK+b;g4E^qiYRJaD*nX{+GGbUwOGMKBcH`889lX#SZUi8cB z1IdgM&326bcfkGWwF6XSWjly_c&1bH1nH2%XRYudQV6r?n(0SlW(JhDw|zlK<~xPhEA6-+-gP0qQia3oWN^A&uFKfh;EqP#@Tn`g7%*4y4-pAphCARr z`Pe=C>LSlsR|@;$jFq=W#)PTZwAsrAJ?(bfKI%9{GsgrfdRGh0o=s{jEBJtaB6fo0?)p8wb~oG^ti`gkj+jR@NMbzDAdh=`P4S52?>9dq-Sh3GB4<LObZk*k7ps=)wyUj{P=bBZ-x~J z;nWmlV`d9zN0&`0JkO}{6hl)_g1Lei;@fM=J(e3Hc{LwUH zXJ$Q1q=wpeNk!ucMV<^OuTYH;S0_!rGxwwE;X+XkG*$Mo2ZP&I2SDpr2UhZiGO z(KJ-t3~q3IT5T`+q85$Roj++`?qpsx;sB8UwJwmM*rdjCP6)t|fKJS*PU-C1tLrrk zVfQ_zPcK$XFmdC<3?w|3E_stSlf++GG>hS8vugP7rOjJvTlQAa`_}edu61pj77ab1 z&&^I?Cr3#=-oLtumAkB**}|sa4&6Bq5zV@0(OG@rSBR6)geAmyaAymO2u>tf25JaI zR&r4R=w{lZ2*uX#r1^rG?bkiY>8Eg36awWO4wUJ)L(lbrp@D@Zgw0td-@?DNY7Mvf zUEDKxU)=ZicUsb62Jm^E?`*7IX{Jzl!qcr+tqg)iZXO0PM;(;fyl<{KMGTWqL;|ix zl|=maki-#xF?*lfd^XB_>vhuI+va~Z1Hb&rQ#tg?TAsR5A$CMKuANsR+LoU%{7m+o zN}dGe^(Xtdz1YatD~Ajg!unD58;dw2LcTxtKO@DKw*cjFFMj%RMv|<&*)Ho2N%@%O zU4h;CAj@&7^oG`aLAWQZwPBvmNqqiG{s{M3zH7aMOc%RL+^Xa%yKK1lEr|$u*ZQL0 zI}$?=Yx?>Ps*oeKiH-?5*({87$*(Aj+3a2BduN2=hmO@gm^FN7UM&(B2GqC+7NqNU z!Tel?y*MGJSp&kcI$xlA+ET()K58$Mok)tw$4oo;_Rt~{g}(?syJtaspbhokM8A8J z*7HV{-g{--3)RI?`Uo229=tvgb~sASXuA9v36x84=(HJh>|7^)&e;V=huY~EhfTL& z$s5?@dXUN`hxK4VDOB04SpT`Lg@O3JNh2D>wMea(%gbQi0{6ByWA($l{qy2*BgI_Stw<_uF*ecd&N?7#Oe6y^1TwlP%@f)<%F-UVfPM{5g_6^-ea zVp6$8XOwLz&V$MFlkZnrmk^!lZLE2IYRx8JtFY;FNB;%-34+p|E%`JhNFg4x;OtzI zJj3-R?PrY|6}e$e?!qsHJ3lq+%NTgJowJ)A(VwI8ytfQ87C~SJcUBrHp>LD?>{C9o zH%$!dmRtp+sBG1^QFlm~NNk{`%#?kd*<|3_&T=2#>bsd?)`lM&;_1lmUc1*x*wAl} zHp3D!P+(@5ekC`e!f7?3wGa2gX%0==aV#Xu>4n&5Z`SZxf{G|{8oQ}Xov1>fB`NQ{ zjz-8}6e8k!q7!5OTsubvBPj?$(c{x=!o_GvF z8;Uhemgd_}zICSs^P%!S6pm@KQLrG<>B24i>ds|{3H zz%MLMY`Ln6mqvokU#*Pk+6qpk_m*BP@P)56_p)gpSgO_|iQkfGKYc^~@?e;QKOwvv zwiLN?7PN^erfD>0fX)Hr1>V^L?nni#KFgL?O;`w${nBmKHiAmmq+a39&Q+PO`6*UK zDI^MJs72w+y7zqVtRc|@a!X#iMs$qOi-e{ANX0bRbWrhy0-m+N*FEA`v?=!EX3CS~ z`*fI1f5a@BRiZr%w5?Xmde4^T4+>kI&({~( zfGbsu#;!)a+IO&{1Fd{S$!R&VCaq0F@Ue@t>{2S4#bKaP3wZC3&_ zK7vizosU04>{d@`wnWy7?@w=l6O`GEFQr-{i7!=?r{+7-=EIoQAHb>y_8p9lWDbgO`6FoLEWHCYVqI zH5Ll?!l#ZXa$Gl?9-hZz<9Ysa5>0iJxd1<+?SqGR$cs!HN1^l+YJ{XdOI^hhe=brE z4*VY{!EcgJ1KPEq9z7v|8$wKpJyy>H&ieCHqFF>VI`U}0B%8A zrSY94^JgTk)I(TNV5w6F?C1#vr?Q3w_c2eAwy-w0O}wOU#BQujFjf2^_nzIG%B->7 zZYR0sqq9Ff_VaQvaZ&f}y(SlHj)BZP&PD0$k3qphWB^)h>2-Wj&qz|{_to6yfjtSg zKirbJ&?349$3U4%MLskF4@{$Qb^%FFHE_84Vajh6iE$7cHFl8DjyEI;C*j6;iP07fT{GC=w3hp^C9*Q4@W95`c zurK;!-k9BFblWQ-wTp!_o4KL|UbYq#$J%x=f(8>5fA*Q=+~pY!Q-l<%E53t}8cWr4 zX^aMy&v$qj_&-f~D7Y~CLqA3YGd%a-I#V|@oQiG=ggNFQU!Ftne@DVJpom=3SWE*ug&|a+` z4YkI*+=rVN?0kkZ={xJ@OogYO@9)fhQJ=lI{FZ-gtWooQ8Dc+gfOQpiq_GYCW)3Wi0$aNmkY)r#~#+sBl;4o z1?rU@hVKSiA6pv)KCP@hf4D~s!LF)OdKE~sklp5k(jWayOS>AIi`O<)dQdf|gGa|^ zrr;KW!YIw^h&l6Qi>b}86{pv=8MQq1Adklw4h7VOcSaQ=V`7n#3(aKpx-E90(Kc2L z4%Y!)bE>UL7qTTD-L+#&Q7UNbzpmIkk>CvH8xo{1Aih}Mu?C| z$fot{3zi|y%+0y`1+tB8rT`!y<SxXzTMJz&iHP|sPwyMO0%1d&D-iX>a)C0{sv%5)=T>~ zSDreRO*KlQN?sSs$_1e%=`eWC9)DMowno0$GSAup`$ZczQ;x6 z&F{0z(pmZ*r9JC+H=kQ-FSF9iTXGVSEbD6ZZySB0kLv{Ibv^0IwNA_^<72?(o3xBB z${y1jt3t3$Yqdu|v!*Y5lD-@qByFnRTqzA6K7!`G{T1A8ZLe;;h|>i3SMp=P6N9~7M1x# z;TCZR2VwKy3cr2LG0Qz^A_j*@%iRq=9=LSX9Nk?GWHwy}3NRAAByl|E%j*85A&xTO za!f*g3fVe3?S3;`if3-1@nRyXt$#n9)iwV5gmF6yQ;Vy`e*b{-YHH~Xf9Z+ys_>|} zEGg<;ySHJ(+ikgSLt)_tNzMtjT)&BKt;MC`VpB;c4DL~u>Bgp6ek00>PIc(R6t=g@ zM54G}9?wkPf6sks(&{{=`cAEm3k@=!L{9LTrn4S6^Hg>?r=-jL&Ehg#Q~r zYwV(+wb96HD`qnEeVHaKWznAu%v*_cTk7A1q=0guJrEJ-D=3+wTvRs%^4ywa?0LQd zP!Ci~aVR<$0ztAS8-vT20Lp-3DKbO_LR_C07s{riM9OsTjehs#DkD1-7lmUls!Scv zOqJ--2!9#;lcoZyRB@#BGHsZRWkZ0C-V?_CG zKw=ON#RsvuPzby={upV#9}o$IK|vxW5bFAD7k7*>|7&O^IP<$ur7xF}$Z8%d0xp)z zt0?~c2!7(%43VH@@<|GAa&8J13EfYS7uM)w0{Q5`G*B)12Z^~)5L9c-G2wi8U>GQt zoJ2z4lk1CJG*yL8f>N2YO`Ov-qNzA{itLZ6F4{l&m3}sWQvX-=qjl2Gl=6kJWmzetqL9s?17vVb0P2}7-SIG+46mL13yBsrCpLZ&RrVtvw_e@&l4xJ`kGKRS%u!PnTF1`0m zS$!LNMJQ~14O*oNjUyC(f1S9>89M$hinSg!%pZu%5U;8CUDCQGL>7>XI`^zDa0fi0 zUq)eKsLD1?XwSA_=J{n6FI0=r(9|qwx~hb2R^izUVAP z1G6^|L7#V~p*gTO|AJz4HlZD}HxEPMI>XS&*nNLOQ96szve?t@;80`jKFO|7%hdiz zhm*vIAX+@A#@i~tqkpGk<{7an(1{+JhQ`X-+yQ0mv_%7RHn&4*JN3{UIGa16Z#sR@ zjyZjQKq)$9(8xG_JD?1mmS}W(KLV-rjIsIp<5}1&0i%c?KIuHFZ(t{58v@M#??JvT zP>5P;aiMqB{iB}Unu>{S{ZNihCNwZda}QLYlNil`qq!T((y%e&;49n7utTFnh5*H4rOZ+f-1US3XuZnphtrLKER)`1&cD z`|z+(;m3BD_eFRgW0g;HTg=z<&uR%?AJMCovHAQn{q&Rj^Rh{EnE_-aTIv(I_@4E- z7P2p=1>4Sb-tr9ZTWxgbwoDc)qbo5qs3qyoVJoquwvg{)Rv5{un0IY{y^SCjQfn)3 z!D*_05KxUmY+_H<2lWSNX6;2X2FXrO=#=HMyS-9k6aq814fQK01r-~%dEH3Pv|iMn z24uUVOC?oS+V1V@9FH0o57CvL3py|y%zS2>ZYiRA+#uz_(+Eum7dJS@$YUDa=0(-^ z1sAkFzKPTLnZ?j~iXZyp zs+5eK5cLsm|9PhYv6GGHz-@0#S}!erc?|4x)32GV7_qFg%vKYUluEq;--{=4HQoRX z#^otNgIwbwVl9Ur{01%KoKM4v8d~ZNn)p^#h;NZJA@1ANz4@tcZ_|m6@}uOmVqQD( zB=K4arleAla#k8G_AIqDyv6zX@LpbW?!Y44Ct=+&1@q;?`J8b- zFy#@oFDX>NJ?CluEL8pah~3k9ikM}+xAZ<$!iuS6VFhY2+4U}j(z0#IlGWKUqPSF21Bn}RKwBuTn*; zL%Dk_aB?sBf@T%dT1{1|d3@xB)H43sO1_&%^0Bf{HkTZ7c5 zekTo{K6)7Ag%}o}q82<=m)_s*gLzLnRd8ufb2W~m(JpW`?B`LYLNc?!V@!w`HQ8bK zLpEYeu|>KI^GEpeOX*2zn#*w^g1*@-aba;H+AFQO0}qm)o;3|67bf+dsvg{xHAD1% zOlMrZejJ+fEyPsCX0xN+s#*>&M2A5e+-ko3RyrIhn&xptGVL~HNdt{ee@3(N&H>l> z+c)Pp^=!aY%bAJoMnaj)3#ZqOu>$*LuaCj!K$XqMQl<(9qI&{{0+QlwnzLmPw8A?M zPvhvKF&AxOt%zgfmBU?~rR`>2><$8<@xxuS-*n}XO& zv9mAL&*Dc(D~{2Jj#U}T?Y^D+I6ZWqx!0uYRU9r_r8k#iLo1dhcKoJcTMpVz*hXFs zr)@e6L4#}~8zm;2c$<%Ug!D@KsY_pdwCq&Fn9~2^1DIE{&|cj~NQ zdj+JjH!~Lr=LP!?s1&%LE;65@qN8SZzCCMEI%gzJlV0wk6S!`7TIj@gMy6EydMxZf z1sRao2Q1CI+2`q5ZXcBu5XT6;e2OF3*1Ao$uN;?c%ox~8Ky>!p@G#h0j6xW=bTpNF zdeA%5Z%rIQWuQq`7=Q2*h}Y|aG{5iq8C6y4OMP*N>XnwyHKPPff$IoB{Et~*e&(e+?B zrCu~HASFoUl7$H}as2wZu^G&o%Q;gk z`b-`ay3_3}O_nuE#&Ck;hPsPfvLPuwnK7QjXEhQNd_c8 z)n>&<)m?7b@e(PcZCd??Bm$xR#++~aNPcZ!&ndd4MrlJsBn#!gKXGM)>vN zb$0{S%%%?F5i>dW*M!j+2yd|Yi}TL|_*MPlOYE^BM6IDHaU(CmA-wD{{*z^z;&1?f)-D%)wOI*}>6;)zrb+{I{N#JQo3uJ{tfl^8fZL zI>A7$lZ_zc81YG>-!IL=TtZ@g6{*RYoKK}0wZDmF#up@6_uw<`UP^M-G1$Dl(a+Dy zuZUfp)C^g8?d8& zD$4~5o_{xiOK`VTC^XHfq7E;MKbVfIjqjOtpNk=5H5AVMW{;V{>fN_v@~z8RCGuHd zObzPYJE7xNL%)}Z2`Duu?4mTWAJ;Sk?z4ILY5O(+sI|!25y|xB`I7VEmX7U*KQd^y zhOmmfSWU`wZhvNKiXMM~qxo1{LOY%WSIr6S?6~giL6JN5?FN@Ayyt16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {C3EAC63A-32F4-3D48-91EF-229CFD089A27} + + + + + + + + + + + + + + {ED8ED187-9497-EB42-AFFD-C4DCE79D01B9} + + + + + + + + + + + + + + {CCEF6E8A-65EB-0F4B-A9F7-D3719BD225D4} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT(ISERROR(SEARCH("3",Q1))) + + + + + 2 + 7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AD1>3 + + + \ No newline at end of file diff --git a/openpyxl/formatting/tests/test_formatting.py b/openpyxl/formatting/tests/test_formatting.py new file mode 100644 index 0000000..8ebd00a --- /dev/null +++ b/openpyxl/formatting/tests/test_formatting.py @@ -0,0 +1,295 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +# package imports +from openpyxl.reader.excel import load_workbook +from openpyxl.xml.functions import tostring, fromstring +from openpyxl.writer.worksheet import write_conditional_formatting +from openpyxl.styles import Border, Side, PatternFill, Color, Font, fills, borders, colors +from openpyxl.styles.differential import DifferentialStyle, DifferentialStyleList +from openpyxl.formatting.formatting import ConditionalFormattingList +from openpyxl.formatting.rule import CellIsRule, FormulaRule, Rule + +# test imports +import pytest +from openpyxl.tests.helper import compare_xml + + +class DummyWorkbook(): + + def __init__(self): + self._differential_styles = DifferentialStyleList() + self.worksheets = [] + +class DummyWorksheet(): + + def __init__(self): + self.conditional_formatting = ConditionalFormattingList() + self.parent = DummyWorkbook() + + +class TestConditionalFormattingList(object): + + + def setup(self): + self.ws = DummyWorksheet() + + def test_conditional_formatting_customRule(self): + + worksheet = self.ws + worksheet.conditional_formatting.add('C1:C10', + Rule(**{'type': 'expression', 'formula': ['ISBLANK(C1)'], + 'stopIfTrue': '1',} + ) + ) + cfs = write_conditional_formatting(worksheet) + xml = b"" + for cf in cfs: + xml += tostring(cf) + + diff = compare_xml(xml, """ + + + ISBLANK(C1) + + + """) + assert diff is None, diff + + def test_write_conditional_formatting(self): + ws = self.ws + cf = ConditionalFormattingList() + ws.conditional_formatting = cf + + fill = PatternFill(start_color=Color('FFEE1111'), + end_color=Color('FFEE1111'), + patternType=fills.FILL_SOLID) + font = Font(name='Arial', size=12, bold=True, + underline=Font.UNDERLINE_SINGLE) + border = Border(top=Side(border_style=borders.BORDER_THIN, + color=Color(colors.DARKYELLOW)), + bottom=Side(border_style=borders.BORDER_THIN, + color=Color(colors.BLACK))) + cf.add('C1:C10', FormulaRule(formula=['ISBLANK(C1)'], font=font, border=border, fill=fill)) + cf.add('D1:D10', FormulaRule(formula=['ISBLANK(D1)'], fill=fill)) + from openpyxl.writer.worksheet import write_conditional_formatting + for _ in write_conditional_formatting(ws): + pass # exhaust generator + + wb = ws.parent + assert len(wb._differential_styles.styles) == 2 + ft1, ft2 = wb._differential_styles.styles + assert ft1.font == font + assert ft1.border == border + assert ft1.fill == fill + assert ft2.fill == fill + + + def test_conditional_font(self): + """Test to verify font style written correctly.""" + + ws = self.ws + cf = ConditionalFormattingList() + ws.conditional_formatting = cf + + # Create cf rule + redFill = PatternFill(start_color=Color('FFEE1111'), + end_color=Color('FFEE1111'), + patternType=fills.FILL_SOLID) + whiteFont = Font(color=Color("FFFFFFFF")) + ws.conditional_formatting.add('A1:A3', + CellIsRule(operator='equal', formula=['"Fail"'], stopIfTrue=False, + font=whiteFont, fill=redFill)) + + from openpyxl.writer.worksheet import write_conditional_formatting + + # First, verify conditional formatting xml + cfs = write_conditional_formatting(ws) + xml = b"" + for cf in cfs: + xml += tostring(cf) + + diff = compare_xml(xml, """ + + + "Fail" + + + """) + assert diff is None, diff + + +def test_conditional_formatting_read(datadir): + datadir.chdir() + reference_file = 'conditional-formatting.xlsx' + wb = load_workbook(reference_file) + ws = wb.active + rules = ws.conditional_formatting + assert len(rules) == 30 + + # First test the conditional formatting rules read + rule = rules['A1:A1048576'][0] + assert dict(rule) == {'priority':'30', 'type': 'colorScale', } + + rule = rules['B1:B10'][0] + assert dict(rule) == {'priority': '29', 'type': 'colorScale'} + + rule = rules['C1:C10'][0] + assert dict(rule) == {'priority': '28', 'type': 'colorScale'} + + rule = rules['D1:D10'][0] + assert dict(rule) == {'priority': '27', 'type': 'colorScale', } + + rule = rules['E1:E10'][0] + assert dict(rule) == {'priority': '26', 'type': 'colorScale', } + + rule = rules['F1:F10'][0] + assert dict(rule) == {'priority': '25', 'type': 'colorScale', } + + rule = rules['G1:G10'][0] + assert dict(rule) == {'priority': '24', 'type': 'colorScale', } + + rule = rules['H1:H10'][0] + assert dict(rule) == {'priority': '23', 'type': 'colorScale', } + + rule = rules['I1:I10'][0] + assert dict(rule) == {'priority': '22', 'type': 'colorScale', } + + rule = rules['J1:J10'][0] + assert dict(rule) == {'priority': '21', 'type': 'colorScale', } + + rule = rules['K1:K10'][0] + assert dict(rule) == {'priority': '20', 'type': 'dataBar'} + + rule = rules['L1:L10'][0] + assert dict(rule) == {'priority': '19', 'type': 'dataBar'} + + rule = rules['M1:M10'][0] + assert dict(rule) == {'priority': '18', 'type': 'dataBar'} + + rule = rules['N1:N10'][0] + assert dict(rule) == {'priority': '17', 'type': 'iconSet'} + + rule = rules['O1:O10'][0] + assert dict(rule) == {'priority': '16', 'type': 'iconSet'} + + rule = rules['P1:P10'][0] + assert dict(rule) == {'priority': '15', 'type': 'iconSet'} + + rule = rules['Q1:Q10'][0] + assert dict(rule) == {'text': '3', 'priority': '14', 'dxfId': '27', + 'operator': 'containsText', 'type': 'containsText'} + assert rule.dxf == DifferentialStyle(font=Font(color='FF9C0006'), + fill=PatternFill(bgColor='FFFFC7CE') + ) + + rule = rules['R1:R10'][0] + assert dict(rule) == {'operator': 'between', 'dxfId': '26', 'type': + 'cellIs', 'priority': '13'} + assert rule.dxf == DifferentialStyle(font=Font(color='FF9C6500'), + fill=PatternFill(bgColor='FFFFEB9C')) + + rule = rules['S1:S10'][0] + assert dict(rule) == {'priority': '12', 'dxfId': '25', 'percent': '1', + 'type': 'top10', 'rank': '10'} + + rule = rules['T1:T10'][0] + assert dict(rule) == {'priority': '11', 'dxfId': '24', 'type': 'top10', + 'rank': '4', 'bottom': '1'} + + rule = rules['U1:U10'][0] + assert dict(rule) == {'priority': '10', 'dxfId': '23', 'type': + 'aboveAverage'} + + rule = rules['V1:V10'][0] + assert dict(rule) == {'aboveAverage': '0', 'dxfId': '22', 'type': + 'aboveAverage', 'priority': '9'} + + rule = rules['W1:W10'][0] + assert dict(rule) == {'priority': '8', 'dxfId': '21', 'type': + 'aboveAverage', 'equalAverage': '1'} + + rule = rules['X1:X10'][0] + assert dict(rule) == {'aboveAverage': '0', 'dxfId': '20', 'priority': '7', + 'type': 'aboveAverage', 'equalAverage': '1'} + + rule = rules['Y1:Y10'][0] + assert dict(rule) == {'priority': '6', 'dxfId': '19', 'type': + 'aboveAverage', 'stdDev': '1'} + + rule = rules['Z1:Z10'][0] + assert dict(rule)== {'aboveAverage': '0', 'dxfId': '18', 'type': + 'aboveAverage', 'stdDev': '1', 'priority': '5'} + assert rule.dxf == DifferentialStyle(font=Font(b=True, i=True, color='FF9C0006'), + fill=PatternFill(bgColor='FFFFC7CE'), + border=Border( + left=Side(style='thin', color=Color(theme=5)), + right=Side(style='thin', color=Color(theme=5)), + top=Side(style='thin', color=Color(theme=5)), + bottom=Side(style='thin', color=Color(theme=5)) + ) + ) + + rule = rules['AA1:AA10'][0] + assert dict(rule) == {'priority': '4', 'dxfId': '17', 'type': + 'aboveAverage', 'stdDev': '2'} + + rule = rules['AB1:AB10'][0] + assert dict(rule) == {'priority': '3', 'dxfId': '16', 'type': + 'duplicateValues'} + + rule = rules['AC1:AC10'][0] + assert dict(rule) == {'priority': '2', 'dxfId': '15', 'type': + 'uniqueValues'} + + rule = rules['AD1:AD10'][0] + assert dict(rule) == {'priority': '1', 'dxfId': '14', 'type': 'expression',} + + +@pytest.fixture +def ConditionalFormatting(): + from ..formatting import ConditionalFormatting + return ConditionalFormatting + + +class TestConditionalFormatting: + + + def test_ctor(self, ConditionalFormatting): + cf = ConditionalFormatting(sqref="A1:B5") + xml = tostring(cf.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_tree(self, ConditionalFormatting): + src = """ + + """ + tree = fromstring(src) + cf = ConditionalFormatting.from_tree(tree) + assert cf.sqref == "A1:B5" + + + def test_eq(self, ConditionalFormatting): + c1 = ConditionalFormatting("A1:B5") + c2 = ConditionalFormatting("A1:B5", pivot=True) + assert c1 == c2 + + + def test_hash(self, ConditionalFormatting): + c1 = ConditionalFormatting("A1:B5") + assert hash(c1) == hash("A1:B5") + + + def test_repr(self, ConditionalFormatting): + c1 = ConditionalFormatting("A1:B5") + assert repr(c1) == "" + + + def test_contains(self, ConditionalFormatting): + c2 = ConditionalFormatting("A1:A5 B1:B5") + assert "B2" in c2 diff --git a/openpyxl/formatting/tests/test_rule.py b/openpyxl/formatting/tests/test_rule.py new file mode 100644 index 0000000..ee752e1 --- /dev/null +++ b/openpyxl/formatting/tests/test_rule.py @@ -0,0 +1,366 @@ +# coding=utf8 +from __future__ import absolute_import +# copyright 2010-2015 openpyxl + + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.styles import Color + +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def FormatObject(): + from ..rule import FormatObject + return FormatObject + + +class TestFormatObject: + + def test_create(self, FormatObject): + xml = fromstring("""""") + cfvo = FormatObject.from_tree(xml) + assert cfvo.type == "num" + assert cfvo.val == 3 + assert cfvo.gte is None + + + def test_serialise(self, FormatObject): + cfvo = FormatObject(type="percent", val=4) + xml = tostring(cfvo.to_tree()) + expected = """""" + diff = compare_xml(xml, expected) + assert diff is None, diff + + + @pytest.mark.parametrize("typ, value, expected", + [ + ('num', '5', 5.0), + ('percent', '70', 70), + ('max', 10, 10), + ('min', '4.2', 4.2), + ('formula', "=A2*4", "=A2*4"), + ('percentile', 10, 10), + ('formula', None, None), + ] + ) + def test_value_types(self, FormatObject, typ, value, expected): + cfvo = FormatObject(type=typ, val=value) + assert cfvo.val == expected + + + def test_cell_reference(self, FormatObject): + cfvo = FormatObject(type="num") + cfvo.val = "$K$6" + assert cfvo.val == "$K$6" + + +@pytest.fixture +def ColorScale(): + from ..rule import ColorScale + return ColorScale + + +@pytest.fixture +def ColorScaleRule(): + from ..rule import ColorScaleRule + return ColorScaleRule + + +class TestColorScale: + + def test_create(self, ColorScale): + src = """ + + + + + + + """ + xml = fromstring(src) + cs = ColorScale.from_tree(xml) + assert len(cs.cfvo) == 2 + assert len(cs.color) == 2 + + + def test_serialise(self, ColorScale, FormatObject): + fo1 = FormatObject(type="min", val="0") + fo2 = FormatObject(type="percent", val="50") + fo3 = FormatObject(type="max", val="0") + + col1 = Color(rgb="FFFF0000") + col2 = Color(rgb="FFFFFF00") + col3 = Color(rgb="FF00B050") + + cs = ColorScale(cfvo=[fo1, fo2, fo3], color=[col1, col2, col3]) + xml = tostring(cs.to_tree()) + expected = """ + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_two_colors(self, ColorScaleRule): + cfRule = ColorScaleRule(start_type='min', start_value=None, + start_color='FFAA0000', end_type='max', end_value=None, + end_color='FF00AA00') + xml = tostring(cfRule.to_tree()) + expected = """ + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_three_colors(self, ColorScaleRule): + cfRule = ColorScaleRule(start_type='percentile', start_value=10, + start_color='FFAA0000', mid_type='percentile', mid_value=50, + mid_color='FF0000AA', end_type='percentile', end_value=90, + end_color='FF00AA00') + xml = tostring(cfRule.to_tree()) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def DataBar(): + from ..rule import DataBar + return DataBar + + +class TestDataBar: + + def test_create(self, DataBar): + src = """ + + + + + + """ + xml = fromstring(src) + db = DataBar.from_tree(xml) + assert len(db.cfvo) == 2 + assert db.color.value == "FF638EC6" + + + def test_serialise(self, DataBar, FormatObject): + fo1 = FormatObject(type="min", val="0") + fo2 = FormatObject(type="percent", val="50") + db = DataBar(minLength=4, maxLength=10, cfvo=[fo1, fo2], color="FF2266", showValue=True) + xml = tostring(db.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def IconSet(): + from ..rule import IconSet + return IconSet + + +class TestIconSet: + + def test_create(self, IconSet): + src = """ + + + + + + + + """ + xml = fromstring(src) + icon = IconSet.from_tree(xml) + assert icon.iconSet == "5Rating" + assert len(icon.cfvo) == 5 + + + def test_serialise(self, IconSet, FormatObject): + fo1 = FormatObject(type="num", val="2") + fo2 = FormatObject(type="num", val="4") + fo3 = FormatObject(type="num", val="6") + fo4 = FormatObject(type="percent", val="0") + icon = IconSet(cfvo=[fo1, fo2, fo3, fo4], iconSet="4ArrowsGray", reverse=True, showValue=False) + xml = tostring(icon.to_tree()) + expected = """ + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +@pytest.fixture +def Rule(): + from ..rule import Rule + return Rule + + +class TestRule: + + def test_create(self, Rule, datadir): + datadir.chdir() + with open("worksheet.xml") as src: + xml = fromstring(src.read()) + + rules = [] + for el in xml.findall("{%s}conditionalFormatting/{%s}cfRule" % (SHEET_MAIN_NS, SHEET_MAIN_NS)): + rules.append(Rule.from_tree(el)) + + assert len(rules) == 30 + assert rules[17].formula == ['2', '7'] + assert rules[-1].formula == ["AD1>3",] + + + def test_serialise(self, Rule): + + rule = Rule(type="cellIs", dxfId="26", priority="13", operator="between") + rule.formula = ["2", "7"] + + xml = tostring(rule.to_tree()) + expected = """ + + 2 + 7 + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_non_ascii_formula(self, Rule): + + rule = Rule(type="cellIs", priority=10, formula=[b"D\xc3\xbcsseldorf".decode("utf-8")]) + + xml = tostring(rule.to_tree()) + expected = """ + + Düsseldorf + + """ + diff = compare_xml(xml, expected) + + assert diff is None, diff + + +def test_formula_rule(): + from ..rule import FormulaRule + from openpyxl.styles.differential import DifferentialStyle + + cf = FormulaRule(formula=['ISBLANK(C1)'], stopIfTrue=True) + assert dict(cf) == {'priority': '0', 'stopIfTrue': '1', 'type': 'expression'} + assert cf.formula == ['ISBLANK(C1)'] + assert cf.dxf == DifferentialStyle() + + +def test_cellis_rule(): + from ..rule import CellIsRule + from openpyxl.styles import PatternFill + + red_fill = PatternFill(start_color='FFEE1111', end_color='FFEE1111', + fill_type='solid') + + rule = CellIsRule(operator='<', formula=['C$1'], stopIfTrue=True, fill=red_fill) + assert dict(rule) == {'operator': 'lessThan', 'priority': '0', 'type': 'cellIs', 'stopIfTrue':'1'} + assert rule.formula == ['C$1'] + assert rule.dxf.fill == red_fill + + +@pytest.mark.parametrize("value, expansion", + [ + ('<=', 'lessThanOrEqual'), + ('>', 'greaterThan'), + ('!=', 'notEqual'), + ('=', 'equal'), + ('>=', 'greaterThanOrEqual'), + ('==', 'equal'), + ('<', 'lessThan'), + ] + ) +def test_operator_expansion(value, expansion): + from ..rule import CellIsRule + cf1 = CellIsRule(operator=value, formula=[]) + cf2 = CellIsRule(operator=expansion, formula=[]) + assert cf1.operator == expansion + assert cf2.operator == expansion + + +def test_iconset_rule(): + from ..rule import IconSetRule + rule = IconSetRule('5Arrows', 'percent', [10, 20, 30, 40, 50]) + xml = tostring(rule.to_tree()) + expected = """ + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +def test_databar_rule(): + from ..rule import DataBarRule + rule = DataBarRule(start_type='percentile', start_value=10, + end_type='percentile', end_value='90', color="FF638EC6") + xml = tostring(rule.to_tree()) + expected = """ + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/formula/__init__.py b/openpyxl/formula/__init__.py new file mode 100644 index 0000000..efe2116 --- /dev/null +++ b/openpyxl/formula/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from .tokenizer import Tokenizer diff --git a/openpyxl/formula/excel_tokenizer_parser.txt b/openpyxl/formula/excel_tokenizer_parser.txt new file mode 100644 index 0000000..91b4cb3 --- /dev/null +++ b/openpyxl/formula/excel_tokenizer_parser.txt @@ -0,0 +1,61 @@ +From ewbachtal@ewbi.com Fri Apr 3 11:00:25 2015 +Date: Fri, 3 Apr 2015 07:58:55 -0700 +From: Eric W. Bachtal +To: 'Paul Harrington' +Subject: RE: [pycel] re-license the parser/tokenizer under a license suitable for incorporation into openpyxl (#11) (fwd) + +Hi Paul, + +I can't speak for Dirk's wishes regarding his Python implementation, but I'm +fine with my original JavaScript or C# Excel formula parser code being +released with a BSD/MIT license, as long as proper attribution is provided +and there's a clear indication that the software is provided "as is", +without warranty of any kind. + +Good luck! + +ewb + +425 241 2505 + + +-----Original Message----- +From: Paul Harrington [mailto:phrrngtn@panix.com] +Sent: Friday, April 3, 2015 7:19 AM +To: info@ewbi.com +Subject: Re: [pycel] re-license the parser/tokenizer under a license +suitable for incorporation into openpyxl (#11) (fwd) + + + +Hi, I was wondering if you would be OK with the parser (specifically the +Python implementation) being released with an BSD/MIT license? + +thanks for the implementation! + +pjjH + + + + +---------- Forwarded message ---------- +Date: Fri, 03 Apr 2015 06:26:39 -0700 +From: Dirk Gorissen +Reply-To: dgorissen/pycel + + +To: dgorissen/pycel +Cc: Paul Harrington +Subject: Re: [pycel] re-license the parser/tokenizer under a license +suitable + for incorporation into openpyxl (#11) + +I did not write the tokenizing/parser code and don't hold the copyright to +it. +Eric did and I just applied a small patch and integrated it. See the header +of tokenizer.py. + +--- +Reply to this email directly or view it on GitHub: +https://github.com/dgorissen/pycel/issues/11#issuecomment-89287122 diff --git a/openpyxl/formula/tests/__init__.py b/openpyxl/formula/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/formula/tests/test_tokenizer.py b/openpyxl/formula/tests/test_tokenizer.py new file mode 100644 index 0000000..e9e5d10 --- /dev/null +++ b/openpyxl/formula/tests/test_tokenizer.py @@ -0,0 +1,591 @@ +from __future__ import absolute_import + +import pytest + +@pytest.fixture +def tokenizer(): + from .. import tokenizer + return tokenizer + +# Constants from tokenizer.Token: +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" +TEXT = 'TEXT' +NUMBER = 'NUMBER' +LOGICAL = 'LOGICAL' +ERROR = 'ERROR' +RANGE = 'RANGE' +OPEN = "OPEN" +CLOSE = "CLOSE" +ARG = "ARG" +ROW = "ROW" + + +class TestTokenizerRegexes(object): + + @pytest.mark.parametrize("string, success", [ + ('1.0E', True), + ('1.53321E', True), + ('9.999E', True), + ('3E', True), + ('12E', False), + ('0.1E', False), + ('0E', False), + ('', False), + ('E', False), + ]) + def test_scientific_re(self, tokenizer, string, success): + regex = tokenizer.Tokenizer.SN_RE + assert bool(regex.match(string)) is success + + @pytest.mark.parametrize('string, expected', [ + (' ', ' '), + (' *', ' '), + (' ', ' '), + (' a', ' '), + (' ', ' '), + (' +', ' '), + ('', None), + ('*', None), + ]) + def test_whitespace_re(self, tokenizer, string, expected): + if expected is None: + assert not tokenizer.Tokenizer.WSPACE_RE.match(string) + else: + assert tokenizer.Tokenizer.WSPACE_RE.match(string) + assert tokenizer.Tokenizer.WSPACE_RE.match(string).group(0) == expected + + @pytest.mark.parametrize('string, expected', [ + ('"spamspamspam"', '"spamspamspam"'), + ('"this is "" a test "" "', '"this is "" a test "" "'), + ('""', '""'), + ('"spam and ""cheese"""+"ignore"', ('"spam and ""cheese"""')), + ('\'"spam and ""cheese"""+"ignore"', None), + ('"oops ""', None), + ]) + def test_string_re(self, tokenizer, string, expected): + regex = tokenizer.Tokenizer.STRING_REGEXES['"'] + if expected is None: + assert not regex.match(string) + else: + assert regex.match(string) + assert regex.match(string).group(0) == expected + + @pytest.mark.parametrize('string, expected', [ + ("'spam and ham'", "'spam and ham'"), + ("'double'' triple''' quadruple ''''", "'double'' triple'''"), + ("'sextuple '''''' and septuple''''''' and more", + "'sextuple '''''' and septuple'''''''",), + ("''", "''"), + ("'oops ''", None), + ("gunk'hello world'", None), + ]) + def test_link_re(self, tokenizer, string, expected): + regex = tokenizer.Tokenizer.STRING_REGEXES["'"] + if expected is None: + assert not regex.match(string) + else: + assert regex.match(string) + assert regex.match(string).group(0) == expected + + +class TestTokenizer(object): + + def test_init(self, tokenizer): + tok = tokenizer.Tokenizer("abcdefg") + assert tok.formula == "abcdefg" + tok = tokenizer.Tokenizer("=abcdefg") + assert tok.formula == "=abcdefg" + + @pytest.mark.parametrize('formula, tokens', [ + ('=IF(A$3<40%,"",INDEX(Pipeline!B$4:B$138,#REF!))', + [('IF(', FUNC, OPEN), + ('A$3', OPERAND, RANGE), + ('<', OP_IN, ""), + ('40', OPERAND, NUMBER), + ('%', OP_POST, ""), + (',', SEP, ARG), + ('""', OPERAND, TEXT), + (',', SEP, ARG), + ('INDEX(', FUNC, OPEN), + ('Pipeline!B$4:B$138', OPERAND, RANGE), + (',', SEP, ARG), + ('#REF!', OPERAND, ERROR), + (')', FUNC, CLOSE), + (')', FUNC, CLOSE)]), + + ("='Summary slices'!$C$3", + [("'Summary slices'!$C$3", OPERAND, RANGE)]), + + ('=-MAX(Pipeline!AA4:AA138)', + [("-", OP_PRE, ""), + ('MAX(', FUNC, OPEN), + ('Pipeline!AA4:AA138', OPERAND, RANGE), + (')', FUNC, CLOSE)]), + + ('=TEXT(-S7/1000,"$#,##0""M""")', + [('TEXT(', FUNC, OPEN), + ('-', OP_PRE, ""), + ('S7', OPERAND, RANGE), + ('/', OP_IN, ""), + ('1000', OPERAND, NUMBER), + (',', SEP, ARG), + ('"$#,##0""M"""', OPERAND, TEXT), + (')', FUNC, CLOSE)]), + + ("=IF(A$3<1.3E-8,\"\",IF(ISNA('External Ref'!K7)," + + '"N/A",TEXT(K7*1E+12,"0")&"bp"', + [('IF(', FUNC, OPEN), + ('A$3', OPERAND, RANGE), + ('<', OP_IN, ""), + ('1.3E-8', OPERAND, NUMBER), + (',', SEP, ARG), + ('""', OPERAND, TEXT), + (',', SEP, ARG), + ('IF(', FUNC, OPEN), + ('ISNA(', FUNC, OPEN), + ("'External Ref'!K7", OPERAND, RANGE), + (')', FUNC, CLOSE), + (',', SEP, ARG), + ('"N/A"', OPERAND, TEXT), + (',', SEP, ARG), + ('TEXT(', FUNC, OPEN), + ('K7', OPERAND, RANGE), + ('*', OP_IN, ""), + ('1E+12', OPERAND, NUMBER), + (',', SEP, ARG), + ('"0"', OPERAND, TEXT), + (')', FUNC, CLOSE), + ('&', OP_IN, ""), + ('"bp"', OPERAND, TEXT)]), + + ('=+IF(A$3<>$B7,"",(MIN(IF({TRUE, FALSE;1,2},A6:B6,$S7))>=' + + 'LOWER_BOUND)*($BR6>$S72123))', + [("+", OP_PRE, ""), + ('IF(', FUNC, OPEN), + ('A$3', OPERAND, RANGE), + ('<>', OP_IN, ""), + ('$B7', OPERAND, RANGE), + (',', SEP, ARG), + ('""', OPERAND, TEXT), + (',', SEP, ARG), + ('(', PAREN, OPEN), + ('MIN(', FUNC, OPEN), + ('IF(', FUNC, OPEN), + ('{', ARRAY, OPEN), + ('TRUE', OPERAND, LOGICAL), + (',', SEP, ARG), + (' ', WSPACE, ''), + ('FALSE', OPERAND, LOGICAL), + (';', SEP, ROW), + ('1', OPERAND, NUMBER), + (',', SEP, ARG), + ('2', OPERAND, NUMBER), + ('}', ARRAY, CLOSE), + (',', SEP, ARG), + ('A6:B6', OPERAND, RANGE), + (',', SEP, ARG), + ('$S7', OPERAND, RANGE ), + (')', FUNC, CLOSE), + (')', FUNC, CLOSE), + ('>=', OP_IN, ''), + ('LOWER_BOUND', OPERAND, RANGE), + (')', PAREN, CLOSE), + ('*', OP_IN, ''), + ('(', PAREN, OPEN), + ('$BR6', OPERAND, RANGE), + ('>', OP_IN, ''), + ('$S72123', OPERAND, RANGE), + (')', PAREN, CLOSE), + (')', FUNC, CLOSE)]), + + ('=(AW$4=$D7)+0%', + [('(', PAREN, OPEN), + ('AW$4', OPERAND, RANGE), + ('=', OP_IN, ''), + ('$D7', OPERAND, RANGE), + (')', PAREN, CLOSE), + ('+', OP_IN, ''), + ('0', OPERAND, NUMBER), + ('%', OP_POST, '')]), + + ('=$A:$A,$C:$C', + [('$A:$A', OPERAND, RANGE), + (',', OP_IN, ""), + ('$C:$C', OPERAND, RANGE)]), + + ("Just text", [("Just text", LITERAL, "")]), + ("123.456", [("123.456", LITERAL, "")]), + ("31/12/1999", [("31/12/1999", LITERAL, "")]), + ("", []), + ]) + def test_parse(self, tokenizer, formula, tokens): + tok = tokenizer.Tokenizer(formula) + result = [(token.value, token.type, token.subtype) + for token in tok.items] + assert result == tokens + + @pytest.mark.parametrize('formula, offset, result', [ + ('"spamspamspam"spam', 0, '"spamspamspam"'), + ('"this is "" a test "" "test', 0, '"this is "" a test "" "'), + ('""', 0, '""'), + ('a"bcd""efg"hijk', 1, '"bcd""efg"'), + ('"oops ""', 0, None), + ("'spam and ham'", 0, "'spam and ham'"), + ("'double'' triple''' quad ''''", 0, "'double'' triple'''"), + ("123'sextuple '''''' and septuple''''''' and more", 3, + "'sextuple '''''' and septuple'''''''"), + ("''", 0, "''"), + ("'oops ''", 0, None), + ]) + def test_parse_string(self, tokenizer, formula, offset, result): + tok = tokenizer.Tokenizer(formula) + del tok.items[:] + tok.offset = offset + if result is None: + with pytest.raises(tokenizer.TokenizerError): + tok._parse_string() + return + assert tok._parse_string() == len(result) + if formula[offset] == '"': + token = tok.items[0] + assert token.value == result + assert token.type == OPERAND + assert token.subtype == TEXT + assert not tok.token + else: + assert not tok.items + assert tok.token[0] == result + assert len(tok.token) == 1 + + @pytest.mark.parametrize('formula, offset, result', [ + ('[abc]def', 0, '[abc]'), + ('[]abcdef', 0, '[]'), + ('[abcdef]', 0, '[abcdef]'), + ('a[bcd]ef', 1, '[bcd]'), + ('ab[cde]f', 2, '[cde]'), + ]) + def test_parse_brackets(self, tokenizer, formula, offset, result): + tok = tokenizer.Tokenizer(formula) + del tok.items[:] + tok.offset = offset + assert tok._parse_brackets() == len(result) + assert not tok.items + assert tok.token[0] == result + assert len(tok.token) == 1 + + def test_parse_brackets_error(self, tokenizer): + tok = tokenizer.Tokenizer('[unfinished business') + with pytest.raises(tokenizer.TokenizerError): + tok._parse_brackets() + + @pytest.mark.parametrize('error', [ + "#NULL!", + "#DIV/0!", + "#VALUE!", + "#REF!", + "#NAME?", + "#NUM!", + "#N/A", + "#GETTING_DATA", + ]) + def test_parse_error(self, tokenizer, error): + tok = tokenizer.Tokenizer(error) + tok.offset = 0 + del tok.items[:] + assert tok._parse_error() == len(error) + assert len(tok.items) == 1 + assert not tok.token + token = tok.items[0] + assert token.value == error + assert token.type == OPERAND + assert token.subtype == ERROR + + def test_parse_error_error(self, tokenizer): + tok = tokenizer.Tokenizer("#NotAnError") + tok.offset = 0 + del tok.items[:] + with pytest.raises(tokenizer.TokenizerError): + tok._parse_error() + + @pytest.mark.parametrize('formula', [' ' * i for i in range(1, 10)]) + def test_parse_whitespace(self, tokenizer, formula): + tok = tokenizer.Tokenizer(formula) + tok.offset = 0 + del tok.items[:] + assert tok._parse_whitespace() == len(formula) + assert len(tok.items) == 1 + token = tok.items[0] + assert token.value == " " + assert token.type == WSPACE + assert token.subtype == "" + assert not tok.token + + @pytest.mark.parametrize('formula, result, type_', [ + ('>=', '>=', OP_IN), + ('<=', '<=', OP_IN), + ('<>', '<>', OP_IN), + ('%', '%', OP_POST), + ('*', '*', OP_IN), + ('/', '/', OP_IN), + ('^', '^', OP_IN), + ('&', '&', OP_IN), + ('=', '=', OP_IN), + ('>', '>', OP_IN), + ('<', '<', OP_IN), + ('+', '+', OP_PRE), + ('-', '-', OP_PRE), + ('=<', '=', OP_IN), + ('><', '>', OP_IN), + ('<<', '<', OP_IN), + ('>>', '>', OP_IN), + ]) + def test_parse_operator(self, tokenizer, formula, result, type_): + tok = tokenizer.Tokenizer(formula) + tok.offset = 0 + del tok.items[:] + assert tok._parse_operator() == len(result) + assert len(tok.items) == 1 + assert not tok.token + token = tok.items[0] + assert token.value == result + assert token.type == type_ + assert token.subtype == '' + + @pytest.mark.parametrize('prefix, char, type_', [ + ('name', '(', FUNC), + ('', '(', PAREN), + ('', '{', ARRAY), + ]) + def test_parse_opener(self, tokenizer, prefix, char, type_): + tok = tokenizer.Tokenizer(prefix + char) + del tok.items[:] + tok.offset = len(prefix) + if prefix: + tok.token.append(prefix) + assert tok._parse_opener() == 1 + assert not tok.token + assert len(tok.items) == 1 + token = tok.items[0] + assert token.value == prefix + char + assert token.type == type_ + assert token.subtype == OPEN + assert len(tok.token_stack) == 1 + assert tok.token_stack[0] is token + + def test_parse_opener_error(self, tokenizer): + tok = tokenizer.Tokenizer('name{') + tok.offset = 4 + tok.token[:] = ('name',) + with pytest.raises(tokenizer.TokenizerError): + tok._parse_opener() + + @pytest.mark.parametrize('formula, offset, opener', [ + ('func(a)', 6, ('func(', FUNC, OPEN)), + ('(a)', 2, ('(', PAREN, OPEN)), + ('{a,b,c}', 6, ('{', ARRAY, OPEN)), + ]) + def test_parse_closer(self, tokenizer, formula, offset, opener): + tok = tokenizer.Tokenizer(formula) + del tok.items[:] + tok.offset = offset + tok.token_stack.append(tokenizer.Token(*opener)) + assert tok._parse_closer() == 1 + assert len(tok.items) == 1 + token = tok.items[0] + assert token.value == formula[offset] + assert token.type == opener[1] + assert token.subtype == CLOSE + + @pytest.mark.parametrize('formula, offset, opener', [ + ('func(a}', 6, ('func(', FUNC, OPEN)), + ('(a}', 2, ('(', PAREN, OPEN)), + ('{a,b,c)', 6, ('{', ARRAY, OPEN)), + ]) + def test_parse_closer_error(self, tokenizer, formula, offset, opener): + tok = tokenizer.Tokenizer(formula) + del tok.items[:] + tok.offset = offset + tok.token_stack.append(tokenizer.Token(*opener)) + with pytest.raises(tokenizer.TokenizerError): + tok._parse_closer() + + @pytest.mark.parametrize('formula, offset, opener, type_, subtype', [ + ("{a;b}", 2, ('{', ARRAY, OPEN), SEP, ROW), + ("{a,b}", 2, ('{', ARRAY, OPEN), SEP, ARG), + ("(a,b)", 2, ('(', PAREN, OPEN), OP_IN, ''), + ("FUNC(a,b)", 6, ('FUNC(', FUNC, OPEN), SEP, ARG), + ("$A$15:$B$20,$A$1:$B$5", 11, None, OP_IN, "") + ]) + def test_parse_separator(self, tokenizer, formula, offset, opener, type_, subtype): + tok = tokenizer.Tokenizer(formula) + del tok.items[:] + tok.offset = offset + if opener: + tok.token_stack.append(tokenizer.Token(*opener)) + assert tok._parse_separator() == 1 + assert len(tok.items) == 1 + token = tok.items[0] + assert token.value == formula[offset] + assert token.type == type_ + assert token.subtype == subtype + + @pytest.mark.parametrize('formula, offset, token, ret', [ + ('1.0E-5', 4, ['1', '.', '0', 'E'], True), + ('1.53321E+3', 8, ['1.53321', 'E'], True), + ('9.9E+12', 4, ['9.', '9E'], True), + ('3E+155', 2, ['9.', '9', 'E'], True), + ('12E+15', 3, ['12', 'E'], False), + ('0.1E-5', 4, ['0', '.1', 'E'], False), + ('0E+7', 2, ['0', 'E'], False), + ('12+', 2, ['1', '2'], False), + ('13-E+', 4, ['E'], False), + ('+', 0, [], False), + ('1.0e-5', 4, ['1', '.', '0', 'e'], True), + ('1.53321e+3', 8, ['1.53321', 'e'], True), + ('9.9e+12', 4, ['9.', '9e'], True), + ('3e+155', 2, ['9.', '9', 'e'], True), + ('12e+15', 3, ['12', 'e'], False), + ('0.1e-5', 4, ['0', '.1', 'e'], False), + ('0e+7', 2, ['0', 'e'], False), + ('12+', 2, ['1', '2'], False), + ('13-e+', 4, ['e'], False), + ('+', 0, [], False), + ]) + def test_check_scientific_notation(self, tokenizer, formula, offset, token, ret): + tok = tokenizer.Tokenizer(formula) + del tok.items[:] + tok.offset = offset + tok.token[:] = token + assert ret is tok.check_scientific_notation() + if ret: + assert offset + 1 == tok.offset + assert token == tok.token[:-1] + assert tok.token[-1] == formula[offset] + else: + assert offset == tok.offset + assert token == tok.token + + def test_assert_empty_token(self, tokenizer): + tok = tokenizer.Tokenizer("") + try: + tok.assert_empty_token() + except tokenizer.TokenizerError: + pytest.fail( + "assert_empty_token raised TokenizerError incorrectly") + tok.token.append("test") + with pytest.raises(tokenizer.TokenizerError): + tok.assert_empty_token() + + def test_save_token(self, tokenizer): + tok = tokenizer.Tokenizer("") + tok.save_token() + assert not tok.items + tok.token.append("test") + tok.save_token() + assert len(tok.items) == 1 + token = tok.items[0] + assert token.value == "test" + assert token.type == OPERAND + + @pytest.mark.parametrize('formula', [ + '=IF(A$3<40%,"",INDEX(Pipeline!B$4:B$138,#REF!))', + "='Summary slices'!$C$3", + '=-MAX(Pipeline!AA4:AA138)', + '=TEXT(-S7/1000,"$#,##0""M""")', + ("=IF(A$3<1.3E-8,\"\",IF(ISNA('External Ref'!K7)," + '"N/A",TEXT(K7*1E+12,"0")&"bp"'), + ('=+IF(A$3<>$B7,"",(MIN(IF({TRUE, FALSE;1,2},A6:B6,$S7))>=' + + 'LOWER_BOUND)*($BR6>$S72123))'), + '=(AW$4=$D7)+0%', + "Just text", + "123.456", + "31/12/1999", + "", + ]) + def test_render(self, tokenizer, formula): + tok = tokenizer.Tokenizer(formula) + assert tok.render() == formula + + +class TestToken(object): + + def test_init(self, tokenizer): + tokenizer.Token('val', 'type', 'subtype') + + @pytest.mark.parametrize('value, subtype', [ + ('"text"', TEXT), + ('#REF!', ERROR), + ('123', NUMBER), + ('0', NUMBER), + ('0.123', NUMBER), + ('.123', NUMBER), + ('1.234E5', NUMBER), + ('1E+5', NUMBER), + ('1.13E-55', NUMBER), + ('TRUE', LOGICAL), + ('FALSE', LOGICAL), + ('A1', RANGE), + ('ABCD12345', RANGE), + ("'Hello world'!R123C[-12]", RANGE), + ("[outside-workbook.xlsx]'A sheet name'!$AB$122", RANGE), + ]) + def test_make_operand(self, tokenizer, value, subtype): + tok = tokenizer.Token.make_operand(value) + assert tok.value == value + assert tok.type == OPERAND + assert tok.subtype == subtype + + @pytest.mark.parametrize('value, type_, subtype', [ + ('{', ARRAY, OPEN), + ('}', ARRAY, CLOSE), + ('(', PAREN, OPEN), + (')', PAREN, CLOSE), + ('FUNC(', FUNC, OPEN), + ]) + def test_make_subexp(self, tokenizer, value, type_, subtype): + tok = tokenizer.Token.make_subexp(value) + assert tok.value == value + assert tok.type == type_ + assert tok.subtype == subtype + + def test_make_subexp_func(self, tokenizer): + tok = tokenizer.Token.make_subexp(')', True) + assert tok.value == ')' + assert tok.type == FUNC + assert tok.subtype == CLOSE + + tok = tokenizer.Token.make_subexp('TEST(', True) + assert tok.value == 'TEST(' + assert tok.type == FUNC + assert tok.subtype == OPEN + + @pytest.mark.parametrize('token, close_val', [ + (('(', PAREN, OPEN), ')'), + (('{', ARRAY, OPEN), '}'), + (('FUNC(', FUNC, OPEN), ')'), + ]) + def test_get_closer(self, tokenizer, token, close_val): + closer = tokenizer.Token(*token).get_closer() + assert closer.value == close_val + assert closer.type == token[1] + assert closer.subtype == CLOSE + + def test_make_separator(self, tokenizer): + token = tokenizer.Token.make_separator(',') + assert token.value == ',' + assert token.type == SEP + assert token.subtype == ARG + + token = tokenizer.Token.make_separator(';') + assert token.value == ';' + assert token.type == SEP + assert token.subtype == ROW diff --git a/openpyxl/formula/tests/test_translate.py b/openpyxl/formula/tests/test_translate.py new file mode 100644 index 0000000..3a81bfc --- /dev/null +++ b/openpyxl/formula/tests/test_translate.py @@ -0,0 +1,235 @@ +from __future__ import absolute_import + +import pytest + +@pytest.fixture +def Translator(): + from .. import translate + return translate.Translator + +@pytest.fixture +def Tokenizer(): + from .. import tokenizer + return tokenizer.Tokenizer + +@pytest.fixture +def TranslatorError(): + from .. import translate + return translate.TranslatorError + +class TestTranslator(object): + + @pytest.mark.parametrize("origin, row, col", [ + ("A1", 1, 1), + ("AA1", 1, 27), + ("AA1001", 1001, 27), + ("AA1001001001", 1001001001, 27), + ("XFD111", 111, 16384), + ]) + def test_init(self, Translator, origin, row, col): + trans = Translator("=formula", origin) + assert trans.row == row + assert trans.col == col + assert trans.tokenizer.formula == "=formula" + + @pytest.mark.parametrize("formula", [ + '=IF(A$3<40%,"",INDEX(Pipeline!B$4:B$138,#REF!))', + "='Summary slices'!$C$3", + '=-MAX(Pipeline!AA4:AA138)', + '=TEXT(-S7/1000,"$#,##0""M""")', + "=IF(A$3<1.3E-8,\"\",IF(ISNA('External Ref'!K7)," + + '"N/A",TEXT(K7*1E+12,"0")&"bp"', + '=+IF(A$3<>$B7,"",(MIN(IF({TRUE, FALSE;1,2},A6:B6,$S7))>=' + + 'LOWER_BOUND)*($BR6>$S72123))', + '=$A:$A,$C:$C', + "Just text" + "123.456", + "31/12/1999", + "", + ]) + def test_get_tokens(self, Translator, Tokenizer, formula): + trans = Translator(formula, "A1") + tok = Tokenizer(formula) + for t1, t2 in zip(tok.items, trans.get_tokens()): + assert t1.value == t2.value + assert t1.type == t2.type + assert t1.subtype == t2.subtype + + @pytest.mark.parametrize("test_str, groups", [ + ["1:1", ("1", "1")], + ["1234:5678", ("1234", "5678")], + ["$1234:78910", ("$1234", "78910")], + ["$12321:$23432", ("$12321", "$23432")], + ["112233:$445566", ("112233", "$445566")], + ["A:A", None], + ["$ABC:AZZ", None], + ["$DEF:$FOV",None], + ["HA:$JA", None], + ["named1", None], + ["A15", None], + ["$AB303", None], + ["YY$101", None], + ["$ZZ$99", None], + ["B2:C3", None], + ["$ATV25:$BBC35", None], + ["WWW$918:WWW$919", None], + ["$III$305:$IIT$503", None], + ]) + def test_row_range_re(self, Translator, test_str, groups): + match = Translator.ROW_RANGE_RE.match(test_str) + if groups is None: + assert match is None + else: + assert match.groups() == groups + + @pytest.mark.parametrize("test_str, groups", [ + ["1:1", None], + ["1234:5678", None], + ["$1234:78910", None], + ["$12321:$23432", None], + ["112233:$445566", None], + ["A:A", ("A", "A")], + ["$ABC:AZZ", ("$ABC", "AZZ")], + ["$DEF:$FOV", ("$DEF", "$FOV")], + ["HA:$JA", ("HA", "$JA")], + ["named1", None], + ["A15", None], + ["$AB303", None], + ["YY$101", None], + ["$ZZ$99", None], + ["B2:C3", None], + ["$ATV25:$BBC35", None], + ["WWW$918:WWW$919", None], + ["$III$305:$IIT$503", None], + ]) + def test_col_range_re(self, Translator, test_str, groups): + match = Translator.COL_RANGE_RE.match(test_str) + if groups is None: + assert match is None + else: + assert match.groups() == groups + + @pytest.mark.parametrize("test_str, groups", [ + ["1:1", None], + ["1234:5678", None], + ["$1234:78910", None], + ["$12321:$23432", None], + ["112233:$445566", None], + ["A:A", None], + ["$ABC:AZZ", None], + ["$DEF:$FOV", None], + ["HA:$JA", None], + ["named1", None], + ["A15", ("A", "15")], + ["$AB303", ("$AB", "303")], + ["YY$101", ("YY", "$101")], + ["$ZZ$99", ("$ZZ", "$99")], + ["B2:C3", None], + ["$ATV25:$BBC35", None], + ["WWW$918:WWW$919", None], + ["$III$305:$IIT$503", None], + ]) + def test_cell_ref_re(self, Translator, test_str, groups): + match = Translator.CELL_REF_RE.match(test_str) + if groups is None: + assert match is None + else: + assert match.groups() == groups + + @pytest.mark.parametrize("test_str, rdelta, value", [ + ('1', 1, '2'), + ('$222333', 1, '$222333'), + ('1048576', -100, '1048476'), + ('$1012023', -100, '$1012023'), + ('101', 0, '101'), + ('$101', 0, '$101'), + ('12', -15, None), + ('$12', -15, '$12'), + ]) + def test_translate_row(self, Translator, TranslatorError, + test_str, rdelta, value): + if value is None: + with pytest.raises(TranslatorError): + Translator.translate_row(test_str, rdelta) + else: + assert Translator.translate_row(test_str, rdelta) == value + + @pytest.mark.parametrize("test_str, cdelta, value", [ + ('A', 1, 'B'), + ('XED', 26, 'XFD'), + ('$XED', 26, '$XED'), + ('WWW', -52, 'WUW'), + ('$WWW', -52, '$WWW'), + ('ABC', 0, 'ABC'), + ('$ABC', 0, '$ABC'), + ('AA', -100, None), + ('$AA', -100, '$AA'), + ]) + def test_translate_col(self, Translator, TranslatorError, + test_str, cdelta, value): + if value is None: + with pytest.raises(TranslatorError): + Translator.translate_col(test_str, cdelta) + else: + assert Translator.translate_col(test_str, cdelta) == value + + @pytest.mark.parametrize("test_str, value", [ + ('A$3', ("", 'A$3')), + ('Pipeline!B$4:B$138', ('Pipeline!', 'B$4:B$138')), + ("'Summary slices'!$C$3", ("'Summary slices'!", '$C$3')), + ("'Lions! Tigers! Bears!'!$OM$1", ("'Lions! Tigers! Bears!'!", '$OM$1')), + ('named_range_1', ("", 'named_range_1')), + ('Sheet-2!named_range_2', ("Sheet-2!", 'named_range_2')), + ]) + def test_strip_ws_name(self, Translator, test_str, value): + assert Translator.strip_ws_name(test_str) == value + + @pytest.mark.parametrize("test_str, rdelta, cdelta, value", [ + ("1:1", 2, 1, "3:3"), + ("$1234:78910", 1, 10, "$1234:78911"), + ("$12321:$23432", 3, 5, "$12321:$23432"), + ("112233:$445566", -3, -20, "112230:$445566"), + ("987:999", 0, 12, "987:999"), + ("1:5", -2, 3, None), + ("A:A", 0, 1, "B:B"), + ("$ABC:AZZ", 1, 3, "$ABC:BAC"), + ("$DEF:$FOV", 25, 25, "$DEF:$FOV"), + ("HA:$JA", -5, -15, "GL:$JA"), + ("named1", -33, 33, "named1"), + ("A15", -3, 4, "E12"), + ("$AB303", 3, 2, "$AB306"), + ("YY$101", 4, 2, "ZA$101"), + ("$ZZ$99", 5, 2, "$ZZ$99"), + ("B2:C3", 4, 3, "E6:F7"), + ("$ATV25:$BBC35", 5, 3, "$ATV30:$BBC40"), + ("WWW$918:WWW$919", 5, 4, "WXA$918:WXA$919"), + ("$III$305:$IIT$503", 25, 35, "$III$305:$IIT$503"), + ]) + def test_translate_range(self, Translator, TranslatorError, test_str, + rdelta, cdelta, value): + if value is None: + with pytest.raises(TranslatorError): + Translator.translate_range(test_str, rdelta, cdelta) + else: + assert value == Translator.translate_range(test_str, + rdelta, cdelta) + + @pytest.mark.parametrize("formula, origin, dest, result", [ + ('=IF(A$3<40%,"",INDEX(Pipeline!B$4:B$138,#REF!))', "A1", "B2", + '=IF(B$3<40%,"",INDEX(Pipeline!C$4:C$138,#REF!))'), + ("='Summary slices'!$C$3", "A1", "B2", "='Summary slices'!$C$3"), + ('=-MAX(Pipeline!AA4:AA138)', "A1", "B2", + '=-MAX(Pipeline!AB5:AB139)'), + ('=TEXT(-\'External Ref\'!K7/DENOMINATOR,"$#,##0""M""")', "A1", "B2", + '=TEXT(-\'External Ref\'!L8/DENOMINATOR,"$#,##0""M""")'), + ("=ROWS('Sh 1'!$1:3)+COLUMNS('Sh 2'!$A:C)", "A1", "B2", + "=ROWS('Sh 1'!$1:4)+COLUMNS('Sh 2'!$A:D)"), + ("Just text", "A1", "B2", "Just text"), + ("123.456", "A1", "B2", "123.456"), + ("31/12/1999", "A1", "B2", "31/12/1999"), + ("", "A1", "B2", ""), + ]) + def test_translate_formula(self, Translator, formula, + origin, dest, result): + trans = Translator(formula, origin) + assert trans.translate_formula(dest) == result diff --git a/openpyxl/formula/tokenizer.py b/openpyxl/formula/tokenizer.py new file mode 100644 index 0000000..4c33052 --- /dev/null +++ b/openpyxl/formula/tokenizer.py @@ -0,0 +1,437 @@ +""" +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 unicode string representing an Excel formula (in A1 notation) + into a sequence of `Token` objects. + + `formula`: The unicode 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(" +") + 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), + ('+-*/^&=><%', 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() + 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( + "Reached end of formula while parsing %s in %s" % + (subtype, 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] == '[' + right = self.formula.find(']', self.offset) + 1 + if right == 0: + raise TokenizerError( + "Encountered unmatched '[' in %s" % self.formula) + self.token.append(self.formula[self.offset: right]) + return right - self.offset + + 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() + 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(err)) + return len(err) + raise TokenizerError( + "Invalid error code at position %d in '%s'" % + (self.offset, 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] == ' ' + self.items.append(Token(' ', 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 charactes 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 = self.items[-1] + is_infix = ( + 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 charactes 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 charactes 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 charactes 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): + """ + Ensure that there's no token currently being parsed. + + If there are unconsumed token contents, it means we hit an unexpected + token transition. In this case, we raise a TokenizerError + + """ + if self.token: + raise TokenizerError( + "Unexpected character at position %d in '%s'" % + (self.offset, 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 initital 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/openpyxl/formula/translate.py b/openpyxl/formula/translate.py new file mode 100644 index 0000000..4385890 --- /dev/null +++ b/openpyxl/formula/translate.py @@ -0,0 +1,165 @@ +from __future__ import absolute_import + +""" +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_from_string, 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 unicode 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. + col, self.row = coordinate_from_string(origin) + self.col = column_index_from_string(col) + 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): + """ + Convert the formula into A1 notation. + + 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) + dcol, drow = coordinate_from_string(dest) + dcol = column_index_from_string(dcol) + row_delta = drow - self.row + col_delta = dcol - 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/openpyxl/packaging/__init__.py b/openpyxl/packaging/__init__.py new file mode 100644 index 0000000..c3085ee --- /dev/null +++ b/openpyxl/packaging/__init__.py @@ -0,0 +1,3 @@ +""" +Stuff related to Office OpenXML packaging: relationships, archive, content types. +""" diff --git a/openpyxl/packaging/core.py b/openpyxl/packaging/core.py new file mode 100644 index 0000000..24d6531 --- /dev/null +++ b/openpyxl/packaging/core.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import datetime + +from openpyxl.compat import safe_string, unicode +from openpyxl.utils.datetime import ( + CALENDAR_WINDOWS_1900, + to_ISO8601, + from_ISO8601, +) +from openpyxl.descriptors import ( + String, + DateTime, + Alias, + ) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.nested import NestedText +from openpyxl.xml.functions import (Element, tostring) +from openpyxl.xml.constants import ( + COREPROPS_NS, + DCORE_NS, + XSI_NS, + DCTERMS_NS, + DCTERMS_PREFIX +) + +class NestedDateTime(DateTime, NestedText): + + expected_type = datetime.datetime + + def to_tree(self, tagname=None, value=None, namespace=None): + namespace = getattr(self, "namespace", namespace) + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + el = Element(tagname) + if value is not None: + el.text = to_ISO8601(value) + return el + + +class QualifiedDateTime(NestedDateTime): + + """In certain situations Excel will complain if the additional type + attribute isn't set""" + + def to_tree(self, tagname=None, value=None, namespace=None): + el = super(QualifiedDateTime, self).to_tree(tagname, value, namespace) + el.set("{%s}type" % XSI_NS, "dcterms:W3CDTF") + return el + + +class DocumentProperties(Serialisable): + """High-level properties of the document. + Defined in ECMA-376 Par2 Annex D + """ + + tagname = "coreProperties" + namespace = COREPROPS_NS + + category = NestedText(expected_type=unicode, allow_none=True) + contentStatus = NestedText(expected_type=unicode, allow_none=True) + keywords = NestedText(expected_type=unicode, allow_none=True) + lastModifiedBy = NestedText(expected_type=unicode, allow_none=True) + lastPrinted = NestedDateTime(allow_none=True) + revision = NestedText(expected_type=unicode, allow_none=True) + version = NestedText(expected_type=unicode, allow_none=True) + last_modified_by = Alias("lastModifiedBy") + + # Dublin Core Properties + subject = NestedText(expected_type=unicode, allow_none=True, namespace=DCORE_NS) + title = NestedText(expected_type=unicode, allow_none=True, namespace=DCORE_NS) + creator = NestedText(expected_type=unicode, allow_none=True, namespace=DCORE_NS) + description = NestedText(expected_type=unicode, allow_none=True, namespace=DCORE_NS) + identifier = NestedText(expected_type=unicode, allow_none=True, namespace=DCORE_NS) + language = NestedText(expected_type=unicode,allow_none=True, namespace=DCORE_NS) + # Dubline Core Terms + created = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) + modified = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) + + __elements__ = ("creator","title", "description", "subject","identifier", + "language", "created", "modified", "lastModifiedBy", "category", + "contentStatus", "version", "revision", "keywords", "lastPrinted", + ) + + + def __init__(self, + category=None, + contentStatus=None, + keywords=None, + lastModifiedBy=None, + lastPrinted=None, + revision=None, + version=None, + created=datetime.datetime.now(), + creator="openpyxl", + description=None, + identifier=None, + language=None, + modified=datetime.datetime.now(), + subject=None, + title=None, + ): + self.contentStatus = contentStatus + self.lastPrinted = lastPrinted + self.revision = revision + self.version = version + self.creator = creator + self.lastModifiedBy = lastModifiedBy + self.modified = modified + self.created = created + self.title = title + self.subject = subject + self.description = description + self.identifier = identifier + self.language = language + self.keywords = keywords + self.category = category diff --git a/openpyxl/packaging/extended.py b/openpyxl/packaging/extended.py new file mode 100644 index 0000000..6fd7993 --- /dev/null +++ b/openpyxl/packaging/extended.py @@ -0,0 +1,138 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl import __version__ + +VERSION = ".".join(__version__.split(".")[:2]) + +from openpyxl.compat import unicode + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, +) +from openpyxl.descriptors.nested import ( + NestedText, +) + +from openpyxl.xml.constants import XPROPS_NS + + +class DigSigBlob(Serialisable): + + __elements__ = __attrs__ = () + + +class VectorLpstr(Serialisable): + + __elements__ = __attrs__ = () + + +class VectorVariant(Serialisable): + + __elements__ = __attrs__ = () + + +class ExtendedProperties(Serialisable): + + """ + See 22.2 + + Most of this is irrelevant + """ + + tagname = "Properties" + + Template = NestedText(expected_type=unicode, allow_none=True) + Manager = NestedText(expected_type=unicode, allow_none=True) + Company = NestedText(expected_type=unicode, allow_none=True) + Pages = NestedText(expected_type=int, allow_none=True) + Words = NestedText(expected_type=int,allow_none=True) + Characters = NestedText(expected_type=int, allow_none=True) + PresentationFormat = NestedText(expected_type=unicode, allow_none=True) + Lines = NestedText(expected_type=int, allow_none=True) + Paragraphs = NestedText(expected_type=int, allow_none=True) + Slides = NestedText(expected_type=int, allow_none=True) + Notes = NestedText(expected_type=int, allow_none=True) + TotalTime = NestedText(expected_type=int, allow_none=True) + HiddenSlides = NestedText(expected_type=int, allow_none=True) + MMClips = NestedText(expected_type=int, allow_none=True) + ScaleCrop = NestedText(expected_type=bool, allow_none=True) + HeadingPairs = Typed(expected_type=VectorVariant, allow_none=True) + TitlesOfParts = Typed(expected_type=VectorLpstr, allow_none=True) + LinksUpToDate = NestedText(expected_type=bool, allow_none=True) + CharactersWithSpaces = NestedText(expected_type=int, allow_none=True) + SharedDoc = NestedText(expected_type=bool, allow_none=True) + HyperlinkBase = NestedText(expected_type=unicode, allow_none=True) + HLinks = Typed(expected_type=VectorVariant, allow_none=True) + HyperlinksChanged = NestedText(expected_type=bool, allow_none=True) + DigSig = Typed(expected_type=DigSigBlob, allow_none=True) + Application = NestedText(expected_type=unicode, allow_none=True) + AppVersion = NestedText(expected_type=unicode, allow_none=True) + DocSecurity = NestedText(expected_type=int, allow_none=True) + + __elements__ = ('Application', 'AppVersion', 'DocSecurity', 'ScaleCrop', + 'LinksUpToDate', 'SharedDoc', 'HyperlinksChanged') + + def __init__(self, + Template=None, + Manager=None, + Company=None, + Pages=None, + Words=None, + Characters=None, + PresentationFormat=None, + Lines=None, + Paragraphs=None, + Slides=None, + Notes=None, + TotalTime=None, + HiddenSlides=None, + MMClips=None, + ScaleCrop=None, + HeadingPairs=None, + TitlesOfParts=None, + LinksUpToDate=None, + CharactersWithSpaces=None, + SharedDoc=None, + HyperlinkBase=None, + HLinks=None, + HyperlinksChanged=None, + DigSig=None, + Application="Microsoft Excel", + AppVersion=VERSION, + DocSecurity=None, + ): + self.Template = Template + self.Manager = Manager + self.Company = Company + self.Pages = Pages + self.Words = Words + self.Characters = Characters + self.PresentationFormat = PresentationFormat + self.Lines = Lines + self.Paragraphs = Paragraphs + self.Slides = Slides + self.Notes = Notes + self.TotalTime = TotalTime + self.HiddenSlides = HiddenSlides + self.MMClips = MMClips + self.ScaleCrop = ScaleCrop + self.HeadingPairs = None + self.TitlesOfParts = None + self.LinksUpToDate = LinksUpToDate + self.CharactersWithSpaces = CharactersWithSpaces + self.SharedDoc = SharedDoc + self.HyperlinkBase = HyperlinkBase + self.HLinks = None + self.HyperlinksChanged = HyperlinksChanged + self.DigSig = None + self.Application = Application + self.AppVersion = AppVersion + self.DocSecurity = DocSecurity + + + def to_tree(self): + tree = super(ExtendedProperties, self).to_tree() + tree.set("xmlns", XPROPS_NS) + return tree diff --git a/openpyxl/packaging/interface.py b/openpyxl/packaging/interface.py new file mode 100644 index 0000000..61b7d30 --- /dev/null +++ b/openpyxl/packaging/interface.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from abc import abstractproperty +from openpyxl.compat.abc import ABC + + +class ISerialisableFile(ABC): + + """ + Interface for Serialisable classes that represent files in the archive + """ + + + @abstractproperty + def id(self): + """ + Object id making it unique + """ + pass + + + @abstractproperty + def _path(self): + """ + File path in the archive + """ + pass + + + @abstractproperty + def _namespace(self): + """ + Qualified namespace when serialised + """ + pass + + + @abstractproperty + def _type(self): + """ + The content type for the manifest + """ + + + @abstractproperty + def _rel_type(self): + """ + The content type for relationships + """ + + + @abstractproperty + def _rel_id(self): + """ + Links object with parent + """ diff --git a/openpyxl/packaging/manifest.py b/openpyxl/packaging/manifest.py new file mode 100644 index 0000000..3cf0374 --- /dev/null +++ b/openpyxl/packaging/manifest.py @@ -0,0 +1,208 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +""" +File manifest +""" +import mimetypes +import os.path + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import String, Sequence +from openpyxl.xml.functions import fromstring +from openpyxl.xml.constants import ( + ARC_CORE, + ARC_CONTENT_TYPES, + ARC_WORKBOOK, + ARC_APP, + ARC_THEME, + ARC_STYLE, + ARC_SHARED_STRINGS, + EXTERNAL_LINK, + THEME_TYPE, + STYLES_TYPE, + XLSX, + XLSM, + XLTM, + XLTX, + WORKSHEET_TYPE, + COMMENTS_TYPE, + SHARED_STRINGS, + DRAWING_TYPE, + CHART_TYPE, + CHARTSHAPE_TYPE, + CHARTSHEET_TYPE, + CONTYPES_NS +) +from openpyxl.xml.functions import tostring + +# initialise mime-types +if not mimetypes.inited: + mimetypes.init() +mimetypes.add_type('application/xml', ".xml") +mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels") +mimetypes.add_type("application/vnd.ms-office.vbaProject", ".bin") +mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml") +mimetypes.add_type("image/x-emf", ".emf") + + +class FileExtension(Serialisable): + + tagname = "Default" + + Extension = String() + ContentType = String() + + def __init__(self, Extension, ContentType): + self.Extension = Extension + self.ContentType = ContentType + + +class Override(Serialisable): + + tagname = "Override" + + PartName = String() + ContentType = String() + + def __init__(self, PartName, ContentType): + self.PartName = PartName + self.ContentType = ContentType + + +DEFAULT_TYPES = [ + FileExtension("rels", "application/vnd.openxmlformats-package.relationships+xml"), + FileExtension("xml", "application/xml"), +] + +DEFAULT_OVERRIDE = [ + Override("/" + ARC_SHARED_STRINGS, SHARED_STRINGS), # Shared strings + Override("/" + ARC_STYLE, STYLES_TYPE), # Styles + Override("/" + ARC_THEME, THEME_TYPE), # Theme + Override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"), + Override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml") +] + + +class Manifest(Serialisable): + + tagname = "Types" + + Default = Sequence(expected_type=FileExtension, unique=True) + Override = Sequence(expected_type=Override, unique=True) + path = "[Content_Types].xml" + + __elements__ = ("Default", "Override") + + def __init__(self, + Default=(), + Override=(), + ): + if not Default: + Default = DEFAULT_TYPES + self.Default = Default + if not Override: + Override = DEFAULT_OVERRIDE + self.Override = Override + + + @property + def filenames(self): + return [part.PartName for part in self.Override] + + + @property + def extensions(self): + """ + Map content types to file extensions + Skip parts without extensions + """ + exts = set([os.path.splitext(part.PartName)[-1] for part in self.Override]) + return [(ext[1:], mimetypes.types_map[ext]) for ext in sorted(exts) if ext] + + + def to_tree(self): + """ + Custom serialisation method to allow setting a default namespace + """ + defaults = [t.Extension for t in self.Default] + for ext, mime in self.extensions: + if ext not in defaults: + mime = FileExtension(ext, mime) + self.Default.append(mime) + tree = super(Manifest, self).to_tree() + tree.set("xmlns", CONTYPES_NS) + return tree + + + def __contains__(self, content_type): + """ + Check whether a particular content type is contained + """ + for t in self.Override: + if t.ContentType == content_type: + return True + + + def find(self, content_type): + """ + Find specific content-type + """ + try: + return next(self.findall(content_type)) + except StopIteration: + return + + + def findall(self, content_type): + """ + Find all elements of a specific content-type + """ + for t in self.Override: + if t.ContentType == content_type: + yield t + + + def append(self, obj): + """ + Add content object to the package manifest + # needs a contract... + """ + ct = Override(PartName=obj.path, ContentType=obj.mime_type) + self.Override.append(ct) + + + def _write(self, archive, workbook): + """ + Write manifest to the archive + """ + self.append(workbook) + self._write_vba(workbook) + self._register_mimetypes(filenames=archive.namelist()) + archive.writestr(self.path, tostring(self.to_tree())) + + + def _register_mimetypes(self, filenames): + """ + Make sure that the mime type for all file extensions is registered + """ + for fn in filenames: + ext = os.path.splitext(fn)[-1] + if not ext: + continue + mime = mimetypes.types_map[ext] + fe = FileExtension(ext[1:], mime) + self.Default.append(fe) + + + def _write_vba(self, workbook): + """ + Add content types from cached workbook when keeping VBA + """ + if workbook.vba_archive: + node = fromstring(workbook.vba_archive.read(ARC_CONTENT_TYPES)) + mf = Manifest.from_tree(node) + filenames = self.filenames + for override in mf.Override: + if override.PartName not in filenames: + self.Override.append(override) diff --git a/openpyxl/packaging/relationship.py b/openpyxl/packaging/relationship.py new file mode 100644 index 0000000..e8c9518 --- /dev/null +++ b/openpyxl/packaging/relationship.py @@ -0,0 +1,166 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import posixpath + +from openpyxl.descriptors import ( + String, + Set, + NoneSet, + Alias, + Sequence, +) +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.xml.constants import REL_NS, PKG_REL_NS +from openpyxl.xml.functions import ( + Element, + fromstring, + tostring +) + + +class Relationship(Serialisable): + """Represents many kinds of relationships.""" + + tagname = "Relationship" + + Type = String() + Target = String() + target = Alias("Target") + TargetMode = String(allow_none=True) + Id = String(allow_none=True) + id = Alias("Id") + + + def __init__(self, + Id=None, + Type=None, + type=None, + Target=None, + TargetMode=None + ): + """ + `type` can be used as a shorthand with the default relationships namespace + otherwise the `Type` must be a fully qualified URL + """ + if type is not None: + Type = "{0}/{1}".format(REL_NS, type) + self.Type = Type + self.Target = Target + self.TargetMode = TargetMode + self.Id = Id + + +class RelationshipList(Serialisable): + + tagname = "Relationships" + + Relationship = Sequence(expected_type=Relationship) + + + def __init__(self, Relationship=()): + self.Relationship = Relationship + + + def append(self, value): + values = self.Relationship[:] + values.append(value) + if not value.Id: + value.Id = "rId{0}".format((len(values))) + self.Relationship = values + + + def __len__(self): + return len(self.Relationship) + + + def __bool__(self): + return bool(self.Relationship) + + + def find(self, content_type): + """ + Find relationships by content-type + NB. these content-types namespaced objects and different to the MIME-types + in the package manifest :-( + """ + for r in self.Relationship: + if r.Type == content_type: + yield r + + + def __getitem__(self, key): + for r in self.Relationship: + if r.Id == key: + return r + raise KeyError("Unknown relationship: {0}".format(key)) + + + def to_tree(self): + tree = Element("Relationships", xmlns=PKG_REL_NS) + for idx, rel in enumerate(self.Relationship, 1): + if not rel.Id: + rel.Id = "rId{0}".format(idx) + tree.append(rel.to_tree()) + + return tree + + +def get_rels_path(path): + """ + Convert relative path to absolutes that can be loaded from a zip + archive. + The path to be passed in is that of containing object (workbook, + worksheet, etc.) + """ + folder, obj = posixpath.split(path) + filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj)) + return filename + + +def get_dependents(archive, filename): + """ + Normalise dependency file paths to absolute ones + + Relative paths are relative to parent object + """ + src = archive.read(filename) + node = fromstring(src) + rels = RelationshipList.from_tree(node) + folder = posixpath.dirname(filename) + parent = posixpath.split(folder)[0] + for r in rels.Relationship: + if r.TargetMode == "External": + continue + elif r.target.startswith("/"): + r.target = r.target[1:] + else: + pth = posixpath.join(parent, r.target) + r.target = posixpath.normpath(pth) + return rels + + +def get_rel(archive, deps, id=None, cls=None): + """ + Get related object based on id or rel_type + """ + if not any([id, cls]): + raise ValueError("Either the id or the content type are required") + if id is not None: + rel = deps[id] + else: + rel = next(deps.find(cls.rel_type)) + + path = rel.target + src = archive.read(path) + tree = fromstring(src) + obj = cls.from_tree(tree) + + rels_path = get_rels_path(path) + try: + obj.deps = get_dependents(archive, rels_path) + except KeyError: + obj.deps = [] + + return obj diff --git a/openpyxl/packaging/tests/__init__.py b/openpyxl/packaging/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/packaging/tests/conftest.py b/openpyxl/packaging/tests/conftest.py new file mode 100644 index 0000000..fe55808 --- /dev/null +++ b/openpyxl/packaging/tests/conftest.py @@ -0,0 +1,10 @@ +import pytest + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) diff --git a/openpyxl/packaging/tests/data/bug137.xlsx b/openpyxl/packaging/tests/data/bug137.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cde2874afe395f6628bd3d332707a013e38d15c9 GIT binary patch literal 10004 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 literal 0 HcmV?d00001 diff --git a/openpyxl/packaging/tests/data/core.xml b/openpyxl/packaging/tests/data/core.xml new file mode 100644 index 0000000..709502a --- /dev/null +++ b/openpyxl/packaging/tests/data/core.xml @@ -0,0 +1,21 @@ + + + TEST_USER + The title + The description + The subject + The identifier + The language + 2010-04-01T20:30:00Z + 2010-04-05T14:05:30Z + SOMEBODY + The category + The status + 2.5 + 0 + one, two, three + 2014-10-14T10:30:00Z + diff --git a/openpyxl/packaging/tests/data/hyperlink.xlsx b/openpyxl/packaging/tests/data/hyperlink.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e2fae8cec93a0099e731ec7d7ae63a9987fbe824 GIT binary patch literal 5181 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 G;Qs)e9=gZ? literal 0 HcmV?d00001 diff --git a/openpyxl/packaging/tests/data/manifest.xml b/openpyxl/packaging/tests/data/manifest.xml new file mode 100644 index 0000000..ebbfd3e --- /dev/null +++ b/openpyxl/packaging/tests/data/manifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/openpyxl/packaging/tests/data/pivot.xlsx b/openpyxl/packaging/tests/data/pivot.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..db6a30f866d155023932a64afab6db9a46226c20 GIT binary patch literal 14504 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 + + + + + + + + + + diff --git a/openpyxl/packaging/tests/data/workbook_links.xml b/openpyxl/packaging/tests/data/workbook_links.xml new file mode 100644 index 0000000..33cb9b7 --- /dev/null +++ b/openpyxl/packaging/tests/data/workbook_links.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/openpyxl/packaging/tests/data/workbook_missing_id.xml b/openpyxl/packaging/tests/data/workbook_missing_id.xml new file mode 100644 index 0000000..06a0be7 --- /dev/null +++ b/openpyxl/packaging/tests/data/workbook_missing_id.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/openpyxl/packaging/tests/data/workbook_security.xlsx b/openpyxl/packaging/tests/data/workbook_security.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e38758a8dc3da50f7d4205e73e6810ee14849aef GIT binary patch literal 21086 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 + TEST_USER + The title + The description + The subject + The identifier + The language + 2010-04-01T20:30:00Z + 2010-04-05T14:05:30Z + SOMEBODY + The category + The status + 2.5 + 0 + one, two, three + 2014-10-14T10:30:00Z + + """ + xml = tostring(SampleProperties.to_tree()) + diff = compare_xml(xml, expected) + assert diff is None, diff + + +def test_from_tree(datadir, SampleProperties): + datadir.chdir() + with open("core.xml") as src: + content = src.read() + + content = fromstring(content) + props = SampleProperties.from_tree(content) + assert props == SampleProperties + + +def test_qualified_datetime(): + from ..core import QualifiedDateTime + dt = QualifiedDateTime() + tree = dt.to_tree("time", datetime.datetime(2015, 7, 20, 12, 30)) + xml = tostring(tree) + expected = """ + """ + + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/packaging/tests/test_extended.py b/openpyxl/packaging/tests/test_extended.py new file mode 100644 index 0000000..47d5394 --- /dev/null +++ b/openpyxl/packaging/tests/test_extended.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def ExtendedProperties(): + from ..extended import ExtendedProperties + return ExtendedProperties + + +class TestExtendedProperties: + + def test_ctor(self, ExtendedProperties): + from ..extended import VERSION + props = ExtendedProperties() + xml = tostring(props.to_tree()) + expected = """ + + Microsoft Excel + {0} + + """.format(VERSION) + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ExtendedProperties): + src = """ + + Microsoft Macintosh Excel + 0 + false + + + + Worksheets + + + 1 + + + + + + Sheet + + + + false + false + false + 14.0300 + + """ + node = fromstring(src) + props = ExtendedProperties.from_tree(node) + assert props == ExtendedProperties( + Application="Microsoft Macintosh Excel", + DocSecurity=0, + ScaleCrop=True, + LinksUpToDate=True, + SharedDoc=True, + HyperlinksChanged=True, + AppVersion='14.0300' + ) diff --git a/openpyxl/packaging/tests/test_interface.py b/openpyxl/packaging/tests/test_interface.py new file mode 100644 index 0000000..326d5b6 --- /dev/null +++ b/openpyxl/packaging/tests/test_interface.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +def test_interface(): + from ..interface import ISerialisableFile + + class DummyFile(ISerialisableFile): + + pass + + with pytest.raises(TypeError): + + df = DummyFile() diff --git a/openpyxl/packaging/tests/test_manifest.py b/openpyxl/packaging/tests/test_manifest.py new file mode 100644 index 0000000..634e1b5 --- /dev/null +++ b/openpyxl/packaging/tests/test_manifest.py @@ -0,0 +1,289 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest +from io import BytesIO +from zipfile import ZipFile + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +from ..manifest import WORKSHEET_TYPE + +@pytest.fixture +def FileExtension(): + from ..manifest import FileExtension + return FileExtension + + +class TestFileExtension: + + def test_ctor(self, FileExtension): + ext = FileExtension( + ContentType="application/xml", + Extension="xml" + ) + xml = tostring(ext.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, FileExtension): + src = """ + + """ + node = fromstring(src) + ext = FileExtension.from_tree(node) + assert ext == FileExtension(ContentType="application/xml", Extension="xml") + + +@pytest.fixture +def Override(): + from ..manifest import Override + return Override + + +class TestOverride: + + def test_ctor(self, Override): + override = Override( + ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", + PartName="/xl/workbook.xml" + ) + xml = tostring(override.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Override): + src = """ + + """ + node = fromstring(src) + override = Override.from_tree(node) + assert override == Override( + ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", + PartName="/xl/workbook.xml" + ) + + +@pytest.fixture +def Manifest(): + from ..manifest import Manifest + return Manifest + + +class TestManifest: + + def test_ctor(self, Manifest): + manifest = Manifest() + xml = tostring(manifest.to_tree()) + expected = """ + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, datadir, Manifest): + datadir.chdir() + with open("manifest.xml") as src: + node = fromstring(src.read()) + manifest = Manifest.from_tree(node) + assert len(manifest.Default) == 2 + defaults = [ + ("application/xml", 'xml'), + ("application/vnd.openxmlformats-package.relationships+xml", 'rels'), + ] + assert [(ct.ContentType, ct.Extension) for ct in manifest.Default] == defaults + + overrides = [ + ('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml', + '/xl/workbook.xml'), + ('application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml', + '/xl/worksheets/sheet1.xml'), + ('application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml', + '/xl/chartsheets/sheet1.xml'), + ('application/vnd.openxmlformats-officedocument.theme+xml', + '/xl/theme/theme1.xml'), + ('application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml', + '/xl/styles.xml'), + ('application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml', + '/xl/sharedStrings.xml'), + ('application/vnd.openxmlformats-officedocument.drawing+xml', + '/xl/drawings/drawing1.xml'), + ('application/vnd.openxmlformats-officedocument.drawingml.chart+xml', + '/xl/charts/chart1.xml'), + ('application/vnd.openxmlformats-package.core-properties+xml', + '/docProps/core.xml'), + ('application/vnd.openxmlformats-officedocument.extended-properties+xml', + '/docProps/app.xml') + ] + assert [(ct.ContentType, ct.PartName) for ct in manifest.Override] == overrides + + + def test_filenames(self, datadir, Manifest): + datadir.chdir() + with open("manifest.xml") as src: + node = fromstring(src.read()) + manifest = Manifest.from_tree(node) + assert manifest.filenames == [ + '/xl/workbook.xml', + '/xl/worksheets/sheet1.xml', + '/xl/chartsheets/sheet1.xml', + '/xl/theme/theme1.xml', + '/xl/styles.xml', + '/xl/sharedStrings.xml', + '/xl/drawings/drawing1.xml', + '/xl/charts/chart1.xml', + '/docProps/core.xml', + '/docProps/app.xml', + ] + + + def test_exts(self, datadir, Manifest): + datadir.chdir() + with open("manifest.xml") as src: + node = fromstring(src.read()) + manifest = Manifest.from_tree(node) + assert manifest.extensions == [ + ('xml', 'application/xml'), + ] + + def test_no_dupe_overrides(self, Manifest): + manifest = Manifest() + assert len(manifest.Override) == 5 + manifest.Override.append("a") + manifest.Override.append("a") + assert len(manifest.Override) == 6 + + + def test_no_dupe_types(self, Manifest): + manifest = Manifest() + assert len(manifest.Default) == 2 + manifest.Default.append("a") + manifest.Default.append("a") + assert len(manifest.Default) == 3 + + + def test_append(self, Manifest): + from openpyxl import Workbook + wb = Workbook() + ws = wb.active + manifest = Manifest() + manifest.append(ws) + assert len(manifest.Override) == 6 + + + def test_write(self, Manifest): + mf = Manifest() + from openpyxl import Workbook + wb = Workbook() + + archive = ZipFile(BytesIO(), "w") + mf._write(archive, wb) + assert "/xl/workbook.xml" in mf.filenames + + + @pytest.mark.parametrize("file, registration", + [ + ('xl/media/image1.png', + ''), + ('xl/drawings/commentsDrawing.vml', + ''), + ] + ) + def test_media(self, Manifest, file, registration): + from openpyxl import Workbook + wb = Workbook() + + manifest = Manifest() + manifest._register_mimetypes([file]) + xml = tostring(manifest.Default[-1].to_tree()) + diff = compare_xml(xml, registration) + assert diff is None, diff + + + def test_vba(self, datadir, Manifest): + datadir.chdir() + from openpyxl import load_workbook + wb = load_workbook('sample.xlsm', keep_vba=True) + + manifest = Manifest() + manifest._write_vba(wb) + partnames = set([t.PartName for t in manifest.Override]) + expected = set([ + '/xl/workbook.xml', + '/xl/worksheets/sheet1.xml', + '/xl/worksheets/sheet2.xml', + '/xl/worksheets/sheet3.xml', + '/xl/theme/theme1.xml', + '/xl/styles.xml', + '/docProps/core.xml', + '/docProps/app.xml', + '/xl/sharedStrings.xml' + ]) + assert partnames == expected + + + def test_no_defaults(self, Manifest): + """ + LibreOffice does not use the Default element + """ + xml = """ + + + + """ + + node = fromstring(xml) + manifest = Manifest.from_tree(node) + exts = manifest.extensions + + assert exts == [] + + + def test_find(self, datadir, Manifest): + datadir.chdir() + with open("manifest.xml", "rb") as src: + xml = src.read() + tree = fromstring(xml) + manifest = Manifest.from_tree(tree) + ws = manifest.find(WORKSHEET_TYPE) + assert ws.PartName == "/xl/worksheets/sheet1.xml" + + + def test_find_none(self, Manifest): + manifest = Manifest() + assert manifest.find(WORKSHEET_TYPE) is None + + + def test_findall(self, datadir, Manifest): + datadir.chdir() + with open("manifest.xml", "rb") as src: + xml = src.read() + tree = fromstring(xml) + manifest = Manifest.from_tree(tree) + sheets = manifest.findall(WORKSHEET_TYPE) + assert len(list(sheets)) == 1 diff --git a/openpyxl/packaging/tests/test_relationship.py b/openpyxl/packaging/tests/test_relationship.py new file mode 100644 index 0000000..39d6aa6 --- /dev/null +++ b/openpyxl/packaging/tests/test_relationship.py @@ -0,0 +1,111 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from zipfile import ZipFile + +import pytest +from openpyxl.tests.helper import compare_xml +from openpyxl.xml.functions import tostring, fromstring + + +@pytest.fixture +def Relationship(): + from ..relationship import Relationship + return Relationship + + +def test_ctor(Relationship): + rel = Relationship(type="drawing", Target="drawings.xml", + TargetMode="external", Id="4") + + assert dict(rel) == {'Id': '4', 'Target': 'drawings.xml', 'TargetMode': + 'external', 'Type': + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing'} + + expected = """ + """ + xml = tostring(rel.to_tree()) + + diff = compare_xml(xml, expected) + assert diff is None, diff + + +def test_sequence(Relationship): + from ..relationship import RelationshipList + rels = RelationshipList() + rels.append(Relationship(type="drawing", Target="drawings.xml", + TargetMode="external", Id="")) + rels.append(Relationship(type="chart", Target="chart1.xml", + TargetMode="", Id="chart")) + xml = tostring(rels.to_tree()) + expected = """ + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + +def test_read(): + from ..relationship import RelationshipList + xml = """ + + + + + + + + """ + node = fromstring(xml) + rels = RelationshipList.from_tree(node) + assert len(rels) == 5 + + +@pytest.mark.parametrize("filename, expected", + [ + ("xl/_rels/workbook.xml.rels", + [ + 'xl/theme/theme1.xml', + 'xl/worksheets/sheet1.xml', + 'xl/chartsheets/sheet1.xml', + 'xl/sharedStrings.xml', + 'xl/styles.xml', + ] + ), + ("xl/chartsheets/_rels/sheet1.xml.rels", + [ + 'xl/drawings/drawing1.xml', + ] + ), + ] +) +def test_get_dependents(datadir, filename, expected): + datadir.chdir() + archive = ZipFile("bug137.xlsx") + + from ..relationship import get_dependents + rels = get_dependents(archive, filename) + assert [r.Target for r in rels.Relationship] == expected + + +def test_get_external_link(datadir): + datadir.chdir() + archive = ZipFile("hyperlink.xlsx") + + from ..relationship import get_dependents + rels = get_dependents(archive, "xl/worksheets/_rels/sheet1.xml.rels") + + assert [r.Target for r in rels.Relationship] == ["http://www.readthedocs.org"] diff --git a/openpyxl/packaging/tests/test_workbook.py b/openpyxl/packaging/tests/test_workbook.py new file mode 100644 index 0000000..1e00e91 --- /dev/null +++ b/openpyxl/packaging/tests/test_workbook.py @@ -0,0 +1,161 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + +from io import BytesIO +from openpyxl.xml.functions import fromstring +from zipfile import ZipFile + +import pytest + + +CHARTSHEET_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet" +WORKSHEET_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" + +from openpyxl.utils.datetime import ( + CALENDAR_MAC_1904, + CALENDAR_WINDOWS_1900, +) +from openpyxl.workbook.protection import WorkbookProtection +from openpyxl.xml.constants import ( + ARC_WORKBOOK, + ARC_WORKBOOK_RELS, +) + + +@pytest.fixture +def WorkbookParser(): + from .. workbook import WorkbookParser + return WorkbookParser + + +class TestWorkbookParser: + + def test_ctor(self, datadir, WorkbookParser): + datadir.chdir() + archive = ZipFile("bug137.xlsx") + + parser = WorkbookParser(archive, ARC_WORKBOOK) + + assert parser.archive is archive + assert parser.sheets == [] + + + def test_parse_calendar(self, datadir, WorkbookParser): + datadir.chdir() + + archive = ZipFile(BytesIO(), "a") + with open("workbook_1904.xml") as src: + archive.writestr(ARC_WORKBOOK, src.read()) + archive.writestr(ARC_WORKBOOK_RELS, b"") + + parser = WorkbookParser(archive, ARC_WORKBOOK) + assert parser.wb.excel_base_date == CALENDAR_WINDOWS_1900 + + parser.parse() + assert parser.wb.code_name is None + assert parser.wb.excel_base_date == CALENDAR_MAC_1904 + + + def test_find_sheets(self, datadir, WorkbookParser): + datadir.chdir() + archive = ZipFile("bug137.xlsx") + parser = WorkbookParser(archive, ARC_WORKBOOK) + + parser.parse() + + output = [] + + for sheet, rel in parser.find_sheets(): + output.append([sheet.name, sheet.state, rel.Target, rel.Type]) + + assert output == [ + ['Chart1', 'visible', 'xl/chartsheets/sheet1.xml', CHARTSHEET_REL], + ['Sheet1', 'visible', 'xl/worksheets/sheet1.xml', WORKSHEET_REL], + ] + + + def test_broken_sheet_ref(self, datadir, recwarn, WorkbookParser): + from openpyxl.workbook.parser import WorkbookPackage + datadir.chdir() + with open("workbook_missing_id.xml", "rb") as src: + xml = src.read() + node = fromstring(xml) + wb = WorkbookPackage.from_tree(node) + + archive = ZipFile(BytesIO(), "a") + archive.write("workbook_links.xml", ARC_WORKBOOK) + archive.writestr(ARC_WORKBOOK_RELS, b"") + + parser = WorkbookParser(archive, ARC_WORKBOOK) + parser.sheets = wb.sheets + sheets = parser.find_sheets() + list(sheets) + w = recwarn.pop() + assert issubclass(w.category, UserWarning) + + + def test_assign_names(self, datadir, WorkbookParser): + datadir.chdir() + archive = ZipFile("print_settings.xlsx") + parser = WorkbookParser(archive, ARC_WORKBOOK) + parser.parse() + + wb = parser.wb + assert len(wb.defined_names.definedName) == 4 + + parser.assign_names() + assert len(wb.defined_names.definedName) == 2 + ws = wb['Sheet'] + assert ws.print_title_rows == "$1:$1" + assert ws.print_titles == "$1:$1" + assert ws.print_area == ['$A$1:$D$5', '$B$9:$F$14'] + + + def test_no_links(self, datadir, WorkbookParser): + datadir.chdir() + + archive = ZipFile(BytesIO(), "a") + with open("workbook_links.xml") as src: + archive.writestr(ARC_WORKBOOK, src.read()) + archive.writestr(ARC_WORKBOOK_RELS, b"") + + parser = WorkbookParser(archive, ARC_WORKBOOK) + assert parser.wb.keep_links is True + + with pytest.raises(KeyError): + parser.parse() + + parser.wb._keep_links = False + parser.parse() + assert parser.wb._external_links == [] + + + def test_pivot_caches(self, datadir, WorkbookParser): + datadir.chdir() + + archive = ZipFile("pivot.xlsx") + parser = WorkbookParser(archive, ARC_WORKBOOK) + parser.parse() + assert list(parser.pivot_caches) == [68] + + + def test_book_views(self, datadir, WorkbookParser): + datadir.chdir() + archive = ZipFile("bug137.xlsx") + + parser = WorkbookParser(archive, ARC_WORKBOOK) + parser.parse() + assert parser.wb.views[0].activeTab == 1 + + + def test_workbook_security(self, datadir, WorkbookParser): + expected_protection = WorkbookProtection() + expected_protection.workbookPassword = 'test' + expected_protection.lockStructure = True + datadir.chdir() + archive = ZipFile("workbook_security.xlsx") + parser = WorkbookParser(archive, ARC_WORKBOOK) + + parser.parse() + + assert parser.wb.security == expected_protection diff --git a/openpyxl/packaging/workbook.py b/openpyxl/packaging/workbook.py new file mode 100644 index 0000000..40fa0ff --- /dev/null +++ b/openpyxl/packaging/workbook.py @@ -0,0 +1,129 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + +""" +OO-based reader +""" + +import posixpath +from warnings import warn + +from openpyxl.xml.functions import fromstring + +from openpyxl.packaging.relationship import ( + get_dependents, + get_rels_path, + get_rel, +) +from openpyxl.packaging.manifest import Manifest +from openpyxl.workbook.parser import WorkbookPackage +from openpyxl.workbook.workbook import Workbook +from openpyxl.workbook.defined_name import ( + _unpack_print_area, + _unpack_print_titles, +) +from openpyxl.workbook.external_link.external import read_external_link +from openpyxl.pivot.cache import CacheDefinition +from openpyxl.pivot.record import RecordList + +from openpyxl.utils.datetime import CALENDAR_MAC_1904 + + +class WorkbookParser: + + _rels = None + + def __init__(self, archive, workbook_part_name): + self.archive = archive + self.workbook_part_name = workbook_part_name + self.wb = Workbook() + self.sheets = [] + + + @property + def rels(self): + if self._rels is None: + self._rels = get_dependents(self.archive, get_rels_path(self.workbook_part_name)) + return self._rels + + + def parse(self): + src = self.archive.read(self.workbook_part_name) + node = fromstring(src) + package = WorkbookPackage.from_tree(node) + if package.properties.date1904: + self.wb.excel_base_date = CALENDAR_MAC_1904 + + self.wb.code_name = package.properties.codeName + self.wb.active = package.active + self.wb.views = package.bookViews + self.sheets = package.sheets + self.wb.calculation = package.calcPr + self.caches = package.pivotCaches + + #external links contain cached worksheets and can be very big + if not self.wb.keep_links: + package.externalReferences = [] + + for ext_ref in package.externalReferences: + rel = self.rels[ext_ref.id] + self.wb._external_links.append( + read_external_link(self.archive, rel.Target) + ) + + if package.definedNames: + package.definedNames._cleanup() + self.wb.defined_names = package.definedNames + + self.wb.security = package.workbookProtection + + + def find_sheets(self): + """ + Find all sheets in the workbook and return the link to the source file. + + Older XLSM files sometimes contain invalid sheet elements. + Warn user when these are removed. + """ + + for sheet in self.sheets: + if not sheet.id: + msg = "File contains an invalid specification for {0}. This will be removed".format(sheet.name) + warn(msg) + continue + yield sheet, self.rels[sheet.id] + + + def assign_names(self): + """ + Bind reserved names to parsed worksheets + """ + defns = [] + + for defn in self.wb.defined_names.definedName: + reserved = defn.is_reserved + if reserved in ("Print_Titles", "Print_Area"): + sheet = self.wb._sheets[defn.localSheetId] + if reserved == "Print_Titles": + rows, cols = _unpack_print_titles(defn) + sheet.print_title_rows = rows + sheet.print_title_cols = cols + elif reserved == "Print_Area": + sheet.print_area = _unpack_print_area(defn) + else: + defns.append(defn) + self.wb.defined_names.definedName = defns + + + @property + def pivot_caches(self): + """ + Get PivotCache objects + """ + d = {} + for c in self.caches: + cache = get_rel(self.archive, self.rels, id=c.id, cls=CacheDefinition) + records = get_rel(self.archive, cache.deps, cache.id, RecordList) + cache.records = records + d[c.cacheId] = cache + return d diff --git a/openpyxl/pivot/__init__.py b/openpyxl/pivot/__init__.py new file mode 100644 index 0000000..ad1d21d --- /dev/null +++ b/openpyxl/pivot/__init__.py @@ -0,0 +1,2 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl diff --git a/openpyxl/pivot/cache.py b/openpyxl/pivot/cache.py new file mode 100644 index 0000000..e4a07c1 --- /dev/null +++ b/openpyxl/pivot/cache.py @@ -0,0 +1,1112 @@ +# Copyright (c) 2010-2018 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__ = ('extLst',) + + 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" + + format = String(allow_none=True) + + def __init__(self, + culture=None, + format=None, + ): + self.culture = culture + self.format = format + + +class ServerFormats(Serialisable): + + count = Integer() + serverFormat = Typed(expected_type=ServerFormat, allow_none=True) + + __elements__ = ('serverFormat',) + + def __init__(self, + count=None, + serverFormat=None, + ): + self.count = count + self.serverFormat = 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=ServerFormats, 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() + allUniqueName = String() + allCaption = String() + dimensionUniqueName = String() + displayFolder = String() + measureGroup = String() + 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=None, + caption=None, + measure=None, + set=None, + parentSet=None, + iconSet=None, + 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/openpyxl/pivot/fields.py b/openpyxl/pivot/fields.py new file mode 100644 index 0000000..bde1c76 --- /dev/null +++ b/openpyxl/pivot/fields.py @@ -0,0 +1,322 @@ +# Copyright (c) 2010-2018 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/openpyxl/pivot/record.py b/openpyxl/pivot/record.py new file mode 100644 index 0000000..f03e642 --- /dev/null +++ b/openpyxl/pivot/record.py @@ -0,0 +1,111 @@ +# Copyright (c) 2010-2018 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/openpyxl/pivot/table.py b/openpyxl/pivot/table.py new file mode 100644 index 0000000..04ca34d --- /dev/null +++ b/openpyxl/pivot/table.py @@ -0,0 +1,1174 @@ +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 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): + + 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() + stringValue1 = String() + stringValue2 = String() + 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 + self.extLst = extLst + + +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() + pivotArea = Typed(expected_type=PivotArea, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotArea',) + + def __init__(self, + action=None, + 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() + 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 = NestedInteger(allow_none=True, attribute="v") + + __elements__ = ('x',) + + def __init__(self, + t="data", + r=0, + i=0, + x=None, + ): + 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/openpyxl/pivot/tests/__init__.py b/openpyxl/pivot/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/pivot/tests/conftest.py b/openpyxl/pivot/tests/conftest.py new file mode 100644 index 0000000..3563b4c --- /dev/null +++ b/openpyxl/pivot/tests/conftest.py @@ -0,0 +1,16 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) + + +# objects under test diff --git a/openpyxl/pivot/tests/data/pivotCacheDefinition.xml b/openpyxl/pivot/tests/data/pivotCacheDefinition.xml new file mode 100644 index 0000000..e1ef05d --- /dev/null +++ b/openpyxl/pivot/tests/data/pivotCacheDefinition.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/pivot/tests/data/pivotCacheRecords.xml b/openpyxl/pivot/tests/data/pivotCacheRecords.xml new file mode 100644 index 0000000..6834d6c --- /dev/null +++ b/openpyxl/pivot/tests/data/pivotCacheRecords.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/pivot/tests/data/pivotTable.xml b/openpyxl/pivot/tests/data/pivotTable.xml new file mode 100644 index 0000000..7c3b417 --- /dev/null +++ b/openpyxl/pivot/tests/data/pivotTable.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/pivot/tests/test_cache.py b/openpyxl/pivot/tests/test_cache.py new file mode 100644 index 0000000..bcd1ad7 --- /dev/null +++ b/openpyxl/pivot/tests/test_cache.py @@ -0,0 +1,199 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from io import BytesIO +from zipfile import ZipFile + +from openpyxl.packaging.manifest import Manifest +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +from ..record import Text + + +@pytest.fixture +def CacheField(): + from ..cache import CacheField + return CacheField + + +class TestCacheField: + + def test_ctor(self, CacheField): + field = CacheField(name="ID") + xml = tostring(field.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, CacheField): + src = """ + + """ + node = fromstring(src) + field = CacheField.from_tree(node) + assert field == CacheField(name="ID") + + +@pytest.fixture +def SharedItems(): + from ..cache import SharedItems + return SharedItems + + +class TestSharedItems: + + def test_ctor(self, SharedItems): + s = [Text(v="Stanford"), Text(v="Cal"), Text(v="UCLA")] + items = SharedItems(_fields=s) + xml = tostring(items.to_tree()) + expected = """ + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, SharedItems): + src = """ + + + + + + """ + node = fromstring(src) + items = SharedItems.from_tree(node) + s = [Text(v="Stanford"), Text(v="Cal"), Text(v="UCLA")] + assert items == SharedItems(_fields=s) + + +@pytest.fixture +def WorksheetSource(): + from ..cache import WorksheetSource + return WorksheetSource + + +class TestWorksheetSource: + + def test_ctor(self, WorksheetSource): + ws = WorksheetSource(name="mydata") + xml = tostring(ws.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, WorksheetSource): + src = """ + + """ + node = fromstring(src) + ws = WorksheetSource.from_tree(node) + assert ws == WorksheetSource(name="mydata") + + +@pytest.fixture +def CacheSource(): + from ..cache import CacheSource + return CacheSource + + +class TestCacheSource: + + def test_ctor(self, CacheSource, WorksheetSource): + ws = WorksheetSource(name="mydata") + source = CacheSource(type="worksheet", worksheetSource=ws) + xml = tostring(source.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, CacheSource, WorksheetSource): + src = """ + + + + """ + node = fromstring(src) + source = CacheSource.from_tree(node) + ws = WorksheetSource(name="mydata") + assert source == CacheSource(type="worksheet", worksheetSource=ws) + + +@pytest.fixture +def CacheDefinition(): + from ..cache import CacheDefinition + return CacheDefinition + + +@pytest.fixture +def DummyCache(CacheDefinition, WorksheetSource, CacheSource, CacheField): + ws = WorksheetSource(name="Sheet1") + source = CacheSource(type="worksheet", worksheetSource=ws) + fields = [CacheField(name="field1")] + cache = CacheDefinition(cacheSource=source, cacheFields=fields) + return cache + + +class TestPivotCacheDefinition: + + def test_read(self, CacheDefinition, datadir): + datadir.chdir() + with open("pivotCacheDefinition.xml", "rb") as src: + xml = fromstring(src.read()) + + cache = CacheDefinition.from_tree(xml) + assert cache.recordCount == 17 + assert len(cache.cacheFields) == 6 + + + def test_to_tree(self, DummyCache): + cache = DummyCache + + expected = """ + + + + + + + + + """ + + xml = tostring(cache.to_tree()) + + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_path(self, DummyCache): + assert DummyCache.path == "/xl/pivotCache/pivotCacheDefinition1.xml" + + + def test_write(self, DummyCache): + out = BytesIO() + archive = ZipFile(out, mode="w") + manifest = Manifest() + + xml = tostring(DummyCache.to_tree()) + DummyCache._write(archive, manifest) + + assert archive.namelist() == [DummyCache.path[1:]] + assert manifest.find(DummyCache.mime_type) diff --git a/openpyxl/pivot/tests/test_fields.py b/openpyxl/pivot/tests/test_fields.py new file mode 100644 index 0000000..a407b82 --- /dev/null +++ b/openpyxl/pivot/tests/test_fields.py @@ -0,0 +1,195 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from datetime import datetime + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def Error(): + from ..record import Error + return Error + + +class TestError: + + def test_ctor(self, Error): + error = Error(v="error") + xml = tostring(error.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Error): + src = """ + + """ + node = fromstring(src) + error = Error.from_tree(node) + assert error == Error(v="error") + + +@pytest.fixture +def Boolean(): + from ..record import Boolean + return Boolean + + +class TestBoolean: + + def test_ctor(self, Boolean): + boolean = Boolean() + xml = tostring(boolean.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Boolean): + src = """ + + """ + node = fromstring(src) + boolean = Boolean.from_tree(node) + assert boolean == Boolean() + + +@pytest.fixture +def Missing(): + from ..record import Missing + return Missing + + +class TestMissing: + + def test_ctor(self, Missing): + missing = Missing() + xml = tostring(missing.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Missing): + src = """ + + """ + node = fromstring(src) + missing = Missing.from_tree(node) + assert missing == Missing() + + +@pytest.fixture +def Number(): + from ..record import Number + return Number + + +class TestNumber: + + def test_ctor(self, Number): + number = Number(v=24) + xml = tostring(number.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Number): + src = """ + + """ + node = fromstring(src) + number = Number.from_tree(node) + assert number == Number(v=15) + + +@pytest.fixture +def Text(): + from ..record import Text + return Text + + +class TestText: + + def test_ctor(self, Text): + text = Text(v="UCLA") + xml = tostring(text.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Text): + src = """ + + """ + node = fromstring(src) + text = Text.from_tree(node) + assert text == Text(v="UCLA") + +@pytest.fixture +def Index(): + from ..record import Index + return Index + + +class TestIndex: + + def test_ctor(self, Index): + record = Index() + xml = tostring(record.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Index): + src = """ + + """ + node = fromstring(src) + record = Index.from_tree(node) + assert record == Index(v=1) + + +@pytest.fixture +def DateTimeField(): + from ..record import DateTimeField + return DateTimeField + + +class TestDateTimeField: + + def test_ctor(self, DateTimeField): + record = DateTimeField(v=datetime(2016, 3, 24)) + xml = tostring(record.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DateTimeField): + src = """ + + """ + node = fromstring(src) + record = DateTimeField.from_tree(node) + assert record == DateTimeField(v=datetime(2016, 3, 24)) diff --git a/openpyxl/pivot/tests/test_record.py b/openpyxl/pivot/tests/test_record.py new file mode 100644 index 0000000..b28d3c7 --- /dev/null +++ b/openpyxl/pivot/tests/test_record.py @@ -0,0 +1,113 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from io import BytesIO +from zipfile import ZipFile + +from openpyxl.packaging.manifest import Manifest +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + +from .test_fields import ( + Index, + Number, + Text, +) + +@pytest.fixture +def Record(): + from ..record import Record + return Record + + +class TestRecord: + + def test_ctor(self, Record, Number, Text, Index): + n = [Number(v=1), Number(v=25)] + s = [Text(v="2014-03-24")] + x = [Index(), Index(), Index()] + fields = n + s + x + field = Record(_fields=fields) + xml = tostring(field.to_tree()) + expected = """ + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Record, Number, Text, Index): + src = """ + + + + + + + + + """ + node = fromstring(src) + n = [Number(v=1), Number(v=25)] + s = [Text(v="2014-03-24")] + x = [Index(), Index(), Index()] + fields = [ + Number(v=1), + Index(), + Text(v="2014-03-24"), + Index(), + Number(v=25), + Index(), + ] + field = Record.from_tree(node) + assert field == Record(_fields=fields) + + +@pytest.fixture +def RecordList(): + from ..record import RecordList + return RecordList + + +class TestRecordList: + + def test_ctor(self, RecordList): + cache = RecordList() + xml = tostring(cache.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RecordList): + src = """ + + """ + node = fromstring(src) + cache = RecordList.from_tree(node) + assert cache == RecordList() + + + def test_write(self, RecordList): + out = BytesIO() + archive = ZipFile(out, mode="w") + manifest = Manifest() + + records = RecordList() + xml = tostring(records.to_tree()) + records._write(archive, manifest) + manifest.append(records) + + assert archive.namelist() == [records.path[1:]] + assert manifest.find(records.mime_type) diff --git a/openpyxl/pivot/tests/test_table.py b/openpyxl/pivot/tests/test_table.py new file mode 100644 index 0000000..51ecb7f --- /dev/null +++ b/openpyxl/pivot/tests/test_table.py @@ -0,0 +1,378 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + +from io import BytesIO +from zipfile import ZipFile + +from openpyxl.packaging.manifest import Manifest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml +from openpyxl.tests.schema import sheet_schema + + + +@pytest.fixture +def PivotField(): + from ..table import PivotField + return PivotField + + +class TestPivotField: + + def test_ctor(self, PivotField): + field = PivotField() + xml = tostring(field.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PivotField): + src = """ + + """ + node = fromstring(src) + field = PivotField.from_tree(node) + assert field == PivotField() + + +@pytest.fixture +def FieldItem(): + from ..table import FieldItem + return FieldItem + + +class TestFieldItem: + + def test_ctor(self, FieldItem): + item = FieldItem() + xml = tostring(item.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, FieldItem): + src = """ + + """ + node = fromstring(src) + item = FieldItem.from_tree(node) + assert item == FieldItem(m=True, x=2) + + +@pytest.fixture +def RowColItem(): + from ..table import RowColItem + return RowColItem + + +class TestRowColItem: + + def test_ctor(self, RowColItem): + fut = RowColItem(x=4) + xml = tostring(fut.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, RowColItem): + src = """ + + + + """ + node = fromstring(src) + fut = RowColItem.from_tree(node) + assert fut == RowColItem(r=1, x=2) + + +@pytest.fixture +def DataField(): + from ..table import DataField + return DataField + + +class TestDataField: + + def test_ctor(self, DataField): + df = DataField(fld=1) + xml = tostring(df.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DataField): + src = """ + + """ + node = fromstring(src) + df = DataField.from_tree(node) + assert df == DataField(fld=4, name="Sum of impressions", baseField=0, baseItem=0) + + +@pytest.fixture +def Location(): + from ..table import Location + return Location + + +class TestLocation: + + def test_ctor(self, Location): + loc = Location(ref="A3:E14", firstHeaderRow=1, firstDataRow=2, firstDataCol=1) + xml = tostring(loc.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Location): + src = """ + + """ + node = fromstring(src) + loc = Location.from_tree(node) + assert loc == Location(ref="A3:E14", firstHeaderRow=1, firstDataRow=2, firstDataCol=1) + +@pytest.fixture +def PivotTableStyle(): + from ..table import PivotTableStyle + return PivotTableStyle + + +class TestPivotTableStyle: + + def test_ctor(self, PivotTableStyle): + style = PivotTableStyle(name="PivotStyleMedium4") + xml = tostring(style.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PivotTableStyle): + src = """ + + """ + node = fromstring(src) + style = PivotTableStyle.from_tree(node) + assert style == PivotTableStyle(name="PivotStyleMedium4", + showRowHeaders=True, showColHeaders=True, showLastColumn=True) + + + def test_no_name(self, PivotTableStyle): + src = """ + + """ + node = fromstring(src) + style = PivotTableStyle.from_tree(node) + assert style == PivotTableStyle() + + +@pytest.fixture +def TableDefinition(): + from ..table import TableDefinition + return TableDefinition + + +@pytest.fixture +def DummyPivotTable(TableDefinition, Location): + """ + Create a minimal pivot table + """ + loc = Location(ref="A3:E14", firstHeaderRow=1, firstDataRow=2, firstDataCol=1) + defn = TableDefinition(name="PivotTable1", cacheId=68, + applyWidthHeightFormats=True, dataCaption="Values", updatedVersion=4, + createdVersion=4, gridDropZones=True, minRefreshableVersion=3, + outlineData=True, useAutoFormatting=True, location=loc, indent=0, + itemPrintTitles=True, outline=True) + return defn + + +class TestPivotTableDefinition: + + def test_ctor(self, DummyPivotTable): + defn = DummyPivotTable + xml = tostring(defn.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, DummyPivotTable, TableDefinition): + src = """ + + + + """ + node = fromstring(src) + defn = TableDefinition.from_tree(node) + assert defn == DummyPivotTable + + + @pytest.mark.lxml_required + def test_validate(self, datadir, TableDefinition): + datadir.chdir() + with open("pivotTable.xml", "rb") as src: + xml = src.read() + node = fromstring(xml) + + # need to convert to and from string to get namespace + defn = TableDefinition.from_tree(node) + tree = defn.to_tree() + generated = tostring(tree) + tree = fromstring(generated) + + sheet_schema.assertValid(tree) + + + def test_write(self, DummyPivotTable): + out = BytesIO() + archive = ZipFile(out, "w") + manifest = Manifest() + + defn = DummyPivotTable + defn._write(archive, manifest) + assert archive.namelist() == [defn.path[1:]] + assert manifest.find(defn.mime_type) + + +@pytest.fixture +def PageField(): + from ..table import PageField + return PageField + + +class TestPageField: + + def test_ctor(self, PageField): + pf = PageField(fld=64, hier=-1) + xml = tostring(pf.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PageField): + src = """ + + """ + node = fromstring(src) + pf = PageField.from_tree(node) + assert pf == PageField(fld=64, hier=-1) + + +@pytest.fixture +def Reference(): + from ..table import Reference + return Reference + + +class TestReference: + + def test_ctor(self, Reference): + ref = Reference(field=4294967294, x=0, selected=False) + xml = tostring(ref.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Reference): + src = """ + + + + """ + node = fromstring(src) + ref = Reference.from_tree(node) + assert ref == Reference(field=4294967294, x=0, selected=False) + + +@pytest.fixture +def PivotArea(): + from ..table import PivotArea + return PivotArea + + +class TestPivotArea: + + def test_ctor(self, PivotArea): + area = PivotArea(type="data", outline=False, fieldPosition=False) + xml = tostring(area.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, PivotArea): + src = """ + + """ + node = fromstring(src) + area = PivotArea.from_tree(node) + assert area == PivotArea(type="data", outline=False, fieldPosition=False) + + +@pytest.fixture +def ChartFormat(): + from ..table import ChartFormat + return ChartFormat + + +class TestChartFormat: + + def test_ctor(self, ChartFormat, PivotArea): + area = PivotArea() + fmt = ChartFormat(chart=0, format=12, series=1, pivotArea=area) + xml = tostring(fmt.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, ChartFormat, PivotArea): + src = """ + + + + """ + node = fromstring(src) + fmt = ChartFormat.from_tree(node) + area = PivotArea() + assert fmt == ChartFormat(chart=0, format=12, series=1, pivotArea=area) diff --git a/openpyxl/reader/__init__.py b/openpyxl/reader/__init__.py new file mode 100644 index 0000000..2283c5f --- /dev/null +++ b/openpyxl/reader/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2010-2018 openpyxl diff --git a/openpyxl/reader/excel.py b/openpyxl/reader/excel.py new file mode 100644 index 0000000..9af91f3 --- /dev/null +++ b/openpyxl/reader/excel.py @@ -0,0 +1,297 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2017 openpyxl +# Copyright (c) 2018 qyou.casia@gmail.com + +"""Read an xlsx file into Python""" + +# Python stdlib imports +from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile +from sys import exc_info +from io import BytesIO +import os.path +import warnings + +# compatibility imports +from openpyxl.compat import unicode, file +from openpyxl.pivot.table import TableDefinition + +# Allow blanket setting of KEEP_VBA for testing +try: + from ..tests import KEEP_VBA +except ImportError: + KEEP_VBA = False + + +# package imports +from openpyxl.utils.exceptions import InvalidFileException +from openpyxl.xml.constants import ( + ARC_SHARED_STRINGS, + ARC_CORE, + ARC_CONTENT_TYPES, + ARC_WORKBOOK, + ARC_THEME, + COMMENTS_NS, + SHARED_STRINGS, + EXTERNAL_LINK, + XLTM, + XLTX, + XLSM, + XLSX, +) + +from openpyxl.comments.comment_sheet import CommentSheet +from openpyxl.workbook import Workbook + +from .strings import read_string_table +from openpyxl.styles.stylesheet import apply_stylesheet + +from openpyxl.packaging.core import DocumentProperties +from openpyxl.packaging.manifest import Manifest, Override +from openpyxl.packaging.workbook import WorkbookParser +from openpyxl.packaging.relationship import get_dependents, get_rels_path + +from openpyxl.worksheet.read_only import ReadOnlyWorksheet +from openpyxl.worksheet.table import Table +from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing +from openpyxl.chart.reader import find_charts, find_images + +from openpyxl.xml.functions import fromstring + +from .worksheet import WorkSheetParser + +# Use exc_info for Python 2 compatibility with "except Exception[,/ as] e" + + +CENTRAL_DIRECTORY_SIGNATURE = b'\x50\x4b\x05\x06' +SUPPORTED_FORMATS = ('.xlsx', '.xlsm', '.xltx', '.xltm') + + +def repair_central_directory(zipFile, is_file_instance): + ''' trims trailing data from the central directory + code taken from http://stackoverflow.com/a/7457686/570216, courtesy of Uri Cohen + ''' + + f = zipFile if is_file_instance else open(zipFile, 'rb+') + data = f.read() + pos = data.find(CENTRAL_DIRECTORY_SIGNATURE) # End of central directory signature + if (pos > 0): + sio = BytesIO(data) + sio.seek(pos + 22) # size of 'ZIP end of central directory record' + sio.truncate() + sio.seek(0) + return sio + + f.seek(0) + return f + + + +def _validate_archive(filename): + """ + Check the file is a valid zipfile + """ + is_file_like = hasattr(filename, 'read') + + if not is_file_like and os.path.isfile(filename): + file_format = os.path.splitext(filename)[-1].lower() + if file_format not in SUPPORTED_FORMATS: + if file_format == '.xls': + msg = ('openpyxl does not support the old .xls file format, ' + 'please use xlrd to read this file, or convert it to ' + 'the more recent .xlsx file format.') + elif file_format == '.xlsb': + msg = ('openpyxl does not support binary format .xlsb, ' + 'please convert this file to .xlsx format if you want ' + 'to open it with openpyxl') + else: + msg = ('openpyxl does not support %s file format, ' + 'please check you can open ' + 'it with Excel first. ' + 'Supported formats are: %s') % (file_format, + ','.join(SUPPORTED_FORMATS)) + raise InvalidFileException(msg) + + + if is_file_like: + # fileobject must have been opened with 'rb' flag + # it is required by zipfile + if getattr(filename, 'encoding', None) is not None: + raise IOError("File-object must be opened in binary mode") + + try: + archive = ZipFile(filename, 'r', ZIP_DEFLATED) + except BadZipfile: + f = repair_central_directory(filename, is_file_like) + archive = ZipFile(f, 'r', ZIP_DEFLATED) + return archive + + +def _find_workbook_part(package): + workbook_types = [XLTM, XLTX, XLSM, XLSX] + for ct in workbook_types: + part = package.find(ct) + if part: + return part + + # some applications reassign the default for application/xml + defaults = set((p.ContentType for p in package.Default)) + workbook_type = defaults & set(workbook_types) + if workbook_type: + return Override("/" + ARC_WORKBOOK, workbook_type.pop()) + + raise IOError("File contains no valid workbook part") + + +def load_workbook(filename, read_only=False, keep_vba=KEEP_VBA, + data_only=False, guess_types=False, keep_links=True): + """Open the given filename and return the workbook + + :param filename: the path to open or a file-like object + :type filename: string or a file-like object open in binary mode c.f., :class:`zipfile.ZipFile` + + :param read_only: optimised for reading, content cannot be edited + :type read_only: bool + + :param keep_vba: preseve vba content (this does NOT mean you can use it) + :type keep_vba: bool + + :param guess_types: guess cell content type and do not read it from the file + :type guess_types: bool + + :param data_only: controls whether cells with formulae have either the formula (default) or the value stored the last time Excel read the sheet + :type data_only: bool + + :param keep_links: whether links to external workbooks should be preserved. The default is True + :type keep_links: bool + + :rtype: :class:`openpyxl.workbook.Workbook` + + .. note:: + + When using lazy load, all worksheets will be :class:`openpyxl.worksheet.iter_worksheet.IterableWorksheet` + and the returned workbook will be read-only. + + """ + archive = _validate_archive(filename) + read_only = read_only + + src = archive.read(ARC_CONTENT_TYPES) + root = fromstring(src) + package = Manifest.from_tree(root) + + wb_part = _find_workbook_part(package) + parser = WorkbookParser(archive, wb_part.PartName[1:]) + wb = parser.wb + wb._data_only = data_only + wb._read_only = read_only + wb._keep_links = keep_links + wb.guess_types = guess_types + wb.template = wb_part.ContentType in (XLTX, XLTM) + parser.parse() + wb._sheets = [] + + if read_only and guess_types: + warnings.warn('Data types are not guessed when using iterator reader') + + valid_files = archive.namelist() + + # If are going to preserve the vba then attach a copy of the archive to the + # workbook so that is available for the save. + if keep_vba: + wb.vba_archive = ZipFile(BytesIO(), 'a', ZIP_DEFLATED) + for name in archive.namelist(): + wb.vba_archive.writestr(name, archive.read(name)) + + + if read_only: + wb._archive = ZipFile(filename) + + # get workbook-level information + if ARC_CORE in valid_files: + src = fromstring(archive.read(ARC_CORE)) + wb.properties = DocumentProperties.from_tree(src) + + + shared_strings = [] + ct = package.find(SHARED_STRINGS) + if ct is not None: + strings_path = ct.PartName[1:] + shared_strings = read_string_table(archive.read(strings_path)) + + + if ARC_THEME in valid_files: + wb.loaded_theme = archive.read(ARC_THEME) + + apply_stylesheet(archive, wb) # bind styles to workbook + pivot_caches = parser.pivot_caches + + # get worksheets + for sheet, rel in parser.find_sheets(): + sheet_name = sheet.name + worksheet_path = rel.target + rels_path = get_rels_path(worksheet_path) + rels = [] + if rels_path in valid_files: + rels = get_dependents(archive, rels_path) + + if not worksheet_path in valid_files: + continue + + if read_only: + ws = ReadOnlyWorksheet(wb, sheet_name, worksheet_path, None, + shared_strings) + + wb._sheets.append(ws) + else: + fh = archive.open(worksheet_path) + ws = wb.create_sheet(sheet_name) + ws._rels = rels + ws_parser = WorkSheetParser(ws, fh, shared_strings) + ws_parser.parse() + + if rels: + # assign any comments to cells + for r in rels.find(COMMENTS_NS): + src = archive.read(r.target) + comment_sheet = CommentSheet.from_tree(fromstring(src)) + for ref, comment in comment_sheet.comments: + ws[ref].comment = comment + + # preserve link to VML file if VBA + if ( + wb.vba_archive is not None + and ws.legacy_drawing is not None + ): + ws.legacy_drawing = rels[ws.legacy_drawing].target + + for t in ws_parser.tables: + src = archive.read(t) + xml = fromstring(src) + table = Table.from_tree(xml) + ws.add_table(table) + + drawings = rels.find(SpreadsheetDrawing._rel_type) + for rel in drawings: + for c in find_charts(archive, rel.target): + ws.add_chart(c, c.anchor) + for img in find_images(archive, rel.target): + ws.add_image(img, img.anchor) + + pivot_rel = rels.find(TableDefinition.rel_type) + for r in pivot_rel: + pivot_path = r.Target + src = archive.read(pivot_path) + tree = fromstring(src) + pivot = TableDefinition.from_tree(tree) + pivot.cache = pivot_caches[pivot.cacheId] + ws.add_pivot(pivot) + + ws.sheet_state = sheet.state + ws._rels = [] # reset + + parser.assign_names() + + #wb._differential_styles.styles = [] # tables may depened upon dxf + + archive.close() + return wb diff --git a/openpyxl/reader/strings.py b/openpyxl/reader/strings.py new file mode 100644 index 0000000..da3802c --- /dev/null +++ b/openpyxl/reader/strings.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.cell.text import Text +from openpyxl.utils.indexed_list import IndexedList + +from openpyxl.xml.functions import iterparse +from openpyxl.xml.constants import SHEET_MAIN_NS + +from .worksheet import _get_xml_iter + + +def read_string_table(xml_source): + """Read in all shared strings in the table""" + + strings = [] + src = _get_xml_iter(xml_source) + STRING_TAG = '{%s}si' % SHEET_MAIN_NS + + for _, node in iterparse(src): + if node.tag == STRING_TAG: + text = Text.from_tree(node).content + text = text.replace('x005F_', '') + node.clear() + + strings.append(text) + + return strings diff --git a/openpyxl/reader/tests/__init__.py b/openpyxl/reader/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/reader/tests/conftest.py b/openpyxl/reader/tests/conftest.py new file mode 100644 index 0000000..9a3b175 --- /dev/null +++ b/openpyxl/reader/tests/conftest.py @@ -0,0 +1,12 @@ +# Copyright (c) 2010-2018 openpyxl +import pytest + + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) diff --git a/openpyxl/reader/tests/data/Table1-XmlFromAccess.xml b/openpyxl/reader/tests/data/Table1-XmlFromAccess.xml new file mode 100644 index 0000000..ace8641 --- /dev/null +++ b/openpyxl/reader/tests/data/Table1-XmlFromAccess.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + ID + + + + Field1 + + + + Field2 + + + + Field3 + + + + DateField + + + + Field5 + + + + + + 5 + + + 1 + + + + 2 + + + + 3 + + + 41621 + + + 4 + + + + + + 6 + + + This + + + + is + + + + fake + + + 41622 + + + data + + + + + + + + \ No newline at end of file diff --git a/openpyxl/reader/tests/data/bug137.xlsx b/openpyxl/reader/tests/data/bug137.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cde2874afe395f6628bd3d332707a013e38d15c9 GIT binary patch literal 10004 zcmeHtg;!K<*ZvGcNRA-g-6f2Kbax}tozfv80@6rGcS?)WDM$=RcXtSg0z-Ewzws%3 zc>R5U!1ujpt(mjVI%i+&?0aAP-gQ(JK!~^iWWXH&06+yGwB28c00ID5$N<1SzyeTD z!qLIa!okf*)62=i)qvg8-i|VN2FRELK!890-{XJT3e;%!D|c|-{Xla7jxA?&?gvs$ z)S!^d6eB)=QOO(isJrgN?)Kg+t-V)RVTn4sbFQ_OrInRX+B=qbR;nC#9;*(!Hk^tv zCVMM>Z$EE&N1XsQZMw+nm}*SQ^hgd4X-5)pifV0VM&kWTfg!adRn&nD6a_yt0%q(@ z?tumZ7k10Ga+E+4_RJ__Gh$l|9`fdx7dd#e8rZAV#bKf4n9)JVJFY3%Vfh&xrI(#)jkzN@3Gor(4OPgn66sGUsP`Q(j2)SefkEv!lv?z*Vjcrv)v(}~ zy=!Tt)=FUG{hFydOH0k!n&^Vl8R*E4K|KQt!3M?9;P8(8j^YM5TH%GrTRjtXI%e&tbLQ{MJgh@!67+ zTP&}P-%|E=MYl*U$YwXk&@9I}(}%9rjAC?jqg;7ggy>Leq`*8|dJ`doyn8W9?eR^l z*wVtrnhKm!H#Z=F>c9N5_7Nw|3H**C-0jc-LqH=J3p-a1_8sMP2vUgq=G%3n9QE8I5(e6^zM&;3po}C7P0_4r+M^C>Di|8%wZrEs2 zkeYHRcQi7JRdSnpPe1W2&DH|vm{MEW;C2(gA~kuuxMXMKnV*nuI(=1K6%j$)m~c&F z3L#U$ML&SK6vT3>^_f_Y#uF7=EF^pu3TY)*WfPo)KDmS9jZz|fDR@y;4^eXT~iU|mhL2@7ffEaKG=xN9CC*3_9 zU2IJq9c_O^p}(k)_=EcJXa8>>@8bKPz_)Fwv*7Q6DL!lhKC0sDcJfs7Jl_CR(G(w( zA}ufXmpzs@1OSG$X2PWYe*gE*)bQB1dKHzGI6(5UGmSn*+(O}t_ zOZE4fiSnQ9RxvA`U_I`4zFn1(iEWlL?3oERai5xcJ)z?ds1$!2Kp&riqnfxOzJq>Q z>)Y&|^YLLJU%alESSxhQ!ppmwu!bL2BYc-uwJs;hbx|zHGZd{A8EHt2yNcYNT$daE<;;S8!2WDMSHB8+bROv`M#&Gr>|7`|ed)iLL zYJGTF2$F!aHQ!4#-E3k@{eZoVQo|_IL49LmEkREGJQ1NS;-~|p%n&c&D+y+LmYQ~o zEFQTdoivn>7rY4~zcocp;K#B&xGBuxzKr>&DO}yW>?~Y=_;9`k^vMF}-DZMAu=tsD z6FR1SWu}A)RZN0t-N;+JUPGw0dHzAw?zEM;z|x`yR3b$jH<@)LYOf17rk=>Z7 z{)~i)l?TcPlfU>cj)erMRGUye_XeId4we~kl#m_Oy?X2(#C{U0qIL}rzLq- zvv^IRp_N|_dlpkH3L8{%n~J-4cIO`jq@ig^k>!5cH8S!kEJ3hSu!A)2raVRy^4k{0 z6$Guh5vYPBPGTzoEy`xVrNf}_b-Z(5v{qKmva%GJyr$lxQp9jnwdpu6nSZmb)wyc5 z=zQNiY1PRx&s#WsNIl$?(0UY)@LoRbnUg4%BT~_Jbs@izZbk1sN`s^R!9kt3P7IOu zCjd8Xu!0wqW2ymqqn&5C$TUK$)`#vp6h9}dsG+>Qr`8O&*MDXqSdz!fAK+G`gIkp5 zPivaHn0VMYSh@bSMgMhq|DP>7D0&bL?rcGO0eg2(9mbyBjV){r8+ki{qCK_)u9NGL zu5@(FAwYtTR|vp(1*A^W7!LB=@~VklQvE4y^#$e(J4)ZfUQzPp+B>@>L0CVkCeAB4 z7oeIwTs1jn#p$dHp_2Af4<8ieu5!z=b9qQ$8EJ#jd}zHp6F+A)D;BJRoM5D=R!=Ox zxIvlLS+ss*H~lp4bv1W%s@*mA>wyKi>dTk<}S-u*r*(->)?{HP3Z;LITWQ~e*C z>R;Ob)H^axNoAcABV>=|fFgGxweF}gqNL6mQG5Vsg9KaRE}fUoca|H#xc1J_OOv*FMb#zDQYRwVMMc#aFx?0)8yL!clADz#s!{C&?;kcco~x0L zHlLHfKzv-$k{~pi!tk=(n=rv>AQ|5*aKmSz4IEUVP z2Ef#A?ENv>$+`PY0?VZ*m8Rx{P0+%N`vnUYtuqjo8*^s?ls@6>F zoJWo!orJzl=go_MK@~; zdkc;~{{P@COh?6Wo(H!TJmW#>=4j7QM}qP+e5NwJREgxHLnygTYLd1_uIQMY56Rrx z3t&g1Niwi$htrN_1fj_-n_e%p6m*GqDl1<_*sm~#OfoeYDoO44{APKL=IO~?VAyL3 zYTD1v^KT8xo`roBrWoh5_Vx(*ra(d-ht_R2;Hfyblp(t}H+8jV(3#Fmi!D|moRr?M z6D&`co)~^?LfGFgNOgZesUfT);4!vl7Xk%vG3?P)$)qJ+JLFU~l7`aTo9nf(n1s^U zd|%iZ;fF5yEnxObe|n`1JY<2aDTw%xMKY>en9e*%*vuC~3r+|nOhTidGn~Bs&^I-S zwxmdKzt+^=#9;Y)?CEB}f!p9dE|k$qcY9jzvAfG9a@zz~+4S~P@7jzkpz$(_QYouc z+R1n6()dx<7qB(19fw-A(x!8SZzpuAdYQ^aqw*G&l9)ZhQ_1@zla@Z-gHV!~PTr*b zlOgx|dAEIJ^MgUU=Qr|ibvzD`ak+?9l1JuVfAf_W3RUErXA5=&JkLvOCtwT<5p0A+ zphi!YXh-b(uwMd3L__eRV$&GR1AM|(*Vj!_T**X(=XE$c_LZ9M~4Rl zpFMRx-duJtv|e9(@!#dLrW%dl$i6;>^+uEnfBV+dO}B6FFC%pxBQ2S$w|<9g|Bf4X zh%+4njdKfL$6XC8BrJb-)Xgk3vXxSPON$09=O~ZIq6ds1!|X*5bFv`QLTl77Bqr;C zGd?Rac6m%Ei6drhPrisimkm>`k7UNrwY!=k%(`ovbr2qLqsw=nF2?Yg?sAi|kjA7i z=6^X)h{4&}Roj|*8hsg{AguP4T#8IlL5VA$h9L$&!rcbZH#K<^qs-l|+JoRJAItc& zP|`+O**gRBK@>O}DF+X;m`D?2Nms-0Fn#a6tx)WN>Yq#Lt1$1oi4w{*-;b@b8?*;r z;%E4RB$}C@UXiEEu&XmZWXtoq=V~aqyKlq$QB6IBY=n>1R3t{@j#}jDd*;ABXI_b8 zfpcIt%c4&*PWNIFL4W|nNm3UvEGrdtM}cud`CF9rwMS=)T5%T-@)F)!T|K6rhK@We zixK+P=?2=~6lirI?5}OKiaIY~}2|LLhv1{tOcmt{Zdzy-HiO&MxN;_PK&0%P&F zhXYS^i@9Cj5{)Xpac%g_90*Q+6bM~N8wsZTYS7lF#FuM3<-`MxjI3SULC$#J>G_G; zfL20~{!MhvQonEs5rajgDLaVLUH*Cguvd|GX?>AwdWMKmwRzhIQ^e$l&SR(aPVa)d8G+>ri{~czZHqDGL6}z>@w4U^?dhe8 z_jysDIBA&dwB%Q4px{tVVkN!C#Net>sqIyn zZLhib^G9y(CX(|*ErkUM!?@UiPS1m+?dY*M8;nAkzj7DiI4~$mjc_<67M9ugKRWhBm`2(* z+W}HIxFAVCG@2(TU|v@Zj*GeU0Y(MBA-l`wM20?oSJNrm?mN0m@y&cjHAGRjPj_9@iW60HJiY8Z1E%RQFUFj5?zi3b&L&fLMlcee`D@-5m{ zl)JL8vMfg!N0gNBFJ}_HsN`41P&P#>Zqz<;Upb3A7EAa7 zYVn};$VwRP$hnts_JT3&G44)?QSoFo(0n0D${z%Ej?V=JiL+%6O#UIM0dE0-o{P zk2Fj_PvsNKr0$}B-Rlgo-953(>FQ{TvT&P9dU2uJd;mC;lb_AS zuT)cj49lW2B%}xE=>OWGQ%VBYQ0f6I(roTg2v!~?A!gLQWq!&%r;(Vnr>Coau!|>; z2=;>s(vefDbnfIxQh{6y>c_H8oD(YgylEh3{st|A7ZPlc-mSnAJS6n_qbTvu`?53Y zWg7^x;E|$`3e49Yk|f7zR@BU@JnDmlcSvkJh83I+`qADw`6)M47u#{ZT7yIdKr$6q zDesJxC&zMEhZ*0sv`M2?OEcGrL(1#ftk3inR9RV~12v{Z5j zqRf0irgd49CR7U4Jl{0+#ojWH#63X|pee>K)!oN*#R#2Jcq@pJc;#<%GH+MLwyb!c ztuQ=!DX=-k$+PBwgxb))%Vn9BUzk#e5rZP^G`fQP1#u?QJj9CYRbh(zGg%U`(3+ii zofX^x2Vi%ADu^50D|^Gr#&48HRluQiWMT_8*oi&$#=6Tddd({e1N1Th7Egj_6uPsh zKvHFkNpuQX+ZeMxETzVAE38(rEZdr|s%_{I4Mx{cS;ErNf&9T|4(4JbIb&p>a=X4b z8a89jOu({W`k?3+=4U+x!K`fECG3WKNyn6y6&k|}TWHXNjHdfj58U3bo{lg(mGwv; zWK(7&wq)*$Cc7!KSXu33KZDLatX=wI&J* zjnWpwgj#J9ribl!o8UdLAV*1{$Ab=(=$p;e;o9;#8UGmTFsRCI}MAcj$Vn|JMRioQKkKj~5 z^$Vk@eQxaLH{tHK?c-I7@S!GT{ z@_lq&4pd8R1cI0bQ4+ADSSetNnxaj-mvjAXfhUu2mDBkRj)3o}_i!c$i9uzvWYN1` zPp)wOc!p$Da@?0hA5XHkk~dearXhl<$-*U0aJm|2@(1J3K|5nZ(oX+=sCaM&WGn* zxivH0Pqww=olQOMTGc2LqpSkjqvQEf2N;oGd5#?s57gc_dk74P#`@jt5)VhKmer=pl@%C!+ z7i+YM2>$#!#-U<~hj+mV0%8w~Uo$F>Plc|b309DmuzbK~^BGgjUu0@vEt5tA+~__u zEf3PysT?0W_dQb8TCw?J@a9Q3QDLdhdEA-&jfrC4%~BwGk@SfzEwj{2sc4|a7nT{D z`S155D1K|UoR>TwQ{i_i;lIDOMpt+>YGJP7=JKN+|51QL2E~5V-oc-mSIF97JDLR4 zart2@sKoV?0m?hM8>HXu@>W!;61Z>%R=|HmaCPAAx?cP zi5~qNAg%ap99xFrp^M{D&)2z4HB#79ou%2Y;sG?TS8q6rx5WdaCqBq1x&(1zDZBZ@ zI#NvTXX9keW=-g|`FzhC^bk|tF{(&_rN`Hu!*aQMv)x9H0 zVuNUvS64ArL=_q;ItziHMy{&yiHkG}9OZ&my$re=J{2DD3b z!}{*8%OwQAZ?^$suiIte%jgX6d}6~}=jM)PsxFRBt{i5LE*3v1L7DsCjyarSfXFyC zm3B_t;AN$2u)=b^0|-0ap7u!?IF1l%{oHmA9a3g6u7!NF7m zAcD1NtGywmQ;S4UeYYxZ?{Zw17OH`(ErVBNQ9vn!Qemy7Z)Bl(Pm9R&ej@T3;!In7 z0-fR)>+KABLcKa9Is0|8zKHEU*{$nQyNW_sgae?+SrCdt`guZa0U)t>Zxp*%CwgMJ z&<>bk!YCHtg3q89;Ma^5Swn!LYq65hPFAz$7AauvI`y*I+fmB#-S)#08GDRu5hz8( z7Yn@yxFxfAAWicOY|9soeYoXn6?x2_xm|5o^2wk4XIQK9r`WECrV-A+eG0f)Si7*# z+{Iq;2g^1-WcciJyx)+PM;#RpiDWS0GNj`*7o^XdAEs~5zMyyVpo$jzuJVCq(@j(m z7><$c6SoS_$o?~5n{Zl8NrBVvB|Mhg{gr+uPEP-2-w)pXdAx%+zDe%G8{e1Uh~3I5 z`UKM&v`(Ae*9FJ|y*iUabJV(W)(M@v(;hV?J5NDE30sgfqNNXK(l{QWOIjdF$kGEl z;kPSG zkIyXhUCf9=jY!a^o|LcN0ES`V@@daX{cPiqu9r%W^6TAM3*960!-;~I=_5D-d?Q5W zIn`U44PwvDV0~v|?`bLpWQ?M`q1_cHXUtHWsA;)MD!-h9!R)Q%AkQ33>2$HM@Od%s zEib*E=-oHhto%j;Padx634BIuy{5WA|C`M~1U7g${r%T4e_QYG^KZV5QC0Xiz`w`# ze+&LNr@&q0mm&Ub!GF&{e-#XZxA6Y|6Vcl^w?|<=kvicY{M;Ih-4?z*y!k1tiuQ-_ z&r!~8(c6=PpQ7X#e~A8*WB$1azh;_$jCOAW-tNBt1SEmam2RaBe`5Yj9Da*P${-VjB80_+Jr8RRIa^Pd~=QV89{VB{67zT>U?5kMdyv literal 0 HcmV?d00001 diff --git a/openpyxl/reader/tests/data/complex-styles-worksheet.xml b/openpyxl/reader/tests/data/complex-styles-worksheet.xml new file mode 100644 index 0000000..0fce484 --- /dev/null +++ b/openpyxl/reader/tests/data/complex-styles-worksheet.xml @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + + + + + + + 20 + + + 20 + + + 20 + + + 20 + + + 22 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 21 + + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + 7 + + + 7 + + + 7 + + + 7 + + + + + 8 + + + 8 + + + 8 + + + 8 + + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 11 + + + 11 + + + 11 + + + 11 + + + + + 12 + + + 12 + + + 12 + + + 12 + + + + + 1.1234500000000001 + + + 1.1234500000000001 + + + 1.1234500000000001 + + + 1.1234500000000001 + + + + + 40939 + + + 40939 + + + 40939 + + + 40939 + + + + + 0.1234 + + + 0.1234 + + + 0.1234 + + + 0.1234 + + + + + 13 + + + + 13 + + + + 13 + + + + 13 + + + + + + 14 + + + 14 + + + 14 + + + 14 + + + + + 19 + + + 19 + + + 19 + + + 19 + + + + + 15 + + + 15 + + + 15 + + + 15 + + + + + 19 + + + 19 + + + 19 + + + 19 + + + + + 16 + + + + 16 + + + + 16 + + + + 16 + + + + + + + + + + + + + + + + 17 + + + 17 + + + 17 + + + 17 + + + + + 18 + + + 18 + + + 18 + + + 18 + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/complex-styles.xlsx b/openpyxl/reader/tests/data/complex-styles.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0d43e28de6542fb27d3dc0732548dc3205cad804 GIT binary patch literal 46700 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 AyZ`_I literal 0 HcmV?d00001 diff --git a/openpyxl/reader/tests/data/empty_with_no_properties.xlsx b/openpyxl/reader/tests/data/empty_with_no_properties.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8eaa8729f05fddf36996d42248d3394618fcb944 GIT binary patch literal 9476 zcmeHt1y>x~wsqsd-9314cS-QZ10)b2xVvjZa3{Ds1PdB8XmAMDxCMvcPH_1;IrqMt z!_EBz?|apwsz;C7Yj*9bx%OIftx}eQfyDv91D*f?07?Mfr$N6!C;*@c9ss}wY(Qy= z+uAr8+c@c}x!V~#>ae(3TT$e$K+$IdpdsV`Z~NbR2TIj-l{(lke0YxqnLylHP?79G z?Jzreu_5Hyb-Z=pT7?Pk?!J=>G4-dEP2LJOfCG0aYxa^uEl;49ZnhGDkUp=3=%h^MkVQ$;jwC!fqC@F5jg$ zpVJRQy(o?&pOGe2iVm$s8+D{{&gyZW-u&UC*>+vyGTG$xDLOuv&D6;gJi}7n=|ruj zDIMm%!a}3QUn&@egIC%@%3!d$op-%k6kF`W9-L@2x$%1pT>qyLT4HOxGNsf1;_Ex$l&etUk z)K0D~V6Pt^?98xEqP`i^psdWxjNFU;K>QQe`JiXVs3BqKJ1hV|1b71FX2tp!-Cb=R zEDdaJEq?~0e^DRyC-os?|Nq{a<3}ty*-&~8{agG;LH4mO3k1#{GX!Ui&ggn1G-wKY zdY(Qf=bW>N`O3}V+uan1KG&2!CoUJ0wd*jE)-?vvGz=JOFOnil2%AYWZ4Z1kde!^2 z%A(uf;kk7Gm^_EmI=H1_Ok2dN$Yp}r)_xEx#>En}!CuHaNdTrr36iI#o@ZRc;gEL0H8z21L4@;*_E!UU^~x-(t8FVPlPXpML#RDZ3ehMMm&_e3zdQ*}QAn7D-u{l#H7Y`_t0hgOrX|Y1vSt zVV{qI!HxOIPD216?U~l5-9VI4_KM4nA3cj$QTehw z{BAk+Saxd;g=Pq!Z))0=Rc@5nU`uwQ74>-X>Oe5WaMvE+-8&MU@M0D2d{24Iojy^% zq+8Qq(kwJe!YAm;WIb$8|E!^D`5^kGRHzQJ(2Pz-pagz4$@7aAf#ogQ1~0#Xhp;p% zn{dwq`&R5_FX)I2T^wx+yH25JJKtyzHu8of*NOFIVmbTm(c2|Nu1@60k^+Q$_;hj2 zmh$s^keM0U%j^q|Ic?M&VlltIr(<&nvCwWQuBu;s7*)^d`BAp9ZzEn8ORDnJ-b@2t z5M^n`;^h89tqt;w|MTfD>;)ZmLLw;%8~{M@`{_HH85=t}vi|zw`nkxYtLa$Iv*Ccq z_e7rvqY=?5&e8U`FfbSgMgdvb2_U6$fZ$=K-z65j|2!0NHIBX$WX?){5msMTA=r(F_2^3 zBbI7&9qG8bmtYE$LEBBpHfq5b3DF&GQee(OW$@z89{uhMM4L#?NOX}D10mx0e2L>d z{lZA&gf8l0$~QcCBr{MpE%muO!g`4l(V$#ovbf5s{9Q%{J5_ux6OqfM-uSY}a!IvX zlh}*A4m(DM(>K|d%IpR?3|sq5S|fhKxgv-&O`=m>)fUKD4d_gqK=@{5aMb>COiSvb zDwc&N;VG6jxdHtaxeheK+FO)Lcn0J!p{~1#QYvGI?@XslyVwu(aL%s=+voaBRc`S7 z#mcz3N|Wx>W_{qREQR0TKZoJPYB`pR{}PpMNIOe;fB1fQ+lXJO@Kn=7Ru*n}DSTNT z@i2pTM*_1l7LzH+tyhTA(efPjhN);vV{1;6r`O>~uNig9ub_ea>zM_=&U!zFSk$J9 zXUMvjND3<}{^_Jc-It{+2S4gUqli+S@ai?VUI{L#o9NYetcN-M3N!x%5j4FJ{)r9b zYQY@R7;tvY_Bv6*9J}mVPZ~>DQG|5A{p_k4R7tYpk%M7`sC87b))7crV9IwPjF-BH zh=9>9WOYwZoM4w3dj^epZi!8i_E10tEZO*`w7h&Aw=l>{RnI?9bqsbIaKBsUvV$ rQXg6nP#TWQxeUzLZ~fp${HTLlk%-jES@S)poezJk-haMNz-l)8=79?8ZSp-{8vy)3)yU+LQo%!rDR|^$xkJ z(4oQIbMm2h;gACQp&~Mkgsik z%po3^=!&FdQha61wDjs))*T^tQ@36L%l*PB< z5g5r0+DRfih2C;gvgqa;;@ffMrDJU)Y8mOAT_{>g4s-gf2c4iDKrd)tu(&lW!{30W&xw=X~tQ@7W z8KM;m6<&4<`2~*BI1J&m{9TWkX&oT+A_og-v44$C0 z4v8Vmu*xn8sUX4zz1B})#|Z)H-r?u`9gUDnr1s|w(X6U6y0%&cUA_Ey+GqkO)f@Zo z(|NBrcBqLGQRKcuU?~V|V@Ioul%qcBtQ=N@k>JzwruYS1(7xKUUTm$!zA*GvJy;(h zi{FLIc#q5VBg1d)+y3?x8K0KRa#o5^yaK+u7kY4(6FfH4{?&vK<7n#385+Ty_##$Y z>4qsgzN7-qnFCMCi4C~nn%mM$LF0S$i}dT;wzU2quh{*yBn=Mv?0COodk=s9{H!eK zGW1L{c*ISJ7dDqeB7jnKwvHn4;XIctiLDqrFE_Khdo+GzU$xu(|^ZU?^*C^^+U%NO*!=k!r{(@TgCVmbi1B@l#iA+ZjwQbOcwVYD4G) zaqe+)s0v2RO`SbhEOTM&ds60t)798B&w(#<-8|aHdp^B1h*Kk{(n3bFLf37s&)pN! zOH#}7&X>eXtE|o62Qt{HY;d`VtS@yZLL#h0sl^cWVGvShMeNg$uz2J)5gJ8~jhX>^ z5+cZa4X+)s%8R*7$axOd7#fhV+{Cbh#L+FIu^1eix0hh-$lmqKcnP_v88g{Esam<{C*N1Em|(W_5j`!9sW`lp)oxFPy%#-}We&k-0vT$-{y1xN(W*f5OKz2x$ z?`Wp)U~Hu7iCm^R#EL1(9ba>4xt`$wws2_V=+1gL!KPNOrR6EXu0YYm@XWk z_Ad#>`s3%YNVYutYUAVRN1jTJ=Q2}H1iM;(7E^&XQh2yQ=V!JolHaj@^d7d}``APh z$0DhFC0oFokZM7Uzpo^si~Dmt145ZgBM~7TQVWViYMdtTP;fsb$BWk{uY!pKQr4^s z?%H(o>akkXeTZ_!)TJ5P%LDuDyavTABRwq1CXi2#?jsahb`JkI)3N8{8YB>Z3x#B~ zsQ;R&le?AiuPHXGDOi4E!)U?3CJ#7>oVIxrYamzyCytKlYXz+MBz%r^rccdZdb2AS z+gw*)!xR7x7v{|5^*H5PyFH+XbB8zl{0dH?!X^Hlh0}n#F1Qf%;~)rYH`yWujD#fM z`@wZ%p7*nJ+`C?wcT^fLX8p+0iA88ath3thqCq$(v{~Xs^4s}ssH|ES zMLDiF!yaYHVx0VxCNT5nVjJE2iFuA`18*Qzh+r!>1q{~|w@ffyj>D_UH z_mP03w}K&+`WUK#-JJ}hofDHcSl4gwM*J>-9e782HU2$;pjo)>I-PDZ-hG|6B4LL; zmh)}rYw0Y^$QM>bXvmHQnjS4-E>#4y0R|U&rR{3^A2*qW7*mZaRJo2za--66_=!^_ zomMZ3vM(}Yvcv82$v)d@GjrS{x`Fe|k4!Yt>2Q$ho_Dus==J3bmm3n7FIExx_<3jJ z+Z3rHlM)Bcvyvk>&MoC^C^sq`~7Y(t-MrEs#frmG`Ha1NWPN-I+!?y?CcGfzp>P|Xz^lLZmat-XWlY}8MJ zYT9SBVgy4sIc3pm1((5GxR++w=S;oQ{Cp-y+&GiuD|l_H604f}yWnzV$l zGkt=A0U=8EL4}5pO25~bYCX{8PzxdIQzeroG#y;$$`Mo)o}L^zBG1JY#^%3zmQI)!M?!-yDqb5Wzm2NNVB(ayb{ylwtEHHow+kI!Cf zV6CsSbUWs}?RV@nbcEARZ~A6;TI;oo!!`Vu36ApVU1!hQ^h_wdB?N^sCdt$v_flnX zqfGDi);RWUYE{abE}`#!(4=T(C>D+ajjJS3`$VRaj)*5sK%PVG#L?ZniAO(%UFzqZ zj^K@shiJSWfFCtokKu7Rh?J7Rb2)e3K;d9{zIo|Cjy?OesJ1&>6L-#guIjUIQBGc%j6utfQdg8$qjW68a;k&`BE>b-&jh?1(!`|8!O=xvaWMJ?4LNuSj0VfF~Npv!O{^n(TG}ius z%Fc}Q`)fZr5tS`6Nm6+^1&(|wx@f#`7jszel%#Exau=&=SA1ta#&M5el13SsCxgHM za;)#k#{!uQBndGjt0B0k-q;^2<@?&TFD12=o*g-f5lFYN$9%FHvWB|GOZSEmZ+Yf? zLzX7Z@{(SFInN#2@vX$ckvVUd%F7^9FdvhFXte4Rm5B4t&-@SVdBx8JFQIxF7eGl^ zy$gl-eu7+f5^rEbGE)%u<>)6A?;_1^UAvQ2ih6k97jf6>>QS{+HGzAX^z9$*9@;ya z{7sKVd^L0z-4-VoT%D&qcXtm;v_&jyb#OF_S{mIeK~>wac?k+90USKaj#4(jaXPsf zrXCQ%#O@$e%zAJEbwF=TgJ6T0UNt;oxk&Cak^nwmvZn9mPkrTO5wK{qJ7$MblE$Xo zw#E~L33<*^+NWSbC&|d?SDb~@Nl_g*Oa>4KuQ&~B$`R8OX0H?C>5J|J1})wcaXNk^ z9F;G2Y}k0_kDjFN-~KHX97wUH^X01oU#{hp9Z!2iMD4;peER2Zx0R5QukSYRkzfL{3{?u8mGH=N}MMQRDKXy)QR~pz$4^<(zaH)^?Wg)sE0QE*SZr13kBdtuHofpxbeY{v5t5-gqx?#ki|7xX}kor2VS<1fpKJy|!ICSZE7 zf+G7;I#KLl3lGBm_HZvPC;i5h{y5m^d_2T4>%gJZvjeGEIHt6Ld0eo!_g} z^9qIz{`xmsRf$}0EsZ*S@sqfN+M6kX%VP-JqLmLSE1Wm>FKmm%y#ckj?e*{qC>`** z*Rj?@(Jav}cCe9gq&4mNvYO)sWpQNIuj3e!%7^{tp^HE%(RhMPkv1H=8@;an~j z`n^*wRv(JntLap6f@o)-_we<2O+NE>Tg|&!-Xd*S&0?ofU~4y}@y+=`k}ULYy`E%f ztgP*iA?uxRb}0uW^AY{;O5<1DN>`n?$z#I+>6$;ZpR559qpjwjK&i)Fnsv`r$zT5eT8tu^_51o;vIB~Ka#6`__Tb3;uAdzljSm@+toy5QQ2SMeDL>9ITGtTlZd z7WZD~2)EV*vprkDfN~!B6PrD|OU7rPfO@B$k(kKNiqo^o$Vl*fIngYz68F+MfFJ6c7_m6Mc$*WXwqJ z32N9A!QyO_jI)aNNv~S%t^p%^cG7#cU|R~8Na?=m;#=bHnlYM=6h*;s+@Xxq5Sw=g7Hq}BCS2=50E0$e0sME0*<;nKJ; z@ysmNP=>{SI9@EOp3d%q`WUe(2edG}oo%=87!~CsZ>1ahxF(pOh?JJsk*jjpKqur! z6laeU6{)vah_-vs-Dh)|5QYL3zIaI58v!CJfbegACsItA}wge3Z*GKj0p`xyU^ zTEaYxGaACcJjjmmS88OaZ)GTMrf+WZGcOVvY)68o#CW&j2^Q@8rd973qI{uz2Kxix zFIeUm6;7pMn^hPkD(Yo&y`I0QMuS&ZNiL<_)jAl1k37-~a)qZsb?;G2X7ZBNbC{1j zPQK;VGn$0ML(ZxFkz0EyMrcgWXfImvlCv(;dGyn(4ozs40a@~%<9tViFN75(f%8ym zHFmK35%;GD2>-r%C}?Jg>iP4_9gqq9WBbFa9?Ek61o&qQ`tR0hkS5CC8q$vq|Jl#` zyJ0XS)%@?>y~j9@dp5t3b|LStJnG;)Hhx_4|82aD^gqV`(Exai^0V}QrY_HTe%vOnH~UyJx-l*va<`5T3l@)ycKTvS;O4njBp00r{F MhH!QsG6n$ne}ZOyW&i*H literal 0 HcmV?d00001 diff --git a/openpyxl/reader/tests/data/extended_conditional_formatting_sheet.xml b/openpyxl/reader/tests/data/extended_conditional_formatting_sheet.xml new file mode 100644 index 0000000..463224f --- /dev/null +++ b/openpyxl/reader/tests/data/extended_conditional_formatting_sheet.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + 1 + + + 1 + + + + + 2 + + + 2 + + + + + 3 + + + 3 + + + + + 4 + + + 4 + + + + + 5 + + + 5 + + + + + 6 + + + 6 + + + + + + + + + + NOT(ISERROR(SEARCH($AA$7,A1))) + $AA$7 + + + + + + + + + + NOT(ISERROR(SEARCH($AA$6,A1))) + $AA$6 + + + + + + + + + + + + + NOT(ISERROR(SEARCH($AA$5,A1))) + $AA$5 + + + + + + + + + + + + + NOT(ISERROR(SEARCH($AA$4,A1))) + $AA$4 + + + + + + + + + + + + + NOT(ISERROR(SEARCH($AA$3,A1))) + $AA$3 + + + + + + + + + + + + + + + NOT(ISERROR(SEARCH($AA$2,A1))) + $AA$2 + + + + + + + + + + + + A1:A6 + + + + + diff --git a/openpyxl/reader/tests/data/frozen_view_worksheet.xml b/openpyxl/reader/tests/data/frozen_view_worksheet.xml new file mode 100644 index 0000000..44f96cd --- /dev/null +++ b/openpyxl/reader/tests/data/frozen_view_worksheet.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/hidden_rows_cols.xml b/openpyxl/reader/tests/data/hidden_rows_cols.xml new file mode 100644 index 0000000..23365f8 --- /dev/null +++ b/openpyxl/reader/tests/data/hidden_rows_cols.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/hidden_sheets.xml b/openpyxl/reader/tests/data/hidden_sheets.xml new file mode 100644 index 0000000..ce16ec3 --- /dev/null +++ b/openpyxl/reader/tests/data/hidden_sheets.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/jasper_sheet.xml b/openpyxl/reader/tests/data/jasper_sheet.xml new file mode 100644 index 0000000..61afeba --- /dev/null +++ b/openpyxl/reader/tests/data/jasper_sheet.xml @@ -0,0 +1,2324 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stock Ventas Recepción + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 11 de September de 2014 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Marca: + + + + + + + + + + + + ADIDAS + + + + + + + + + + + + + + Desc: + + + + + + + + + + 647196 FEF A JSY (HOMBRE) / YEAH +mola + + + + + + + + + + + Lineal: + + + + + + + + + + NO ASIGNADO + + + + + + + + + + + PMay: + + + + + + + + + + 0.00 + + + + + + + + + + + PVP: + + + + + + + + + + + 19.90 € + + + + + + + + + + + + + + + + + + + + Referencia: + + + + + + + + + + + + 02924303 + + + + + + + + + + + + + + Sección: + + + + + + + + + + TEXTIL + + + + + + + + + + + Tempo: + + + + + + + + + + 2009 O/I + + + + + + + + + + + PCost: + + + + + + + + + + 5.65 € + + + + + + + + + + + GenB: + + + + + + + + + + + 10.00 € + + + + + + + + + + + + + + + + + + + + Cod. Prov: + + + + + + + + + + + + 647196/MOLA + + + + + + + + + + + + + + Familia: + + + + + + + + + + REPLICAS + + + + + + + + + + + Género: + + + + + + + + + + HOMBRE + + + + + + + + + + + PmC: + + + + + + + + + + 5.65 € + + + + + + + + + + + Reb: + + + + + + + + + + + + + + + + + + + + + + + + Color: + + + + + + + + + + + + mola/mogollón+yeah + + + + + + + + + + + + + + Subfamilia: + + + + + + + + + + CAM. OFICIAL.HOMBRE + + + + + + + + + + + + + + PmV: + + + + + + + + + + 14.06 € + + + + + + + + + + + Margen: + + + + + + + + + + + 51.37 % + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + S + + + + + + + + + + + M + + + + + + + + + + L + + + + + + + + + + + XL + + + + + + + + + + Total + + + + + + + + + + + + + + + + + + + Descripción + + + + + + + + + + + + Precio + + + + + + + + + + + Uni + + + + + + + + + + + + + + + 011 TIENDA ID 11 + + + + + + + + + + Stock: + + + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + Ofe 23150 10% DESCUENTO + + + + + + + + + + + + 18.00 € + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + Transito: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tari PVP_RECOMENDADO + + + + + + + + + + + + 17.41 € + + + + + + + + + + + 4 + + + + + + + + + + + + + + + + Ventas: + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tari PVP_ACTUAL + + + + + + + + + + + + 9.91 € + + + + + + + + + + + 11 + + + + + + + + + + + + + + + 109 TIENDA ID 109 + + + + + + + + + + Stock: + + + + + + + + + + + -13 + + + + + + + + + + + 8 + + + + + + + + + + + + + + + + + -5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Pre. Medio y Tot Uni: + + + + + + + + + + + + 12.92 € + + + + + + + + + + + 18 + + + + + + + + + + + + + + + + Transito: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ventas: + + + + + + + + + + + 10 + + + + + + + + + + + 7 + + + + + + + + + + + + + + + + + 17 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 200 ALMACEN ID 2 + + + + + + + + + + Stock: + + + + + + + + + + + 100 + + + + + + + + + + + 5 + + + + + + + + + + 5 + + + + + + + + + + + 5 + + + + + + + + + + 115 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Transito: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ventas: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 210 ALMACEN ID 2 + + + + + + + + + + Stock: + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Transito: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ventas: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Total Stock: + + + + + + + + + + + + 186 + + + + + + + + + + + 13 + + + + + + + + + + 5 + + + + + + + + + + + 5 + + + + + + + + + + 209 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Total Ventas: + + + + + + + + + + + + 11 + + + + + + + + + + + 7 + + + + + + + + + + + + + + + + + 18 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 109 TIENDA ID 109 + + + + + + + + + + null + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Total Recepciones: + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Página 1 de + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openpyxl/reader/tests/data/legacy_drawing.xlsm b/openpyxl/reader/tests/data/legacy_drawing.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..7ac1c2037244baf537e387258e103efa9318abee GIT binary patch literal 12452 zcmb7KWmr{N7rt~$gQT={Bi-HI-Q6t+(nxoAhk!_TgLJnv3eti!=y%omMsQ|yhUYmv z=l(eFUVH7e-n~jr5)2#-002M(A3^}vkgP@;2msIz2>_r1Z~>Zvwl+@2Hcq-q?smqG z+O%%gR?12+05Cwo2+G&T*$oZ=0=^9f0DSw`6F)4oL5~)2F4io#Nh6`97B*Hy0jHP1 zBnF0PS81DC^f|VIVt;Yec|wI)38h5EAeOCi^fJQM%JgYnZGoD8-b|B*mMW@zmZDCb zjeKb0mhQVUa&n(Zz2+_rtEld!y<>x`W*9MuQoQLLaf+L`7$rZ9qGJ%e^Py+eQ5Ouf zMt~ZBh=J7p%^;;^Qm106dBLFqM7AK-*pNP!wu4w9W|EL=FFliKPyzZf-|kz7cb}jn z8c3C1?)Az|LN>002;h@!W{iSl?yC4Tf_2<@E>lkFw5TU2z4Q(~8i5#j&Z~7jXF;7o zP_UvmFH;`wd2WBXbNd=wCfPheLW_zSoh7*VbykkI(~(8gb(V7z3Vaj?Y5#%CEyU&# zIE4`SQnI?@{%z!t{fRU?3Z4F5MPXu26&8rX!oxhK(OOnKVpb&iFVfMo>+@hYhukrO z?x}d&ZzXNFIDKbtZSu4?K0VaTtu!w*%g8~jwtW`RymD}f6ut~Tqc638H2dC6GcMBY z=ElX;QYBFoW&Uc@*MP^1We3b^{x;a;3qTHd(6GQm1_0`O-9*2={P7PGc+k2I##WAW z-_ABALP;eX`22LXU%&rhC^gFT#Jj-nr9c4yxIe$A{fhD{11XCcw&3PdxA;s&aBh)P?zI1e#mYQo`6xE2tD836+=uJ2OTzv@ zkk%uG%tCpVfP`2`s`sQp#4+c>B48P;3tG0H&a2t%i6mc)iLDOu=wc=M&wOdfG472V z=2{Q(U(+_aaOvnlP|EygeZv6tWn^n8=U{91UsUcf2%^OZ^i6r7%8~Cv7}`1*|IRa? z#pp_Q(WCXOK(q<=d!(35t_dvq3}oO!tDvn2IMku1@{)|(tSnbAHX?*XC2po&H*M*} z(7BX|dv#ncY=e$73&NP`8y<(EXOTCfIJ5N&JYAD?f8)qLZ!0bj{jk>-5@9}TF zRh@v!#{yCv(p}~2+u8k2_vtbGvOuE`I25}HqI6BLk%E>sN5am+NZ2CjRV8ADO}nD+N^nS&MxXTc-mQmeQ8(=O=h``x^JFV`AgTR zN^XLt*hEYNd`}1;cs&$iT$7h1C1h#2wEDSrNXD4)ok8I_bAjmR!urfyZY@jCiFqU@ zaUO7jCJK5fJNxWKu@!%4%WjxOc+^UFt~?aG-dgfcK%?-%%Pc%`c921bzlB69^lvPUS)y7tz@37(YxkdF;( zU`5yspcb>uEj0-tF4wxSB>@5*P5PQ=Ew>9{?}}Ku*QefaMyX{I(ITj~&l;42-U`2E ze1*S}vxakaDU&~rb%ne>YnmCZxuIF}2{B}~er7UTEsl~8UpGh7gB7Xj4W`vbX2L)j z0q@h1fcMbLh>5g88~jhx3-%WunfiWtTYUrh&)x?HJUut7-%{P*Q{`9J1%B*gW^8Tz zTZkWQd2Nty-3kE!6ykipR{wO zU2f%d(P7-2O@x99{^`Zrc04Kt%B5Gao- z45d)vNZBB3>v|1$P??9+G*TVkKZDeL5i{>U+TEHdIYr@MMQ{K55qm5**%*ik9^5AQ zjh)y56&wRKI8iD#Bw``Zs%?Z48>qjt!bdM&fgEKRp^8?+Ow{^m>A%(laU(zC%WdJP*Y&9+liY{)EnVKv8y_~dRy;p%Ax6R<(fEjRc#0VkjY~TOpSi); zVu43Rv*Gm6cUBAd5c6R_e}=C7oYgcb*-l%*c^MA`q8+@MqeXSxnHNP-7@>VvUJK;F zZlqD`;c!Dn|HN=IS+;t8(oQ{h7^2i`k3NCL%z{_3#V?HMh|pPFv4n7fNC;t#EjbnW{4V3+cXLW{>q1vwNxZOZ1&XLz=NtSQ(~{?GWBn*mP(Ybb zB|9l?nnMa<`$WR+hnzMMUJ}K|Tr;hy;?klEagc=Ndhz8kkH1B!3Td2WP$Dv%bDg6u zCkn_xMLmXmuO(`8B2L$7Z!wij79DXbCZiW>P(2tg52G_2Wd(2FE^b|aD3>#mFjHs# zFqu`XmJz)mj-WXulIck=3B|5QzCs@}d$AQ0tn>A=`HZf*RU{_T(T@h&LD!EGwyn9O zU0{rQu3>Lm1=>Q<8)*|q+i2+|E6dIo*i-9kKjJI)o=t~E<1o^#qYS}8(kCN2zD|@bpT#ljh?VoL{mr&OVOCd`!A1F)?AMp1n{)u6zU$WY%3^fateu zQ`rPcxmIL00tL!AMZ>DYaxzHG_IaUj=)uqgbRtV}Sx@Wptx-6<>w?S?EnHA#(1Pb9Rt(G7(e4B zN;foN+OR2ngy<}}4@xA^^UC?@6?*TPPfwgjie56oIU8%-M7#mf@1pjYWxS?A8!i?9 z5RB`}WmZekB(k?~T` zGyb)LPSe59E^p2WuFS^eiROYa7mTlH$V9UoHJNA)PY-M52a&2R*Pmq!7rx(939pjH zIKLV>gKs^Paj9uZ$*FaT?iZ!l zf}>O{5Ku1`$BZiBpr3MWTGi^%@)|t|OGG6^tfMa~5AQ@M_7lt`6XSz>aoRWIWM9y- zo$AKHH>zXGjfuiKIgniU)Vs$~1R_MKFo5Wsa)05S;n_u`Y@rSEdxAa0B|JaQCv{9S zd6Px*u!YCg8x-v$$CS+q)>ET3J{rAlC}-D(91gxVY9G7pSZ&^LN@A+O!!LkS@` zb8g41oGB7LxpIVAJP|=WIoD0A6DSaI^Hxxg^Z_QO1IFBwnSJt}Z~}%1P^qnoUm&C> z$sT;*Zo>0?e^uR4#0GV<-g8wkgg{ah!q>72e{?X{5jE!A`gx_lM6OGxmhwV=^RpUF z=-!(NG~KWiVVS)L*f+4*3_P%*r@{Ju`U-3_FB&TiHX{^>SY2r%@w&!ZVspqO9J`Kb zo@B{XjH42=a!tOPF16R&(k;KbV_G{u<#cF&F^M}F-Z?P4BZ>S?hSyvQ!!QKQ@alm^ zM*I(GKg{fY=9l-^^sy{zFa(qVN%$P>vtZFJ$r5d#KK;H3c-^`uU$heMs}llr&D-l2 z(0&NCTJCO887BM z&gNzv8aa#S+fsRdqy}HrKEN&*xD8a4DKIblrJ@+W#(6&!_4O3KK0oEzWig`?U<^n> zhrWkM?`=$6A&}?iB$1z@79%3X%=0p*`jeFJFLGu%X(FGFrNyj}ta!qkt6ulNNLIl2 zQ~g6#+a3@Vi-oBkNDXGTHEC%S08LkHBi9@bSVfgb0`=)MBv!hRRElx?N_gv}l z=j3-StHMfqszy8dp?)awYvCzag_89tP=RK^!qY#{**Eq5s<_aj+;6i;kJN&>&5P9B z_qsX%i4lZPcov8Pvsh!AxrGsOHXE#XpM4vTV1G<{{&Pf#r^g%cm&??MDh?mDEY84Z zD3~4b&#%&aqLV19GEOpi%ST%6-Z#@HkD2L>xUOCZUtKD`7QSAE1&juF)U(qySD46e z8iC~7aldSrBNNqxl@)D@&@$#XI?YuO_4D#Zi5I|HrXSj)z)jkc=tOx}0KS2P0;l0t zvVvaXK(pxRf}(5Elkn7fmz~mn-iV{s?NdaB(nLxpGo_V9v=E9!R?0r8zY zRwYbCr~!~v<-gC)d5UBmq|4k)|V6V7P%n{ z2SIu!IEMNeu}YS@r<=pvb;}i4hl-RHmrn6&-tC%5vfz6a@s)bnhw53zq&ywA??Ux> zR5hoOCriarHZca7v%Mpdh0=S)r#hdUagR>gb74nZYt#DgRQFhOSagEaRz^hzbshBc z#0iBb_4%zH8Ft8Xgn3D42W1Rydo7yk$u;q5B-<4^6`;2%3 z^PjJH`0vbVoEN3?4B=bxb5tOaWlef9{PdEzi^@MXE&+a+!7 zIyY|(0kfWcu5w)I7pGC>HS^Qt38kfrNM1(0@r{_FLbLoXpI#5I7l48}p_Glin@5Mh z@a%|Gf)+h02}==)~>ovN|zjVQc>q#8Z^Glj61{wby9Tk#W&d4 z;=?_L5~8%Yi*pJnx8!vTA_KxaaL&7)B5%dt?NKxJv|Q+iyIjOBW1U%EOIWblFGL$h z&4eDxOs^`w!BBK*NnyOA-M*5Lb!tj}z3BNwBv4@D6W@k9uh`-T=!Yt_gps`;7)Z)V zh7*ZIp%OWA+u~1c?{hYMvg#x=&_d0BX`u|iw@~TGR3L4u?s&g?D@gkar167G3rMj< zOmW^rGErN~*Z09;ydsM6h$7D;^7pE^J+E;h-OQMoD7R#v35=qei1(_)j8WR^IFD3)5{wb* zN@Q9$3+C19YzgnC+;TSR^W-$xumAMaBEqHG+S|l()uKcrri^a)}|QNCk&SDILxd5S{|=FrVz8! zHEK0C3`$waCpHhrG?_2xA&{{v-`EIj_K?4XK8ikzf3gVMv+f1n*4s<56za6P8^&sB zh=4C-95-Rp`=oaPg}QobE)YK*GT$ho7aOgH>KyvW zfP-k7rS5@dnZVA%F0ZHa>L)B8W+BV%$S2eI2FAXpo66KQA!Ex{d{z03OUMs*$Zcz7 zz|T#39AxIBoxO7KMqAY8j|{yF2#r@}M!P>dn!Tf1Wo85U1i^7Ts8=&Fb9G5^<0o=L zw;LY{rmn#AMs-gvHT@8|2R_YLvflS}&oXo&1Tf`>l|_m7v~=0fR8nzoAs zXkMAsU!J?kTKAQw3fY(RT9=4VYt$?`3e!i()7SIaq@Jy~w!1Vs7c&nd+Sz!nu-kbS zBwlM|(M$$XC9iE$cL$4?z|yK>eLib`7)H;S@WvOKR6P)JXmxOYOLp>E_G3=RGGu@l z69`LJe9bTkG<=zw^iZ-z61Z)#lQebFQ*^YHJwPKy2R-~`J9A2Or&8Gb?u;KN*YP7W zmNk^o0+w_wn89k2gm)4eWY$m`livJgFLZ2EK$M*YsdXJX_B&9k=iKwzjGv{*A7dH~ z7psWGpTQG=DkmT<7s9g1&yj`T&+i6WnlQqXKi{{RI#3$%uaBu#Xk>}km@X5FPZFNY z!t_{tram%~OTvQ~$zytKQ|3~Y$HG}z$L@{R*GHUQth@|e_LMk67WMHs8!Rt@j4W!Q z`BQr@-#Sw!gc2An{XmA-R<;m{mQk98Z+Tb{mxjkG7NNr3qSy43!Wd%m0w1&xOOna? z%lCx(HWUQr8iDe;_PU@fm}RUzGUtliI&tE9*n4RZMZ)?~4>zLEj|=96S({qqgYf6c z^oSVo#Rsf*Gt3dk#*aGB4Y-^Eq;9KT=Sm$6{f4cymsuw_Iw2oXlJ_LFoA4ST_nF|m zH#*ArGg+;}tLq?bMX{~-XcSo-1Oi!dXE0@Iqoi;af}CKIh+CFMmF?}uCFj-@^oiy? z)FiT9unK%xAMP`oGg|4e3NeLsW5T3!HjAwja>c6VjN5^42zWre);a_IxSysUI$kjD zsjPlZ*-Eaz7t0O(KE9WZPqrHtUj~qX{-9$XPKC#J&@-sY?nL`^sig+49>U%)w7+8? z_j*o#oAIN$U7HOfwSHVfWx-_%`+mP9k_fz?jUjiwye}pkt-tCgi)6TZ>~)1>-Ndd( z`k*F(LT6*IV&wRqlEs&ZpZjitL9sl>^Qq{Em{Xsf8JTfOw%I-O&NzT*3fjk(v3uw> z+a1%owLF}-$~JmqrinzD;9u@HbK(`^^3Y~y)C=UFLl=baL5HD}gVld+RdF~mb|e8) zq6*-H{WHw}0e|a~2qXLteCd(+##>&vnwiiEs~S*KOKCOO`9P5yQ%<`CI$?v=CBogD zS1iTkT3 zy-Dl`<8EB0O;6RgC6UO>Nj0wSOjs!N5@TBZMp~`w0Pg-C!-`PV^?(FB5A0$5VHS=~ z?pDUX-CRwaWGFEDe0~`487kW&r(1e~WKKuywNHSxx)^K=Wo=sUbeGuijQI!eLoo*( zX-POqx*0S4m9Ups`L1PX+W}aEH3LVlc-w=E1roLP8E@RHcBJWFN0h|CmZ<@0b-n3k zKXt0nPj8po@BxUxJp5sLsj2^*!6-@1Gi@e_1PL5*Jgeb|xdnO+jYlL9;*fslLJH+} zBtu3Y>5#;S9Tm@EX)Jdx65tLV_yj~#n;>%LBP_v^*Wg8%*&787Bj_A=nw3(`Lg9`p z_rSSmzKfHBi8`*H7*JQ*7HNQw1ng&5ndY~Z#ItrF?lm7zc?r@fJvRv!&%n?y^TRbV zUWQ=~k-*@_P&z7G(@~f2?;$Kft$+@2@rvmhZ;t7PpLH+TV%Ca(X>1($xXox`tR;b& z?Vz~Q&bhK1XFK8M@ewk|rQ*%-qwUTvgN~5^Hf?_2Tq4$X5oFJZr7M$g$RtfC5#YlLp?znB)*>Jf1)s&Xts_PFBl z=N#=V=Y(9@KF8Td&Xhjm^5GE&X~tAHtU_?QJhq+AQTdSAKD-p12k|oNHUnrXlBY6DSyUlA87f{GZwDk51j4 zs@K1oYmg`|&_4otvM8{u`Ui7;hxl5gHL$g{ykEolhdG#*7-`9FdZfTZ-!FtiO9f?* zf~^`ckz2fpqdT@!y;Zg()%& zeW@3+(nBOkAGL)yz=Fr)$rUBMncHXkV)Ij+Bx4WnAhpeV7ip~5969c5NfD6|0#(N` zL&GJETxV_;4DT{OI=FnE{+9p#=yvYL>=0=ItA>U^#z6ee{&%(a%WPGS-v&sA&8j<( zs8=LWVpY(h36!msY0(uZ(xjUSF(UBhANZ`eZjeJ1CuTG=EvF|h^&D$aIe_lwjbllZ zF_31#5$Ew3eeyW@OrP8BUJjuuA}B()y3N?urs>p+f8L7=18bHzK5%V}+< z3Y|o;9;cO%+ob0)wqqtbX$a>RvOK8haZ&#QKdNX2gT?px@vt2y%o!8pU$h;pVM?C9 zvZRu2u3yb3N0vKQxkL!WyNxoXFqFNu;;oAxYbcdM?00`$L$58O9@5tuM*f090kd5O zR+lh#r2H+^8^L!>9HFRb>j8<6=Z=60YD*4ImEGz`QVy#x)3XoR?WmB~7aO`e%Z21F zCWmbr=cQTUymG}mlzsX>WD*QMlnfNw7`goE+Op-FJLyNZ zjY=k!re)4?{Twn#gk5D2P(gHS)Ddi+h@d6}=m9dd4Ff&|Ua?nHwzf*>EqzhqulNWM zoJaOmBuXqk1*%l`w9^$PHTT0$sw;}M@!wQ6a$-gFpuY&}=Y$wyL2;kFzmnJ&~f*$X`+DSDNn<#R8C zj5vfaaK%LxMQue)tWnm|@Xa0KhEB3HNl&7ho?IV_`1NaANcZ6AVZtZ|wQIWZZ{=Ks z_0JE&J6zSJEj5V5n#1KB0~HGbiVya~{_hXbJp{4|007i7AkXLP+ux4VL3|&n`>7QC z9r*7j5g>tO((ivrfPMZCK>Ytx9T4j))%`8j40_e8ivHqx4|99bF?g^(3 zx9sMiw?en)F{w(nwPx_k%&Cl+BzfpEqZaAdBNB$xG zzwof{r}@`L)7^!bk$6wZGo@((|{U+KKN01fa5(7*o_`Nsl()II;*0{1Ie zcNeI|zQ4fzde*Pd|DIJ4?r9u={|){BDf!p_1Q7CHsQiA0e^+49pCSJb{zq8xE0KR_ z{C-w_7oX|THXG{oZHoR-$i^cL?QQ==OdNa#unS z;yZA6W9om+BVT*1_v7=s_`)RL@b4o3nqPk$*!_6&F6a*FH_)G=%YV`=1n|QA+VH{w NNWuUBf{(xc`X8V6InMw9 literal 0 HcmV?d00001 diff --git a/openpyxl/reader/tests/data/legacy_drawing_worksheet.xml b/openpyxl/reader/tests/data/legacy_drawing_worksheet.xml new file mode 100644 index 0000000..58901d9 --- /dev/null +++ b/openpyxl/reader/tests/data/legacy_drawing_worksheet.xml @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + 4 + + + + + 0 + + + + + 1 + + + 1 + + + + + 2 + + + + + 2 + + + 3 + + + + + 4 + + + + + 35 + + + + + + + + + + + + + + + + + 2 + 152400 + 2 + 123825 + + + 3 + 438150 + 3 + 190500 + + + + + + + + + + + + + 7 + 381000 + 3 + 0 + + + 9 + 476250 + 4 + 47625 + + + + + + + + + + + + + 0 + 476250 + 1 + 142875 + + + 4 + 285750 + 14 + 9525 + + + + + + + + + + + + + 2 + 190500 + 5 + 19050 + + + 3 + 371475 + 6 + 38100 + + + + + + + + + + + + + 2 + 257175 + 6 + 66675 + + + 3 + 323850 + 7 + 95250 + + + + + + + + + + + + + 2 + 304800 + 7 + 180975 + + + 3 + 485775 + 9 + 19050 + + + + + + + + + + + + + 5 + 400050 + 2 + 9525 + + + 10 + 28575 + 14 + 19050 + + + + + + + + + + + + + 7 + 390525 + 5 + 133350 + + + 9 + 85725 + 8 + 114300 + + + + + + + + + + + + + 8 + 0 + 9 + 171450 + + + 8 + 190500 + 11 + 0 + + + + + + + + + + + + + 2 + 38100 + 11 + 9525 + + + 4 + 0 + 12 + 9525 + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/null_file.xlsx b/openpyxl/reader/tests/data/null_file.xlsx new file mode 100644 index 0000000..e69de29 diff --git a/openpyxl/reader/tests/data/protected_sheet.xml b/openpyxl/reader/tests/data/protected_sheet.xml new file mode 100644 index 0000000..287242b --- /dev/null +++ b/openpyxl/reader/tests/data/protected_sheet.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + 0 + + + 1 + + + 2 + + + 3 + + + + + SUM(1) + 1 + + + SUM(1) + 1 + + + + 1 + + + + 1 + + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/shared-strings-rich.xml b/openpyxl/reader/tests/data/shared-strings-rich.xml new file mode 100644 index 0000000..a5f8672 --- /dev/null +++ b/openpyxl/reader/tests/data/shared-strings-rich.xml @@ -0,0 +1,33 @@ + + + + Welcome + + + + to the best + + + + + + + + + shop in + + + + + + + + + + town + + + + let's play + + \ No newline at end of file diff --git a/openpyxl/reader/tests/data/sharedStrings-emptystring.xml b/openpyxl/reader/tests/data/sharedStrings-emptystring.xml new file mode 100644 index 0000000..2434289 --- /dev/null +++ b/openpyxl/reader/tests/data/sharedStrings-emptystring.xml @@ -0,0 +1,2 @@ + +Testing empty cell diff --git a/openpyxl/reader/tests/data/sharedStrings.xml b/openpyxl/reader/tests/data/sharedStrings.xml new file mode 100644 index 0000000..1e62056 --- /dev/null +++ b/openpyxl/reader/tests/data/sharedStrings.xml @@ -0,0 +1,2 @@ + +This is cell A1 in Sheet 1This is cell G5 diff --git a/openpyxl/reader/tests/data/sharedStrings2.xml b/openpyxl/reader/tests/data/sharedStrings2.xml new file mode 100644 index 0000000..918564a --- /dev/null +++ b/openpyxl/reader/tests/data/sharedStrings2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/openpyxl/reader/tests/data/worksheet_formulae.xml b/openpyxl/reader/tests/data/worksheet_formulae.xml new file mode 100644 index 0000000..355125e --- /dev/null +++ b/openpyxl/reader/tests/data/worksheet_formulae.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + 3 + + + + + 12345 + + + 0 + + + + + 12345 + 12345 + + + 4 + + + + + A2+A3 + 24690 + + + 5 + + + + + SUM(A2:A4) + 49380 + + + 6 + + + + + 2 + + + + + A4*2 + 49380 + + + B4*2 + #VALUE! + + + + 0 + + + + 0 + + + + 0 + + + + + 1 + + + + + 1 + + + 11 + + + SUM(A10:A14*B10:B14) + 605 + + + + + 2 + + + 22 + + + 605 + + + + + 3 + + + 33 + + + 605 + + + + + 4 + + + 44 + + + 605 + + + + + 5 + + + 55 + + + 605 + + + + + IF(ISBLANK(B16), "Düsseldorf", B16) + Düsseldorf + + + + + + + + + + + + diff --git a/openpyxl/reader/tests/data/worksheet_without_coordinates.xml b/openpyxl/reader/tests/data/worksheet_without_coordinates.xml new file mode 100644 index 0000000..b6905ec --- /dev/null +++ b/openpyxl/reader/tests/data/worksheet_without_coordinates.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + 2 + + + 4 + + + 3 + + + 6 + + + 9 + + + + + 7 + + + 10 + + + 0 + + + 8 + + + 21.899999618530273 + + + + + 1 + + + + + 5 + + + + + diff --git a/openpyxl/reader/tests/test_excel.py b/openpyxl/reader/tests/test_excel.py new file mode 100644 index 0000000..2e0d222 --- /dev/null +++ b/openpyxl/reader/tests/test_excel.py @@ -0,0 +1,164 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from io import BytesIO +from tempfile import NamedTemporaryFile +from zipfile import BadZipfile, ZipFile + +from openpyxl.packaging.manifest import Manifest, Override +from openpyxl.utils.exceptions import InvalidFileException +from openpyxl.xml.functions import fromstring +from openpyxl.xml.constants import ( + ARC_WORKBOOK, + XLSM, + XLSX, + XLTM, + XLTX, +) + +import pytest + + +@pytest.fixture +def load_workbook(): + from ..excel import load_workbook + return load_workbook + + +def test_read_empty_file(datadir, load_workbook): + datadir.chdir() + with pytest.raises(BadZipfile): + load_workbook('null_file.xlsx') + + +def test_load_workbook_from_fileobj(datadir, load_workbook): + """ can a workbook be loaded from a file object without exceptions + This tests for regressions of + https://bitbucket.org/openpyxl/openpyxl/issue/433 + """ + datadir.chdir() + with open('empty_with_no_properties.xlsx', 'rb') as f: + load_workbook(f) + + +def test_repair_central_directory(): + from ..excel import repair_central_directory, CENTRAL_DIRECTORY_SIGNATURE + + data_a = b"foobarbaz" + CENTRAL_DIRECTORY_SIGNATURE + data_b = b"bazbarfoo1234567890123456890" + + # The repair_central_directory looks for a magic set of bytes + # (CENTRAL_DIRECTORY_SIGNATURE) and strips off everything 18 bytes past the sequence + f = repair_central_directory(BytesIO(data_a + data_b), True) + assert f.read() == data_a + data_b[:18] + + f = repair_central_directory(BytesIO(data_b), True) + assert f.read() == data_b + + +@pytest.mark.parametrize('wb_type, wb_name', [ + (ct, name) for ct in [XLSX, XLSM, XLTX, XLTM] + for name in ['/' + ARC_WORKBOOK, '/xl/spqr.xml'] +]) +def test_find_standard_workbook_part(datadir, wb_type, wb_name): + from ..excel import _find_workbook_part + + src = """ + + + + """.format(wb_type, wb_name) + node = fromstring(src) + package = Manifest.from_tree(node) + + assert _find_workbook_part(package) == Override(wb_name, wb_type) + + +def test_no_workbook(): + from ..excel import _find_workbook_part + + with pytest.raises(IOError): + part = _find_workbook_part(Manifest()) + + +def test_overwritten_default(): + from ..excel import _find_workbook_part + + src = """ + + + + """ + node = fromstring(src) + package = Manifest.from_tree(node) + + assert _find_workbook_part(package) == Override("/xl/workbook.xml", XLSX) + + +@pytest.mark.parametrize("extension", + ['.xlsb', '.xls', 'no-format'] + ) +def test_invalid_file_extension(extension, load_workbook): + tmp = NamedTemporaryFile(suffix=extension) + with pytest.raises(InvalidFileException): + load_workbook(filename=tmp.name) + + +def test_style_assignment(datadir, load_workbook): + datadir.chdir() + + wb = load_workbook("complex-styles.xlsx") + assert len(wb._alignments) == 9 + assert len(wb._fills) == 6 + assert len(wb._fonts) == 8 + assert len(wb._borders) == 7 + assert len(wb._number_formats) == 0 + assert len(wb._protections) == 1 + + +@pytest.mark.parametrize("ro", [False, True]) +def test_close_read(datadir, load_workbook, ro): + datadir.chdir() + + wb = load_workbook("complex-styles.xlsx", read_only=ro) + assert hasattr(wb, '_archive') is ro + + wb.close() + + if ro: + assert wb._archive.fp is None + + +@pytest.mark.parametrize("wo", [False, True]) +def test_close_write(wo): + from openpyxl.workbook import Workbook + wb = Workbook(write_only=wo) + wb.close() + + +def test_read_stringio(load_workbook): + filelike = BytesIO(b"certainly not a valid XSLX content") + # Test invalid file-like objects are detected and not handled as regular files + with pytest.raises(BadZipfile): + load_workbook(filelike) + + +def test_load_workbook_with_vba(datadir, load_workbook): + datadir.chdir() + + test_file = 'legacy_drawing.xlsm' + # open the workbook directly from the file + wb1 = load_workbook(test_file, keep_vba=True) + # open again from a BytesIO copy + with open(test_file, 'rb') as f: + wb2 = load_workbook(BytesIO(f.read()), keep_vba=True) + assert wb1.vba_archive.namelist() == wb2.vba_archive.namelist() + assert wb1.vba_archive.namelist() == ZipFile(test_file, 'r').namelist() + + +def test_no_external_links(datadir, load_workbook): + datadir.chdir() + + wb = load_workbook("bug137.xlsx", keep_links=False) + assert wb.keep_links is False diff --git a/openpyxl/reader/tests/test_strings.py b/openpyxl/reader/tests/test_strings.py new file mode 100644 index 0000000..8d5892a --- /dev/null +++ b/openpyxl/reader/tests/test_strings.py @@ -0,0 +1,31 @@ +# Copyright (c) 2010-2018 openpyxl + + +# package imports +from openpyxl.reader.strings import read_string_table + + +def test_read_string_table(datadir): + datadir.chdir() + src = 'sharedStrings.xml' + with open(src) as content: + assert read_string_table(content.read()) == [ + u'This is cell A1 in Sheet 1', u'This is cell G5'] + + +def test_empty_string(datadir): + datadir.chdir() + src = 'sharedStrings-emptystring.xml' + with open(src) as content: + assert read_string_table(content.read()) == [u'Testing empty cell', u''] + + +def test_formatted_string_table(datadir): + datadir.chdir() + src = 'shared-strings-rich.xml' + with open(src) as content: + assert read_string_table(content.read()) == [ + u'Welcome', + u'to the best shop in town', + u" let's play " + ] diff --git a/openpyxl/reader/tests/test_style.py b/openpyxl/reader/tests/test_style.py new file mode 100644 index 0000000..93207a9 --- /dev/null +++ b/openpyxl/reader/tests/test_style.py @@ -0,0 +1,65 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.reader.excel import load_workbook +from openpyxl.styles import ( + Color, + Font, + PatternFill, + Border, + Side, + Alignment, +) + + +def test_read_complex_style(datadir): + datadir.chdir() + wb = load_workbook("complex-styles.xlsx") + ws = wb.active + assert ws.column_dimensions['A'].width == 31.1640625 + + assert ws.column_dimensions['I'].font == Font(name="Calibri", sz=12.0, color='FF3300FF', scheme='minor') + assert ws.column_dimensions['I'].fill == PatternFill(patternType='solid', + fgColor='FF006600', bgColor=Color(indexed=64)) + + assert ws['A2'].font == Font(sz=10, name='Arial', color=Color(theme=1)) + assert ws['A3'].font == Font(sz=12, name='Arial', bold=True, color=Color(theme=1)) + assert ws['A4'].font == Font(sz=14, name='Arial', italic=True, color=Color(theme=1)) + + assert ws['A5'].font.color.value == 'FF3300FF' + assert ws['A6'].font.color.value == 9 + assert ws['A7'].fill.start_color.value == 'FFFFFF66' + assert ws['A8'].fill.start_color.value == 8 + assert ws['A9'].alignment.horizontal == 'left' + assert ws['A10'].alignment.horizontal == 'right' + assert ws['A11'].alignment.horizontal == 'center' + assert ws['A12'].alignment.vertical == 'top' + assert ws['A13'].alignment.vertical == 'center' + assert ws['A15'].number_format == '0.00' + assert ws['A16'].number_format == 'mm-dd-yy' + assert ws['A17'].number_format == '0.00%' + + assert 'A18:B18' in ws.merged_cells + + assert ws['A19'].border == Border( + left=Side(style='thin', color='FF006600'), + top=Side(style='thin', color='FF006600'), + right=Side(style='thin', color='FF006600'), + bottom=Side(style='thin', color='FF006600'), + ) + + assert ws['A21'].border == Border( + left=Side(style='double', color=Color(theme=7)), + top=Side(style='double', color=Color(theme=7)), + right=Side(style='double', color=Color(theme=7)), + bottom=Side(style='double', color=Color(theme=7)), + ) + + assert ws['A23'].fill == PatternFill(patternType='solid', + start_color='FFCCCCFF', end_color=(Color(indexed=64))) + assert ws['A23'].border.top == Side(style='mediumDashed', color=Color(theme=6)) + + assert 'A23:B24' in ws.merged_cells + + assert ws['A25'].alignment == Alignment(wrapText=True) + assert ws['A26'].alignment == Alignment(shrinkToFit=True) diff --git a/openpyxl/reader/tests/test_worksheet.py b/openpyxl/reader/tests/test_worksheet.py new file mode 100644 index 0000000..e70020a --- /dev/null +++ b/openpyxl/reader/tests/test_worksheet.py @@ -0,0 +1,742 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +import datetime +from io import BytesIO +from zipfile import ZipFile + +from lxml.etree import iterparse, fromstring + +from openpyxl import load_workbook +from openpyxl.compat import unicode +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.worksheet import Worksheet +from openpyxl.worksheet.pagebreak import Break, PageBreak +from openpyxl.packaging.relationship import Relationship, RelationshipList + + +def test_get_xml_iter(): + #1 file object + #2 stream (file-like) + #3 string + #4 zipfile + from openpyxl.reader.worksheet import _get_xml_iter + from tempfile import TemporaryFile + + FUT = _get_xml_iter + s = b"" + stream = FUT(s) + assert isinstance(stream, BytesIO), type(stream) + + u = unicode(s) + stream = FUT(u) + assert isinstance(stream, BytesIO), type(stream) + + f = TemporaryFile(mode='rb+', prefix='openpyxl.', suffix='.unpack.temp') + stream = FUT(f) + assert stream == f + f.close() + + t = TemporaryFile() + z = ZipFile(t, mode="w") + z.writestr("test", "whatever") + stream = FUT(z.open("test")) + assert hasattr(stream, "read") + + try: + z.close() + except IOError: + # you can't just close zipfiles in Windows + z.close() # python 2.7 + + +@pytest.fixture +def Workbook(): + from openpyxl.styles.styleable import StyleArray + from openpyxl.styles import numbers + + class DummyStyle: + number_format = numbers.FORMAT_GENERAL + font = "" + fill = "" + border = "" + alignment = "" + protection = "" + + def copy(self, **kw): + return self + + + class DummyWorkbook: + + guess_types = False + data_only = False + _colors = [] + encoding = "utf8" + + def __init__(self): + self._differential_styles = [] + self.shared_strings = IndexedList() + self.shared_strings.add("hello world") + self._fonts = IndexedList() + self._fills = IndexedList() + self._number_formats = IndexedList() + self._borders = IndexedList() + self._alignments = IndexedList() + self._protections = IndexedList() + self._cell_styles = IndexedList() + self.vba_archive = None + for i in range(29): + self._cell_styles.add((StyleArray([i]*9))) + self._cell_styles.add(StyleArray([0,4,6,0,0,1,0,0,0])) #fillId=4, borderId=6, alignmentId=1)) + self.sheetnames = [] + + + def create_sheet(self, title): + return Worksheet(self) + + + return DummyWorkbook() + + +@pytest.fixture +def WorkSheetParser(Workbook): + """Setup a parser instance with an empty source""" + from .. worksheet import WorkSheetParser + ws = Workbook.create_sheet('sheet') + return WorkSheetParser(ws, None, {0:'a'}) + + +@pytest.fixture +def WorkSheetParserKeepVBA(Workbook): + """Setup a parser instance with an empty source""" + Workbook.vba_archive=True + from .. worksheet import WorkSheetParser + ws = Workbook.create_sheet('sheet') + return WorkSheetParser(ws, {0:'a'}, {}) + + +def test_col_width(datadir, WorkSheetParser): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + + with open("complex-styles-worksheet.xml", "rb") as src: + cols = iterparse(src, tag='{%s}col' % SHEET_MAIN_NS) + for _, col in cols: + parser.parse_column_dimensions(col) + assert set(ws.column_dimensions) == set(['A', 'C', 'E', 'I', 'G']) + assert ws.column_dimensions['A'].style_id == 0 + assert dict(ws.column_dimensions['A']) == {'max': '1', 'min': '1', + 'customWidth': '1', + 'width': '31.1640625'} + + +def test_hidden_col(datadir, WorkSheetParser): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + + with open("hidden_rows_cols.xml", "rb") as src: + cols = iterparse(src, tag='{%s}col' % SHEET_MAIN_NS) + for _, col in cols: + parser.parse_column_dimensions(col) + assert 'D' in ws.column_dimensions + assert dict(ws.column_dimensions['D']) == {'customWidth': '1', 'hidden': + '1', 'max': '4', 'min': '4'} + + +def test_styled_col(datadir, WorkSheetParser): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + + with open("complex-styles-worksheet.xml", "rb") as src: + cols = iterparse(src, tag='{%s}col' % SHEET_MAIN_NS) + for _, col in cols: + parser.parse_column_dimensions(col) + assert 'I' in ws.column_dimensions + cd = ws.column_dimensions['I'] + assert cd.style_id == 28 + assert dict(cd) == {'customWidth': '1', 'max': '9', 'min': '9', 'width': '25', 'style':'28'} + + +def test_hidden_row(datadir, WorkSheetParser): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + + with open("hidden_rows_cols.xml", "rb") as src: + rows = iterparse(src, tag='{%s}row' % SHEET_MAIN_NS) + for _, row in rows: + parser.parse_row(row) + assert 2 in ws.row_dimensions + assert dict(ws.row_dimensions[2]) == {'hidden': '1'} + + +def test_styled_row(datadir, WorkSheetParser): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + parser.shared_strings = dict((i, i) for i in range(30)) + + with open("complex-styles-worksheet.xml", "rb") as src: + rows = iterparse(src, tag='{%s}row' % SHEET_MAIN_NS) + for _, row in rows: + parser.parse_row(row) + assert 23 in ws.row_dimensions + rd = ws.row_dimensions[23] + assert rd.style_id == 28 + assert dict(rd) == {'s':'28', 'customFormat':'1'} + + +def test_sheet_protection(datadir, WorkSheetParser): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + + with open("protected_sheet.xml", "rb") as src: + tree = iterparse(src, tag='{%s}sheetProtection' % SHEET_MAIN_NS) + for _, tag in tree: + parser.parse_sheet_protection(tag) + assert dict(ws.protection) == { + 'autoFilter': '0', 'deleteColumns': '0', + 'deleteRows': '0', 'formatCells': '0', 'formatColumns': '0', 'formatRows': + '0', 'insertColumns': '0', 'insertHyperlinks': '0', 'insertRows': '0', + 'objects': '0', 'password': 'DAA7', 'pivotTables': '0', 'scenarios': '0', + 'selectLockedCells': '0', 'selectUnlockedCells': '0', 'sheet': '1', 'sort': + '0' + } + + +def test_formula_without_value(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + + src = """ + + IF(TRUE, "y", "n") + + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 'f' + assert ws['A1'].value == '=IF(TRUE, "y", "n")' + + +def test_formula(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + + src = """ + + IF(TRUE, "y", "n") + y + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 'f' + assert ws['A1'].value == '=IF(TRUE, "y", "n")' + + +def test_formula_data_only(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + parser.data_only = True + + src = """ + + 1+2 + 3 + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 'n' + assert ws['A1'].value == 3 + + +def test_string_formula_data_only(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + parser.data_only = True + + src = """ + + IF(TRUE, "y", "n") + y + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 's' + assert ws['A1'].value == 'y' + + +def test_number(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + + src = """ + + 1 + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 'n' + assert ws['A1'].value == 1 + + + +def test_datetime(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + + src = """ + + 2011-12-25T14:23:55 + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 'd' + assert ws['A1'].value == datetime.datetime(2011, 12, 25, 14, 23, 55) + + +def test_string(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + + src = """ + + 0 + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 's' + assert ws['A1'].value == "a" + + +def test_boolean(WorkSheetParser): + parser = WorkSheetParser + ws = parser.ws + + src = """ + + 1 + + """ + element = fromstring(src) + + parser.parse_cell(element) + assert ws['A1'].data_type == 'b' + assert ws['A1'].value is True + + +def test_inline_string(WorkSheetParser, datadir): + parser = WorkSheetParser + ws = parser.ws + datadir.chdir() + + with open("Table1-XmlFromAccess.xml") as src: + sheet = fromstring(src.read()) + + element = sheet.find("{%s}sheetData/{%s}row/{%s}c" % (SHEET_MAIN_NS, SHEET_MAIN_NS, SHEET_MAIN_NS)) + parser.parse_cell(element) + assert ws['A1'].data_type == 's' + assert ws['A1'].value == "ID" + + +def test_inline_richtext(WorkSheetParser, datadir): + parser = WorkSheetParser + ws = parser.ws + datadir.chdir() + with open("jasper_sheet.xml", "rb") as src: + sheet = fromstring(src.read()) + + element = sheet.find("{%s}sheetData/{%s}row[2]/{%s}c[18]" % (SHEET_MAIN_NS, SHEET_MAIN_NS, SHEET_MAIN_NS)) + assert element.get("r") == 'R2' + parser.parse_cell(element) + cell = ws['R2'] + assert cell.data_type == 's' + assert cell.value == "11 de September de 2014" + + +def test_legacy_drawing(datadir): + datadir.chdir() + wb = load_workbook("legacy_drawing.xlsm", keep_vba=True) + sheet1 = wb['Sheet1'] + assert sheet1.legacy_drawing == 'xl/drawings/vmlDrawing1.vml' + sheet2 = wb['Sheet2'] + assert sheet2.legacy_drawing == 'xl/drawings/vmlDrawing2.vml' + + +def test_cell_style(WorkSheetParser, datadir): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + parser.shared_strings[1] = "Arial Font, 10" + + with open("complex-styles-worksheet.xml") as src: + sheet = fromstring(src.read()) + + element = sheet.find("{%s}sheetData/{%s}row[2]/{%s}c[1]" % (SHEET_MAIN_NS, SHEET_MAIN_NS, SHEET_MAIN_NS)) + assert element.get('r') == 'A2' + assert element.get('s') == '2' + parser.parse_cell(element) + assert ws['A2']._style == parser.styles[2] + assert ws['A2'].style_id == 2 + + +def test_cell_exotic_style(WorkSheetParser, datadir): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + parser.styles = [None, None, [0,0,0,0,0,0,1,1,0]] + + src = """ + + + """ + + sheet = fromstring(src) + parser.parse_cell(sheet) + assert ws['A1'].pivotButton is False + + cell = ws['D4'] + assert cell.pivotButton is True + assert cell.quotePrefix is True + + +def test_sheet_views(WorkSheetParser, datadir): + datadir.chdir() + parser = WorkSheetParser + + with open("frozen_view_worksheet.xml") as src: + sheet = src.read() + + parser.source = sheet + parser.parse() + ws = parser.ws + view = ws.sheet_view + + assert view.zoomScale == 200 + assert len(view.selection) == 3 + + +def test_legacy_document_keep(WorkSheetParserKeepVBA, datadir): + parser = WorkSheetParserKeepVBA + datadir.chdir() + + with open("legacy_drawing_worksheet.xml") as src: + sheet = fromstring(src.read()) + + element = sheet.find("{%s}legacyDrawing" % SHEET_MAIN_NS) + parser.parse_legacy_drawing(element) + assert parser.ws.legacy_drawing == 'rId3' + + +def test_legacy_document_no_keep(WorkSheetParser, datadir): + parser = WorkSheetParser + datadir.chdir() + + with open("legacy_drawing_worksheet.xml") as src: + sheet = fromstring(src.read()) + + element = sheet.find("{%s}legacyDrawing" % SHEET_MAIN_NS) + parser.parse_legacy_drawing(element) + assert parser.ws.legacy_drawing is None + + +@pytest.fixture +def Translator(): + from openpyxl.formula import translate + return translate.Translator + +def test_shared_formula(WorkSheetParser, Translator): + parser = WorkSheetParser + src = """ + + + 9 + + """ + element = fromstring(src) + parser.shared_formula_masters['0'] = Translator("=A4*B4", "A1") + parser.parse_cell(element) + assert parser.ws['A9'].value == "=A12*B12" + + +import warnings +warnings.simplefilter("always") # so that tox doesn't suppress warnings. + +def test_extended_conditional_formatting(WorkSheetParser, datadir, recwarn): + datadir.chdir() + parser = WorkSheetParser + + with open("extended_conditional_formatting_sheet.xml") as src: + sheet = fromstring(src.read()) + + element = sheet.find("{%s}extLst" % SHEET_MAIN_NS) + parser.parse_extensions(element) + w = recwarn.pop() + assert issubclass(w.category, UserWarning) + + +def test_row_dimensions(WorkSheetParser): + src = """""" + element = fromstring(src) + + parser = WorkSheetParser + parser.parse_row(element) + + assert 2 not in parser.ws.row_dimensions + + +def test_shared_formulae(WorkSheetParser, datadir): + datadir.chdir() + parser = WorkSheetParser + ws = parser.ws + parser.shared_strings = ["Whatever"] * 7 + + with open("worksheet_formulae.xml") as src: + parser.source = src.read() + + parser.parse() + + assert set(ws.formula_attributes) == set(['C10']) + + # Test shared forumlae + assert ws['B7'].data_type == 'f' + assert ws['B7'].value == '=B4*2' + assert ws['C7'].value == '=C4*2' + assert ws['D7'].value == '=D4*2' + assert ws['E7'].value == '=E4*2' + + # Test array forumlae + assert ws['C10'].data_type == 'f' + assert ws.formula_attributes['C10']['ref'] == 'C10:C14' + assert ws['C10'].value == '=SUM(A10:A14*B10:B14)' + + +def test_cell_without_coordinates(WorkSheetParser, datadir): + datadir.chdir() + with open("worksheet_without_coordinates.xml", "rb") as src: + xml = src.read() + + sheet = fromstring(xml) + + el = sheet.find(".//{%s}row" % SHEET_MAIN_NS) + + parser = WorkSheetParser + parser.shared_strings = ["Whatever"] * 10 + parser.parse_row(el) + + assert parser.ws.max_row == 1 + assert parser.ws.max_column == 5 + + +def test_external_hyperlinks(WorkSheetParser): + src = """ + + + + """ + from openpyxl.packaging.relationship import Relationship, RelationshipList + + r = Relationship(type="hyperlink", Id="rId1", Target="../") + rels = RelationshipList() + rels.append(r) + + parser = WorkSheetParser + parser.source = src + parser.ws._rels = rels + + parser.parse() + + assert parser.ws['A1'].hyperlink.target == "../" + + +def test_local_hyperlinks(WorkSheetParser): + src = """ + + + + + + """ + parser = WorkSheetParser + parser.source = src + parser.parse() + + assert parser.ws['B4'].hyperlink.location == "'STP nn000TL-10, PKG 2.52'!A1" + + +def test_merge_cells(WorkSheetParser): + src = """ + + + + + + + + """ + + parser = WorkSheetParser + parser.source = src + + parser.parse() + + assert parser.ws.merged_cells == "C2:F2 B19:C20 E19:G19" + + +def test_conditonal_formatting(WorkSheetParser): + src = """ + + + + + + + + + """ + from openpyxl.styles.differential import DifferentialStyle + + parser = WorkSheetParser + dxf = DifferentialStyle() + parser.differential_styles = [dxf] * 30 + parser.source = src + + parser.parse() + + assert parser.ws.conditional_formatting['T1:T10'][-1].dxf == dxf + + +def test_sheet_properties(WorkSheetParser): + src = """ + + + + + + + + """ + parser = WorkSheetParser + parser.source = src + parser.parse() + + assert parser.ws.sheet_properties.tabColor.rgb == "FF92D050" + assert parser.ws.sheet_properties.codeName == "Sheet3" + + +def test_sheet_format(WorkSheetParser): + + src = """ + + + + """ + parser = WorkSheetParser + parser.source = src + parser.parse() + + assert parser.ws.sheet_format.defaultRowHeight == 14.25 + assert parser.ws.sheet_format.baseColWidth == 15 + + +def test_tables(WorkSheetParser): + src = """ + + + + + + """ + + parser = WorkSheetParser + r = Relationship(type="table", Id="rId1", Target="../tables/table1.xml") + rels = RelationshipList() + rels.append(r) + parser.ws._rels = rels + + parser.source = src + parser.parse() + + assert parser.tables == ["../tables/table1.xml"] + + +def test_auto_filter(WorkSheetParser): + src = """ + + + + + + + + """ + + parser = WorkSheetParser + parser.source = src + parser.parse() + ws = parser.ws + + assert ws.auto_filter.ref == "A1:AK3237" + assert ws.auto_filter.sortState.ref == "A2:AM3269" + assert ws.sort_state.ref is None + + +@pytest.mark.xfail +def test_sort_state(WorkSheetParser): + src = """ + + + + + + """ + + parser = WorkSheetParser + parser.source = src + parser.parse() + ws = parser.ws + + assert ws.sort_state.ref == "A2:AM3269" + + +def test_page_break(WorkSheetParser): + src = """ + + + + + + """ + expected_pagebreak = PageBreak() + expected_pagebreak.append(Break(id=15)) + + parser = WorkSheetParser + parser.source = src + parser.parse() + ws = parser.ws + + assert ws.page_breaks == expected_pagebreak diff --git a/openpyxl/reader/worksheet.py b/openpyxl/reader/worksheet.py new file mode 100644 index 0000000..a02a3c7 --- /dev/null +++ b/openpyxl/reader/worksheet.py @@ -0,0 +1,338 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +"""Reader for a single worksheet.""" +from io import BytesIO +from warnings import warn + +# compatibility imports +from openpyxl.xml.functions import iterparse + +# package imports +from openpyxl.cell import Cell +from openpyxl.worksheet.filters import AutoFilter, SortState +from openpyxl.cell.read_only import _cast_number +from openpyxl.cell.text import Text +from openpyxl.worksheet import Worksheet +from openpyxl.worksheet.dimensions import ( + ColumnDimension, + RowDimension, + SheetFormatProperties, +) +from openpyxl.worksheet.header_footer import HeaderFooter +from openpyxl.worksheet.hyperlink import Hyperlink +from openpyxl.worksheet.merge import MergeCells +from openpyxl.worksheet.page import PageMargins, PrintOptions, PrintPageSetup +from openpyxl.worksheet.pagebreak import PageBreak +from openpyxl.worksheet.protection import SheetProtection +from openpyxl.worksheet.views import SheetViewList +from openpyxl.worksheet.datavalidation import DataValidationList +from openpyxl.xml.constants import ( + SHEET_MAIN_NS, + REL_NS, + EXT_TYPES, + PKG_REL_NS +) +from openpyxl.xml.functions import safe_iterator, localname +from openpyxl.styles import Color +from openpyxl.styles import is_date_format +from openpyxl.formatting import Rule +from openpyxl.formatting.formatting import ConditionalFormatting +from openpyxl.formula.translate import Translator +from openpyxl.worksheet.properties import WorksheetProperties +from openpyxl.utils import ( + coordinate_from_string, + get_column_letter, + column_index_from_string, + coordinate_to_tuple, + ) +from openpyxl.utils.datetime import from_excel, from_ISO8601 +from openpyxl.descriptors.excel import ExtensionList, Extension +from openpyxl.worksheet.table import TablePartList + + +def _get_xml_iter(xml_source): + """ + Possible inputs: strings, bytes, members of zipfile, temporary file + Always return a file like object + """ + if not hasattr(xml_source, 'read'): + try: + xml_source = xml_source.encode("utf-8") + except (AttributeError, UnicodeDecodeError): + pass + return BytesIO(xml_source) + else: + try: + xml_source.seek(0) + except: + # could be a zipfile + pass + return xml_source + + +class WorkSheetParser(object): + + CELL_TAG = '{%s}c' % SHEET_MAIN_NS + VALUE_TAG = '{%s}v' % SHEET_MAIN_NS + FORMULA_TAG = '{%s}f' % SHEET_MAIN_NS + MERGE_TAG = '{%s}mergeCell' % SHEET_MAIN_NS + INLINE_STRING = "{%s}is" % SHEET_MAIN_NS + + def __init__(self, ws, xml_source, shared_strings): + self.ws = ws + self.source = xml_source + self.shared_strings = shared_strings + self.guess_types = ws.parent.guess_types + self.data_only = ws.parent.data_only + self.styles = ws.parent._cell_styles + self.differential_styles = ws.parent._differential_styles + self.keep_vba = ws.parent.vba_archive is not None + self.shared_formula_masters = {} # {si_str: Translator()} + self._row_count = self._col_count = 0 + self.tables = [] + + def parse(self): + dispatcher = { + '{%s}mergeCells' % SHEET_MAIN_NS: self.parse_merge, + '{%s}col' % SHEET_MAIN_NS: self.parse_column_dimensions, + '{%s}row' % SHEET_MAIN_NS: self.parse_row, + '{%s}conditionalFormatting' % SHEET_MAIN_NS: self.parser_conditional_formatting, + '{%s}legacyDrawing' % SHEET_MAIN_NS: self.parse_legacy_drawing, + '{%s}sheetProtection' % SHEET_MAIN_NS: self.parse_sheet_protection, + '{%s}extLst' % SHEET_MAIN_NS: self.parse_extensions, + '{%s}hyperlink' % SHEET_MAIN_NS: self.parse_hyperlinks, + '{%s}tableParts' % SHEET_MAIN_NS: self.parse_tables, + } + + properties = { + '{%s}printOptions' % SHEET_MAIN_NS: ('print_options', PrintOptions), + '{%s}pageMargins' % SHEET_MAIN_NS: ('page_margins', PageMargins), + '{%s}pageSetup' % SHEET_MAIN_NS: ('page_setup', PrintPageSetup), + '{%s}headerFooter' % SHEET_MAIN_NS: ('HeaderFooter', HeaderFooter), + '{%s}autoFilter' % SHEET_MAIN_NS: ('auto_filter', AutoFilter), + '{%s}dataValidations' % SHEET_MAIN_NS: ('data_validations', DataValidationList), + #'{%s}sheet/{%s}sortState' % (SHEET_MAIN_NS, SHEET_MAIN_NS): ('sort_state', SortState), + '{%s}sheetPr' % SHEET_MAIN_NS: ('sheet_properties', WorksheetProperties), + '{%s}sheetViews' % SHEET_MAIN_NS: ('views', SheetViewList), + '{%s}sheetFormatPr' % SHEET_MAIN_NS: ('sheet_format', SheetFormatProperties), + '{%s}rowBreaks' % SHEET_MAIN_NS: ('page_breaks', PageBreak), + } + + stream = _get_xml_iter(self.source) + it = iterparse(stream, tag=dispatcher) + + 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.ws, prop[0], obj) + element.clear() + + self.ws._current_row = self.ws.max_row + + + def parse_cell(self, element): + value = element.find(self.VALUE_TAG) + if value is not None: + value = value.text + formula = element.find(self.FORMULA_TAG) + data_type = element.get('t', 'n') + coordinate = element.get('r') + self._col_count += 1 + style_id = element.get('s') + + # assign formula to cell value unless only the data is desired + if formula is not None and not self.data_only: + data_type = 'f' + if formula.text: + value = "=" + formula.text + else: + value = "=" + formula_type = formula.get('t') + if formula_type: + if formula_type != "shared": + self.ws.formula_attributes[coordinate] = dict(formula.attrib) + + else: + si = formula.get('si') # Shared group index for shared formulas + + # The spec (18.3.1.40) defines shared formulae in + # terms of the following: + # + # `master`: "The first formula in a group of shared + # formulas" + # `ref`: "Range of cells which the formula applies + # to." It's a required attribute on the master + # cell, forbidden otherwise. + # `shared cell`: "A cell is shared only when si is + # used and t is `shared`." + # + # Whether to use the cell's given formula or the + # master's depends on whether the cell is shared, + # whether it's in the ref, and whether it defines its + # own formula, as follows: + # + # Shared? Has formula? | In ref Not in ref + # ========= ==============|======== =============== + # Yes Yes | master impl. defined + # No Yes | own own + # Yes No | master impl. defined + # No No | ?? N/A + # + # The ?? is because the spec is silent on this issue, + # though my inference is that the cell does not + # receive a formula at all. + # + # For this implementation, we are using the master + # formula in the two "impl. defined" cases and no + # formula in the "??" case. This choice of + # implementation allows us to disregard the `ref` + # parameter altogether, and does not require + # computing expressions like `C5 in A1:D6`. + # Presumably, Excel does not generate spreadsheets + # with such contradictions. + if si in self.shared_formula_masters: + trans = self.shared_formula_masters[si] + value = trans.translate_formula(coordinate) + else: + self.shared_formula_masters[si] = Translator(value, coordinate) + + + style_array = None + if style_id is not None: + style_id = int(style_id) + style_array = self.styles[style_id] + + if coordinate: + row, column = coordinate_to_tuple(coordinate) + else: + row, column = self._row_count, self._col_count + + cell = Cell(self.ws, row=row, col_idx=column, style_array=style_array) + self.ws._cells[(row, column)] = cell + + if value is not None: + if data_type == 'n': + value = _cast_number(value) + if is_date_format(cell.number_format): + data_type = 'd' + value = from_excel(value) + elif data_type == 'b': + value = bool(int(value)) + elif data_type == 's': + value = self.shared_strings[int(value)] + elif data_type == 'str': + data_type = 's' + elif data_type == 'd': + value = from_ISO8601(value) + + else: + if data_type == 'inlineStr': + child = element.find(self.INLINE_STRING) + if child is not None: + data_type = 's' + richtext = Text.from_tree(child) + value = richtext.content + + if self.guess_types or value is None: + cell.value = value + else: + cell._value = value + cell.data_type = data_type + + + def parse_merge(self, element): + merged = MergeCells.from_tree(element) + for c in merged.mergeCell: + self.ws.merge_cells(c.ref) + + + def parse_column_dimensions(self, col): + attrs = dict(col.attrib) + column = get_column_letter(int(attrs['min'])) + attrs['index'] = column + if 'style' in attrs: + attrs['style'] = self.styles[int(attrs['style'])] + dim = ColumnDimension(self.ws, **attrs) + self.ws.column_dimensions[column] = dim + + + def parse_row(self, row): + attrs = dict(row.attrib) + + if "r" in attrs: + self._row_count = int(attrs['r']) + else: + self._row_count += 1 + self._col_count = 0 + keys = set(attrs) + for key in keys: + if key == "s": + attrs['s'] = self.styles[int(attrs['s'])] + elif key.startswith('{'): + del attrs[key] + + + keys = set(attrs) + if keys != set(['r', 'spans']) and keys != set(['r']): + # don't create dimension objects unless they have relevant information + dim = RowDimension(self.ws, **attrs) + self.ws.row_dimensions[dim.index] = dim + + for cell in safe_iterator(row, self.CELL_TAG): + self.parse_cell(cell) + + + def parser_conditional_formatting(self, element): + cf = ConditionalFormatting.from_tree(element) + for rule in cf.rules: + if rule.dxfId is not None: + rule.dxf = self.differential_styles[rule.dxfId] + self.ws.conditional_formatting[cf] = rule + + + def parse_sheet_protection(self, element): + self.ws.protection = SheetProtection.from_tree(element) + password = element.get("password") + if password is not None: + self.ws.protection.set_password(password, True) + + + def parse_legacy_drawing(self, element): + if self.keep_vba: + # For now just save the legacy drawing id. + # We will later look up the file name + self.ws.legacy_drawing = element.get('{%s}id' % REL_NS) + + + 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_hyperlinks(self, element): + link = Hyperlink.from_tree(element) + 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: + cell.hyperlink = link + else: + self.ws[link.ref].hyperlink = link + + + def parse_tables(self, element): + for t in TablePartList.from_tree(element).tablePart: + rel = self.ws._rels[t.id] + self.tables.append(rel.Target) diff --git a/openpyxl/styles/__init__.py b/openpyxl/styles/__init__.py new file mode 100644 index 0000000..e11dd8b --- /dev/null +++ b/openpyxl/styles/__init__.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +from .alignment import Alignment +from .borders import Border, Side +from .colors import Color +from .fills import PatternFill, GradientFill, Fill +from .fonts import Font, DEFAULT_FONT +from .numbers import NumberFormatDescriptor, is_date_format, is_builtin +from .protection import Protection +from .named_styles import NamedStyle diff --git a/openpyxl/styles/alignment.py b/openpyxl/styles/alignment.py new file mode 100644 index 0000000..ef88f83 --- /dev/null +++ b/openpyxl/styles/alignment.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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 = Min(min=0) + relativeIndent = Min(min=0) + 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/openpyxl/styles/borders.py b/openpyxl/styles/borders.py new file mode 100644 index 0000000..ad6722c --- /dev/null +++ b/openpyxl/styles/borders.py @@ -0,0 +1,114 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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) + 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=Side(), right=Side(), top=Side(), + bottom=Side(), diagonal=Side(), 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() diff --git a/openpyxl/styles/builtins.py b/openpyxl/styles/builtins.py new file mode 100644 index 0000000..63074f5 --- /dev/null +++ b/openpyxl/styles/builtins.py @@ -0,0 +1,1398 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/styles/cell_style.py b/openpyxl/styles/cell_style.py new file mode 100644 index 0000000..e4a36d5 --- /dev/null +++ b/openpyxl/styles/cell_style.py @@ -0,0 +1,203 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 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/openpyxl/styles/colors.py b/openpyxl/styles/colors.py new file mode 100644 index 0000000..aa8874a --- /dev/null +++ b/openpyxl/styles/colors.py @@ -0,0 +1,193 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import re +from openpyxl.compat import safe_string, basestring +from openpyxl.descriptors import ( + Descriptor, + String, + Bool, + MinMax, + Integer, + Typed, + Sequence +) +from openpyxl.descriptors.excel import HexBinary, ExtensionList +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', 'System Foreground', 'System Background' #60-64 +) + +# 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 = basestring + + 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, basestring): + value = Color(rgb=value) + super(ColorDescriptor, self).__set__(instance, value) + + +class MRUColorList(Serialisable): + + color = Sequence(expected_type=Color, ) + + __elements__ = ('color',) + + def __init__(self, + color=None, + ): + self.color = color + + +class RgbColor(Serialisable): + + rgb = HexBinary() + + def __init__(self, + rgb=None, + ): + self.rgb = rgb + + +class IndexedColorList(Serialisable): + + rgbColor = Sequence(expected_type=RgbColor, ) + + __elements__ = ('rgbColor',) + + def __init__(self, + rgbColor=(), + ): + self.rgbColor = rgbColor + + +class ColorList(Serialisable): + + indexedColors = Typed(expected_type=IndexedColorList, allow_none=True) + mruColors = Typed(expected_type=MRUColorList, allow_none=True) + + __elements__ = ('indexedColors', 'mruColors') + + def __init__(self, + indexedColors=None, + mruColors=None, + ): + if indexedColors is None: + indexedColors = IndexedColorList() + self.indexedColors = indexedColors + self.mruColors = mruColors + + + @property + def index(self): + vals = self.indexedColors.rgbColor + return [val.rgb for val in vals] diff --git a/openpyxl/styles/differential.py b/openpyxl/styles/differential.py new file mode 100644 index 0000000..a95bf7b --- /dev/null +++ b/openpyxl/styles/differential.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from openpyxl.descriptors import ( + Integer, + String, + Typed, + Sequence, + Alias, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.styles import ( + Font, + Fill, + GradientFill, + PatternFill, + 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): + """ + Deduping container for differential styles. + """ + + tagname = "dxfs" + + dxf = Sequence(expected_type=DifferentialStyle) + styles = Alias("dxf") + + + def __init__(self, dxf=()): + 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) + + __nonzero__ = __bool__ + + + def __getitem__(self, idx): + return self.styles[idx] diff --git a/openpyxl/styles/fills.py b/openpyxl/styles/fills.py new file mode 100644 index 0000000..72a5f4f --- /dev/null +++ b/openpyxl/styles/fills.py @@ -0,0 +1,224 @@ +from __future__ import absolute_import, division +# Copyright (c) 2010-2018 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, safe_iterator +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/openpyxl/styles/fonts.py b/openpyxl/styles/fonts.py new file mode 100644 index 0000000..11b6b0f --- /dev/null +++ b/openpyxl/styles/fonts.py @@ -0,0 +1,114 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/styles/named_styles.py b/openpyxl/styles/named_styles.py new file mode 100644 index 0000000..d706b7c --- /dev/null +++ b/openpyxl/styles/named_styles.py @@ -0,0 +1,292 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from collections import OrderedDict +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, + 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) + 164 + 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/openpyxl/styles/numbers.py b/openpyxl/styles/numbers.py new file mode 100644 index 0000000..8611c8d --- /dev/null +++ b/openpyxl/styles/numbers.py @@ -0,0 +1,188 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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_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'"[^"]+"' +LOCALE_GROUP = r'\[\$[^\]]+\]' +STRIP_RE = re.compile("{0}|{1}|{2}".format(COLORS, LITERAL_GROUP, LOCALE_GROUP), re.IGNORECASE + re.UNICODE) + + +# 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) + return re.search("[dmhysDMHYS]", 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.""" + return BUILTIN_FORMATS[index] + + +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/openpyxl/styles/protection.py b/openpyxl/styles/protection.py new file mode 100644 index 0000000..4160e62 --- /dev/null +++ b/openpyxl/styles/protection.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/styles/proxy.py b/openpyxl/styles/proxy.py new file mode 100644 index 0000000..b324b17 --- /dev/null +++ b/openpyxl/styles/proxy.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/styles/styleable.py b/openpyxl/styles/styleable.py new file mode 100644 index 0000000..4755743 --- /dev/null +++ b/openpyxl/styles/styleable.py @@ -0,0 +1,141 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +from copy import copy +from warnings import warn + +from .numbers import BUILTIN_FORMATS, 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) + 164 + 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 < 164: + return BUILTIN_FORMATS.get(idx, "General") + coll = getattr(instance.parent.parent, self.collection) + return coll[idx - 164] + + +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 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() + + __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) + + + @property + def pivotButton(self): + if self._style is None: + return False + return bool(self._style[6]) + + + @property + def quotePrefix(self): + if self._style is None: + return False + return bool(self._style[7]) diff --git a/openpyxl/styles/stylesheet.py b/openpyxl/styles/stylesheet.py new file mode 100644 index 0000000..f8af127 --- /dev/null +++ b/openpyxl/styles/stylesheet.py @@ -0,0 +1,230 @@ +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Sequence +) +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 .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_REVERSE +) +from .alignment import Alignment +from .protection import Protection +from .named_styles import ( + NamedStyle, + _NamedCellStyle, + _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 in self.custom_formats: + named_style.number_format = self.custom_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 numFmtIds with a floor of 164 + """ + custom = self.custom_formats + formats = self.number_formats + for style in 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] + continue + style.numFmtId = formats.add(fmt) + 164 + + + 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) + + 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 + + for ns in wb._named_styles: + ns.bind(wb) + + 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 + + from .numbers import NumberFormat + fmts = [] + for idx, code in enumerate(wb._number_formats, 164): + 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/openpyxl/styles/table.py b/openpyxl/styles/table.py new file mode 100644 index 0000000..5af4bc1 --- /dev/null +++ b/openpyxl/styles/table.py @@ -0,0 +1,92 @@ +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 = Typed(expected_type=TableStyleElement, allow_none=True) + + __elements__ = ('tableStyleElement',) + + def __init__(self, + name=None, + pivot=None, + table=None, + count=None, + tableStyleElement=None, + ): + 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/openpyxl/styles/tests/__init__.py b/openpyxl/styles/tests/__init__.py new file mode 100644 index 0000000..ea0f465 --- /dev/null +++ b/openpyxl/styles/tests/__init__.py @@ -0,0 +1 @@ +# Unit tests for style and formatting objects. diff --git a/openpyxl/styles/tests/conftest.py b/openpyxl/styles/tests/conftest.py new file mode 100644 index 0000000..fd1f673 --- /dev/null +++ b/openpyxl/styles/tests/conftest.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl +import pytest + + +@pytest.fixture +def datadir(): + """DATADIR as a LocalPath""" + import os + here = os.path.split(__file__)[0] + DATADIR = os.path.join(here, "data") + from py._path.local import LocalPath + return LocalPath(DATADIR) + + +# objects under test + + +@pytest.fixture +def FormatRule(): + """Formatting rule class""" + from openpyxl.formatting.rules import FormatRule + return FormatRule diff --git a/openpyxl/styles/tests/data/alignment_styles.xml b/openpyxl/styles/tests/data/alignment_styles.xml new file mode 100644 index 0000000..be52492 --- /dev/null +++ b/openpyxl/styles/tests/data/alignment_styles.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/builtins_as_custom_number_formats.xml b/openpyxl/styles/tests/data/builtins_as_custom_number_formats.xml new file mode 100644 index 0000000..8eeba66 --- /dev/null +++ b/openpyxl/styles/tests/data/builtins_as_custom_number_formats.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/complex-styles.xml b/openpyxl/styles/tests/data/complex-styles.xml new file mode 100644 index 0000000..22945c5 --- /dev/null +++ b/openpyxl/styles/tests/data/complex-styles.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/dxf_style.xml b/openpyxl/styles/tests/data/dxf_style.xml new file mode 100644 index 0000000..4c167f7 --- /dev/null +++ b/openpyxl/styles/tests/data/dxf_style.xml @@ -0,0 +1,2069 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/empty-workbook-styles.xml b/openpyxl/styles/tests/data/empty-workbook-styles.xml new file mode 100644 index 0000000..567fd70 --- /dev/null +++ b/openpyxl/styles/tests/data/empty-workbook-styles.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openpyxl/styles/tests/data/no_number_format.xml b/openpyxl/styles/tests/data/no_number_format.xml new file mode 100644 index 0000000..287989f --- /dev/null +++ b/openpyxl/styles/tests/data/no_number_format.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/none_value_styles.xml b/openpyxl/styles/tests/data/none_value_styles.xml new file mode 100644 index 0000000..c96eeec --- /dev/null +++ b/openpyxl/styles/tests/data/none_value_styles.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/rgb_colors.xml b/openpyxl/styles/tests/data/rgb_colors.xml new file mode 100755 index 0000000..e8cb31a --- /dev/null +++ b/openpyxl/styles/tests/data/rgb_colors.xml @@ -0,0 +1,2024 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/simple-styles.xml b/openpyxl/styles/tests/data/simple-styles.xml new file mode 100644 index 0000000..7862aba --- /dev/null +++ b/openpyxl/styles/tests/data/simple-styles.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openpyxl/styles/tests/data/styles_number_formats.xml b/openpyxl/styles/tests/data/styles_number_formats.xml new file mode 100644 index 0000000..486419c --- /dev/null +++ b/openpyxl/styles/tests/data/styles_number_formats.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/data/worksheet_unprotected_style.xml b/openpyxl/styles/tests/data/worksheet_unprotected_style.xml new file mode 100644 index 0000000..24a58b9 --- /dev/null +++ b/openpyxl/styles/tests/data/worksheet_unprotected_style.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/styles/tests/test_alignments.py b/openpyxl/styles/tests/test_alignments.py new file mode 100644 index 0000000..7472477 --- /dev/null +++ b/openpyxl/styles/tests/test_alignments.py @@ -0,0 +1,27 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +@pytest.fixture +def Alignment(): + from .. alignment import Alignment + return Alignment + + +def test_default(Alignment): + al = Alignment() + assert dict(al) == {} + + +def test_round_trip(Alignment): + args = {'horizontal':'center', 'vertical':'top', 'textRotation':'45', 'indent':'4'} + al = Alignment(**args) + assert dict(al) == args + + +def test_alias(Alignment): + al = Alignment(text_rotation=90, shrink_to_fit=True, wrap_text=True) + assert dict(al) == { 'textRotation':'90', + 'shrinkToFit':'1', + 'wrapText':'1'} diff --git a/openpyxl/styles/tests/test_borders.py b/openpyxl/styles/tests/test_borders.py new file mode 100644 index 0000000..c794433 --- /dev/null +++ b/openpyxl/styles/tests/test_borders.py @@ -0,0 +1,80 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring + +from openpyxl.tests.helper import compare_xml +from ..colors import Color +from .. import colors + + +@pytest.fixture +def Side(): + from ..borders import Side + return Side + + +@pytest.fixture +def Border(): + from ..borders import Border + return Border + + +class TestBorder: + + def test_create(self, Border): + src = """ + + + + + + + + + + + + + + + + """ + xml = fromstring(src) + bd = Border.from_tree(xml) + assert bd.left.style == "thin" + assert bd.right.color.value == "FF006600" + assert bd.diagonal.style == None + + + def test_serialise(self, Border, Side): + medium_blue = Side(border_style='medium', color=Color(colors.BLUE)) + bd = Border(left=medium_blue, + right=medium_blue, + top=medium_blue, + bottom=medium_blue, + outline=False, + diagonalDown=True, + ) + xml = tostring(bd.to_tree()) + expected = """ + + + + + + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/styles/tests/test_cell_style.py b/openpyxl/styles/tests/test_cell_style.py new file mode 100644 index 0000000..1ac72d0 --- /dev/null +++ b/openpyxl/styles/tests/test_cell_style.py @@ -0,0 +1,198 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl +import pytest + +from copy import copy + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def StyleArray(): + from ..cell_style import StyleArray + return StyleArray + + +class TestStyleArray: + + + def test_ctor(self, StyleArray): + style = StyleArray(range(9)) + assert style.fontId == 0 + assert style.numFmtId == 3 + assert style.xfId == 8 + + + def test_hash(self, StyleArray): + s1 = StyleArray((range(9))) + s2 = StyleArray((range(9))) + assert hash(s1) == hash(s2) + + + def test_copy(self, StyleArray): + s1 = StyleArray((range(9))) + s2 = copy(s1) + assert type(s1) == type(s2) + assert s1 == s2 + + +@pytest.fixture +def CellStyle(): + from ..cell_style import CellStyle + return CellStyle + + +class TestCellStyle: + + def test_ctor(self, CellStyle): + cell_style = CellStyle(xfId=0) + xml = tostring(cell_style.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, CellStyle): + from ..alignment import Alignment + src = """ + + + + """ + node = fromstring(src) + cell_style = CellStyle.from_tree(node) + assert cell_style == CellStyle( + alignment=Alignment(horizontal="center"), + applyAlignment=True, + xfId=0, + ) + + + def test_to_array(self, CellStyle): + from ..cell_style import StyleArray + xf = CellStyle( + numFmtId=43, + fontId=1, + fillId=2, + borderId=4, + xfId=None, + quotePrefix=True, + pivotButton=True, + applyNumberFormat=None, + applyFont=None, + applyFill=None, + applyBorder=None, + applyAlignment=None, + applyProtection=None, + alignment=None, + protection=None, + ) + style = xf.to_array() + assert style == StyleArray([1, 2, 4, 43, 0, 0, 1, 1, 0]) + + + def test_from_array(self, CellStyle): + from ..cell_style import StyleArray + style = StyleArray([5, 10, 15, 0, 0, 0, 1, 1, 15]) + xf = CellStyle.from_array(style) + assert dict(xf) == {'borderId': '15', 'fillId': '10', 'fontId': '5', + 'numFmtId': '0', 'pivotButton': '1', 'quotePrefix': '1', 'xfId': + '15'} + + +@pytest.fixture +def CellStyleList(): + from ..cell_style import CellStyleList + return CellStyleList + + +class TestCellStyleList: + + def test_ctor(self, CellStyleList): + cell_style = CellStyleList() + xml = tostring(cell_style.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, CellStyleList): + src = """ + + """ + node = fromstring(src) + cell_style = CellStyleList.from_tree(node) + assert cell_style == CellStyleList() + + + def test_to_array(self, CellStyleList): + src = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ + node = fromstring(src) + xfs = CellStyleList.from_tree(node) + styles = xfs._to_array() + assert len(styles) == 29 + assert len(xfs.alignments) == 9 + assert len(xfs.prots) == 1 diff --git a/openpyxl/styles/tests/test_colors.py b/openpyxl/styles/tests/test_colors.py new file mode 100644 index 0000000..6a339cd --- /dev/null +++ b/openpyxl/styles/tests/test_colors.py @@ -0,0 +1,81 @@ +from openpyxl.styles.colors import Color +import pytest + + +@pytest.mark.parametrize("value", ['00FFFFFF', 'efefef']) +def test_argb(value): + from ..colors import aRGB_REGEX + assert aRGB_REGEX.match(value) is not None + + +class TestColor: + + def test_ctor(self): + c = Color() + assert c.value == "00000000" + assert c.type == "rgb" + assert dict(c) == {'rgb': '00000000'} + + def test_rgb(self): + c = Color(rgb="FFFFFFFF") + assert c.value == "FFFFFFFF" + assert c.type == "rgb" + assert dict(c) == {'rgb': 'FFFFFFFF'} + + def test_indexed(self): + c = Color(indexed=4) + assert c.value == 4 + assert c.type == "indexed" + assert dict(c) == {'indexed': "4"} + + def test_auto(self): + c = Color(auto=1) + assert c.type is "auto" + assert c.value is True + assert dict(c) == {'auto': "1"} + + def test_theme(self): + c = Color(theme="1") + assert c.value == 1 + assert c.type == "theme" + assert dict(c) == {'theme': "1"} + + def test_tint(self): + c = Color(tint=0.5) + assert c.tint == 0.5 + assert dict(c) == {'rgb': '00000000', 'tint': "0.5"} + + def test_highlander(self): + c = Color(rgb="FFFFFFF", indexed=4, theme=2, auto=False) + assert c.value == 4 + assert c.type == "indexed" + + def test_validation(self): + c = Color() + with pytest.raises(TypeError): + c.value = 4 + + + def test_adding(self): + c1 = Color(rgb="FF0000") + c2 = Color(rgb="00FF00") + c3 = c1 + c2 + assert c3.rgb == "00FF0000" + + + def test_cannot_add_other_types(self): + c = Color() + with pytest.raises(TypeError): + c + 4 + + +def test_color_descriptor(): + from ..colors import ColorDescriptor + + class DummyStyle(object): + + value = ColorDescriptor('value') + + style = DummyStyle() + style.value = "efefef" + assert dict(style.value) == {'rgb': '00efefef'} diff --git a/openpyxl/styles/tests/test_differential.py b/openpyxl/styles/tests/test_differential.py new file mode 100644 index 0000000..cb09b22 --- /dev/null +++ b/openpyxl/styles/tests/test_differential.py @@ -0,0 +1,52 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.xml.constants import SHEET_MAIN_NS + +from openpyxl.styles import Font, Color, PatternFill + +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def DifferentialStyle(): + from ..differential import DifferentialStyle + return DifferentialStyle + + +def test_parse(DifferentialStyle, datadir): + datadir.chdir() + with open("dxf_style.xml") as content: + src = content.read() + xml = fromstring(src) + formats = [] + for node in xml.findall("{%s}dxfs/{%s}dxf" % (SHEET_MAIN_NS, SHEET_MAIN_NS) ): + formats.append(DifferentialStyle.from_tree(node)) + assert len(formats) == 164 + cond = formats[1] + assert cond.font == Font(underline="double", b=False, color=Color(auto=1), strikethrough=True, italic=True) + assert cond.fill == PatternFill(end_color='FFFFC7CE') + + +def test_serialise(DifferentialStyle): + cond = DifferentialStyle() + cond.font = Font(name="Calibri", family=2, sz=11) + cond.fill = PatternFill() + xml = tostring(cond.to_tree()) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff diff --git a/openpyxl/styles/tests/test_fills.py b/openpyxl/styles/tests/test_fills.py new file mode 100644 index 0000000..967f92c --- /dev/null +++ b/openpyxl/styles/tests/test_fills.py @@ -0,0 +1,289 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.styles.colors import BLACK, WHITE, Color +from openpyxl.xml.functions import tostring, fromstring + +from openpyxl.tests.helper import compare_xml + +@pytest.fixture +def GradientFill(): + from openpyxl.styles.fills import GradientFill + return GradientFill + + +@pytest.fixture +def Stop(): + from openpyxl.styles.fills import Stop + return Stop + + +class TestGradientFill: + + def test_empty_ctor(self, GradientFill): + gf = GradientFill() + assert gf.type == 'linear' + assert gf.degree == 0 + assert gf.left == 0 + assert gf.right == 0 + assert gf.top == 0 + assert gf.bottom == 0 + assert gf.stop == [] + + + def test_ctor(self, GradientFill): + gf = GradientFill(degree=90, left=1, right=2, top=3, bottom=4) + assert gf.degree == 90 + assert gf.left == 1 + assert gf.right == 2 + assert gf.top == 3 + assert gf.bottom == 4 + + + @pytest.mark.parametrize("colors", + [ + [Color(BLACK), Color(WHITE)], + [BLACK, WHITE], + ] + ) + def test_stop_sequence(self, GradientFill, Stop, colors): + gf = GradientFill(stop=[Stop(colors[0], 0), Stop(colors[1], .5)]) + assert gf.stop[0].color.rgb == BLACK + assert gf.stop[1].color.rgb == WHITE + assert gf.stop[0].position == 0 + assert gf.stop[1].position == .5 + + + @pytest.mark.parametrize("colors,rgbs,positions", [ + ([Color(BLACK), Color(WHITE)], [BLACK, WHITE], [0, 1]), + ([BLACK, WHITE], [BLACK, WHITE], [0, 1]), + ([BLACK, WHITE, BLACK], [BLACK, WHITE, BLACK], [0, .5, 1]), + ([WHITE], [WHITE], [0]), + ]) + def test_color_sequence(self, Stop, colors, rgbs, positions): + from ..fills import _assign_position + + stops = _assign_position(colors) + + assert [stop.color.rgb for stop in stops] == rgbs + assert [stop.position for stop in stops] == positions + + + def test_invalid_stop_color_mix(self, Stop): + from ..fills import _assign_position + with pytest.raises(ValueError): + _assign_position([Stop(BLACK, .1), WHITE]) + + + def test_duplicate_position(self, Stop): + from ..fills import _assign_position + + with pytest.raises(ValueError): + _assign_position([Stop(BLACK, 0.5), Stop(BLACK, 0.5)]) + + + def test_dict_interface(self, GradientFill): + gf = GradientFill(degree=90, left=1, right=2, top=3, bottom=4) + assert dict(gf) == {'bottom': "4", 'degree': "90", 'left':"1", + 'right': "2", 'top': "3", 'type': 'linear'} + + + def test_serialise(self, GradientFill, Stop): + gf = GradientFill(degree=90, left=1, right=2, top=3, bottom=4, + stop=[Stop(BLACK, 0), Stop(WHITE, 1)]) + xml = tostring(gf.to_tree()) + expected = """ + + + + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_create(self, GradientFill, Stop): + src = """ + + + + + + + + + + + """ + xml = fromstring(src) + fill = GradientFill.from_tree(xml) + assert fill.stop == [Stop(Color(theme=0), position=0), + Stop(Color(theme=4), position=1)] + + +@pytest.fixture +def PatternFill(): + from ..fills import PatternFill + return PatternFill + + +class TestPatternFill: + + def test_ctor(self, PatternFill): + pf = PatternFill() + assert pf.patternType is None + assert pf.fgColor == Color() + assert pf.bgColor == Color() + + + def test_dict_interface(self, PatternFill): + pf = PatternFill(fill_type='solid') + assert dict(pf) == {'patternType':'solid'} + + + def test_serialise(self, PatternFill): + pf = PatternFill('solid', 'FF0000', 'FFFF00') + xml = tostring(pf.to_tree()) + expected = """ + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + @pytest.mark.parametrize("src, args", + [ + (""" + + + + + + + """, + dict(patternType='solid', + start_color=Color(theme=0, tint=-0.14999847407452621), + end_color=Color(indexed=64) + ) + ), + (""" + + + + + + + """, + dict(patternType='solid', + start_color=Color(theme=0), + end_color=Color(indexed=64) + ) + ), + (""" + + + + + + + """, + dict(patternType='solid', + start_color=Color(indexed=62), + end_color=Color(indexed=64) + ) + ), + ] + ) + def test_create(self, PatternFill, src, args): + xml = fromstring(src) + assert PatternFill.from_tree(xml) == PatternFill(**args) + + +def test_create_empty_fill(): + from ..fills import Fill + + src = fromstring("") + assert Fill.from_tree(src) is None + + +class TestStop: + + def test_ctor(self, Stop): + stop = Stop('999999', .5) + xml = tostring(stop.to_tree()) + expected = """ + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, Stop): + src = """ + + + + """ + node = fromstring(src) + stop = Stop.from_tree(node) + assert stop == Stop('999999', .5) + + + @pytest.mark.parametrize('position', [0, .5, 1]) + def test_position_valid(self, Stop, position): + # smoke test + Stop('999999', position) + + + @pytest.mark.parametrize('position,exception', [ + (-.1, ValueError), + (1.1, ValueError), + (None, TypeError) + ]) + def test_position_invalid(self, Stop, position, exception): + with pytest.raises(exception): + Stop('999999', position) + + + +def test_read_fills(): + # Make sure we pass the right class + + from ..fills import Fill + s = """ + + + + + + + + + + + + + + + + + + + """ + xml = fromstring(s) + for node in xml: + fill = Fill.from_tree(node) diff --git a/openpyxl/styles/tests/test_fonts.py b/openpyxl/styles/tests/test_fonts.py new file mode 100644 index 0000000..6481c59 --- /dev/null +++ b/openpyxl/styles/tests/test_fonts.py @@ -0,0 +1,77 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from openpyxl.xml.functions import tostring, fromstring + +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def Font(): + from ..fonts import Font + return Font + + +class TestFont: + + def test_ctor(self, Font): + f = Font() + assert f.name is None + assert f.size is None + assert not f.bold + assert not f.italic + assert not f.underline + assert f.strikethrough is None + assert f.color is None + assert f.vertAlign is None + assert f.charset is None + + + def test_serialise(self): + from ..fonts import DEFAULT_FONT + ft = DEFAULT_FONT + xml = tostring(ft.to_tree()) + expected = """ + + + + + + + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_create(self, Font): + src = """ + + + + + + + + + + """ + xml = fromstring(src) + ft = Font.from_tree(xml) + assert ft == Font(name='Calibri', charset=204, family=2, sz=11, + vertAlign='superscript', underline='single', color="FF3300FF") + + + def test_nested_empty(self, Font): + src = """ + + + + + + """ + xml = fromstring(src) + ft = Font.from_tree(xml) + assert ft == Font(bold=True, underline="single") diff --git a/openpyxl/styles/tests/test_named_style.py b/openpyxl/styles/tests/test_named_style.py new file mode 100644 index 0000000..edb4752 --- /dev/null +++ b/openpyxl/styles/tests/test_named_style.py @@ -0,0 +1,239 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +from array import array + +from ..fonts import Font +from ..borders import Border +from ..fills import PatternFill +from ..alignment import Alignment +from ..protection import Protection +from ..cell_style import CellStyle, StyleArray + +from openpyxl import Workbook + +from openpyxl.xml.functions import fromstring, tostring +from openpyxl.tests.helper import compare_xml + + +@pytest.fixture +def NamedStyle(): + from ..named_styles import NamedStyle + return NamedStyle + + +class TestNamedStyle: + + def test_ctor(self, NamedStyle): + style = NamedStyle() + + assert style.font == Font() + assert style.border == Border() + assert style.fill == PatternFill() + assert style.protection == Protection() + assert style.alignment == Alignment() + assert style.number_format == "General" + assert style._wb is None + + + def test_dict(self, NamedStyle): + style = NamedStyle() + assert dict(style) == {'name':'Normal', 'hidden':'0', 'xfId':'0'} + + + def test_bind(self, NamedStyle): + style = NamedStyle(xfId=0) + + wb = Workbook() + style.bind(wb) + + assert style._wb is wb + + + def test_as_tuple(self, NamedStyle): + style = NamedStyle() + assert style.as_tuple() == array('i', (0, 0, 0, 0, 0, 0, 0, 0, 0)) + + + def test_as_xf(self, NamedStyle): + style = NamedStyle(xfId=0) + style.alignment = Alignment(horizontal="left") + + xf = style.as_xf() + assert xf == CellStyle(numFmtId=0, fontId=0, fillId=0, borderId=0, + applyNumberFormat=None, + applyFont=None, + applyFill=None, + applyBorder=None, + applyAlignment=True, + applyProtection=None, + alignment=Alignment(horizontal="left"), + protection=None, + ) + + + def test_as_name(self, NamedStyle, _NamedCellStyle): + style = NamedStyle(xfId=0) + + name = style.as_name() + assert name == _NamedCellStyle(name='Normal', xfId=0, hidden=False) + + + @pytest.mark.parametrize("attr, key, collection, expected", + [ + ('font', 'fontId', '_fonts', 0), + ('fill', 'fillId', '_fills', 0), + ('border', 'borderId', '_borders', 0), + ('alignment', 'alignmentId', '_alignments', 0), + ('protection', 'protectionId', '_protections', 0), + ('number_format', 'numFmtId', '_number_formats', 164), + ] + ) + def test_recalculate(self, NamedStyle, attr, key, collection, expected): + style = NamedStyle(xfId=0) + wb = Workbook() + wb._number_formats.append("###") + style.bind(wb) + style._style = StyleArray([1, 1, 1, 1, 1, 1, 1, 1, 1]) + + obj = getattr(wb, collection)[0] + setattr(style, attr, obj) + assert getattr(style._style, key) == expected + + +@pytest.fixture +def _NamedCellStyle(): + from ..named_styles import _NamedCellStyle + return _NamedCellStyle + + +class TestNamedCellStyle: + + def test_ctor(self, _NamedCellStyle): + named_style = _NamedCellStyle(xfId=0, name="Normal", builtinId=0) + xml = tostring(named_style.to_tree()) + expected = """ + + """ + diff = compare_xml(xml, expected) + assert diff is None, diff + + + def test_from_xml(self, _NamedCellStyle): + src = """ +
bG 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 literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/genuine/empty_no_dimensions.xlsx b/openpyxl/tests/data/genuine/empty_no_dimensions.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e402883cb832541b75ff0e0eac09e26766fca02d GIT binary patch literal 10300 zcmb7~1yq$=(|`}seP|Djgdm;L4bt66cOFteN;-r?iy)l}(xpgY?~5Ex^4^>5BNuiL^A4QY9n4(vt({57KGBtlEFvWSaQQV>>SP;N|oMB)O)5%n=FjS2)xnp5r|Xk$zyOJ&tFYS zTzyFP!Y#kGjIzZvpzq8$%@JWXaPVBLH$8lN3&#Svin$7v)X68fWoP)p&51w^6^oHd z5?k;~ouuuUD3z*M_E5@T8k>4(q$F=^0Ft^H`ZVGY=e#mQ#a8w+9DnA_l&Wm71gljr--lHQr<0FeCY2*#OjD=% zd`NpD$v57QS1`!rhg$-32B-zkDNMbL=U+TWLJ%|UxXwXf;_)0nerm!av|(BG$R|?| z1NMpCr`Of}+N-adsaXael4ujh!KPfZe}0my0qggXQ8^+QX-3c6I#3qw%*26(&qu=Tww?rkS6Oe ztb)9oKk1pM)CtlZq5i7$;yph$lk=?T*Sgrk3|yieEQX9y z>BbE)_A(aG`clm0(gAvtGe`I?oU}P@mU|+=Rqv7OWM
-9^4#wUfrDm68W&%)&# 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 K=0Nf7*Z%=_7p>0# literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/genuine/guess_types.xlsx b/openpyxl/tests/data/genuine/guess_types.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5cfa19ff8e95c03f555c992492e5abfc3d1b52af GIT binary patch literal 30133 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 T++d&zH~2?@d-R6i;;VlFf}8Yn literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/genuine/libreoffice_nrt.xlsx b/openpyxl/tests/data/genuine/libreoffice_nrt.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cdf0724d9ec7363b53e6d2cd11bd351b68ae158b GIT binary patch literal 4534 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 literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/genuine/sample.xlsx b/openpyxl/tests/data/genuine/sample.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..cf6d056bab8f5671e755425fa7bd677d29f36193 GIT binary patch literal 11395 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 b+w2xCzx;2oRr6NOb9+jN7W~?x{%ijS3E1IO literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/reader/bug328_hyperlinks.xml b/openpyxl/tests/data/reader/bug328_hyperlinks.xml new file mode 100644 index 0000000..0b85b79 --- /dev/null +++ b/openpyxl/tests/data/reader/bug328_hyperlinks.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + HYPERLINK("#NoeudLNG!I4", "LINK") + + + + + \ No newline at end of file diff --git a/openpyxl/tests/data/reader/bug393-worksheet.xml b/openpyxl/tests/data/reader/bug393-worksheet.xml new file mode 100644 index 0000000..d5a2c5f --- /dev/null +++ b/openpyxl/tests/data/reader/bug393-worksheet.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + + + 1 + + + 2 + + + 3 + + + + + 1 + + + 2 + + + 3 + + + + + 1 + + + 2 + + + 3 + + + + diff --git a/openpyxl/tests/data/reader/empty_rows.xml b/openpyxl/tests/data/reader/empty_rows.xml new file mode 100644 index 0000000..be2b5a5 --- /dev/null +++ b/openpyxl/tests/data/reader/empty_rows.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + + + + + + diff --git a/openpyxl/tests/data/reader/merged-ranges.xml b/openpyxl/tests/data/reader/merged-ranges.xml new file mode 100644 index 0000000..0967c10 --- /dev/null +++ b/openpyxl/tests/data/reader/merged-ranges.xml @@ -0,0 +1,2 @@ + +12340123464646546464610 \ No newline at end of file diff --git a/openpyxl/tests/data/reader/nonstandard_workbook_name.xlsx b/openpyxl/tests/data/reader/nonstandard_workbook_name.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..994f53025deda30f50e6a33d24f252b49943b754 GIT binary patch literal 6399 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 E0eayqr~m)} literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/reader/null_archive.xlsx b/openpyxl/tests/data/reader/null_archive.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..967263ab8a68d2f5626d2ba7b24575fa340bf924 GIT binary patch literal 126 zcmWIWW@h1H0D+T5%64D|l;8u>d8Iiy@oAYksd^PT#T5bGj7%a7xK)ERGBPNDC?G)B S%gP24WduSSAgu%9FaQ96VG&#a literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/reader/sheet2.xml b/openpyxl/tests/data/reader/sheet2.xml new file mode 100644 index 0000000..1ce864b --- /dev/null +++ b/openpyxl/tests/data/reader/sheet2.xml @@ -0,0 +1,341 @@ + + + + + + + + 1 + + + 0.01 + + + 100 + + + + + 2 + + + 0.02 + + + 101 + + + + + 3 + + + 0.03 + + + 102 + + + + + 4 + + + 0.04 + + + 103 + + + + + 5 + + + 0 + + + 0.05 + + + 104 + + + + + 6 + + + 0.06 + + + 105 + + + + + 7 + + + 7.0000000000000007E-2 + + + 106 + + + + + 8 + + + 0.08 + + + 107 + + + + + 9 + + + 0.09 + + + 108 + + + + + 10 + + + 0.1 + + + 109 + + + + + 11 + + + 0.11 + + + 110 + + + + + 12 + + + 0.12 + + + 111 + + + + + 13 + + + 0.13 + + + 112 + + + + + 14 + + + 0.14000000000000001 + + + 113 + + + + + 15 + + + 0.15 + + + 114 + + + + + 16 + + + 0.16 + + + 115 + + + + + 17 + + + 0.17 + + + 116 + + + + + 18 + + + 0.18 + + + 117 + + + + + 19 + + + 0.19 + + + 118 + + + + + 20 + + + 0.2 + + + 119 + + + + + 21 + + + 0.21 + + + 120 + + + + + 22 + + + 0.22 + + + 121 + + + + + 23 + + + 0.23 + + + 122 + + + + + 24 + + + 0.24 + + + 123 + + + + + 25 + + + 0.25 + + + 124 + + + + + 26 + + + 0.26 + + + 125 + + + + + 27 + + + 0.27 + + + 126 + + + + + 28 + + + 0.28000000000000003 + + + 127 + + + + + 29 + + + 0.28999999999999998 + + + 128 + + + + + 30 + + + 0.3 + + + 129 + + + + + diff --git a/openpyxl/tests/data/reader/sheet2_invalid_dimension.xml b/openpyxl/tests/data/reader/sheet2_invalid_dimension.xml new file mode 100644 index 0000000..90f7a93 --- /dev/null +++ b/openpyxl/tests/data/reader/sheet2_invalid_dimension.xml @@ -0,0 +1,341 @@ + + + + + + + + 1 + + + 0.01 + + + 100 + + + + + 2 + + + 0.02 + + + 101 + + + + + 3 + + + 0.03 + + + 102 + + + + + 4 + + + 0.04 + + + 103 + + + + + 5 + + + 0 + + + 0.05 + + + 104 + + + + + 6 + + + 0.06 + + + 105 + + + + + 7 + + + 7.0000000000000007E-2 + + + 106 + + + + + 8 + + + 0.08 + + + 107 + + + + + 9 + + + 0.09 + + + 108 + + + + + 10 + + + 0.1 + + + 109 + + + + + 11 + + + 0.11 + + + 110 + + + + + 12 + + + 0.12 + + + 111 + + + + + 13 + + + 0.13 + + + 112 + + + + + 14 + + + 0.14000000000000001 + + + 113 + + + + + 15 + + + 0.15 + + + 114 + + + + + 16 + + + 0.16 + + + 115 + + + + + 17 + + + 0.17 + + + 116 + + + + + 18 + + + 0.18 + + + 117 + + + + + 19 + + + 0.19 + + + 118 + + + + + 20 + + + 0.2 + + + 119 + + + + + 21 + + + 0.21 + + + 120 + + + + + 22 + + + 0.22 + + + 121 + + + + + 23 + + + 0.23 + + + 122 + + + + + 24 + + + 0.24 + + + 123 + + + + + 25 + + + 0.25 + + + 124 + + + + + 26 + + + 0.26 + + + 125 + + + + + 27 + + + 0.27 + + + 126 + + + + + 28 + + + 0.28000000000000003 + + + 127 + + + + + 29 + + + 0.28999999999999998 + + + 128 + + + + + 30 + + + 0.3 + + + 129 + + + + + diff --git a/openpyxl/tests/data/reader/sheet2_no_dimension.xml b/openpyxl/tests/data/reader/sheet2_no_dimension.xml new file mode 100644 index 0000000..a8b47c9 --- /dev/null +++ b/openpyxl/tests/data/reader/sheet2_no_dimension.xml @@ -0,0 +1,340 @@ + + + + + + + 1 + + + 0.01 + + + 100 + + + + + 2 + + + 0.02 + + + 101 + + + + + 3 + + + 0.03 + + + 102 + + + + + 4 + + + 0.04 + + + 103 + + + + + 5 + + + 1 + + + 0.05 + + + 104 + + + + + 6 + + + 0.06 + + + 105 + + + + + 7 + + + 7.0000000000000007E-2 + + + 106 + + + + + 8 + + + 0.08 + + + 107 + + + + + 9 + + + 0.09 + + + 108 + + + + + 10 + + + 0.1 + + + 109 + + + + + 11 + + + 0.11 + + + 110 + + + + + 12 + + + 0.12 + + + 111 + + + + + 13 + + + 0.13 + + + 112 + + + + + 14 + + + 0.14000000000000001 + + + 113 + + + + + 15 + + + 0.15 + + + 114 + + + + + 16 + + + 0.16 + + + 115 + + + + + 17 + + + 0.17 + + + 116 + + + + + 18 + + + 0.18 + + + 117 + + + + + 19 + + + 0.19 + + + 118 + + + + + 20 + + + 0.2 + + + 119 + + + + + 21 + + + 0.21 + + + 120 + + + + + 22 + + + 0.22 + + + 121 + + + + + 23 + + + 0.23 + + + 122 + + + + + 24 + + + 0.24 + + + 123 + + + + + 25 + + + 0.25 + + + 124 + + + + + 26 + + + 0.26 + + + 125 + + + + + 27 + + + 0.27 + + + 126 + + + + + 28 + + + 0.28000000000000003 + + + 127 + + + + + 29 + + + 0.28999999999999998 + + + 128 + + + + + 30 + + + 0.3 + + + 129 + + + + + diff --git a/openpyxl/tests/data/reader/sheet2_no_span.xml b/openpyxl/tests/data/reader/sheet2_no_span.xml new file mode 100644 index 0000000..1ec49c6 --- /dev/null +++ b/openpyxl/tests/data/reader/sheet2_no_span.xml @@ -0,0 +1,340 @@ + + + + + + + 1 + + + 0.01 + + + 100 + + + + + 2 + + + 0.02 + + + 101 + + + + + 3 + + + 0.03 + + + 102 + + + + + 4 + + + 0.04 + + + 103 + + + + + 5 + + + 1 + + + 0.05 + + + 104 + + + + + 6 + + + 0.06 + + + 105 + + + + + 7 + + + 7.0000000000000007E-2 + + + 106 + + + + + 8 + + + 0.08 + + + 107 + + + + + 9 + + + 0.09 + + + 108 + + + + + 10 + + + 0.1 + + + 109 + + + + + 11 + + + 0.11 + + + 110 + + + + + 12 + + + 0.12 + + + 111 + + + + + 13 + + + 0.13 + + + 112 + + + + + 14 + + + 0.14000000000000001 + + + 113 + + + + + 15 + + + 0.15 + + + 114 + + + + + 16 + + + 0.16 + + + 115 + + + + + 17 + + + 0.17 + + + 116 + + + + + 18 + + + 0.18 + + + 117 + + + + + 19 + + + 0.19 + + + 118 + + + + + 20 + + + 0.2 + + + 119 + + + + + 21 + + + 0.21 + + + 120 + + + + + 22 + + + 0.22 + + + 121 + + + + + 23 + + + 0.23 + + + 122 + + + + + 24 + + + 0.24 + + + 123 + + + + + 25 + + + 0.25 + + + 124 + + + + + 26 + + + 0.26 + + + 125 + + + + + 27 + + + 0.27 + + + 126 + + + + + 28 + + + 0.28000000000000003 + + + 127 + + + + + 29 + + + 0.28999999999999998 + + + 128 + + + + + 30 + + + 0.3 + + + 129 + + + + + diff --git a/openpyxl/tests/data/reader/vba+comments.xlsm b/openpyxl/tests/data/reader/vba+comments.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..e8bc9b844862ac63793c30ad2ded784b22829ac0 GIT binary patch literal 22554 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 H4bcAw>Wag` literal 0 HcmV?d00001 diff --git a/openpyxl/tests/data/writer/Content_types_vba.xml b/openpyxl/tests/data/writer/Content_types_vba.xml new file mode 100644 index 0000000..da73185 --- /dev/null +++ b/openpyxl/tests/data/writer/Content_types_vba.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/data/writer/font.xml b/openpyxl/tests/data/writer/font.xml new file mode 100644 index 0000000..00beff8 --- /dev/null +++ b/openpyxl/tests/data/writer/font.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/openpyxl/tests/data/writer/styles.xml b/openpyxl/tests/data/writer/styles.xml new file mode 100644 index 0000000..c3a6921 --- /dev/null +++ b/openpyxl/tests/data/writer/styles.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/helper.py b/openpyxl/tests/helper.py new file mode 100644 index 0000000..f3d65ff --- /dev/null +++ b/openpyxl/tests/helper.py @@ -0,0 +1,21 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +# Python stdlib imports +from lxml.doctestcompare import LXMLOutputChecker, PARSE_XML + + +def compare_xml(generated, expected): + """Use doctest checking from lxml for comparing XML trees. Returns diff if the two are not the same""" + checker = LXMLOutputChecker() + + class DummyDocTest(): + pass + + ob = DummyDocTest() + ob.want = expected + + check = checker.check_output(expected, generated, PARSE_XML) + if check is False: + diff = checker.output_difference(ob, generated, PARSE_XML) + return diff diff --git a/openpyxl/tests/long/dump_writer_performance.py b/openpyxl/tests/long/dump_writer_performance.py new file mode 100644 index 0000000..03a097a --- /dev/null +++ b/openpyxl/tests/long/dump_writer_performance.py @@ -0,0 +1,19 @@ +import openpyxl +from openpyxl.compat import range + +import tempfile + +import pytest + +@pytest.mark.parametrize("mode", (True, False)) +def test_large_append(mode): + print("Using write only mode {0}".format(mode)) + wb = openpyxl.Workbook(optimized_write=mode) + ws = wb.create_sheet() + row = ('this is some text', 3.14) + total_rows = int(2e4) + for idx in range(total_rows): + if not idx % 10000: + print("%.2f%%" % (100 * (float(idx) / float(total_rows)))) + ws.append(row) + wb.save(tempfile.TemporaryFile(mode='wb')) diff --git a/openpyxl/tests/long/long_dump_thread.py b/openpyxl/tests/long/long_dump_thread.py new file mode 100644 index 0000000..9048191 --- /dev/null +++ b/openpyxl/tests/long/long_dump_thread.py @@ -0,0 +1,22 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +import threading +from io import BytesIO + +from openpyxl.workbook import Workbook + + +def test_thread_safe_dump(): + + def dump_workbook(): + wb = Workbook(optimized_write=True) + ws = wb.create_sheet() + ws.append(range(30)) + wb.save(filename=BytesIO()) + + for thread_idx in range(400): + thread = threading.Thread(target=dump_workbook) + thread.start() + print("starting thread %d" % thread_idx) diff --git a/openpyxl/tests/notes_on_namespaces.rst b/openpyxl/tests/notes_on_namespaces.rst new file mode 100644 index 0000000..da7c6be --- /dev/null +++ b/openpyxl/tests/notes_on_namespaces.rst @@ -0,0 +1,13 @@ +Correct use of namespaces when generating +========================================= + + +Current situation +----------------- + +Most namespace tags are directly generated in openpyxl without explicit or +reliable use of namespaces, eg. `Element("c:valAx")`. These should be +replaced using qualified tagnames: +`Element("{http://schemas.openxmlformats.org/drawingml/2006/chart}valAx"). +This, together with registered namespace prefixes ensures that tags are always +correctly generated and that serialised XML is valid. diff --git a/openpyxl/tests/schema.py b/openpyxl/tests/schema.py new file mode 100644 index 0000000..b333195 --- /dev/null +++ b/openpyxl/tests/schema.py @@ -0,0 +1,49 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +import os +from zipfile import ZipFile + +from lxml.etree import XMLSchema +from lxml.etree import parse + +# Provide schema based validators, lxml required +# use schema.validate(Element) or schema.assertValid(Element) for messages + +SCHEMA_FOLDER = os.path.join(os.path.dirname(__file__), 'schemas') + +sheet_src = os.path.join(SCHEMA_FOLDER, 'sml.xsd') +sheet_schema = XMLSchema(file=sheet_src) + +chart_src = os.path.join(SCHEMA_FOLDER, 'dml-chart.xsd') +chart_schema = XMLSchema(file=chart_src) + +drawing_src = os.path.join(SCHEMA_FOLDER, 'dml-spreadsheetDrawing.xsd') +drawing_schema = XMLSchema(file=drawing_src) + +drawing_main_src = os.path.join(SCHEMA_FOLDER, "dml-main.xsd") + +shared_src = os.path.join(SCHEMA_FOLDER, "shared-commonSimpleTypes.xsd") + +extended_src = os.path.join(SCHEMA_FOLDER, "shared-documentPropertiesExtended.xsd") + +rel_src = os.path.join(SCHEMA_FOLDER, "opc-relationships.xsd") + +sml_files = ['xl/styles.xml'] # , 'xl/workbook.xml'] + + +def validate_archive(file_path): + zipfile = ZipFile(file_path) + try: + for entry in zipfile.infolist(): + filename = entry.filename + f = zipfile.open(entry) + root = parse(f).getroot() + if filename in sml_files or filename.startswith('xl/worksheets/sheet'): + if root.get('{http://www.w3.org/XML/1998/namespace}space'): + # not allowed by schema + del root.attrib['{http://www.w3.org/XML/1998/namespace}space'] + sheet_schema.assertValid(root) + finally: + zipfile.close() diff --git a/openpyxl/tests/schemas/dml-chart.xsd b/openpyxl/tests/schemas/dml-chart.xsd new file mode 100755 index 0000000..bc325f9 --- /dev/null +++ b/openpyxl/tests/schemas/dml-chart.xsd @@ -0,0 +1,1499 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-chartDrawing.xsd b/openpyxl/tests/schemas/dml-chartDrawing.xsd new file mode 100755 index 0000000..afa4f46 --- /dev/null +++ b/openpyxl/tests/schemas/dml-chartDrawing.xsd @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-compatibility.xsd b/openpyxl/tests/schemas/dml-compatibility.xsd new file mode 100755 index 0000000..eb3fd3e --- /dev/null +++ b/openpyxl/tests/schemas/dml-compatibility.xsd @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-diagram.xsd b/openpyxl/tests/schemas/dml-diagram.xsd new file mode 100755 index 0000000..cfd0e87 --- /dev/null +++ b/openpyxl/tests/schemas/dml-diagram.xsd @@ -0,0 +1,1091 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-lockedCanvas.xsd b/openpyxl/tests/schemas/dml-lockedCanvas.xsd new file mode 100755 index 0000000..687eea8 --- /dev/null +++ b/openpyxl/tests/schemas/dml-lockedCanvas.xsd @@ -0,0 +1,11 @@ + + + + + diff --git a/openpyxl/tests/schemas/dml-main.xsd b/openpyxl/tests/schemas/dml-main.xsd new file mode 100755 index 0000000..74e736b --- /dev/null +++ b/openpyxl/tests/schemas/dml-main.xsd @@ -0,0 +1,3048 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-picture.xsd b/openpyxl/tests/schemas/dml-picture.xsd new file mode 100755 index 0000000..1dbf051 --- /dev/null +++ b/openpyxl/tests/schemas/dml-picture.xsd @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-spreadsheetDrawing.xsd b/openpyxl/tests/schemas/dml-spreadsheetDrawing.xsd new file mode 100755 index 0000000..f1af17d --- /dev/null +++ b/openpyxl/tests/schemas/dml-spreadsheetDrawing.xsd @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/dml-wordprocessingDrawing.xsd b/openpyxl/tests/schemas/dml-wordprocessingDrawing.xsd new file mode 100755 index 0000000..cff9927 --- /dev/null +++ b/openpyxl/tests/schemas/dml-wordprocessingDrawing.xsd @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/opc-coreProperties.xsd b/openpyxl/tests/schemas/opc-coreProperties.xsd new file mode 100755 index 0000000..888c0fc --- /dev/null +++ b/openpyxl/tests/schemas/opc-coreProperties.xsd @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/opc-relationships.xsd b/openpyxl/tests/schemas/opc-relationships.xsd new file mode 100755 index 0000000..762dcbe --- /dev/null +++ b/openpyxl/tests/schemas/opc-relationships.xsd @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/pml.xsd b/openpyxl/tests/schemas/pml.xsd new file mode 100755 index 0000000..4460e0f --- /dev/null +++ b/openpyxl/tests/schemas/pml.xsd @@ -0,0 +1,1676 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-additionalCharacteristics.xsd b/openpyxl/tests/schemas/shared-additionalCharacteristics.xsd new file mode 100755 index 0000000..c20f3bf --- /dev/null +++ b/openpyxl/tests/schemas/shared-additionalCharacteristics.xsd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-bibliography.xsd b/openpyxl/tests/schemas/shared-bibliography.xsd new file mode 100755 index 0000000..ac60252 --- /dev/null +++ b/openpyxl/tests/schemas/shared-bibliography.xsd @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-commonSimpleTypes.xsd b/openpyxl/tests/schemas/shared-commonSimpleTypes.xsd new file mode 100755 index 0000000..6ddad5a --- /dev/null +++ b/openpyxl/tests/schemas/shared-commonSimpleTypes.xsd @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-customXmlDataProperties.xsd b/openpyxl/tests/schemas/shared-customXmlDataProperties.xsd new file mode 100755 index 0000000..2bddce2 --- /dev/null +++ b/openpyxl/tests/schemas/shared-customXmlDataProperties.xsd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-customXmlSchemaProperties.xsd b/openpyxl/tests/schemas/shared-customXmlSchemaProperties.xsd new file mode 100755 index 0000000..8a8c18b --- /dev/null +++ b/openpyxl/tests/schemas/shared-customXmlSchemaProperties.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-documentPropertiesCustom.xsd b/openpyxl/tests/schemas/shared-documentPropertiesCustom.xsd new file mode 100755 index 0000000..5c42706 --- /dev/null +++ b/openpyxl/tests/schemas/shared-documentPropertiesCustom.xsd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-documentPropertiesExtended.xsd b/openpyxl/tests/schemas/shared-documentPropertiesExtended.xsd new file mode 100755 index 0000000..853c341 --- /dev/null +++ b/openpyxl/tests/schemas/shared-documentPropertiesExtended.xsd @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-documentPropertiesVariantTypes.xsd b/openpyxl/tests/schemas/shared-documentPropertiesVariantTypes.xsd new file mode 100755 index 0000000..da835ee --- /dev/null +++ b/openpyxl/tests/schemas/shared-documentPropertiesVariantTypes.xsd @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-math.xsd b/openpyxl/tests/schemas/shared-math.xsd new file mode 100755 index 0000000..a07f392 --- /dev/null +++ b/openpyxl/tests/schemas/shared-math.xsd @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/shared-relationshipReference.xsd b/openpyxl/tests/schemas/shared-relationshipReference.xsd new file mode 100755 index 0000000..9e86f1b --- /dev/null +++ b/openpyxl/tests/schemas/shared-relationshipReference.xsd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/sml.xsd b/openpyxl/tests/schemas/sml.xsd new file mode 100755 index 0000000..fa396e8 --- /dev/null +++ b/openpyxl/tests/schemas/sml.xsd @@ -0,0 +1,4430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/vml-main.xsd b/openpyxl/tests/schemas/vml-main.xsd new file mode 100755 index 0000000..4e4a29f --- /dev/null +++ b/openpyxl/tests/schemas/vml-main.xsd @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/vml-officeDrawing.xsd b/openpyxl/tests/schemas/vml-officeDrawing.xsd new file mode 100755 index 0000000..ca2575c --- /dev/null +++ b/openpyxl/tests/schemas/vml-officeDrawing.xsd @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/vml-presentationDrawing.xsd b/openpyxl/tests/schemas/vml-presentationDrawing.xsd new file mode 100755 index 0000000..dd079e6 --- /dev/null +++ b/openpyxl/tests/schemas/vml-presentationDrawing.xsd @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/openpyxl/tests/schemas/vml-spreadsheetDrawing.xsd b/openpyxl/tests/schemas/vml-spreadsheetDrawing.xsd new file mode 100755 index 0000000..3dd6cf6 --- /dev/null +++ b/openpyxl/tests/schemas/vml-spreadsheetDrawing.xsd @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/vml-wordprocessingDrawing.xsd b/openpyxl/tests/schemas/vml-wordprocessingDrawing.xsd new file mode 100755 index 0000000..f1041e3 --- /dev/null +++ b/openpyxl/tests/schemas/vml-wordprocessingDrawing.xsd @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/schemas/wml.xsd b/openpyxl/tests/schemas/wml.xsd new file mode 100755 index 0000000..84be5cb --- /dev/null +++ b/openpyxl/tests/schemas/wml.xsd @@ -0,0 +1,3644 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openpyxl/tests/test_backend.py b/openpyxl/tests/test_backend.py new file mode 100644 index 0000000..1ec6d97 --- /dev/null +++ b/openpyxl/tests/test_backend.py @@ -0,0 +1,59 @@ +# +# 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. +# +# @license: http://www.opensource.org/licenses/mit-license.php +# @author: see AUTHORS file + +"""Make sure we're using the fastest backend available""" + +from openpyxl import LXML + +try: + from xml.etree.cElementTree import Element as cElement + C = True +except ImportError: + C = False + +try: + from lxml.etree import Element as lElement +except ImportError: + lElement is None + +from xml.etree.ElementTree import Element as pyElement + + +def test_backend(): + from openpyxl.xml.functions import Element + if LXML is True: + assert Element == lElement + elif C is True: + assert Element == cElement + else: + assert Element == pyElement + + +def test_namespace_register(): + from openpyxl.xml.functions import Element, tostring + from openpyxl.xml.constants import SHEET_MAIN_NS + + e = Element('{%s}sheet' % SHEET_MAIN_NS) + xml = tostring(e) + if hasattr(xml, "decode"): + xml = xml.decode("utf-8") + assert xml.startswith(" + + + 1 + + + 0.01 + + + 100 + + + + """ + + ws = ReadOnlyWorksheet(DummyWorkbook, "Sheet", "", "", []) + + xml = fromstring(src) + row = tuple(ws._get_row(xml, 11, 11)) + values = [c.value for c in row] + assert values == [0.01] + + row = tuple(ws._get_row(xml, 1, 11)) + values = [c.value for c in row] + assert values == [None, None, None, 1, None, None, None, None, None, None, 0.01] + + +def test_read_empty_row(datadir, DummyWorkbook, ReadOnlyWorksheet): + + ws = ReadOnlyWorksheet(DummyWorkbook, "Sheet", "", "", []) + + src = """ + + """ + element = fromstring(src) + row = ws._get_row(element, max_col=10) + row = tuple(row) + assert len(row) == 10 + + +def test_get_empty_cells_nonempty_row(datadir, DummyWorkbook, ReadOnlyWorksheet): + """Fix for issue #908. + + Get row slice which only contains empty cells in a row containing non-empty + cells earlier in the row. + """ + + datadir.join("reader").chdir() + + src = b""" + + + + 1 + + + + """ + + ws = ReadOnlyWorksheet(DummyWorkbook, "Sheet", "", "", []) + + xml = fromstring(src) + + min_col = 8 + max_col = 9 + row = tuple(ws._get_row(xml, min_col=min_col, max_col=max_col)) + + assert len(row) == 2 + assert all(cell is EMPTY_CELL for cell in row) + values = [cell.value for cell in row] + assert values == [None, None] + + +@pytest.mark.parametrize("row, column", + [ + (2, 1), + (3, 1), + (5, 1), + ] + ) +def test_read_cell_from_empty_row(DummyWorkbook, ReadOnlyWorksheet, row, column): + src = BytesIO() + src.write(b""" + + + + + + """) + src.seek(0) + ws = ReadOnlyWorksheet(DummyWorkbook, "Sheet", "", "", []) + ws._xml = src + cell = ws._get_cell(row, column) + assert cell is EMPTY_CELL + + +def test_read_empty_rows(datadir, DummyWorkbook, ReadOnlyWorksheet): + + ws = ReadOnlyWorksheet(DummyWorkbook, "Sheet", "", "empty_rows.xml", []) + rows = tuple(ws.rows) + assert len(rows) == 7 + + +def test_read_without_coordinates(DummyWorkbook, ReadOnlyWorksheet): + + ws = ReadOnlyWorksheet(DummyWorkbook, "Sheet", "", "", ["Whatever"]*10) + src = """ + + + 2 + + + 4 + + + 3 + + + 6 + + + 9 + + + """ + + element = fromstring(src) + row = tuple(ws._get_row(element, min_col=1, max_col=None, row_counter=1)) + assert row[0].value == "Whatever" + + +@pytest.mark.parametrize("read_only", [False, True]) +def test_read_empty_sheet(datadir, read_only): + datadir.join("genuine").chdir() + wb = load_workbook("empty.xlsx", read_only=read_only) + ws = wb.active + assert tuple(ws.rows) == tuple(ws.iter_rows()) diff --git a/openpyxl/tests/test_read.py b/openpyxl/tests/test_read.py new file mode 100644 index 0000000..2304ab4 --- /dev/null +++ b/openpyxl/tests/test_read.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import pytest + +# compatibility imports +from openpyxl.compat import unicode + +# package imports +from openpyxl.styles import numbers +from openpyxl.reader.excel import load_workbook + + +@pytest.mark.parametrize("cell, number_format", + [ + ('A1', numbers.FORMAT_GENERAL), + ('A2', numbers.FORMAT_DATE_XLSX14), + ('A3', numbers.FORMAT_NUMBER_00), + ('A4', numbers.FORMAT_DATE_TIME3), + ('A5', numbers.FORMAT_PERCENTAGE_00), + ] + ) +def test_read_general_style(datadir, cell, number_format): + datadir.join("genuine").chdir() + wb = load_workbook('empty-with-styles.xlsx') + ws = wb["Sheet1"] + assert ws[cell].number_format == number_format + + +def test_read_no_theme(datadir): + datadir.join("genuine").chdir() + wb = load_workbook('libreoffice_nrt.xlsx') + assert wb + + +@pytest.mark.parametrize("guess_types, dtype", + ( + (True, float), + (False, unicode), + ) + ) +def test_guess_types(datadir, guess_types, dtype): + datadir.join("genuine").chdir() + wb = load_workbook('guess_types.xlsx', guess_types=guess_types) + ws = wb.active + assert isinstance(ws['D2'].value, dtype) diff --git a/openpyxl/tests/test_vba.py b/openpyxl/tests/test_vba.py new file mode 100644 index 0000000..3574883 --- /dev/null +++ b/openpyxl/tests/test_vba.py @@ -0,0 +1,121 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +# Python stdlib imports +from io import BytesIO +import zipfile + +# package imports +from openpyxl.reader.excel import load_workbook +from openpyxl.writer.excel import save_virtual_workbook +from openpyxl.xml.functions import fromstring +from openpyxl.xml.constants import CONTYPES_NS + + +def test_content_types(datadir): + datadir.join('reader').chdir() + fname = 'vba+comments.xlsm' + wb = load_workbook(fname, keep_vba=True) + buf = save_virtual_workbook(wb) + ct = fromstring(zipfile.ZipFile(BytesIO(buf), 'r').open('[Content_Types].xml').read()) + s = set() + for el in ct.findall("{%s}Override" % CONTYPES_NS): + pn = el.get('PartName') + assert pn not in s, 'duplicate PartName in [Content_Types].xml' + s.add(pn) + + +def test_save_with_vba(datadir): + datadir.join('reader').chdir() + fname = 'vba-test.xlsm' + wb = load_workbook(fname, keep_vba=True) + buf = save_virtual_workbook(wb) + files = set(zipfile.ZipFile(BytesIO(buf), 'r').namelist()) + expected = set(['xl/drawings/_rels/vmlDrawing1.vml.rels', + 'xl/worksheets/_rels/sheet1.xml.rels', + '[Content_Types].xml', + 'xl/drawings/vmlDrawing1.vml', + 'xl/ctrlProps/ctrlProp1.xml', + 'xl/vbaProject.bin', + 'docProps/core.xml', + '_rels/.rels', + 'xl/theme/theme1.xml', + 'xl/_rels/workbook.xml.rels', + 'customUI/customUI.xml', + 'xl/styles.xml', + 'xl/worksheets/sheet1.xml', + 'xl/sharedStrings.xml', + 'docProps/app.xml', + 'xl/ctrlProps/ctrlProp2.xml', + 'xl/workbook.xml', + 'xl/activeX/activeX2.bin', + 'xl/activeX/activeX1.bin', + 'xl/media/image2.emf', + 'xl/activeX/activeX1.xml', + 'xl/activeX/_rels/activeX2.xml.rels', + 'xl/media/image1.emf', + 'xl/activeX/_rels/activeX1.xml.rels', + 'xl/activeX/activeX2.xml', + ]) + assert files == expected + +def test_save_with_saved_comments(datadir): + datadir.join('reader').chdir() + fname = 'vba-comments-saved.xlsm' + wb = load_workbook(fname, keep_vba=True) + buf = save_virtual_workbook(wb) + files = set(zipfile.ZipFile(BytesIO(buf), 'r').namelist()) + expected = set([ + 'xl/styles.xml', + 'docProps/core.xml', + 'xl/_rels/workbook.xml.rels', + 'xl/drawings/vmlDrawing1.vml', + 'xl/comments/comment1.xml', + 'docProps/app.xml', + '[Content_Types].xml', + 'xl/worksheets/sheet1.xml', + 'xl/sharedStrings.xml', + 'xl/worksheets/_rels/sheet1.xml.rels', + '_rels/.rels', + 'xl/workbook.xml', + 'xl/theme/theme1.xml' + ]) + assert files == expected + +def test_save_without_vba(datadir): + datadir.join('reader').chdir() + fname = 'vba-test.xlsm' + vbFiles = set(['xl/activeX/activeX2.xml', + 'xl/drawings/_rels/vmlDrawing1.vml.rels', + 'xl/activeX/_rels/activeX1.xml.rels', + 'xl/drawings/vmlDrawing1.vml', + 'xl/activeX/activeX1.bin', + 'xl/media/image1.emf', + 'xl/vbaProject.bin', + 'xl/activeX/_rels/activeX2.xml.rels', + 'xl/worksheets/_rels/sheet1.xml.rels', + 'customUI/customUI.xml', + 'xl/media/image2.emf', + 'xl/ctrlProps/ctrlProp1.xml', + 'xl/activeX/activeX2.bin', + 'xl/activeX/activeX1.xml', + 'xl/ctrlProps/ctrlProp2.xml', + 'xl/drawings/drawing1.xml' + ]) + + wb = load_workbook(fname, keep_vba=False) + buf = save_virtual_workbook(wb) + files1 = set(zipfile.ZipFile(fname, 'r').namelist()) + files2 = set(zipfile.ZipFile(BytesIO(buf), 'r').namelist()) + difference = files1.difference(files2) + assert difference.issubset(vbFiles), "Missing files: %s" % ', '.join(difference - vbFiles) + +def test_save_same_file(tmpdir, datadir): + fname = 'vba-test.xlsm' + p1 = datadir.join('reader').join(fname) + p2 = tmpdir.join(fname) + p1.copy(p2) + tmpdir.chdir() + wb = load_workbook(fname, keep_vba=True) + wb.save(fname) diff --git a/openpyxl/utils/__init__.py b/openpyxl/utils/__init__.py new file mode 100644 index 0000000..cf7e863 --- /dev/null +++ b/openpyxl/utils/__init__.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + + +from .cell import ( + absolute_coordinate, + cols_from_range, + column_index_from_string, + coordinate_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/openpyxl/utils/bound_dictionary.py b/openpyxl/utils/bound_dictionary.py new file mode 100644 index 0000000..d4b7da4 --- /dev/null +++ b/openpyxl/utils/bound_dictionary.py @@ -0,0 +1,27 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 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/openpyxl/utils/cell.py b/openpyxl/utils/cell.py new file mode 100644 index 0000000..e29f5cc --- /dev/null +++ b/openpyxl/utils/cell.py @@ -0,0 +1,215 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +""" +Collection of utilities used within the package and also available for client code +""" +import re + +from openpyxl.compat import basestring +from .exceptions import CellCoordinatesException + +# constants +COORD_RE = re.compile(r'^[$]?([A-Z]+)[$]?(\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 colums, return all the columns in the series. + + The start and end columns can be either column letters or 1-based + indexes. + """ + if isinstance(start, basestring): + start = column_index_from_string(start) + if isinstance(end, basestring): + 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.upper()) + if not match: + msg = 'Invalid cell coordinates (%s)' % coord_string + raise CellCoordinatesException(msg) + column, row = match.groups() + row = int(row) + if not row: + msg = "There is no row 0 (%s)" % 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.upper()) + if not m: + raise ValueError("Value is not a valid coordinate range") + + d = m.groupdict('') + for k, v in d.items(): + if v: + d[k] = "${0}".format(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 + """ + m = ABSOLUTE_RE.match(range_string) + if not m: + raise ValueError("{0} is not a valid coordinate or range") + min_col, min_row, sep, max_col, max_row = m.groups() + + 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 + """ + col, row = coordinate_from_string(coordinate) + return 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("'", "''") + if (" " in sheetname + or "-" in sheetname + or "," in sheetname + or "'" in sheetname + ): + sheetname = u"'{0}'".format(sheetname) + return sheetname diff --git a/openpyxl/utils/dataframe.py b/openpyxl/utils/dataframe.py new file mode 100644 index 0000000..4a243b5 --- /dev/null +++ b/openpyxl/utils/dataframe.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2018 openpyxl + +import operator +from openpyxl.compat import accumulate, zip + + +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_levels(df.columns.levels) + 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 + + cols = None + if df.index.nlevels > 1: + cols = zip(*expand_levels(df.index.levels)) + + if index: + yield df.index.names + + for idx, v in enumerate(df.index): + row = [data[j][idx] for j in range(ncols)] + if index: + if cols: + v = list(next(cols)) + else: + v = [v] + row = v + row + yield row + + +def expand_levels(levels): + """ + Multiindexes need expanding so that subtitles repeat + """ + widths = (len(s) for s in levels) + widths = list(accumulate(widths, operator.mul)) + size = max(widths) + + for level, width in zip(levels, widths): + padding = int(size/width) # how wide a title should be + repeat = int(width/len(level)) # how often a title is repeated + row = [] + for v in level: + title = [None]*padding + title[0] = v + row.extend(title) + row = row*repeat + yield row diff --git a/openpyxl/utils/datetime.py b/openpyxl/utils/datetime.py new file mode 100644 index 0000000..1adf325 --- /dev/null +++ b/openpyxl/utils/datetime.py @@ -0,0 +1,146 @@ +from __future__ import absolute_import +from __future__ import division +# Copyright (c) 2010-2018 openpyxl + +"""Manage Excel date weirdness.""" + +# Python stdlib imports +import datetime +from datetime import timedelta, tzinfo +from math import isnan +import re + +from jdcal import ( + gcal2jd, + jd2gcal, + MJD_0 +) + + +# constants +MAC_EPOCH = datetime.date(1904, 1, 1) +WINDOWS_EPOCH = datetime.date(1899, 12, 30) +CALENDAR_WINDOWS_1900 = sum(gcal2jd(WINDOWS_EPOCH.year, WINDOWS_EPOCH.month, WINDOWS_EPOCH.day)) +CALENDAR_MAC_1904 = sum(gcal2jd(MAC_EPOCH.year, MAC_EPOCH.month, MAC_EPOCH.day)) +SECS_PER_DAY = 86400 + +EPOCH = datetime.datetime.utcfromtimestamp(0) +ISO_FORMAT = '%Y-%m-%dT%H:%M:%SZ' +ISO_REGEX = re.compile(r''' +(?P(?P\d{4})-(?P\d{2})-(?P\d{2}))?T? +(?P

#Rz2G+$9_a2rl zCPYI=IMxVVmdBFY_Vb1)33Q{c0MJL!hNFv(0l8uaYa%2V4HSvuk^jt>8f|3QPfvex z?D70uGT*}$&;6N0jNhE^bb;oM!&i&M($LNgi>rJR07Hapj~&5C;>WPKOr% zK3{MiIBJpchm(%k5sK6jP58WiG7_z0cr`V;x_q{lPxhr9t^^)T1}C1M^ttpeH@1d+ zU`%cIR6sMsH>@Zlvdx{+k=6Cw(lE;(M7O7LMYCpL?AqTVU|Je{0ocKL4FHOVoKfF| zCPy=DpbbQHBuA4eyh;K{xn(6Li8mw=pvC}bbO_Wwb;~ClD5=EOKZ{6FcRz4nbS}R- z2^p`n?1`6|W+Yy~4x41Drip5$o$9K+kG7=gyncAurF!rjRgA_$Rqijz(u2$quZ$P z;5e^vW1ws7jT}A@mEB>=@$|{PEIjZCc6suRJrgc1i2DNREPAI{A!}Hl9(;CC?YvL? z4yR>y=R8{~x;uZBD0?#gm+9wKe|9UWLu!#f0w|uA7zPao_`76#wS0Fb7=;T9cZA#=jjqei#+=UfR zeb5p)e#412dT>8xGv)TmidYL1Th;CT6+fNar7NWwr-XcZP7o=Uoa<0Dd_~;CsSP1p zK?=%7uh7MNDIjK}ebY-*XPaK{-7SYv8P4YTfhKDM3;YBI%K2%x?*C8AZRStQ?U{gc zbGQsycKM?CLOxtWZ_o_P#a38m^h9sTEQyOFszu{sYzJ z2a8huj!4z@1H*W^(2{@B<^EbpPE@}VX^;etBYD?7qdi!r^XG%LUm-RUP=kxUT;ID( zeHgB#BNr*U?EAN+Hnh?_%hKd<%>BwxM3GO@Dhl@U;L+2wezqJOVGZ8i!V{f)MDZ(Q zn6@T5o3M20eY!Ktg9fws(3DKNnuT)7IJ59Jw&Thtsb`Fbj#2*7Qw16JJskr*as(@e{u$_-vv~>k# zCBlDHnO`f>g5pOl17ZY-dyu7&);^<@-yLJG;`hhe;7xux5<{r`@v~*-Ch<3)|1BBd zKl59I(|quFbot|FhxKa1j(>p)7#=3Ff+&g&*U(+3bdZMyWOBud|1C(U{m{6#jXbCd z`?u{~5vMRDA8MhbyJhftqZ5&|0;Qn-Ru(%SPT1Q@O1Um?_i7bA`1FwTOm;roo*cV2 zQ!Uen{kPNc$M<9ZNg_Z4BB!)55|#sAPhN{psisc03j4%7m+WW`zy`fqo!mUhzxS5F zg3jCMT4ES1KDQD;HwX@lbzYc!ly7x{`RNK;15nxFBxd_}`j+(@cEQM%#ro*pI7;)H zt^*ZvxrHD-GcDr#Ev=>g=OcOP|CsWHO-8K(O85t-c}E-f`>K)6`|`E%Cc_N@ew8+k z{+#@8hz_Dw9<&93Sm^^)jg2QC^#q;cIC9}v+M^Rc)o?!-?ne%n{2RiQWk4CEuFke> zY<7Fwt&Q;R-u7LTRfs+v{!9+$sLFK>arwVgZpeb(eX)nh+QoIw=f4+fa{1g-dGVTk zY}@*b`?9|3bT-o@P7gxK9|`zTOLSHAtI($+3P0cWnye!WYq}HG4W|~JCI%E8lGBD90@Bpnsi;d`whR6zxvQ3(; z+?YW9)x<}o|Iy1O*mY*oRub=;2h2D>(r%Y8j3PS0vKLb(&i?zV-@(4=>?{cTozHOZ z)VEqAKVo<}u$_Qbw;y(Od&-(EP#1G~ z%$ahf7_cyflEeNLDoCR!RTlfy4Kyq@^%WO4H^NA2bR;fTV&t3#W3}mB4VDG`|5N^g zMAsNDL)vVKeaz@jtd)g;e3O*L^fy`vLu=CBv8W=v|AY@t0=DpNdp6g8G3*UAXvzA=YTR&B2ESjewy+1t-tpQDwdP~}y}pD3ycJt5yEj+8dP{?t z2K>8SU0#X1E`1)`zb}YS_!CZlp$0S7G#c&7oc;^OtW@->XH^Hbec>ZeTKo^+wy@y# z2nQ$;Uw-~8-x4m+XLXqh-z$4V6&YJeb2L!f-0J^t+uWIOK#qt-7|;5YBv7hr?1efE2aQr1Zk@9|1JfM z06>;1BEapP+6YzR+sfqq(th#%ZqDSLBiU`QL8V*yACdi|K~Stt+MFHG?v{vz5I|my zmQ5G^zWJgy5x@HVV<#Sxib6{KUtRjbg7&@US#&w{^orfKvc0|%Eg;o18^n}@DI?+c z1SiCp=+Zb+rTrx$!WXo}x4OaG#=#1mdA<@oi*PSR0xx*hyQMIi-JDL3E)o0}1tGp{ z|4{7A+$JVH5T<~a_hVn6xC~B@0cp#opJY1q$1ufzaO3|Fh63^~H7>z1m@eqq6_Tz^ zE&PR2LjEmTA&mdFK>krd7zL<8pG8o>FpZJBYjDj`mi&uIoIiGt^Ho{+zXx5qpa&DW zin6+uZ7b^Ez7?P$WhN)WCg~)E7V9KSDu@320T>QyHPszE%9H&IIAVyX&+<7YZN(gw zpPA_iI6H~!ESJ@nx`&y1iK+fFRssXgg=E9~rqzguUuhz=4Mn-=tfJ-O((`kucXFYk zy|M&GdMfs>#ONTLQ)H433}Yu%5M9wrvC6xqBwjZ%oCqzV0ZXxRyMJ#D@r469Z3b=b zJaU;-HxgQI#6~<95v~R`hXKsJ{17Vw3BUh?S2Rcs^K4ymvCiJ};C-bkO^@^B+TZEw zD>7^;#(nR86=?pw7F23vT<$^N{qSJZGxlI3>K(1nos0wDuvDC+;PuYoGN_RPc!S*eGcUJ9#Y|Li{R2*_^v(Wika z^S^i;;`qt^{zZrvI>aD6A_xJl0xCPe%Eijc~D`5&q8Lc^v<-K7t zI_UjMF;ca|VA0d1tPZUdfFQ(Q29@0)RFQ1vCU4c-__QJ9H`eegJ9LQNf*OPCCWD#o zlL7*58#wxelrlu0Qv)^r7j{7whlZkMfI(Hf*g~Z1y^j=eGx@o*fWsU|Z&3y?$p!KYZbp=~5lDZ3T%GSOI&n6e5t@EG zgzXa%&9H4@&aO2LADwy_HsJ4*`TrmIN_HKU8(WD|EB-=`JUPvTP$93;$grW4`@vj2 z{F}C2LA7W!16=F$7ySRYG=xw!X>%uME(J2{ES#xuH&xw7+}7$7z`L@g3ogw{Qs*K6 zwP4{c5^(&WMpJx|+&$Cb2pC{zu;K z)$m~mXwk==H*1cq@0G6pqSFa17`^6~*BRJdjY$V?E=(3UP2vAROPxZ07StksAOI#+ z_EktjaZk%`-S0|Ok&)xmr+U`?zCsOpDzQB(6j8sqC&O^`fdE3!K$J@UpBO-?!fd~a zQ)Xr`Cfm)pgNTQd;1?2>nWl7gbkdF&Lei=tAg%t?lqQkcmrE1`6e;Hq1FKJEvYQg(CP zSoZb^O%Y9bXi)TWN$py!H92?0rgUlab4CR3L6GF;wj>SwY;016f z1!tzwIeX{YBcC~A)Bn)5#=&aF^;wKoW>l<04*LOPE)N4lf=Yz%?0o2ryM3eYB`iXC zaQ?0mlm=A@e0UdVcmfRtl7ckc&y7u+$1*h{Z|(MVODvDm@l0XVIv|k{g>(Gaoj`P0 zn4qbBNp%Fpl&sAFj2yCHp=^3yrw7Go-yOcj6KAS;nfs=Z^zW~P+t{ZpciG==5p=!W zNuKeeQ=YdXt^_+hh_=aP`W^~Rw)okBW3Yg|W*dVC9rwky=*{gygUc}~#v6T0DL4=* zpM>~P@$gF49~Hqm>nhe#Oj`VuRR61O3kA67nkIk+um4W^DzW^{S1OMBV zs8)#_4&|REMyZAK!iySUDu{$YHdLAujo7JRVcSeTokJs%)O7Bfw=-eP_x{L!d)0q{ zPV2D2+;`@ID?pplc3b{IueF(8@+5Tn z+kIxoP#@i())UH#%+qe|34)v9WvLj&$S(veKq9wTA(YkKZBP9K@@zA@>S`z7^NdV=j6+R<7BIV;_Ed) zed;`HLKloJLGuGc^nQ2B6$JP!P^@;e*WFnd>Q-V0OR#7e21$@O&5th_d{r%bg4Bz~ z4-=5{@?YqG(pp%VsOX3ZY6&1>#REb;FD%~|t5s9i$j4wI*s^wjNr>Hqx4RAn-(ROK z2R0Uee7_>as$7r$>F!mn|4jHhTt*!0PeGy?p?M$@X!=R_VJ+6fv z-5->^+_yN?nT&kn*KYKExI0uc%hUO^HAe!WNo^S|`8wPT^E$eAu3wI#FZHi4A4pff z#Krzm-A`YnUJE@UP4@u{BFYjKBsj`R!!01zt+$o74^GCMI<1LRROI8SPbYM`EZ~g! zFvf0|vf=Do5O9-8{L{K<2|BCe&*}>C59{w>gt$vmpYXPed%2{{WXx%;i`pjLCTPBn zWt_h?`k;INe&)@vtgIaNp|phCwGg5NWlJ045OYYCn2o+|4k7|rvC~R1Sq6N0%}MGl@z3eHfDd{5?+8Xyj`;9A?eO$!oq(Zy=gby-#>dk zJb!b}1vTCIi8Ta2H$CHC1ZHErqP$dT$_dz4`4MU@+^SkZ4`3S^#BM+14__`ZzD2S_ ze0?oWMG{Ou@3*VZF+T-kxd%;V&!F?_&vL0XDbr^4i?2gfU%#7h38^o#M`w7c|2q>vNACd>m* zxQ0?dkbxBc5s^f&qW>eg8vl{--yRa~06`3hB)Kh+5gI*rFp)& zz`mUtj0~Nk&*ynO9ccZWo-&+nW30K<`NiFa*8Zs-M@td-KfKvU3_vjwHb9=-elNz* zbtho1P*b7 zCXGakIu$GvX|37&Iz@W=_lQw{fwgWWzo8&#CCE}0p zT%oovx~3`}T)Ef$t(y}BYzC+ei?sl|-+yk22QX{0u(~UBX%uhBYlRCj$Bs=$>k9{n zaolWCMEL)fPxB8T4zfmaM!I+73tw$c8$LQ}$DuewW3Cx!$`Jyv!hi2~4}=<9#|BNV zL@+aNoU^p)`#^F3_B#Am_3X;axq#nfQm3}fUd-Zh?D&(LNW;|$y243I#>s!$JpOYv zBL8r2yk<|s24oX(Hhb81N{(<~UCsV#2rac3K69vxVRP*1S~yINo&dxDzVshl)3LMx zBCZd-;k@r2N8`!}y#NUsbolQ0|3oez%l{YD%}4;T*o4ISH~NLt|1T<5Nc0Sq8z!HW z{(txp=${$_tfusT;h2@8D2)U%!Cq=>b4*57BqKIP`P>; zDJLuQ9>Ib@K4{#VT93YXv^S3qfwr)0FUwAW%_SL0cmOyF2y1?jv50@U6#xJbd~e7< zTm)csn{-7({`Rb52kQ93b^?MvZA!CPBC2%2N|^MRfRF{<{_R3zZ-7%yjnJs2POYQYzxUgv~y+L-;KGct%0j+1qD$N<*%CE^D?504kFv% zn{+b!8=M_wtV}hj&x?J}@zn44z58RPKAX*>sWcdm26#s`A=0jhL!KD{@CY*F0asqJ zJap&S?Um!==6VEku-z5LW9c*8E6sGHO3AADe8PVl z?AW&(H%zc(=<8&=iIuZ%m3}kY<)}fz5TMxP=0mK|cO-`%=F1arPyP(gbxFOsL%_*w zMg1nNHw{_bxUqox%Z`^G(3yg47f2IdZXqyCc5Xn1)1x8*Z=nnSlWV~rUX$a0kMV+7 zZ}UdO9{S{1$L}Q~C04N;5$g>I9X>0`o z`KL(y=#tmGd4YVH0>{xQl!tqA)W01WfZl1zCBWR=mI**+9Cyy#e#K4!b_;;u3V1e0 zX!MLip*nz?i$L1y$f8-U(o#@O36}k={e-RjBLwJ0^4%Ae_?%UYtrftXl+R$}_uZM# z)&uVXU936dr2n6Hf%<<5nNgviIQFzS2zuQ~;Ge#gK!;b~GiZV4i+hq1DF4S0TT_lg zf?qqgzZx2n{X-WC?{X31=h|{ybwWPNuYax@A~~LaiyYja5h$qEVd`9%;I8CwfF`dl zL5T^GAO-$ddY=X-vcEaAj_fXhTuh-##yd+k{wG@%5dr>2GI-KHDt?vP^{@*uaPKQXeXs`F9QhDDtwxH zM)lq<9_)?YvR+FIN_C{G6g)}rf7bL0xv~P8(+(Obrl!VSc}ba>fe~}!*2JZFxV%`6 zct?ZhYdlScehoujV2NHAue_IGXT^;eIGqM0Gd4n{ZU}(EfNioA7^ITdj%tb~cV?aR zjUMgGAr|*dZzZE9zoCS}eu_QH9bDtVRV``5xx79rZsvvBtL+L&2YU_J`bCENzx(iF zkl!L^%>mj?790JXSm;OkRw?27x^7vU|F+MOXbPw{4TuKI?i*#&Clfa>a!z%(lxQ=K zEQ;x{c}y_x%hEa7xO(saZB_vMfqgjCBa(Qt)U^GmI&xCP*ViF95R z{|s9Cib_AKXSCy>CD-Lvw-28K6I8y*B;C#|^nJvfyuQMB&7S$m#yj{_QMFTu8{9($ zB&IkIE@<;n_207cgyh8NtLp9}lcRy@G>J;bmMg`4A$bjr-ZmE+_cwNd86`+tyxFCv z$Dp=+vLFl9VLffU)K4MUoxrN6Q;iMqBjvk`R4Pu3Ws(nsm0gLT%6dT2HCn-maxNKi zaiP*&bo+zvYyIP4e?EFy2UhH9r@}9$$bU@NBKS^~($A zd~>E2F=sUlh!w_GzL1g|Xl|3~5AMGV?A~PP)s#()N!po^&6$?k_qJB+VqMMj!DAu> z)U?phUj{?Zc`AQbMGrYyk_?zfCt7s6kwX7ihW(BdZ{IiEYcSj*|8Dov@HM55oeaom zs){kWJTK7s=}Xb#3@F7&@b|RsHtwO#G~XgjsB>$yY4<-{$h;eJZmpgM{9?lM+pYpR zNXfkqFD*CxPI%E7_oO3L5q@4M!3F6;TekTKvRGV*=K&g*KaOfcQOCNs*T-CS8E~yANA&n1gz4%SlI^ZBs=hU zBUn(RaPRs@E4QsTvKskCNY@eI|JNpOVYej#ble3qI(gsET1)K1d{n*vq_JI3Yq>V&LFKrv>l5a+`*>pKc@wr2`r8 z>gwvtZYX}O`=!joH7imWdJWh74I-+K=G%UAemksHm#rBx(Xb-(*}(QsS;3orpq|7cJ)Vk*i0EI|^^MBX zgx|T%qtk!bQO(pK^r3poc{H_mu%&q0*aoBF1mSArWp*1zE=8OFGtM?&`=(d8e;PO7 zKxoHIjfovIm?){cApVNj;&h5cCY9>f)d^0(zl=4__W|V9VUe=^$pY5~c9<&}u<{m-9*$?qb-VU5^!BD>af6T{3)0KE6}82?$4>T>bEJEsoeXfh z>-7-2u9trVI-UO6cuu})!mylN{IXxBl=WBnyoQO_^%-UW?{lMnXbCcPh&|&_G!8Am zOhi1H$qn1Jg(oouH#SmN&;0$$4-x>(GaJ7FrIM^@@XXGP1?%(u={mY|_pq?M-OtpI zO_g+*|21>;F*jH zmE4oBg=V9C%eSkkaDV4o?=q7O#Ny-P{Ef$x^>_P%&DNV}j;69A+E_GqPOiRl=g>B5 z^VRf~bLk^Gu^QbM?VT=A1G@Ux1tjq;Vk+^yMWvrp5uGZ;|t*!g(9Uum+&{#vWY zJq!AQv+4}nCh_JW=bqIHZS~adv#Q@zW$fbaEdHrP1gxUUf_{uvOYlVEqv1Fv6{WFX zyi*j5W#W$NjF6TZ>^y9vqV&9QjSJ|vV}$jJxqRnZ?(XM_dx=NdnI1lx&WXY0a@iR~ z*EO29r_0yx51HE{*0mEBklk_7;#~4^{n0<u zGk}NO+#08L+{_H;L)X&0ucXn$Iv`3v zry5LCC8x&^d~Ls4N1E#Px4Bs~$$yzzTmwmim6s9Y*gHKnE%HVuPVkw)4w|R36}o3KUx%jLqg+e8 zSz3I20I6iM-fX@Ipg)QVjZP=BHxL{hbMv);%}#Iqkmxm0KdpzMX{VCEO2qbQ3^BM| zx@TyQa-0cLyFp*A&Hz-!>mD=P@;auHs=ap4ZP4TGmtHrPD&X^)X~bVDLB0JlvkjRN zE*z1b4gW}NHk+NsWgVgWqe=VEH|LAh2JrP}+h#K-x|~Ro0?lwoI|pA3xXTuW@Z^Po zw11dZMQK!uA7F3lLux!>?Z{C&Z7Md%+5YH9Tff0X2tE_xpTs+=;YCpHOr$lnj=|@P z9>(*8;qiP5%E`g6*69q#_x(V;TyJh>M>>7FJBmOXrSdRE#@V`IR_hKe_ue1fR+R2Yy$IT@?LQ_L(ktGn@{w+(B#-~{@0p9Ezyc< z2ru21#BkV5;TU})B@|FHs^OK%=NRVD3Y<977G%KSagd zl%6K|NP2U8i{WI62cddrpi~^eU!TwV!hM~HS%`+fMvY*2@ zJ@-^;%=t4o3jWJm@ML#cm+c5efC$;W^;nKv$K;@k9^dnbc(neR(- z&FNZOBbUZAsZ+pc3`u~lelt8jw{-cu$6mSmXD6_OfKdy=ARk3O1VlK@>Ie!H&CFb< zSQ}ut?J>_L>8aEliTBp@F|%pMG46K45y!QM&q4g`&=xN39nQcoi);ijEf2o0j!YxT zk(LTE!f$!B-JT-m$g|$+7py=w78d1n5>hg0SWHHc9Pg*Jg|#YMjXuO6HO9hSXRk*Q z6<6-Ng{JrRZo6qipIg=hhGLVRcggZgA|)IGBh z@aik&hf9~c>2d}kT9yc>(+NVcR9ZluP$&@v1%$l3eEfVw2$pu-OK%Px5f6u$DX&-k zDM*m-vF>+F+l^*R#$<59 zcqFUMHf4)l6Juj>K+Y~Y-q4@`V4&3a<<}(}A=-zkr5EZx6RHhe{eL zPRg9ht}f-)v|rtGQA4Xu7HhiZ_AD(2m5u27`z**f^z>kR%anjA(HNCD+t&AB53N+f~9cWlFoc+HHo`1^^T6`Ge!}FIQ_5YP19D zO^hwGoi4u+muqg^JQDC#ej@*t9P4c*UdDDyiObJ+7Osp&?WxJd)(2zUYD( zaxk6Wpbdi+m#^7Ht@pd`YKNiX{lwY+MDgFC>{MVqMZbCUw7l7z_OySg*?%#9XYF_t z%Oo6Ew$uPGeHU`0-s=H*Kz#YUM)6LmeCn{JI-CC98@fTY$NV6$~sBiniMu zw_9{PevSOBVK!swMylR(oHK-?N)w({F$R3J_p;2lUrhaDetX@I1>ftpPloD#=(|&U z3==atp@7Sd!}z;A3$vJ|EFB{Q`Yq;S`$%Qoltugj3X}j~a~|jmh9-3;*wsHPO3?`@ zDJhXJr`E!vbX?(UlHXY(mRGiijJ^sh6_X+sgVg==hvu@ju!{U*)T~X`6IcFAduFW| z!E@-bA6iz&?3?Yf-dwy7o#-w|d#ft*;w6Hd%=N`Kb(E0iy2n$(%{KrNEE4D*l0 zCw3c;RiNTR?HpRB(6nrKFXEZhA;)4=2kLV&7u_K;$>d#?^JyUl1P8f zK+e9zSV4K!RQVIN#96lAXsDMQddP@p=^{Cu1?#)z$e=y{f?{e$0t zUBL~IVeNMNKtaI4v8lMSPSFm~cK1dxvX);8Pay}i#04x2SiLevvkI3J`1F0xyiZOw z%t3QjJb>;yGyN~$Z=~(0Mh3Wc)pu6=A#Wh;`#ybRdi`d%bE}qML04iP&ec;TO~?vy zbW_Ca?N6DVXf@B4?)Qb*&n*KxzmhB`aTonE^5N!{v$NBDSX&;Tx(PsH?WN(YLEkIl zS`3b()<2kADqZ{n5u>ViTyIU#!hchw|3ogO^GBA)M6+}o^a=r#ttG)!TU@j{-0BXr z$0n=X6MLrStTcA}Vgp|h*qsTWkWHSfi-Fw@UaLeVSkR<{uN0{@f93xkf7Lwa1B*Je%$O8Fh27G*%b_cptWY0Q;jGr;HoQG$9b6K3aBb`FmiDTOue$6@EvDb zsG0VMWDe>{JCWKbf%WgWfNdjuW>wYN4Of;*eY@$0*Ecl{_5HffnJfIIsrmU`Jw#h^ zGdxkHZK!DD_9pL3&Y!jjT8$?I+r291y280YUw8<)KKGg);WyCQ);;V1u8IZJw3X|s z(m(?Ja(eut{3}psN!;uH6bsNd5GY*-Bc$U6N4%kE@Ae7s+8hzYYryu-y$VH9s>lgO z41ee2%fm_K5`s8DEwsdyT8+?5qOmbCDbBT{ZWT3IualL5X7PBUIUY?2IAUo79gMZI zyClzAZAWS_7V8W}klQRF`O~El19jHmHI4!gnY{00!!PY#Ra#-)G=-+LI#cF@)8ynCLkc_sT`w#N{A~LcHoA;Pu&dsMI{I1Zqe?}0-C2iaouiMJ(1q7 z{~k&?Ho($WFf%|yTs>23?d4CuROAT8t~0=T+sM9BB=7E`z5 z?e;%OMMP@rnr&Txv7*4ZTrOf!0`~UzZ!a3A_af-J{2G`hL0IPP4y8KMk|QIOWeE1P zxUsh!Fwkzy5$pX7Fy)mLszG-Sh_Dj;QnZ2zT}~lJJ0V3TP)i*pyOEtAo6T4b9J$XZ zwb~x(O>Qlu+x;|la(us97m6+p4@t%E!fcGOv3F5=I{I1Cd-{7jAce(91f={FDJH(( zjkZXDl0m6G1sTlU<>n74;b>y`TeR^!%#n5yTz-~k9N=VYv=J>BTUo}Bbz;p@hx`}g zHkwRg!2m#R-|b+ne!m6Uv{ej~LgI)Ny3Z$jDiaV3i_BiWLhGWXU+6%+pD{u~Le=(h zHBc9wN?cGxC5X`F{46aW+!I@wa)vf%6UC542GX6O^!bZGW&7t&AUuh3ZW@#6-py|R z$x5A}V)+d9vuFdbe1n~>R59C$`YpSy;Yiw7Hs_FAFtr}#-kkpo98e*jJeGt5Q>=y% zm>ev23;0_AGb#*Tb%`BYLvLQXd;snx)C|1%jVw7hEW@%HgWKuFV@Q*QWrf0)^!&sa zkJCvkv;Nu1Y=PTZSqTCvDkwgF&RV$1((+z)U4%-555F=LI`-gnITT#O7ENFExytBnuv*0X$NjV< zjW#$q@dwjFWh!EfWa&F2Z;19^EW|p2gsIUna#Ji4jrq(eW*80Pb7`rX-6SKRcUi%? zwZjzyO=UC$i0lTG8Wc1nR84mFgT0Xqd_7frd?&;yMVO0OdG@K_V`%R7wMM{s;Y1H9 zqy?xn&$Gf}f)$imh}#Ra2O;#o3e%=Tm^%cGd;lmr-pt?5<93XyMsFrAUO_ey4%@8Uw>@{>l`+L*=d;qnO?*4O8K)`$J5QCND>tG!H0UB@#@nF1Rjcifi}I|h;odS=f_0ws58mbP@86EE zVjsr}rqrz^iad<;H54AOHN5Kl@?O&RKxUJyR?yW3{YUc@sUJjSrATpRL2aZa4wlOh zYEkipb_NVVnQ9>Cb_V*1FxaVP4B{HBq<|D9pHBY>*kla0I^5Zv&k_r%))JW~U$+iG zbhvD%ro~iO6{OBi3S&*4I)WS0WQl)QguIQ>-C%PgT*J~0ROG>GG1Zv8)JpX|E8doW zVp7k7_DxJL;*?g&Yt32CVTarMoU<}E?Ydbv)ho6 z5EbdIv!_7?BDiD`0-7?9=3044S)X^ww%3A!P%3WotDx&#&)OtL?i@wG#2UbGEG;7v znR>=MuqHYzd6x}j+Yj|6;fOC-S(l_ZryU9@-y`*&zynZd+5OQ~UB9Dy1v!J`&WJjp zN)t(r5H_20Aa&btzBl!Y2)>BLzP+z|pAJVB*pemgsO;SW`qaCOr^7U;OP#oI4zNy| ztv0BNwvHHKWFxa~J238qB>JWOsZs{dwrYo4=Y1rzN@VE$9YreIJS%Z|Cdsl)SZFw|oFiMoG&ddfT_|%!Md1Q37cdgN~&g<2|>2wbC8ZfF7 zAVkk>h_JbT?G0;w{Bf-123|F91!L;S{sikGt<+|LNJ6fF8ddcLf56}Zvv$1C#X=5F zdH);8HW*Fy(32T@%}*fy)2+e zq#iPb#6$PZL(?XEfg)DA_h`Mpu*K(IWe?+$)b~WJTLfj^`GVXb9Q>^t#F(PNC!cbWL+BP)@VckoU zpLj-xX%1TzK+rb5;+W%MB96qfD5ye0whIoR&gP2{07xHSx7lJ(43L0>vbbFHRq1|C zDFB{zEB+DIz4h7#mF@1K;v8kHg5%Od_{%1PxG#~>Pv*jWXR&KuyIDjkZ8Dt=tr=?z z@Q;Un@!WHIg$?!y<z zeAH8DG()u8U3LLAk)hZ7w1EvB(?T4h(psFKE!`}WK$VVnL<{Rgohuqf>1?JzDHg{Kq{S99)dqBf%dPg}8eM6V z503kX7GVJej%1i>yUP~0TLZ7VQF1#+$A)6DjlwpRaARm3>UjRHvZKKux{#-Q8{yuX zk4Wjm6r8pFJYtOrQgT)cF;(^?&8#QI+X5BPA_Z90(%$`i!0m1vy53>LMk<#!4S;Kdh%<#M{nEzjZ8^tCB6&wvB&`P zcEZ_et9nBZK#nCvpO?P*+?5M75p@TOlIybxt{Cn^Y(z#Ub!5+nK7hy@(Sh2ip(3zJE;-( zBI|=Vqo+kH4fBIKHTwJC(E+kh30kqCU7mQ2nNs-zEbzwQjiS7z;0CH&(&jh|7LGCb zltgHuMg-twO=;!2#%E2;=ha$jf?FCzeiPo05*2I6)1L5|*rx~v z*qn5-^RlrfgIn=^{=vUxCj#hk?Xw0Bh~=ycLg-hLmM!$&&R@tUdsW|6>H&kkxn{VK z%lh3a#CeNjdIipYIDeHyD*Q4{62qlM-)GiTe3Dn@zG-k%R%Xbh=@w_mt!GAZY2s3B z=6I`q(jXoWkdq2Lsq-7C?z=YJGFN>}5pc1* zCEWOdn`!Vhf+Tdb@o(0VhnKRH7FVTe_<=6mQ6;bD^V13eeg0t$-KFw|F<25=oq(qD zrA5=^!Hp3kBMzjX%KaAam+hc@K2ThzL`VyXODaZ1$4xTMZ z;kUR=Nt_YQ!AbSYgVihVC+bognGTayMd&Wfhc2(j9!rL)6(lTSkiopLKD4Wk>mL#o9d;Nwoeya< z86@|f5#8*b6P?a(3m6TN^uJ)Ev7=)tc+eEB!i)RasB}^ulAUGNnLIIJ0-A2+U0#wV|L=M~ z9UZ&8;;<9m%C#e>Tl8~WrrUin-C2*3hTr*Uwl&$-HsbvVpy)Y0fYpUe|4TD!r!L&yW7I8Fs@{DBtjqqOg z2bdrQU&*bcpD*3G;PN{hX!mQF$FQsN@>`IOmK-lI$YvBk;BUO{v(Sg}q>^^`N77Om ztgz5%)ciTT-nH)cCh1cBom2s3fdQsIY!B_mw6~YmS(8N+T5vr7iU@NroRzfF8DVN7 z+T-;Ie4{8NQa z=SVvqmo(LyZL&xnp8*Lao-HexChS;ku*!sqhjOYZ4g}|dW-v;an92;ac~RfZwkl8B z9wu7!h^Xz@E%Is(zhTMC&(o{Xt`*7uB!n8Rlf^vClsnTsK2(O@tG*Wmg;{@m^lQl+ z)&Q*(K@>rIO7P(8;dI_?zBq~m6aycBd&jGC7wqe{X`|%C%(O+k8WQu^p7_o&4qdYQpG=K;Iuf61O#F)&d-XtH&Tt=Y782Fr zioiv;H7vuFmN;;)6junrA}9~km7KLB`5jqLhxFN@+LOJoWBhfa)sZ=)3k0An4Fi59 zjMh}J`+J{DYV^@<8aBN#C;)MfNv74-id~Y=s=9s$Jx_^D&hVacI=C1dV!N?{LzvM7 z10HPG32|mt8OzbPY>nh%<%{@aqhqJz)AXPL{avwqBT?^{;}fl$L;{^kY6P?#d*hAQ zdI8iPquOx#4OOklg81p!X{eJfIxs=4!+dAwO6fVK$iLy>pfb^~MDnv+GJg;- z8Tku+c6Rn;wSk;WCi4%2;SfKym<6Q318@ubi6-(E5`>xwuJwTn0-ZD&BtU6&I>G3; zzr7yG4Srh-V86Ka0wM;h7RsmKzk}oseYO{R#|CEW3^fl`%=7L#l(1IuCTo6bPtsnb zgDw>|S%-9U+($t%I*5BF)@*U-AmF3L8$y71aIE-_FgG*xGTz$vjogISQ=jZ#qVHe7!GCf zz0IZOzI-gl%Tli-GQtY6&$`v4pic4SssDq!-}HC16PXN;pCsShS*kVDP4A#}s>_k~ z-<;=cr2!yaoZ-B8FoN?6u^r!Su+xph$o&#oBU)62#3#2)p}hk(EoN%u|m%2994)CF)~gB)?kLQ~#H^x)cZ(V|VldEmi*1 zc))qx!Rt8%Zw?QtqUjg5>hVNyfEIiO6c7FJE*Ant;ZJbBC?Wh`3^U3=CDl3kEm5vz zU)71}&L0e(>ik?9uBSqGM&=Thp2v03bg`t1L^C~|y0|Ua9TCGUl?@xmO z-KEIp1VH4~kg&3h9~~eG<$>?$tnA3cI2-m8zz7Jk(+Gv0n-U(#j#=JyF%DROV`N)H zWNqLJ+=BnCypZ7a7-XJE?oaqC(~8khU@thq+!H^i&fmVpNX{hh;d>r$v#)K!v%?pT zOL$60zTU93JO1E1J9??rHU(&1qI>9uK0}8s$qWhYqKhzrHwm7`q)YMhZN>Rm*fH&bSXDrIPE&R$RUdD%>j>L z3c2WnFGwH{)#fc`J(4~UE%Ti46c5O&9C|Q65QzY6^h8BL)*N z0<`rwTWB~Mck&Coyq(F#5N=qqbflSW4z?dG+FVK6CZ zJWG2=X^D_VOZ4iEMFL6G&9R@-%I#Yo#Ej|oJ{p-`rC+t>)pEbTO6O~dHJg7}r8=s6 z^@l4IaXX;;X6~MifsDs$g>lt(PK(3qweq73n+|gKcR{17?7nf&FYF*cx=H#b#T(l~ z0ljnrL_h(on>sge;cnt;4s30NLinJm7q<(mg-oXp*5U(;%E^Yb5Q2l=KLi;fm()DF z`U$ps7Q*%A?M={$V|ZXduP+F4Zlxv_=yTI`Tb!0MD%Ln5!Af#moIRDzzlphPyw?HZ zgr(!n2x#$X3L>9(+mFw6-_8U`g7&Hp-8?aJX{hw`HY+frdC-6(CJ`|6hW~l#=y~qw zRd0OKAzu+QI{hLWR&7umi1g$R+r>A!dE)_K`0@e zU|IS4HFY>=lc?G<;c+=hS_@Hf`eYv@={kc73k&beCf|OItxWm;ZbDox%HL0u#)>a;P~raCmh@N4fRxqi4QnVi(5lgpEeO-TF(@F?=6R`-$(%!%=ANR= zP5pc;1npjvAA{YF;OqMP3m#BukJsn~$P7b)@VtKjRd`m<)Acv_5G|9;(S%7I4rn_5 z(4KYb4ltUUmp9lNJazuq_45_v_qyi?=fV;(gJEA){T#uNT#mpUg*KHnGZfF}#z=g* z0-&8BY^hvRY}Y?2tddZG!g)M-CBS`@D-;Tc!R10d6oVhL?eku>TE5F~1HAsC(ZyD6 z^3$v^(tBgAzn^sFcbQ}T5K*oxM;r;Xn`q@HsX9M|dQSpGdL6>Opml8BH~2@Y`YJ5$ zTg>sTmnqjkF}9_Sqxqv}sJoyKJr-8fsFx2$1XqbA5wN!qEIE`-K9Q=?{^@C0ims2k zgTU+KvaTV((ie{vuNKa1xkSCPcJ}#P^}9coD=Tn_fb$T8boARd_(yq4t4H?jolmRa@OQY9&~a_NR>< z3zua@>*E2iwy($y4GjrxnIg>%c0+_qKf7P{A`ldU?~kX+fgI)|_1igL30F$k_U67Q z>9BlRiGko#SRqIdf5Ih;@&88{e_SBnUxQttv@)Lv)#y||{Y^ECJPT0*?s{X;ZwXSp zlxo!NqEp-4P;D<>2Ld>XXgYfk9N&vT92qLG10Y3=awXU07(Oo{Bj&gy2O~$qw&9(+ z+A`JQ)up46vQnZ3gD#d#f;Hd!7bq6VwAp>9N@rzFWfxK}{Jqea*o?yY$RAarL=^{) zr_P0ps&=E~5#rvpveo7*ra4+S*$am|Kd|1zkxtRB(dn!;pC{95u>Hu;80t1kL;z`n2B*(-|2Ksep@Nj~r6+<_1 zSmNN|P=_)6bJ75a0B#Ugd}{3DwC|Uq1!hDMe+b;>x|ATbGh2mZBv(oQ%wbdZgV6~p zW7TVg;}sK(H^-laKMNR_)gl+F_Xu;|m}KTD@dr6qxdc@@j_^Z1?x>H=s6K|$s7+R@ z2C^mUXP0$9gnAiJ3Dum z+&NcT=a8zx*gXw34a3KtndA81{dSKS64c4GP>+7DN}#lR=KeE|)Gw-!l%Xp*K8jIy z94q4#qtc;Ih32u7Mw=SzHE)}J z+-)*S-LQIJ7ymK>Iy^l+{YyQ{~ zwRPBGu}CSNT?@d8ZUAB!7Z;~r`_>GN11EdD1ST1ST#gR}9*o`AoTb%0G8B#4g@CBJ zrZ283yP`N9{{D9v@o*co${Nbv;N1tr<_q_DDobc01@7heX(W7ZM3QzNlCdqY+Gww| z>4&BjKL4>ZI%20i%oop&x>cJU)~)O#e;x7WDju6;=Gw*hlnjz4gZ{3Q`mnypjhe$3Phv&07sVvZ2NxW8c5p# z^3TnJqU`S5<9U=+-5u+$RDo}3>q3Rz8=AQBFO4mo?I$KP?Y4qZN<5sSs2RY(HCFK9 zxnD}|vng`SSI&&t8YqWGI}3`|^aaCaRuZoKG2%jTuMZS|v3XFZZ9dTKYCe3OB%+5< z%X@p9@p~4^m~MSWwWLTrkvd+Gc&EP*co{(hx*`K~{T|yOs|)21<=4=(2E%2$Q>Gv~ zp#fdNF4O*QZ?|zb`J`UyEqu3E#X?E0A4rj0=#wIB$donC#kcJ)U$iaBTD&v5>Ns1L ztsqv1j8Z44GY&u}rS1SPM4f&=7{Dm2bO*WF?d!YY^L{zUev>N@=(HiC28Rse*X4$C~+vJ1Rw1x0B9NhX#IL{S<0}rg&_b;xa`Iq4v{Pmu)G`O%=6I`9F zb*M`xc>Wg`4lZvN#O1mWwuVu}pPGij3YW7;)ol&3&xY!iS|s>pM?b?OdmM2D@3&R< zw@U#Hp>4>R%56Vb+bv06*1g)>lzXz8n*-jBGZqdfrfEAhQ~U;bB1SePARwUl+o-6h z!I3y(QgR%jtj_F~|IGpjg&TzD`{0)`|NUBsn1{?^hrndTfsx`DQGo9ex@EW_g_YX& z&kgc@K0G_GOgLE*`x?VPvjm-by$E)nEHkvWB}C|S(|giXaMn zysS7OIXUPA!d_AiVs(P=Nz@&wERyfIJvu6W=Acun2h{cb<=OGP<*9J~ylR@0gyRfx zSoj8(CKc_Ed)?pFa9tp0>_yJ`NRz@cm@tltF?Xwfr(_~*`Vs|v6t-*o0dg5HNMT-8 z^Up_OeCp($HpN#Y+?8O-vetKON`K(Sk0*P^8W~{V5??M#W z$BA$-|H_2=-9!+DhLcJx7`FIrb){B6mD`;W4u@UNbgrt?ld$75k#pZZ=nc{Aay=#)Zd1K`1ZO-S;)J=Qet@s; z$}T_&F(qU8X7T8{?){m#B~cMLi4|j?(`*OP6Tz44nDF{S;6ui+i(}qQuk)|#+QAQv zcXoYh4ym0PM#z`ebwe%oX(QHy`RAvnAVb&)aAQ z_UY|M6Ac!tLBmwDhd79c{(znqT+5|O{~72nfLAs)qr&r-?zYQw{cROgo&UR_y_wuF zxOh&soklD^56 z1YPs^^OIzycM)M}jO6=|*2~M+(0Cn{hNQ)&Llv>-d1W#p z1u69KY zfC16Sz`&p|-mKYd6EA?)2gBp>5DP^N<9zKhuP?-uRYUS&vIeYSW|Qyft{3_qmUfk6 z3|)nWt_cpbsXfU_wB;cIofHeoj9j3p)8$SqxFcAl3%4Y@aytt330g-$f2i$uJ(UK+3fHC`Te%sO@L!(wbh}nAdJ+AeD+(g zHqY|>mifC6ewdgoF0|6b?!soM)c9|H1N^Hl+&#X*P?43o2vab1ZsgKM674VxkR~DY z%K)-$DgpV6+dDZi%j9-PnlF*Hb-FbB zb4dFCaP^h}RdvrBs2oAMJ0+yMJEglrLZrJvBoETv-Hmj2NvD(u(%s$7-RSH8cklhc zC-&KU#l$nunwizy6vVMAuhi&(9%}tx*l$FOHLPujV(DH%L1WLF-rHQZdrLFGxbc;j zr*eY*Jn+NPAss>mQ>OvN#pCsIq?iJuki)8Qk8v97IUY`ftANen;Plp`UG+7Sjd)tK z7TP#(pLQZ$R*{^P%^L$!TjF>EizIEu*`d>o8zj5>ZT09T{?vlF?eK^c%{LIt zzoi>hu=_yyTo%?XQEk@EP6c-I8ib`;eThHmV`)8LZF^%o7$l~cETN3HO$TZHC9feSE=T6Uw45oP{32= zp@#6!=THVq=ZVE({X%e0DN!Pj=-SzB61%zdaHu=?(8q=R5KFMRzCIVN;$z|zZh~7x zR}ja+!I8x6SWpWqIR4h_0wT?$#%?A}pi(XAo>BUdDU^ML5G0YqYK}!~rLEWC_DEGX zqS=gC@eW=SF0X)2UzkJ+4|9t+Tp*OL=-%Z`T|)JlDwn;SEYrF^HKd2mVdc#ac{ZYg z`w|Q4%N2(`N+yFIm>{{ve4VPZ_Zw!UOs7j3l{F0`*!1{Kg!IUGA@bjI&C!V6)ux^| zw2$IHc#rzgWhqj{7(r}b#bJcdVaBKN?eg(ZAl!;s=!6-5w~=v;=(@fVJU&2BFx@{4 zsP)^{FJ^H$TEMkbuZXP$YgF9gtQTIAR=9eTHH@?Lfe!}<>dAcPNlrLssng{A!_TXP z00IIatuWxkfINiwC59ks;PgvPA{;mJRGq$}ESw;e!lfr9UE}mKAO3Q&)rj@7|AMf9 zg80l>Zh>NbQ+(X8p&7`_%gc5BvLVl?ZmuOd%@cQmx*rH0iSWw4G#1iV)YyShSAHk6 zIYMV+HhCC*@^xT&(87mE54gtC>Kx`C(3mI9S{8E3Pgbpt(aGDyB2&2FjeUPH5~pg! z-O}CN`=Cu>kUWtSOn8EdyBy^Afk1giJiG!o9{h7MeG>QkQmfTKQ5HS1{pvY)XKqRD zw=Ou8OnV~OT5=OT8xBTI5y-BYw%1=r6P4y1FOVCRjBnZVs}cdg*DKqm z3@R_6t6cs*@#=o@$<9D_XY-;$wGVvpc3kGy0oP$>gRUEfchLCwUL8KU(Z6=vO732> zJm(7Sse8FNP~J2qujbgD_s`@=Q<)&#yE`RtDA|V>Kx#q34h)7$$a}HGK@ASj4vBtJ z!V3A>?Cfg^s;RTj8B{E{vp*>q1)7k5oLeNv$&eSO(^EvpddnFMmhTSbF@ngJEmGsC zlp<$Y9pzx#qNULJ!bMxTZxsXUxK(O_wzG2lRV*m`Ai;lrFo z-!HSDRO;9wx}+Sd;u1pRc~S=&V0$NSzXXRx_`A5HtJjx21>34aj)^Eq2H1Drc7Kd%xs#fg<+4Ygo4H9EaPaSLkLW zkg_E3X1s=d2(hae)!;1x9;l|(QTGNIza+nMjr!gyfyhuo~mYon3n~u%lS)HwbO05ZDj+yBdK}CxOo{5)>%sKO)i$T4s2sg6)I{r%JB=2!&qK z-!<>1Pgi&M8#X{q(4w3G7~7jaYfb?_yQma#hMBb2eP3D8D#SskUAjsyv9f&x*6N-! zpea-1cOxeZBvZC0W3PCqR`Y<1ROxQHS-UFtDkU|-REVPmrV9QB3z#TivBbCU4M=v6 zmq-^962k`k5?F?`?+qPlYrYo|c}-F@XGcb%3ZTYET<8ot^?XuTgUZ4G0yC*EOJH2#WG}J+cqe{osTXq7j z@T;|Q4|L7Z2!i$tmB{`$;sb~iuwW_jLJh0dZp~?5kw7F~3@G#buYIG_HNs@PENE0^6L*OeL9hxjbCs)YhsIJR`RIO^gqwJ=dRECUurUYGR$JQ_$~) zDW04b{Q5Tg-#@_4C@rpt=IlVxSPrvEK+vWOx`7euOQD0;UKI4kz|S z!CUBvqnvhFtpqB*;@=JrL{o6J=x=ltGK~)zL4?C*Oqskq%H)$EB~-Xtri(QW@!e6N zbFNUAn36MkF!f_s%p-M>)G%51a6*HzS9s&Em!}~p6hDG^5N&m_-3l zp(3P{3;PV9E%7ESl)8;U58z)gF3`XM6j^-l#5ZGU5Z<1!9narckF&gZY(O1c@~alo z9tBtLAEd9p`lnME>^5>J=F@7wVJ|2DyZB5#;%I*rJB(zW$8M7+Om!<;u`HmhjCFxP zRWeM*uQ=L&W&ZCV071fdSx@U~Fi6(qCn6PQJqiP2-5lM!5?2mKnOorM-{cn!lFj;K z3wfq~ij=KgS*Is)f&P6B4-l8iHz)!||81uXAmQ3aHQOnFt&9*WN~N?`CqQ{t<0^iz zO#n(W9GaFkXMbtill=-y#v@{2H7*FC)StyrWAoTsUpJf;z052490r{CcNUvt7Q7>o zGlV{V#mJ@JmSy^!;eIsX=&!&T(AgV`Jpp*izJ;Od9gP0-QMvC3%N84$0*ZmkMIt4wVYsv&$9?`&Zoad3)olQkAvFAcoTAba$sUTVx7Z?44 z@K+kc^dq%cT;Z)fviPv&3G+$j^mce(q{L~RWl;h52ShDm-+=^m!>L3gCo#stU-)`F zDlB4{T*!arNFS8&UTAD33lRg4{H4JrJqr?)|E(&Sgr95tXA0BxlR|Af9xGBJ!sURC z5ICX9R)T-Kz~sXN1C*@C(CL4B_x1lpVNouR2*Iv-Kf!r#T=z^S9t&x6C+n0UaJV>c z^d$b>nHR12maED52GEJK#_pRv0|vNOX}gDW#*@onRf|)KPxl!eN?Dxb?f+RrK%!tk zZW1Q`bSur>urYKiv~9X5DirW$>pQS-p%rE=*X-{qL}oECqF13}41XV-X@vYWfJs8S ze?Wi{L`yBM?`L&6vbUpb6(J^IW6x`rjAgR^oEggR{2WapH4Wuqw>z)%aTsK) zV~*7Eb9u+geo&)d5U@$DY*;Yz^8^whGv`}-VLfni5x~e_IPkx5yvuTTz!U@W>BbAV z@L|6}jnBmuQ0M+0fn)sW%AJrto{X9KS6g1(%3ryXL6cvru~ksZ-R5^hB|@|>c8Ng# zpK$E`_mpwDO4#QMRc&` zvmn!9(0yveuXSSnf18_?2G;AQ1#+^ELV<9y8>AHF)WJC#XtNA}wgYf)sH9bw*LKRFlg|+em!rORtHn@W#Wu_9L zGfn?*QHK7Dld#BjBudLb91gg(zi}m%QdLcT-V*&=ics>b`IBF{JmhT2A3x@;4Ab- zf3Ti2DXI!UJ1u0?x>8fU?lTCEdh=k0D#9)_x4S<;?E4Rd0Xi|RAoMvjaSdLp(>W0R zomQuYTMKc4EY|oEBMig_2@7{E=0Sz{T1Yyl&H2K#G^kDI!3ZuhHS6@2uDy* z8($Wnzx$9qo|m^b7F_Xf&-Fpg+O2iNpkkENLtl3R&A^tLY9$1B*QViUEc8IxH$(r1 z0sB)Ln1@DkkYxVT{YEjtI~^5~PlaHF-;xR%`;j9W|I>arP&1-!@8+CKts*A&43_YA zT`XlusqiGOuL6gkkg_t`PYbV8bC{auHI}Jn81@GqR%@=B<|F+AXH<(D=kS1poh0Jf zqxpt0$lt0aB(bL{xzc!Y%kT&YR&JzeBJ7}%T0A6Q0wxt-{n7*&*0O-yL1=)|rJ|$r zOHV&#hqf{uNt)OI&2akAYu9H1O-nArf1LgsU6LO-G7?!d(&~7rEj;7xFbI#3+4P^I z$%+9lF~2n3kO4{tQ?xxjH2%~KcEzWgsZMV4|65@8|FrCHW#7*W4)Z^ieS(2_+$SXe zw2VSg_H@WBXJ$bfPExkB9(Yi(AA2CxNYUhUTz^FT zlkR-fB5pm?D{aqs=PKZT%y^srK0k;P*4AJ1Q-9owu3#B_rb<113w(udhlOLkp@uDOaH^*FIdd^ z&vS`Twb@mR9`CS*qlh?Oecv-E+8YTV`!{uc(AU2ZJPWV4T=#U+6klJ4MvQr4P&VfO zw62H}uoT$NgP>wZJF*xF=l#K?+&@eLFzzB^#r@K)5KSQA=ig4__<;wFArO@<R<63CiM6U zeAf`2%W*5Ls^;ZI;d*30z_3XWKJ~9Dl&Gb{i#-Oh4xYCq!%8X6al_L$#S;zMnM&zZ zKgMJ76~j!EOO9lT=s;48i@;RU>BD~+*O8-rH*O0`P^rtvmh-Kfbvm;SBiK{XnsDrp zndyP$*6Taa6R>l$Arly+D;NxITm66=dkxH`k_F_}4^*_S{O{~uL^wQT*df;L_NO@k zzbBH$*jfTc8eB;YYf{JYQ>D z$7J&U^faTv*7`ZK?a@3}l0@*f=xYDx%zoOKIpaGeu=J71Ism}l}9;=-BSLvI*R3T?`221k6p97BFx&(n5vtg=x3>lxEThes;B?qleASYD6Hz3}%G5s|G zYvj75$hXkq16n1g3Q802Gr>}w8W;$pu=Vvgr-;{pj$lH56<_)IS~{|JHsbl9L(9$1 z#Wr~&zq=))=i=2WVuYR{e;)E~BpX567nDeKoJBjjI}=W?<%{DG$$jxBd~b^froF+y ze8(J_`)AVLXAiyz4=od?UCDMA!GSRXyGu>6_LRtDk%sdQ4V=a7<6|LF(51tnwy!WL z1Jvzf3Dx7I=hHC2VywQHw)kprbR!mS;8&4}Eoaf7#N4==&<=c0lePZH_O7Ev(`Zrp zB|ZLmoO0DPH1tY1PzKKBT31L4!N-n|d+lwF#$vrqK9OOGAL=nFiw}&R?#Nx@2#4Em zkZ4Voe4-uo-ggyGGFW{w>)^?R>QIWM4cnBsC)@pa&e9%KE-UF9X~*^r(p7 zZD*mrg6qj~O1yN=?bYDZ`_l==j#Vp>HzBGeOsZpyqP^iS8ZYDvyYtil05zFyH5!FS z@aU-nRrj%o_x0pOy2O|eRU7QKE;smj-ae{1yWc>s#X^MxUk9b#2Ue2|qJg=_-dn5J zNYJMLX%>F=RM^~7)k&~}Px%?%^3w-ps2HNROWMqR^LyWL%)d?N{b=SHB3K~Q=^bN2OGa(H1Dhw3*jLJEC)NS*uD z;QJQII@HBcl{r5Rr)!H$Pnh-Iq>3eCw*={Kpt?_~Jw6jX^EflC0O-$k-9={)^T73% zzp$O;(BMhhN|Q}wZdu?9SAi`xf0-7CaLR~(RxlUFmshlz8Om8pw^myY zXehrV0#?r#1xPLj49=XV5{&x|q6a@OK{BUGK5i^WFEbZja_CORI&-s(_sEBHh@;c# zu1#sk94-3q#K`{WRTQB59l>YjbWAskExVsf3aYyab@QC4;}&&PIrQ6;N)He7%nyW^ zk#S$r^J`xpC%MWHmG#MWEq+F5-F#SsZOe1q8-dhev=gp~R#@Q*Ki1=}B*b6#M6GFw zUn14RaM8hZeE;HE^d$jLc}!moPVMMFT8>;%mWO5IIGV)kj&g*qx*w8T;B&;q{YjSZ zf*@SvOA^{=&J3|nOnfYeD`TlD7SZdPWgGunmAyQ)yOY;MJ*|z;l3L)I7gY?l|3rS~ zdlH`;N^>1@7w^nav+sOlPfAqC&g75vw)w8y?G+13v3AGiM#=4RMnsXqllDgIJxGKF zSG=S5$6dZ-{Qh6aWVClkm7_!&c&{yj&maKZMkIZ~tZ`5e|2w-kI}kZJiCsqp@6XnF zK_!&U1wC@7+aHInuc%IDwQ{qdo8dxQ%^Pmb=cDHg(U#u2Qrzu59n(gFYM-Ga__XG7 zrdMeBX4^0qzP`~I5$$UobA%1kq-&#Zr3>>2gG< zHrr`Hqe{AU~UE32@S$f5g+5^L-e^QLj z_1hZuj!{#K{wyz%bNz9~MD2Wg!K-UgC>dXRbWLi95%&sZ;dGB_rL24Mkx_O}H(7pM z6+`C^YWSlo1mmMe)fwL*qFyUjX6r2uj~eeVG!QA`0Pp}B?^0&=4ID4N145R6v8I^! zKGK0uu8lYWp#7v-)FYzG;t&{cP%C?|AEDEaN1AQQW||C)DXQV86({%*{S#NGDl|yA z(9q4qK?P1(6MoN2Xe^Bj4GUq*=s%jFP@{NVbJS=Ra;InIaU5Mxtl8Srn9z%)Um~K( z70JQ5T1D(3?h3>a5Z%Bx{eN($HZKlIc&0C^E{3a8aM>&HeF`ZLNMI@?k5 z)fdZhguS@bMuOBJ5$(^lhqJg|YK-dQ>;DHSWwYP_a04`xDZjaJr~c4Hw{k^O4IWa_ zJ{3H(AWz_ZRSWk1mzyr5re2&k*77fYP7=Zd@g$xA#2nTCKbt%TYNTh3S3WCCx~mni z?th3eFzkY~kZEXxG8R+%(Rkb-@LS+-9GAF72H%V<7v2z%ijMCt`Tyxph9VM65q*|Alr;KFTkV z04^cG^svMbr#9O0!IJkF9?xOk9A8V&XjXG6LBTEkdv{hWc!{Gp3QFfQmv9V}M}7mm z0YJQFk4m84TIjXgkg`aYObzoRF7w^k9=IF^Finh($g8#g5^iTZcqNX-dRP+B5AB-&kSRM-3NEr^ZLHVNn+&1x5bbnH>@U z(C-E7eE9%hhjO&>FDwDWvIPNR#sPyZVFTmCho(N3FL(DYb6G5Jx%8KrlE{81`vvva zvTy{KdX~Gm?uj37Y$D(qdFHx;{g(2H;scBZ?Eh?u+&dtemxCN=z|RC%3;Ew%c=VWJ zkVX>}=WSq+$Z7dqEEr&v_(#_CK~;4wEcPNj*<79Ej`mMO2rnVQTseHLXyG?9Nb-Hb zE3%!PfF_K03UN0Tpi*TQ?1lg2(@_}euG`Sjky9q69pieB)MaEEbBd)cDsl-6eO2$^ zcgLOEmI>blN&}9W@YBBe{|%A#VIeP`KEw}$wl<^bvNT?G@x1w7SNmD9IFXN*XuG^u zsv2C@;vx?2LMt9NwxaThI}p)nO%4yO`W+NBChSvuS~eT7RmSLrT9@a~%)mL2pxuPd(Z&s39qnx{6K}eILAW4^QBiDm7$mm4daP>R zM4wLgJO+Q}BrK-8W&GagIHBX@SkBk$o*+XU+#IGLZoKyM5@8|F#>f$QO^uj`{^Oh% zcPfmjJN^aSspzvy#?I0lAqSh+f3sqMyui^@2-9tQbDnmXMUEZq%~ykx>W~|yEEF(9 zf-lj*FPQ_AemW3<&BXNynaJyq*Xm)DPauuRhT(F}fh`K01l$I~0-QQc<{fRkS+Y@| zI1iCN*^Xy3cdTmY?Yz>%`PBBc+P zb2_zZ!y}@CacbqW_35A2!|EhF{n}j4f%dJ zdl64(`fT-tk-dkD4@}L0f_&dJ0{_Za9FPd8chLwf(;_RZKJsz)bX0*0s6(~;$n^HL zwTw2L1D+;rx~14YOD`W*Sy+N=QuR)n#(UVHx|$r`+rlYW$&nnNdn`Orjf)>0XjR)* zXg0@tp}}phS9dLzMa=E$0F7W`hiI#jue}vhoS*H?ou}gCDPWJMeSMnT6jf&vsL0Yh z6tu%ao&(A=4mN=PzC7`>cl%*aMhIs?qn$fa!MkNCC)`F&uPeE~J(SvYb^mMA5soT=NXb_vAY!E(cyvYR>2Q;G*y15jC6#gXzs~W{<>_t)dff-{M@zD z<9n0d=iyioE~LneYh-hoGTlJKq7+OoVKdo0&sRf}$c-tEv(kngW6wm28S7Tpb+ZJa zIR4=i(jPBihyrk->|$I`!yG+ow}+?r_wTmq>nSTl|7HlfI#XYHTrtZ7T|nLSP~QA3k+5Bpui2 zMV8q-ZCw-NF88;3;HO6qQ0?uLz`dwIPk~T(qiOgw?V>I>7KH7A zOcv;MR4!*SbW}E_!)T@K9jPNK?iW~^{$dn)GE2T&IH&*gQGaxkQ$AtV%t?#V1Bqz6 z-ZDlf%?ZM&+|?PFi?rq4fY2*-fKySkrm-J8&?d^QEfBAZ{&#K)poi-;>9jrej@ z&dfaBanc5`+5S++LhoTzwU|^NIY{7LFW0!!xV`YUN!Ojr$LI$~g=9An^J3m3A{CG* zidf2X5(f1ZbTeBy$@%J-GME}xpZxmO5v>ogC&xqENeM{583)KsZ^kkaDvq3}`;764 z>Re|tU@#QBGm-xY_mN~Pw)>ggJ;#ojp?~L#{TpX=$svZW)a8#|Ij4l<@f*ZnG_v0e7u5JXBz79|y4PMo`2d>qZ; zgYy{9W&CM=^!>_mN^xR|%#K^KT)e!7yOa4?i}E%E=#`=`N&+zO*6Bqn?0VLkcuw@b z1&P?!Ytc5o#2gc&B4)y(Xf@LpPxr^`KuxY@iwix06i*yFU>EvO$9bkp6|fXB7^xXW z)w2o2$k~?Z?1gN^DbDyjSN7HFb}V)9xjyrQx}q(Z$%zS&o+Z9wW-vtT{G6O|<5e!%l!zesuA;#I zaukBoM%rtG&QM-9(+GM(D<9xB=*wh)~IDTRa`H z(YGMOQTJHE^F9_rj-Ws??vk56>}I1204S!CJ{QBi(!}zk1fHi?o*n}G#g*7FRua{XdC`J&BDeCQh)c&AG`PTD7 z3G&Sw5my#R1JIQa5ZN5AP=|uyGY?xqsE3crZwSAi*13A3}yPOYo7MOmN zH7za14bvOx+n{7{GOU?%Ihr#dnt4CzFfgfCSKfx~aI8kDBMt%CUeXRUETKayJA08Q zNJ=o1Z6B<*7&L89Ne8-4{dD#ZUo|WMW%&-62A?Tc;-TldeSX@|F#~PS6$sxrJU>0c zAR*QFP&lL}fz{B*KfN@Q!&=+%PBhF!moieKl2V3zUH`^#Crx5vajIC0&0^jEpdkPKswEv2odk?b~5o@ubw6MVBcy))7ePKDGcjo{=d+OtGIIn>bMoN?0hxDd#E)`d{zXUdL6RurmA<=!PHrd84>q@J}i0BNv> zREF}U2fDypE8`fvF*(~91ny!ezY z8a#uv_Go)edR;D2b@w<3Bd;I=!KkrxCKD?p&wkGk1nVX*n-wDzoI@Zu5m{9r4J6U` z7s#bn+OLZ@HUb^KRQRFGXWC)R(k*^b!c$Qz^aUJWy*8?DBPd_*pT<^_7A8hneEmq) zb{Luw-S)V;;!JkDa&RTIvXW1SaESI53qvQDc`AaCw^IK*=;3xd^W^;8a-_J4fkJ-< zvKC*DzZ%TVjWpi)l?wTwivdxfpTo+PovyYuLvEyd6W{-Xz^aVwtJqi zA8Frf&8J@auZT)WaDHghc}YA3t9QSgoGg?PZbdyYSg)3)hD@R+4rMeuXREfO|DZLv zhoPj%3hn%z=b`gX;o4<&E0bMXPEO68iBY>A0qA-#J^ilz2VkL+Va)#2RFiK)H4^o| z`IWS7zb1`jd4TtNME=}HAj86?TU%2zD4I;d;71zwo)E4}NJ`5nWJ=ttvY&gBbnr#5 z!%h<$9^*|O5RJ&of6z!muqNHEAp?0U#jiZXaKynjG+mxoY4pDHC{oCcXPHUu;tY>3g!>eA3Fhmq-;0X+ExFykr_jgnnWCMr)#Gy6 zq|R2RwmVGKAK9(NnF*umIT@+Hh*b()Ed!{(FZ;){j%Jkg6(7SR1%C&J(X6{0K1-MW zZj2YLzFxU5+drL-8MDtDumfL3sR$3fUqhy%q|m4HVG)Ch=mC1)&&%Bc?d6$GMsVt~ zJPl z+Uw+uvjn6<&CZTh9!%vY?d5-!`NS7)MeB~oy!o20u(A9tPzT;{gemgdzg9PZ-uCeL zd({mAM*OL)kTr#e4VRB6HXes|OlD@}YImUF)5Gl^e@np32zN5r2eNtOdv9GCQ%C+= zok6p^u#e!Is4KH0&5)0QSDnK(V%s+$51jB$i~+aEF&a-xGOHqB$}g_4*wvOb+ls*mj)1wHoB8 z497|V?)>owT6boNBn0;?uz_NOSH;7mEA=H%p5;tJr+K30=Zxw~Q1%nF_bS9eYj*yQ4=wPS_!dac_0 z=oq9IL!(%Fs@I48{D|75rZNV#4pZ3x_bs2V3w^9CD)0HJz*;md35Xd83U;@u_`Aay zL`rw>?(T{Si^JNJZS_HQFbD-WYt72P>$P^SF6emz>g68_bM2}8y2+jKOxRQhgz7d~)?V|DUD&n#55l?>N6 zi#D^HyDhGuhE@eui=NR`x2!s1p9hiAk52!dd7$MAo5Q;Usvn!dZ>*GZA{L=kXAXj~ zO{~L#rNZzUdD-x}ui+%a2&Nf$-HdpKLyaPTm`fdrG^b!&5T}l^Ig!MHwlQaAY`OsR|t(z)5LBKb(Jj zU-?{C))*HJU%9F#xMv89_&Ka<3jUnas`<-k3i}PU}seyXKtiEp>~<44TV@Hl^Dytg-V6uo(6 zikznUd2!KKq6H)hYyG;(s;|;p{f>X#WSPPzwsy-nXchB2GjJbM3DItV-%bWm>!`_z zwJeYsK-u6Hpc*?ZHQa7*FIVz=Vj<7V*)DbxuJVwg4DuZi-P9QQQ_a>u31Vx|pP;2q z){~uHjbkYqr852~t>gO&KX|LCe!^E%$HypRyS>|^6LwH8Xw|tnoXGsC3$j$V1&ff! zsq%v11;#9*k6XN;xyfn{H%46_cVjUsH5%%NG|JXR3`rSz->xHjkZF093Z^%rti+16 zTecfk@1mHi(^6V>b@fZ-&`W7A4x8EdDDKS*g@Gjj0CWJ#Nv4)Z;b*?WrMKZ72Q^B^ z2RcEyvsz{xd{ItX?SYgP6XM#{rAXf|kFK50(W9ZI?FJmrDbSdGJz9e3+ckBEF+hGQ z!|u4a|D=U4`WkZuH?-+MHM^rOIKM#3%>qAEynGIe&IIKXI>^bmTBJ$?^lFo9d#n5h zbOYJ9KsrDcY-T)novPfdEW^+cOi%u@mFxCsYT(bGss~GL+Jxd`L(M&-#vrVh%vAcT z2{T*Gg>aF>wfwguq9sH|PLg zO_u;x-=>-P0AxzXrTFKNk`p|jgFa|&iu`h|xKfKT@!ayoLkAC9pU-mcZ*AKa0knwK+kg2XS>-YIbL-zx$wWV%d zgbLxyT^9P>%Jt=aOKat>?n_KJwtVl&^k4~~%z5kEwOBY>f1SqLHmmb+OQA^iO^1e+ z#;(ipir=r-`GbcUR7B)6Mj6yHM&)MU%Yh&bCawz=-k#<& zLzf!ctjzVRSG2rRqQa`uRE&>bSK8Z7mlpF~>4zk3-A&hLs8zg)0%vp-4B890)h|9Y z2?}fb0;k4bpM|84EzT3}Im`^hntm{J~Xd9=&*G8(shV#11!5^Q<=fRc&6 z)p4o8lf4Eqdxy0&$N_eUHy|s#0s|%PfmoaTH=jh&uri^tSj>fn)R1=6a>^}Jx5 z&V4P#;N%~~I$Wi9A|ZCk^C*AU{DzV!t!_Q8|E;~Te<lwM3ic(;qmPg(7z+AjZw9Q z0i;m=es3+CN}Cy^^rIqMwhozrx@CgXfaZv1A*fp5{mJ#5YSEiFt84q$2D;R^Xepq)(sakR zYgCy`8|%<^yk=(0HTov?$AA_gy#4xFkWfKhV?#r%gW=38EfCw=xsw%M{5vJ};+nU4 z(!MZ86cIV)JlV5)nhz<;;aL_oR7VO1Y`q>`%5c^JXA+G?N8+ z@w@sW5E>EO-6-8XpZ`Ao184}6qvUqmhoUzS6lWsiw0cSb-U zbU?O#H|xo7K{zQ0qd-YqWL$6k-%ltW!nxn&xC5CvvKZ?lR6Yej@#BD z`$QaZ6WtVw-8Y0iVm{^-@0X^`gA#BAvtUNUuLG

ydII41%St3CsjZE;O!KDwTXr+@|PV*Wk&p0 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